Remove ability to delete video imports
authorChocobozzz <me@florianbigard.com>
Fri, 3 Aug 2018 07:43:00 +0000 (09:43 +0200)
committerChocobozzz <me@florianbigard.com>
Mon, 6 Aug 2018 09:19:16 +0000 (11:19 +0200)
Users should remove the linked video instead

client/src/app/videos/+video-watch/video-watch.component.html
client/src/app/videos/+video-watch/video-watch.component.ts
server/controllers/api/videos/import.ts
server/helpers/youtube-dl.ts
server/lib/job-queue/handlers/video-import.ts
server/middlewares/validators/video-imports.ts
server/models/video/video.ts

index f39b5a94a494227702ea9a428662097534ac1493..5a132112d920f17148a296a94d7eafb890d8bae9 100644 (file)
@@ -8,6 +8,10 @@
     </div>
   </div>
 
+  <div i18n class="alert alert-warning" *ngIf="isVideoToImport()">
+    The video is being imported, it will be available when the import is finished.
+  </div>
+
   <div i18n class="alert alert-warning" *ngIf="isVideoToTranscode()">
     The video is being transcoded, it may not work properly.
   </div>
index 995fb8e2baf229a932853ce7fbf6bee717325170..04bcc6cd1f4ed744a45a364cad221c6ca433bbaf 100644 (file)
@@ -289,6 +289,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     return this.video && this.video.state.id === VideoState.TO_TRANSCODE
   }
 
+  isVideoToImport () {
+    return this.video && this.video.state.id === VideoState.TO_IMPORT
+  }
+
   hasVideoScheduledPublication () {
     return this.video && this.video.scheduledUpdate !== undefined
   }
index 680d8665fdfcf0eaea0f4b2b5201d8dfef6b3714..ca7a5f9cac8edabd4365d8215d68f694868b2de9 100644 (file)
@@ -4,8 +4,7 @@ import {
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
   authenticate,
-  videoImportAddValidator,
-  videoImportDeleteValidator
+  videoImportAddValidator
 } from '../../../middlewares'
 import { CONFIG, IMAGE_MIMETYPE_EXT, PREVIEWS_SIZE, sequelizeTypescript, THUMBNAILS_SIZE } from '../../../initializers'
 import { getYoutubeDLInfo, YoutubeDLInfo } from '../../../helpers/youtube-dl'
@@ -39,12 +38,6 @@ videoImportsRouter.post('/imports',
   asyncRetryTransactionMiddleware(addVideoImport)
 )
 
