diff options
Diffstat (limited to 'server/controllers/api/videos/import.ts')
-rw-r--r-- | server/controllers/api/videos/import.ts | 160 |
1 files changed, 94 insertions, 66 deletions
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index 2c225315c..ee63c7b77 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts | |||
@@ -83,32 +83,15 @@ async function addTorrentImport (req: express.Request, res: express.Response, to | |||
83 | let magnetUri: string | 83 | let magnetUri: string |
84 | 84 | ||
85 | if (torrentfile) { | 85 | if (torrentfile) { |
86 | torrentName = torrentfile.originalname | 86 | const result = await processTorrentOrAbortRequest(req, res, torrentfile) |
87 | if (!result) return | ||
87 | 88 | ||
88 | // Rename the torrent to a secured name | 89 | videoName = result.name |
89 | const newTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, getSecureTorrentName(torrentName)) | 90 | torrentName = result.torrentName |
90 | await move(torrentfile.path, newTorrentPath, { overwrite: true }) | ||
91 | torrentfile.path = newTorrentPath | ||
92 | |||
93 | const buf = await readFile(torrentfile.path) | ||
94 | const parsedTorrent = parseTorrent(buf) as parseTorrent.Instance | ||
95 | |||
96 | if (parsedTorrent.files.length !== 1) { | ||
97 | cleanUpReqFiles(req) | ||
98 | |||
99 | return res.status(HttpStatusCode.BAD_REQUEST_400) | ||
100 | .json({ | ||
101 | code: ServerErrorCode.INCORRECT_FILES_IN_TORRENT, | ||
102 | error: 'Torrents with only 1 file are supported.' | ||
103 | }) | ||
104 | } | ||
105 | |||
106 | videoName = isArray(parsedTorrent.name) ? parsedTorrent.name[0] : parsedTorrent.name | ||
107 | } else { | 91 | } else { |
108 | magnetUri = body.magnetUri | 92 | const result = processMagnetURI(body) |
109 | 93 | magnetUri = result.magnetUri | |
110 | const parsed = magnetUtil.decode(magnetUri) | 94 | videoName = result.name |
111 | videoName = isArray(parsed.name) ? parsed.name[0] : parsed.name as string | ||
112 | } | 95 | } |
113 | 96 | ||
114 | const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) | 97 | const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) |
@@ -116,26 +99,26 @@ async function addTorrentImport (req: express.Request, res: express.Response, to | |||
116 | const thumbnailModel = await processThumbnail(req, video) | 99 | const thumbnailModel = await processThumbnail(req, video) |
117 | const previewModel = await processPreview(req, video) | 100 | const previewModel = await processPreview(req, video) |
118 | 101 | ||
119 | const tags = body.tags || undefined | ||
120 | const videoImportAttributes = { | ||
121 | magnetUri, | ||
122 | torrentName, | ||
123 | state: VideoImportState.PENDING, | ||
124 | userId: user.id | ||
125 | } | ||
126 | const videoImport = await insertIntoDB({ | 102 | const videoImport = await insertIntoDB({ |
127 | video, | 103 | video, |
128 | thumbnailModel, | 104 | thumbnailModel, |
129 | previewModel, | 105 | previewModel, |
130 | videoChannel: res.locals.videoChannel, | 106 | videoChannel: res.locals.videoChannel, |
131 | tags, | 107 | tags: body.tags || undefined, |
132 | videoImportAttributes, | 108 | user, |
133 | user | 109 | videoImportAttributes: { |
110 | magnetUri, | ||
111 | torrentName, | ||
112 | state: VideoImportState.PENDING, | ||
113 | userId: user.id | ||
114 | } | ||
134 | }) | 115 | }) |
135 | 116 | ||
136 | // Create job to import the video | 117 | // Create job to import the video |
137 | const payload = { | 118 | const payload = { |
138 | type: torrentfile ? 'torrent-file' as 'torrent-file' : 'magnet-uri' as 'magnet-uri', | 119 | type: torrentfile |
120 | ? 'torrent-file' as 'torrent-file' | ||
121 | : 'magnet-uri' as 'magnet-uri', | ||
139 | videoImportId: videoImport.id, | 122 | videoImportId: videoImport.id, |
140 | magnetUri | 123 | magnetUri |
141 | } | 124 | } |
@@ -184,45 +167,22 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
184 | previewModel = await processPreviewFromUrl(youtubeDLInfo.thumbnailUrl, video) | 167 | previewModel = await processPreviewFromUrl(youtubeDLInfo.thumbnailUrl, video) |
185 | } | 168 | } |
186 | 169 | ||
187 | const tags = body.tags || youtubeDLInfo.tags | ||
188 | const videoImportAttributes = { | ||
189 | targetUrl, | ||
190 | state: VideoImportState.PENDING, | ||
191 | userId: user.id | ||
192 | } | ||
193 | const videoImport = await insertIntoDB({ | 170 | const videoImport = await insertIntoDB({ |
194 | video, | 171 | video, |
195 | thumbnailModel, | 172 | thumbnailModel, |
196 | previewModel, | 173 | previewModel, |
197 | videoChannel: res.locals.videoChannel, | 174 | videoChannel: res.locals.videoChannel, |
198 | tags, | 175 | tags: body.tags || youtubeDLInfo.tags, |
199 | videoImportAttributes, | 176 | user, |
200 | user | 177 | videoImportAttributes: { |
178 | targetUrl, | ||
179 | state: VideoImportState.PENDING, | ||
180 | userId: user.id | ||
181 | } | ||
201 | }) | 182 | }) |
202 | 183 | ||
203 | // Get video subtitles | 184 | // Get video subtitles |
204 | try { | 185 | await processYoutubeSubtitles(youtubeDL, targetUrl, video.id) |
205 | const subtitles = await youtubeDL.getYoutubeDLSubs() | ||
206 | |||
207 | logger.info('Will create %s subtitles from youtube import %s.', subtitles.length, targetUrl) | ||
208 | |||
209 | for (const subtitle of subtitles) { | ||
210 | const videoCaption = new VideoCaptionModel({ | ||
211 | videoId: video.id, | ||
212 | language: subtitle.language, | ||
213 | filename: VideoCaptionModel.generateCaptionName(subtitle.language) | ||
214 | }) as MVideoCaption | ||
215 | |||
216 | // Move physical file | ||
217 | await moveAndProcessCaptionFile(subtitle, videoCaption) | ||
218 | |||
219 | await sequelizeTypescript.transaction(async t => { | ||
220 | await VideoCaptionModel.insertOrReplaceLanguage(videoCaption, t) | ||
221 | }) | ||
222 | } | ||
223 | } catch (err) { | ||
224 | logger.warn('Cannot get video subtitles.', { err }) | ||
225 | } | ||
226 | 186 | ||
227 | // Create job to import the video | 187 | // Create job to import the video |
228 | const payload = { | 188 | const payload = { |
@@ -358,3 +318,71 @@ async function insertIntoDB (parameters: { | |||
358 | 318 | ||
359 | return videoImport | 319 | return videoImport |
360 | } | 320 | } |
321 | |||
322 | async function processTorrentOrAbortRequest (req: express.Request, res: express.Response, torrentfile: Express.Multer.File) { | ||
323 | const torrentName = torrentfile.originalname | ||
324 | |||
325 | // Rename the torrent to a secured name | ||
326 | const newTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, getSecureTorrentName(torrentName)) | ||
327 | await move(torrentfile.path, newTorrentPath, { overwrite: true }) | ||
328 | torrentfile.path = newTorrentPath | ||
329 | |||
330 | const buf = await readFile(torrentfile.path) | ||
331 | const parsedTorrent = parseTorrent(buf) as parseTorrent.Instance | ||
332 | |||
333 | if (parsedTorrent.files.length !== 1) { | ||
334 | cleanUpReqFiles(req) | ||
335 | |||
336 | res.status(HttpStatusCode.BAD_REQUEST_400) | ||
337 | .json({ | ||
338 | code: ServerErrorCode.INCORRECT_FILES_IN_TORRENT, | ||
339 | error: 'Torrents with only 1 file are supported.' | ||
340 | }) | ||
341 | |||
342 | return undefined | ||
343 | } | ||
344 | |||
345 | return { | ||
346 | name: extractNameFromArray(parsedTorrent.name), | ||
347 | torrentName | ||
348 | } | ||
349 | } | ||
350 | |||
351 | function processMagnetURI (body: VideoImportCreate) { | ||
352 | const magnetUri = body.magnetUri | ||
353 | const parsed = magnetUtil.decode(magnetUri) | ||
354 | |||
355 | return { | ||
356 | name: extractNameFromArray(parsed.name), | ||
357 | magnetUri | ||
358 | } | ||
359 | } | ||
360 | |||
361 | function extractNameFromArray (name: string | string[]) { | ||
362 | return isArray(name) ? name[0] : name | ||
363 | } | ||
364 | |||
365 | async function processYoutubeSubtitles (youtubeDL: YoutubeDL, targetUrl: string, videoId: number) { | ||
366 | try { | ||
367 | const subtitles = await youtubeDL.getYoutubeDLSubs() | ||
368 | |||
369 | logger.info('Will create %s subtitles from youtube import %s.', subtitles.length, targetUrl) | ||
370 | |||
371 | for (const subtitle of subtitles) { | ||
372 | const videoCaption = new VideoCaptionModel({ | ||
373 | videoId, | ||
374 | language: subtitle.language, | ||
375 | filename: VideoCaptionModel.generateCaptionName(subtitle.language) | ||
376 | }) as MVideoCaption | ||
377 | |||
378 | // Move physical file | ||
379 | await moveAndProcessCaptionFile(subtitle, videoCaption) | ||
380 | |||
381 | await sequelizeTypescript.transaction(async t => { | ||
382 | await VideoCaptionModel.insertOrReplaceLanguage(videoCaption, t) | ||
383 | }) | ||
384 | } | ||
385 | } catch (err) { | ||
386 | logger.warn('Cannot get video subtitles.', { err }) | ||
387 | } | ||
388 | } | ||