]>
Commit | Line | Data |
---|---|---|
1 | import { getAPId } from '@server/helpers/activitypub' | |
2 | import { retryTransactionWrapper } from '@server/helpers/database-utils' | |
3 | import { JobQueue } from '@server/lib/job-queue' | |
4 | import { loadVideoByUrl, VideoLoadByUrlType } from '@server/lib/model-loaders' | |
5 | import { MVideoAccountLightBlacklistAllFiles, MVideoImmutable, MVideoThumbnail } from '@server/types/models' | |
6 | import { APObject } from '@shared/models' | |
7 | import { refreshVideoIfNeeded } from './refresh' | |
8 | import { APVideoCreator, fetchRemoteVideo, SyncParam, syncVideoExternalAttributes } from './shared' | |
9 | ||
10 | type GetVideoResult <T> = Promise<{ | |
11 | video: T | |
12 | created: boolean | |
13 | autoBlacklisted?: boolean | |
14 | }> | |
15 | ||
16 | type GetVideoParamAll = { | |
17 | videoObject: APObject | |
18 | syncParam?: SyncParam | |
19 | fetchType?: 'all' | |
20 | allowRefresh?: boolean | |
21 | } | |
22 | ||
23 | type GetVideoParamImmutable = { | |
24 | videoObject: APObject | |
25 | syncParam?: SyncParam | |
26 | fetchType: 'only-immutable-attributes' | |
27 | allowRefresh: false | |
28 | } | |
29 | ||
30 | type GetVideoParamOther = { | |
31 | videoObject: APObject | |
32 | syncParam?: SyncParam | |
33 | fetchType?: 'all' | 'only-video' | |
34 | allowRefresh?: boolean | |
35 | } | |
36 | ||
37 | function getOrCreateAPVideo (options: GetVideoParamAll): GetVideoResult<MVideoAccountLightBlacklistAllFiles> | |
38 | function getOrCreateAPVideo (options: GetVideoParamImmutable): GetVideoResult<MVideoImmutable> | |
39 | function getOrCreateAPVideo (options: GetVideoParamOther): GetVideoResult<MVideoAccountLightBlacklistAllFiles | MVideoThumbnail> | |
40 | ||
41 | async function getOrCreateAPVideo ( | |
42 | options: GetVideoParamAll | GetVideoParamImmutable | GetVideoParamOther | |
43 | ): GetVideoResult<MVideoAccountLightBlacklistAllFiles | MVideoThumbnail | MVideoImmutable> { | |
44 | // Default params | |
45 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } | |
46 | const fetchType = options.fetchType || 'all' | |
47 | const allowRefresh = options.allowRefresh !== false | |
48 | ||
49 | // Get video url | |
50 | const videoUrl = getAPId(options.videoObject) | |
51 | let videoFromDatabase = await loadVideoByUrl(videoUrl, fetchType) | |
52 | ||
53 | if (videoFromDatabase) { | |
54 | if (allowRefresh === true) { | |
55 | // Typings ensure allowRefresh === false in only-immutable-attributes fetch type | |
56 | videoFromDatabase = await scheduleRefresh(videoFromDatabase as MVideoThumbnail, fetchType, syncParam) | |
57 | } | |
58 | ||
59 | return { video: videoFromDatabase, created: false } | |
60 | } | |
61 | ||
62 | const { videoObject } = await fetchRemoteVideo(videoUrl) | |
63 | if (!videoObject) throw new Error('Cannot fetch remote video with url: ' + videoUrl) | |
64 | ||
65 | // videoUrl is just an alias/rediraction, so process object id instead | |
66 | if (videoObject.id !== videoUrl) return getOrCreateAPVideo({ ...options, fetchType: 'all', videoObject }) | |
67 | ||
68 | try { | |
69 | const creator = new APVideoCreator(videoObject) | |
70 | const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(creator.create.bind(creator), syncParam.thumbnail) | |
71 | ||
72 | await syncVideoExternalAttributes(videoCreated, videoObject, syncParam) | |
73 | ||
74 | return { video: videoCreated, created: true, autoBlacklisted } | |
75 | } catch (err) { | |
76 | // Maybe a concurrent getOrCreateAPVideo call created this video | |
77 | if (err.name === 'SequelizeUniqueConstraintError') { | |
78 | const alreadyCreatedVideo = await loadVideoByUrl(videoUrl, fetchType) | |
79 | if (alreadyCreatedVideo) return { video: alreadyCreatedVideo, created: false } | |
80 | } | |
81 | ||
82 | throw err | |
83 | } | |
84 | } | |
85 | ||
86 | // --------------------------------------------------------------------------- | |
87 | ||
88 | export { | |
89 | getOrCreateAPVideo | |
90 | } | |
91 | ||
92 | // --------------------------------------------------------------------------- | |
93 | ||
94 | async function scheduleRefresh (video: MVideoThumbnail, fetchType: VideoLoadByUrlType, syncParam: SyncParam) { | |
95 | if (!video.isOutdated()) return video | |
96 | ||
97 | const refreshOptions = { | |
98 | video, | |
99 | fetchedType: fetchType, | |
100 | syncParam | |
101 | } | |
102 | ||
103 | if (syncParam.refreshVideo === true) { | |
104 | return refreshVideoIfNeeded(refreshOptions) | |
105 | } | |
106 | ||
107 | await JobQueue.Instance.createJobWithPromise({ | |
108 | type: 'activitypub-refresher', | |
109 | payload: { type: 'video', url: video.url } | |
110 | }) | |
111 | ||
112 | return video | |
113 | } |