diff options
Diffstat (limited to 'server/controllers/api/videos/source.ts')
-rw-r--r-- | server/controllers/api/videos/source.ts | 206 |
1 files changed, 0 insertions, 206 deletions
diff --git a/server/controllers/api/videos/source.ts b/server/controllers/api/videos/source.ts deleted file mode 100644 index 75fe68b6c..000000000 --- a/server/controllers/api/videos/source.ts +++ /dev/null | |||
@@ -1,206 +0,0 @@ | |||
1 | import express from 'express' | ||
2 | import { move } from 'fs-extra' | ||
3 | import { sequelizeTypescript } from '@server/initializers/database' | ||
4 | import { CreateJobArgument, CreateJobOptions, JobQueue } from '@server/lib/job-queue' | ||
5 | import { Hooks } from '@server/lib/plugins/hooks' | ||
6 | import { regenerateMiniaturesIfNeeded } from '@server/lib/thumbnail' | ||
7 | import { uploadx } from '@server/lib/uploadx' | ||
8 | import { buildMoveToObjectStorageJob } from '@server/lib/video' | ||
9 | import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist' | ||
10 | import { buildNewFile } from '@server/lib/video-file' | ||
11 | import { VideoPathManager } from '@server/lib/video-path-manager' | ||
12 | import { buildNextVideoState } from '@server/lib/video-state' | ||
13 | import { openapiOperationDoc } from '@server/middlewares/doc' | ||
14 | import { VideoModel } from '@server/models/video/video' | ||
15 | import { VideoSourceModel } from '@server/models/video/video-source' | ||
16 | import { MStreamingPlaylistFiles, MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' | ||
17 | import { VideoState } from '@shared/models' | ||
18 | import { logger, loggerTagsFactory } from '../../../helpers/logger' | ||
19 | import { | ||
20 | asyncMiddleware, | ||
21 | authenticate, | ||
22 | replaceVideoSourceResumableInitValidator, | ||
23 | replaceVideoSourceResumableValidator, | ||
24 | videoSourceGetLatestValidator | ||
25 | } from '../../../middlewares' | ||
26 | |||
27 | const lTags = loggerTagsFactory('api', 'video') | ||
28 | |||
29 | const videoSourceRouter = express.Router() | ||
30 | |||
31 | videoSourceRouter.get('/:id/source', | ||
32 | openapiOperationDoc({ operationId: 'getVideoSource' }), | ||
33 | authenticate, | ||
34 | asyncMiddleware(videoSourceGetLatestValidator), | ||
35 | getVideoLatestSource | ||
36 | ) | ||
37 | |||
38 | videoSourceRouter.post('/:id/source/replace-resumable', | ||
39 | authenticate, | ||
40 | asyncMiddleware(replaceVideoSourceResumableInitValidator), | ||
41 | (req, res) => uploadx.upload(req, res) // Prevent next() call, explicitely tell to uploadx it's the end | ||
42 | ) | ||
43 | |||
44 | videoSourceRouter.delete('/:id/source/replace-resumable', | ||
45 | authenticate, | ||
46 | (req, res) => uploadx.upload(req, res) // Prevent next() call, explicitely tell to uploadx it's the end | ||
47 | ) | ||
48 | |||
49 | videoSourceRouter.put('/:id/source/replace-resumable', | ||
50 | authenticate, | ||
51 | uploadx.upload, // uploadx doesn't next() before the file upload completes | ||
52 | asyncMiddleware(replaceVideoSourceResumableValidator), | ||
53 | asyncMiddleware(replaceVideoSourceResumable) | ||
54 | ) | ||
55 | |||
56 | // --------------------------------------------------------------------------- | ||
57 | |||
58 | export { | ||
59 | videoSourceRouter | ||
60 | } | ||
61 | |||
62 | // --------------------------------------------------------------------------- | ||
63 | |||
64 | function getVideoLatestSource (req: express.Request, res: express.Response) { | ||
65 | return res.json(res.locals.videoSource.toFormattedJSON()) | ||
66 | } | ||
67 | |||
68 | async function replaceVideoSourceResumable (req: express.Request, res: express.Response) { | ||
69 | const videoPhysicalFile = res.locals.updateVideoFileResumable | ||
70 | const user = res.locals.oauth.token.User | ||
71 | |||
72 | const videoFile = await buildNewFile({ path: videoPhysicalFile.path, mode: 'web-video' }) | ||
73 | const originalFilename = videoPhysicalFile.originalname | ||
74 | |||
75 | const videoFileMutexReleaser = await VideoPathManager.Instance.lockFiles(res.locals.videoAll.uuid) | ||
76 | |||
77 | try { | ||
78 | const destination = VideoPathManager.Instance.getFSVideoFileOutputPath(res.locals.videoAll, videoFile) | ||
79 | await move(videoPhysicalFile.path, destination) | ||
80 | |||
81 | let oldWebVideoFiles: MVideoFile[] = [] | ||
82 | let oldStreamingPlaylists: MStreamingPlaylistFiles[] = [] | ||
83 | |||
84 | const inputFileUpdatedAt = new Date() | ||
85 | |||
86 | const video = await sequelizeTypescript.transaction(async transaction => { | ||
87 | const video = await VideoModel.loadFull(res.locals.videoAll.id, transaction) | ||
88 | |||
89 | oldWebVideoFiles = video.VideoFiles | ||
90 | oldStreamingPlaylists = video.VideoStreamingPlaylists | ||
91 | |||
92 | for (const file of video.VideoFiles) { | ||
93 | await file.destroy({ transaction }) | ||
94 | } | ||
95 | for (const playlist of oldStreamingPlaylists) { | ||
96 | await playlist.destroy({ transaction }) | ||
97 | } | ||
98 | |||
99 | videoFile.videoId = video.id | ||
100 | await videoFile.save({ transaction }) | ||
101 | |||
102 | video.VideoFiles = [ videoFile ] | ||
103 | video.VideoStreamingPlaylists = [] | ||
104 | |||
105 | video.state = buildNextVideoState() | ||
106 | video.duration = videoPhysicalFile.duration | ||
107 | video.inputFileUpdatedAt = inputFileUpdatedAt | ||
108 | await video.save({ transaction }) | ||
109 | |||
110 | await autoBlacklistVideoIfNeeded({ | ||
111 | video, | ||
112 | user, | ||
113 | isRemote: false, | ||
114 | isNew: false, | ||
115 | isNewFile: true, | ||
116 | transaction | ||
117 | }) | ||
118 | |||
119 | return video | ||
120 | }) | ||
121 | |||
122 | await removeOldFiles({ video, files: oldWebVideoFiles, playlists: oldStreamingPlaylists }) | ||
123 | |||
124 | const source = await VideoSourceModel.create({ | ||
125 | filename: originalFilename, | ||
126 | videoId: video.id, | ||
127 | createdAt: inputFileUpdatedAt | ||
128 | }) | ||
129 | |||
130 | await regenerateMiniaturesIfNeeded(video) | ||
131 | await video.VideoChannel.setAsUpdated() | ||
132 | await addVideoJobsAfterUpload(video, video.getMaxQualityFile()) | ||
133 | |||
134 | logger.info('Replaced video file of video %s with uuid %s.', video.name, video.uuid, lTags(video.uuid)) | ||
135 | |||
136 | Hooks.runAction('action:api.video.file-updated', { video, req, res }) | ||
137 | |||
138 | return res.json(source.toFormattedJSON()) | ||
139 | } finally { | ||
140 | videoFileMutexReleaser() | ||
141 | } | ||
142 | } | ||
143 | |||
144 | async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVideoFile) { | ||
145 | const jobs: (CreateJobArgument & CreateJobOptions)[] = [ | ||
146 | { | ||
147 | type: 'manage-video-torrent' as 'manage-video-torrent', | ||
148 | payload: { | ||
149 | videoId: video.id, | ||
150 | videoFileId: videoFile.id, | ||
151 | action: 'create' | ||
152 | } | ||
153 | }, | ||
154 | |||
155 | { | ||
156 | type: 'generate-video-storyboard' as 'generate-video-storyboard', | ||
157 | payload: { | ||
158 | videoUUID: video.uuid, | ||
159 | // No need to federate, we process these jobs sequentially | ||
160 | federate: false | ||
161 | } | ||
162 | }, | ||
163 | |||
164 | { | ||
165 | type: 'federate-video' as 'federate-video', | ||
166 | payload: { | ||
167 | videoUUID: video.uuid, | ||
168 | isNewVideo: false | ||
169 | } | ||
170 | } | ||
171 | ] | ||
172 | |||
173 | if (video.state === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE) { | ||
174 | jobs.push(await buildMoveToObjectStorageJob({ video, isNewVideo: false, previousVideoState: undefined })) | ||
175 | } | ||
176 | |||
177 | if (video.state === VideoState.TO_TRANSCODE) { | ||
178 | jobs.push({ | ||
179 | type: 'transcoding-job-builder' as 'transcoding-job-builder', | ||
180 | payload: { | ||
181 | videoUUID: video.uuid, | ||
182 | optimizeJob: { | ||
183 | isNewVideo: false | ||
184 | } | ||
185 | } | ||
186 | }) | ||
187 | } | ||
188 | |||
189 | return JobQueue.Instance.createSequentialJobFlow(...jobs) | ||
190 | } | ||
191 | |||
192 | async function removeOldFiles (options: { | ||
193 | video: MVideo | ||
194 | files: MVideoFile[] | ||
195 | playlists: MStreamingPlaylistFiles[] | ||
196 | }) { | ||
197 | const { video, files, playlists } = options | ||
198 | |||
199 | for (const file of files) { | ||
200 | await video.removeWebVideoFile(file) | ||
201 | } | ||
202 | |||
203 | for (const playlist of playlists) { | ||
204 | await video.removeStreamingPlaylistFiles(playlist) | ||
205 | } | ||
206 | } | ||