Skip to content

Commit a149397

Browse files
authored
Merge pull request #287 from Jinvic/dev
嵌入B站视频支持av/bv号
2 parents 92c9b05 + da9ead7 commit a149397

8 files changed

+214
-102
lines changed

backend/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ func main() {
8989
}
9090

9191
migrateTo3(tx, myLogger)
92+
migrateIframeVideoUrl(tx, myLogger)
9293

9394
e.HideBanner = true
9495
err = e.Start(fmt.Sprintf(":%d", cfg.Port))

backend/migrate.go

+87-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7+
"regexp"
8+
"strings"
9+
710
"github.com/kingwrcy/moments/db"
811
"github.com/kingwrcy/moments/handler"
912
"github.com/kingwrcy/moments/vo"
1013
"github.com/rs/zerolog"
1114
"github.com/tidwall/gjson"
1215
"gorm.io/gorm"
13-
"strings"
1416
)
1517

1618
func migrateTo3(tx *gorm.DB, log zerolog.Logger) {
@@ -173,3 +175,87 @@ WHERE
173175
((createdAt NOT LIKE '%-%' AND length(createdAt) = 13) OR
174176
(updatedAt NOT LIKE '%-%' AND length(updatedAt) = 13))`)
175177
}
178+
179+
func migrateIframeVideoUrl(tx *gorm.DB, log zerolog.Logger) {
180+
var memos []db.Memo
181+
tx.Find(&memos)
182+
183+
bilibiliUrlReg := regexp.MustCompile(`src=['"](?:https?:)?(?:\/)*([^'"]+)['"]`)
184+
youtubeUrlRegList := []*regexp.Regexp{
185+
regexp.MustCompile(`v=([^&#]+)`),
186+
regexp.MustCompile(`youtu\.be\/([^\/\?]+)`),
187+
}
188+
189+
for _, memo := range memos {
190+
var ext vo.MemoExt
191+
err := json.Unmarshal([]byte(memo.Ext), &ext)
192+
if err != nil {
193+
log.Warn().Msgf("memo id: %d 的 ext 不是标准的 json 格式 => %s", memo.Id, memo.Ext)
194+
continue
195+
}
196+
197+
// 测试数据开始
198+
// if ext.Video.Value != "" {
199+
// ext.Video.Type = "bilibili"
200+
// ext.Video.Value = `<iframe src="//player.bilibili.com/player.html?isOutside=true&aid=123&bvid=FDA1FAD&cid=123&p=1" scrolling></iframe>`
201+
// ext.Video.Value = `//player.bilibili.com/player.html?isOutside=true&aid=123&bvid=FDA1FAD&cid=123&p=1`
202+
// ext.Video.Value = `https://player.bilibili.com/player.html?isOutside=true&aid=123&bvid=FDA1FAD&cid=123&p=1`
203+
// }
204+
205+
// if ext.Video.Value != "" {
206+
// ext.Video.Type = "youtube"
207+
// ext.Video.Value = "https://www.youtube.com/watch?v=hacdT_G2Ara&q=123"
208+
// ext.Video.Value = "https://youtu.be/hacdT_G2Ara?si=aa_a_a_aaa"
209+
// ext.Video.Value = "https://youtu.be/hacdT_G2Ara"
210+
// ext.Video.Value = "//www.youtube.com/embed/hacdT_G2Ara"
211+
// ext.Video.Value = "https://www.youtube.com/embed/hacdT_G2Ara"
212+
// }
213+
// 测试数据结束
214+
215+
if ext.Video.Value == "" ||
216+
strings.HasPrefix(ext.Video.Value, "https://player.bilibili.com/player.html") ||
217+
strings.HasPrefix(ext.Video.Value, "https://www.youtube.com/embed") {
218+
continue
219+
}
220+
221+
log.Info().Msgf("开始迁移 memo id: %d 的 %s url: %s", memo.Id, ext.Video.Type, ext.Video.Value)
222+
223+
if strings.HasPrefix(ext.Video.Value, "//") {
224+
ext.Video.Value = fmt.Sprintf("https:%s", ext.Video.Value)
225+
} else if strings.HasPrefix(ext.Video.Value, "http://") {
226+
ext.Video.Value = strings.Replace(ext.Video.Value, "http://", "https://", 1)
227+
} else if ext.Video.Type == "bilibili" {
228+
matchResult := bilibiliUrlReg.FindStringSubmatch(ext.Video.Value)
229+
if matchResult == nil {
230+
continue
231+
}
232+
233+
ext.Video.Value = fmt.Sprintf(`https://%s`, matchResult[1])
234+
} else if ext.Video.Type == "youtube" {
235+
for _, youtubeUrlReg := range youtubeUrlRegList {
236+
matchResult := youtubeUrlReg.FindStringSubmatch(ext.Video.Value)
237+
if matchResult == nil {
238+
continue
239+
}
240+
241+
ext.Video.Value = fmt.Sprintf(
242+
`https://www.youtube.com/embed/%s`,
243+
matchResult[1],
244+
)
245+
break
246+
}
247+
} else {
248+
log.Info().Msgf("视频地址无需迁移")
249+
continue
250+
}
251+
252+
log.Info().Msgf("迁移后的 url: %s", ext.Video.Value)
253+
extContent, _ := json.Marshal(ext)
254+
memo.Ext = string(extContent)
255+
if err = tx.Save(&memo).Error; err == nil {
256+
log.Info().Msgf("迁移 memo id: %d 成功", memo.Id)
257+
} else {
258+
log.Error().Msgf("迁移 memo id: %d 失败, 原因:%v", memo.Id, err)
259+
}
260+
}
261+
}

front/components/BilibiliPreview.vue

-22
This file was deleted.

front/components/Memo.vue

+2-6
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,8 @@
5353
<music-preview v-if="extJSON.music && extJSON.music.id" v-bind="extJSON.music"/>
5454
<douban-book-preview v-if="extJSON.doubanBook && extJSON.doubanBook.title" :book="extJSON.doubanBook"/>
5555
<douban-movie-preview v-if="extJSON.doubanMovie && extJSON.doubanMovie.title" :movie="extJSON.doubanMovie"/>
56-
<youtube-preview v-if="extJSON.video && extJSON.video.type === 'youtube' && extJSON.video.value"
57-
:url="extJSON.video.value"/>
58-
<bilibili-preview v-if="extJSON.video && extJSON.video.type === 'bilibili' && extJSON.video.value"
59-
:url="extJSON.video.value"/>
60-
<video-preview v-if="extJSON.video && extJSON.video.type === 'online' && extJSON.video.value"
61-
:url="extJSON.video.value"/>
56+
<video-preview-iframe v-if="extJSON.video && ['bilibili', 'youtube'].includes(extJSON.video.type) && extJSON.video.value" :url="extJSON.video.value"/>
57+
<video-preview v-if="extJSON.video && extJSON.video.type === 'online' && extJSON.video.value" :url="extJSON.video.value"/>
6258
</div>
6359

6460
<div class="text-[#576b95] font-medium dark:text-white text-xs mt-2 mb-1 select-none flex items-center gap-0.5"

front/components/MemoEdit.vue

+1-2
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@
7979
v-bind="state.music"/>
8080
<douban-book-preview :book="doubanData" v-if="doubanType === 'book' && doubanData&& doubanData.title"/>
8181
<douban-movie-preview :movie="doubanData" v-if="doubanType === 'movie' && doubanData&& doubanData.title"/>
82-
<youtube-preview v-if="state.video.type === 'youtube' && state.video.value" :url="state.video.value"/>
83-
<bilibili-preview v-if="state.video.type === 'bilibili' && state.video.value" :url="state.video.value"/>
82+
<video-preview-iframe v-if="['bilibili', 'youtube'].includes(state.video.type) && state.video.value" :url="state.video.value"/>
8483
<video-preview v-if="state.video.type === 'online' && state.video.value" :url="state.video.value"/>
8584
</div>
8685
</div>

front/components/UploadVideo.vue

+98-44
Original file line numberDiff line numberDiff line change
@@ -51,32 +51,32 @@
5151

5252
<UButtonGroup>
5353
<UButton @click="confirm(close)">确定</UButton>
54-
<UButton color="white" @click="reset(close)">清空</UButton>
54+
<UButton color="white" @click="reset()">清空</UButton>
5555
</UButtonGroup>
5656
</div>
5757
</template>
5858
</UPopover>
5959
</template>
6060

6161
<script setup lang="ts">
62-
import type {Video, VideoType} from "~/types";
63-
import {toast} from "vue-sonner";
64-
import {useUpload} from "~/utils";
62+
import type { Video, VideoType } from "~/types";
63+
import { toast } from "vue-sonner";
64+
import { useUpload } from "~/utils";
6565
6666
const props = withDefaults(defineProps<Video>(), {
6767
type: "youtube",
6868
value: ""
6969
})
7070
const emit = defineEmits(['confirm'])
71-
const videoType = ref<VideoType>(props.type)
71+
7272
const youtubeUrl = ref('')
7373
const bilibiliUrl = ref('')
7474
const onlineUrl = ref('')
75-
const youtubeUrlRegs = [/v=([^&#]+)/, /youtu\.be\/(.*)\?/]
7675
const progress = ref(0)
7776
const filename = ref('')
7877
const total = ref(0)
7978
const current = ref(0)
79+
8080
const items = [{
8181
slot: 'uploadVideo',
8282
label: '本地'
@@ -85,8 +85,36 @@ const items = [{
8585
label: '在线'
8686
}]
8787
88+
const youtubeUrlTemplateList = [
89+
{
90+
reg: /src=['"](?:https?:)?(?:\/)*([^'"]+)['"]/,
91+
template: 'https://@{placeholder}',
92+
},
93+
{
94+
reg: /v=([^&#]+)/,
95+
template: 'https://www.youtube.com/embed/@{placeholder}',
96+
},
97+
{
98+
reg: /youtu\.be\/([^\/\?]+)/,
99+
template: 'https://www.youtube.com/embed/@{placeholder}',
100+
},
101+
]
102+
const bilibiliUrlTemplateList = [
103+
{
104+
reg: /src=['"](?:https?:)?(?:\/)*([^'"]+)['"]/,
105+
template: 'https://@{placeholder}',
106+
},
107+
{
108+
reg: /av(\d+)/i,
109+
template: 'https://player.bilibili.com/player.html?aid=@{placeholder}',
110+
},
111+
{
112+
reg: /(bv[\w]+)/i,
113+
template: 'https://player.bilibili.com/player.html?bvid=@{placeholder}',
114+
},
115+
]
116+
88117
watch(props, () => {
89-
videoType.value = props.type
90118
if (props.type === 'youtube') {
91119
youtubeUrl.value = props.value
92120
} else if (props.type === 'bilibili') {
@@ -98,7 +126,7 @@ watch(props, () => {
98126
99127
const handleUploadVideo = async (files: FileList) => {
100128
for (let i = 0; i < files.length; i++) {
101-
if (files[i].type.indexOf("video") < 0){
129+
if (files[i].type.indexOf("video") < 0) {
102130
toast.error("只能上传视频文件");
103131
return
104132
}
@@ -114,69 +142,95 @@ const handleUploadVideo = async (files: FileList) => {
114142
onlineUrl.value = result[0]
115143
}
116144
}
145+
146+
const emitUrl = (type: VideoType, value: string) => {
147+
if (type !== 'youtube') {
148+
youtubeUrl.value = ''
149+
}
150+
151+
if (type !== 'bilibili') {
152+
bilibiliUrl.value = ''
153+
}
154+
155+
if (type !== 'online') {
156+
onlineUrl.value = ''
157+
}
158+
159+
emit('confirm', {
160+
type,
161+
value,
162+
})
163+
}
164+
117165
const confirm = (close: Function) => {
118-
const match = bilibiliUrl.value.match(/src=['"]([^'"]+)['"]/)
166+
if (bilibiliUrl.value.trim() && youtubeUrl.value.trim()) {
167+
toast.warning("请勿同时填写两个地址")
168+
return
169+
}
170+
119171
if (bilibiliUrl.value.trim()) {
120-
videoType.value = 'bilibili'
121-
if (match && match.length > 1) {
122-
emit('confirm', {
123-
type: videoType.value,
124-
value: bilibiliUrl.value
125-
})
172+
if (bilibiliUrl.value.startsWith('https://player.bilibili.com/player.html')) {
173+
emitUrl('bilibili', bilibiliUrl.value)
126174
close()
127-
} else {
128-
toast.warning("无效的B站视频地址")
129175
return
130176
}
177+
178+
for (const bilibiliUrlTemplate of bilibiliUrlTemplateList) {
179+
const { reg, template } = bilibiliUrlTemplate
180+
const [_, matchedValue] = bilibiliUrl.value.match(reg) || []
181+
if (matchedValue) {
182+
const url = template.replace('@{placeholder}', matchedValue)
183+
emitUrl('bilibili', url)
184+
close()
185+
return
186+
}
187+
}
188+
189+
toast.warning("无效的B站视频地址")
131190
return
132191
}
133192
134193
if (youtubeUrl.value.trim()) {
135-
videoType.value = 'youtube'
136-
let success = false
137-
for (let i = 0; i < youtubeUrlRegs.length; i++) {
138-
const match = youtubeUrl.value.match(youtubeUrlRegs[i])
139-
if (match && match.length > 1) {
140-
success = true
141-
break
142-
}
143-
}
144-
if (success) {
145-
emit('confirm', {
146-
type: videoType.value,
147-
value: youtubeUrl.value
148-
})
194+
if (youtubeUrl.value.startsWith('https://www.youtube.com/embed')) {
195+
emitUrl('youtube', youtubeUrl.value)
149196
close()
150-
} else {
151-
toast.warning("无效的Youtube视频地址")
197+
return
152198
}
199+
200+
for (const youtubeUrlTemplate of youtubeUrlTemplateList) {
201+
const { reg, template } = youtubeUrlTemplate
202+
const [_, matchedValue] = youtubeUrl.value.match(reg) || []
203+
if (matchedValue) {
204+
const url = template.replace('@{placeholder}', matchedValue)
205+
emitUrl('youtube', url)
206+
close()
207+
return
208+
}
209+
}
210+
211+
toast.warning("无效的Youtube视频地址")
153212
return
154213
}
214+
155215
if (onlineUrl.value.trim()) {
156-
videoType.value = 'online'
157-
emit('confirm', {
158-
type: videoType.value,
159-
value: onlineUrl.value.trim()
160-
})
216+
emitUrl('online', onlineUrl.value.trim())
161217
close()
162218
return
163219
}
164220
}
165221
166-
const reset = (close: Function) => {
167-
videoType.value = 'youtube'
222+
const reset = () => {
168223
youtubeUrl.value = ''
169224
bilibiliUrl.value = ''
170225
onlineUrl.value = ''
226+
171227
emit('confirm', {
172-
type: videoType.value,
228+
type: 'youtube',
173229
value: ""
174230
})
175231
}
176232
177233
178234
</script>
179235

180-
<style scoped>
181-
182-
</style>
236+
<style scoped></style>

0 commit comments

Comments
 (0)