-videoImportsRouter.delete('/imports/:id',
-  authenticate,
-  asyncMiddleware(videoImportDeleteValidator),
-  asyncRetryTransactionMiddleware(deleteVideoImport)
-)
-
 // ---------------------------------------------------------------------------
 
 export {
@@ -145,15 +138,3 @@ async function addVideoImport (req: express.Request, res: express.Response) {
 
   return res.json(videoImport.toFormattedJSON())
 }
-
-async function deleteVideoImport (req: express.Request, res: express.Response) {
-  await sequelizeTypescript.transaction(async t => {
-    const videoImport = res.locals.videoImport
-    const video = videoImport.Video
-
-    await videoImport.destroy({ transaction: t })
-    await video.destroy({ transaction: t })
-  })
-
-  return res.status(204).end()
-}
index 43156bb227172e4c958a6b8679095997a05a811d..ff1fbf59fe1eb422f4424699c75df8f9819ef994 100644 (file)
@@ -30,7 +30,7 @@ function getYoutubeDLInfo (url: string): Promise<YoutubeDLInfo> {
 }
 
 function downloadYoutubeDLVideo (url: string) {
-  const hash = crypto.createHash('sha256').update(url).digest('base64')
+  const hash = crypto.createHash('sha256').update(url).digest('hex')
   const path = join(CONFIG.STORAGE.VIDEOS_DIR, hash + '-import.mp4')
 
   logger.info('Importing video %s', url)
index 4f2faab7dfbb650e8047eef51ccf609863ac7d38..3b9d08d3b74ebd1a16f9d59b2c25484ee05b8ffa 100644 (file)
@@ -12,6 +12,7 @@ import { doRequestAndSaveToFile } from '../../../helpers/requests'
 import { VideoState } from '../../../../shared'
 import { JobQueue } from '../index'
 import { federateVideoIfNeeded } from '../../activitypub'
+import { VideoModel } from '../../../models/video/video'
 
 export type VideoImportPayload = {
   type: 'youtube-dl'
@@ -26,9 +27,13 @@ async function processVideoImport (job: Bull.Job) {
   logger.info('Processing video import in job %d.', job.id)
 
   const videoImport = await VideoImportModel.loadAndPopulateVideo(payload.videoImportId)
-  if (!videoImport) throw new Error('Cannot import video %s: the video import entry does not exist anymore.')
+  if (!videoImport || !videoImport.Video) {
+    throw new Error('Cannot import video %s: the video import or video linked to this import does not exist anymore.')
+  }
 
   let tempVideoPath: string
+  let videoDestFile: string
+  let videoFile: VideoFileModel
   try {
     // Download video from youtubeDL
     tempVideoPath = await downloadYoutubeDLVideo(videoImport.targetUrl)
@@ -47,11 +52,14 @@ async function processVideoImport (job: Bull.Job) {
       fps,
       videoId: videoImport.videoId
     }
-    const videoFile = new VideoFileModel(videoFileData)
+    videoFile = new VideoFileModel(videoFileData)
+    // Import if the import fails, to clean files
+    videoImport.Video.VideoFiles = [ videoFile ]
 
     // Move file
-    const destination = join(CONFIG.STORAGE.VIDEOS_DIR, videoImport.Video.getVideoFilename(videoFile))
-    await renamePromise(tempVideoPath, destination)
+    videoDestFile = join(CONFIG.STORAGE.VIDEOS_DIR, videoImport.Video.getVideoFilename(videoFile))
+    await renamePromise(tempVideoPath, videoDestFile)
+    tempVideoPath = null // This path is not used anymore
 
     // Process thumbnail
     if (payload.downloadThumbnail) {
@@ -77,15 +85,21 @@ async function processVideoImport (job: Bull.Job) {
     await videoImport.Video.createTorrentAndSetInfoHash(videoFile)
 
     const videoImportUpdated: VideoImportModel = await sequelizeTypescript.transaction(async t => {
-      await videoFile.save({ transaction: t })
+      // Refresh video
+      const video = await VideoModel.load(videoImport.videoId, t)
+      if (!video) throw new Error('Video linked to import ' + videoImport.videoId + ' does not exist anymore.')
+      videoImport.Video = video
+
+      const videoFileCreated = await videoFile.save({ transaction: t })
+      video.VideoFiles = [ videoFileCreated ]
 
       // Update video DB object
-      videoImport.Video.duration = duration
-      videoImport.Video.state = CONFIG.TRANSCODING.ENABLED ? VideoState.TO_TRANSCODE : VideoState.PUBLISHED
-      const videoUpdated = await videoImport.Video.save({ transaction: t })
+      video.duration = duration
+      video.state = CONFIG.TRANSCODING.ENABLED ? VideoState.TO_TRANSCODE : VideoState.PUBLISHED
+      const videoUpdated = await video.save({ transaction: t })
 
       // Now we can federate the video
-      await federateVideoIfNeeded(videoImport.Video, true, t)
+      await federateVideoIfNeeded(video, true, t)
 
       // Update video import object
       videoImport.state = VideoImportState.SUCCESS
@@ -112,7 +126,7 @@ async function processVideoImport (job: Bull.Job) {
     try {
       if (tempVideoPath) await unlinkPromise(tempVideoPath)
     } catch (errUnlink) {
-      logger.error('Cannot cleanup files after a video import error.', { err: errUnlink })
+      logger.warn('Cannot cleanup files after a video import error.', { err: errUnlink })
     }
 
     videoImport.error = err.message
index 0dedcf803cdf4fa7c0e342ab60c7b88a163e6e4c..e0a55297685f0fcc8cff58a1f3df9cec97731130 100644 (file)
@@ -1,14 +1,12 @@
 import * as express from 'express'
-import { body, param } from 'express-validator/check'
+import { body } from 'express-validator/check'
 import { isIdValid } from '../../helpers/custom-validators/misc'
 import { logger } from '../../helpers/logger'
 import { areValidationErrors } from './utils'
 import { getCommonVideoAttributes } from './videos'
-import { isVideoImportTargetUrlValid, isVideoImportExist } from '../../helpers/custom-validators/video-imports'
+import { isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports'
 import { cleanUpReqFiles } from '../../helpers/utils'
-import { isVideoChannelOfAccountExist, isVideoNameValid, checkUserCanManageVideo } from '../../helpers/custom-validators/videos'
-import { VideoImportModel } from '../../models/video/video-import'
-import { UserRight } from '../../../shared'
+import { isVideoChannelOfAccountExist, isVideoNameValid } from '../../helpers/custom-validators/videos'
 
 const videoImportAddValidator = getCommonVideoAttributes().concat([
   body('targetUrl').custom(isVideoImportTargetUrlValid).withMessage('Should have a valid video import target URL'),
@@ -31,29 +29,10 @@ const videoImportAddValidator = getCommonVideoAttributes().concat([
   }
 ])
 
-const videoImportDeleteValidator = [
-  param('id').custom(isIdValid).not().isEmpty().withMessage('Should have a valid id'),
-
-  async (req: express.Request, res: express.Response, next: express.NextFunction) => {
-    logger.debug('Checking videoImportDeleteValidator parameters', { parameters: req.body })
-
-    if (areValidationErrors(req, res)) return
-    if (!await isVideoImportExist(req.params.id, res)) return
-
-    const user = res.locals.oauth.token.User
-    const videoImport: VideoImportModel = res.locals.videoImport
-
-    if (!await checkUserCanManageVideo(user, videoImport.Video, UserRight.UPDATE_ANY_VIDEO, res)) return
-
-    return next()
-  }
-]
-
 // ---------------------------------------------------------------------------
 
 export {
-  videoImportAddValidator,
-  videoImportDeleteValidator
+  videoImportAddValidator
 }
 
 // ---------------------------------------------------------------------------
index f32010014d7b0b49423c4a6c39798902ca0d0df8..67711b102afbb148d39a2e76dea2f0385161ecce 100644 (file)
@@ -957,8 +957,10 @@ export class VideoModel extends Model<VideoModel> {
       })
   }
 
-  static load (id: number) {
-    return VideoModel.findById(id)
+  static load (id: number, t?: Sequelize.Transaction) {
+    const options = t ? { transaction: t } : undefined
+
+    return VideoModel.findById(id, options)
   }
 
   static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) {