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