]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/controllers/api/videos/index.ts
Improve real world script
[github/Chocobozzz/PeerTube.git] / server / controllers / api / videos / index.ts
index e70a5319ec0b1b3ca6790ef99acfbca6fd070906..7a9cd9d37486b03a01512b2a19376181c1f22a49 100644 (file)
@@ -1,7 +1,7 @@
 import * as express from 'express'
 import * as Promise from 'bluebird'
 import * as multer from 'multer'
-import * as path from 'path'
+import { extname, join } from 'path'
 
 import { database as db } from '../../../initializers/database'
 import {
@@ -16,7 +16,8 @@ import {
   addEventToRemoteVideo,
   quickAndDirtyUpdateVideoToFriends,
   addVideoToFriends,
-  updateVideoToFriends
+  updateVideoToFriends,
+  JobScheduler
 } from '../../../lib'
 import {
   authenticate,
@@ -35,7 +36,7 @@ import {
   logger,
   retryTransactionWrapper,
   generateRandomString,
-  getFormatedObjects,
+  getFormattedObjects,
   renamePromise
 } from '../../../helpers'
 import { TagInstance } from '../../../models'
@@ -49,19 +50,18 @@ const videosRouter = express.Router()
 
 // multer configuration
 const storage = multer.diskStorage({
-  destination: function (req, file, cb) {
+  destination: (req, file, cb) => {
     cb(null, CONFIG.STORAGE.VIDEOS_DIR)
   },
 
-  filename: function (req, file, cb) {
+  filename: (req, file, cb) => {
     let extension = ''
     if (file.mimetype === 'video/webm') extension = 'webm'
     else if (file.mimetype === 'video/mp4') extension = 'mp4'
     else if (file.mimetype === 'video/ogg') extension = 'ogv'
     generateRandomString(16)
       .then(randomString => {
-        const filename = randomString
-        cb(null, filename + '.' + extension)
+        cb(null, randomString + '.' + extension)
       })
       .catch(err => {
         logger.error('Cannot generate random string for file name.', err)
@@ -92,7 +92,7 @@ videosRouter.put('/:id',
   videosUpdateValidator,
   updateVideoRetryWrapper
 )
-videosRouter.post('/',
+videosRouter.post('/upload',
   authenticate,
   reqFiles,
   videosAddValidator,
@@ -127,15 +127,15 @@ export {
 
 // ---------------------------------------------------------------------------
 
-function listVideoCategories (req: express.Request, res: express.Response, next: express.NextFunction) {
+function listVideoCategories (req: express.Request, res: express.Response) {
   res.json(VIDEO_CATEGORIES)
 }
 
-function listVideoLicences (req: express.Request, res: express.Response, next: express.NextFunction) {
+function listVideoLicences (req: express.Request, res: express.Response) {
   res.json(VIDEO_LICENCES)
 }
 
-function listVideoLanguages (req: express.Request, res: express.Response, next: express.NextFunction) {
+function listVideoLanguages (req: express.Request, res: express.Response) {
   res.json(VIDEO_LANGUAGES)
 }
 
@@ -143,7 +143,7 @@ function listVideoLanguages (req: express.Request, res: express.Response, next:
 // We need this because we run the transaction in SERIALIZABLE isolation that can fail
 function addVideoRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
   const options = {
-    arguments: [ req, res, req.files.videofile[0] ],
+    arguments: [ req, res, req.files['videofile'][0] ],
     errorMessage: 'Cannot insert the video with many retries.'
   }
 
@@ -155,8 +155,9 @@ function addVideoRetryWrapper (req: express.Request, res: express.Response, next
     .catch(err => next(err))
 }
 
-function addVideo (req: express.Request, res: express.Response, videoFile: Express.Multer.File) {
-  const videoInfos: VideoCreate = req.body
+function addVideo (req: express.Request, res: express.Response, videoPhysicalFile: Express.Multer.File) {
+  const videoInfo: VideoCreate = req.body
+  let videoUUID = ''
 
   return db.sequelize.transaction(t => {
     const user = res.locals.oauth.token.User
@@ -168,22 +169,22 @@ function addVideo (req: express.Request, res: express.Response, videoFile: Expre
 
     return db.Author.findOrCreateAuthor(name, podId, userId, t)
       .then(author => {
-        const tags = videoInfos.tags
+        const tags = videoInfo.tags
         if (!tags) return { author, tagInstances: undefined }
 
         return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances }))
       })
       .then(({ author, tagInstances }) => {
         const videoData = {
-          name: videoInfos.name,
+          name: videoInfo.name,
           remote: false,
-          extname: path.extname(videoFile.filename),
-          category: videoInfos.category,
-          licence: videoInfos.licence,
-          language: videoInfos.language,
-          nsfw: videoInfos.nsfw,
-          description: videoInfos.description,
-          duration: videoFile['duration'], // duration was added by a previous middleware
+          extname: extname(videoPhysicalFile.filename),
+          category: videoInfo.category,
+          licence: videoInfo.licence,
+          language: videoInfo.language,
+          nsfw: videoInfo.nsfw,
+          description: videoInfo.description,
+          duration: videoPhysicalFile['duration'], // duration was added by a previous middleware
           authorId: author.id
         }
 
@@ -191,28 +192,69 @@ function addVideo (req: express.Request, res: express.Response, videoFile: Expre
         return { author, tagInstances, video }
       })
       .then(({ author, tagInstances, video }) => {
+        const videoFileData = {
+          extname: extname(videoPhysicalFile.filename),
+          resolution: 0, // TODO: improve readability,
+          size: videoPhysicalFile.size
+        }
+
+        const videoFile = db.VideoFile.build(videoFileData)
+        return { author, tagInstances, video, videoFile }
+      })
+      .then(({ author, tagInstances, video, videoFile }) => {
         const videoDir = CONFIG.STORAGE.VIDEOS_DIR
-        const source = path.join(videoDir, videoFile.filename)
-        const destination = path.join(videoDir, video.getVideoFilename())
+        const source = join(videoDir, videoPhysicalFile.filename)
+        const destination = join(videoDir, video.getVideoFilename(videoFile))
 
         return renamePromise(source, destination)
           .then(() => {
             // This is important in case if there is another attempt in the retry process
-            videoFile.filename = video.getVideoFilename()
-            return { author, tagInstances, video }
+            videoPhysicalFile.filename = video.getVideoFilename(videoFile)
+            return { author, tagInstances, video, videoFile }
           })
       })
-      .then(({ author, tagInstances, video }) => {
+      .then(({ author, tagInstances, video, videoFile }) => {
+        const tasks = []
+
+        tasks.push(
+          video.createTorrentAndSetInfoHash(videoFile),
+          video.createThumbnail(videoFile),
+          video.createPreview(videoFile)
+        )
+
+        if (CONFIG.TRANSCODING.ENABLED === true) {
+          // Put uuid because we don't have id auto incremented for now
+          const dataInput = {
+            videoUUID: video.uuid
+          }
+
+          tasks.push(
+            JobScheduler.Instance.createJob(t, 'videoTranscoder', dataInput)
+          )
+        }
+
+        return Promise.all(tasks).then(() => ({ author, tagInstances, video, videoFile }))
+      })
+      .then(({ author, tagInstances, video, videoFile }) => {
         const options = { transaction: t }
 
         return video.save(options)
           .then(videoCreated => {
-            // Do not forget to add Author informations to the created video
+            // Do not forget to add Author information to the created video
             videoCreated.Author = author
+            videoUUID = videoCreated.uuid
 
-            return { tagInstances, video: videoCreated }
+            return { tagInstances, video: videoCreated, videoFile }
           })
       })
+      .then(({ tagInstances, video, videoFile }) => {
+        const options = { transaction: t }
+        videoFile.videoId = video.id
+
+        return videoFile.save(options)
+          .then(() => video.VideoFiles = [ videoFile ])
+          .then(() => ({ tagInstances, video }))
+      })
       .then(({ tagInstances, video }) => {
         if (!tagInstances) return video
 
@@ -224,7 +266,7 @@ function addVideo (req: express.Request, res: express.Response, videoFile: Expre
           })
       })
       .then(video => {
-        // Let transcoding job send the video to friends because the videofile extension might change
+        // Let transcoding job send the video to friends because the video file extension might change
         if (CONFIG.TRANSCODING.ENABLED === true) return undefined
 
         return video.toAddRemoteJSON()
@@ -234,9 +276,9 @@ function addVideo (req: express.Request, res: express.Response, videoFile: Expre
           })
       })
   })
-  .then(() => logger.info('Video with name %s created.', videoInfos.name))
+  .then(() => logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoUUID))
   .catch((err: Error) => {
-    logger.debug('Cannot insert the video.', { error: err.stack })
+    logger.debug('Cannot insert the video.', err)
     throw err
   })
 }
@@ -258,14 +300,14 @@ function updateVideoRetryWrapper (req: express.Request, res: express.Response, n
 function updateVideo (req: express.Request, res: express.Response) {
   const videoInstance = res.locals.video
   const videoFieldsSave = videoInstance.toJSON()
-  const videoInfosToUpdate: VideoUpdate = req.body
+  const videoInfoToUpdate: VideoUpdate = req.body
 
   return db.sequelize.transaction(t => {
     let tagsPromise: Promise<TagInstance[]>
-    if (!videoInfosToUpdate.tags) {
+    if (!videoInfoToUpdate.tags) {
       tagsPromise = Promise.resolve(null)
     } else {
-      tagsPromise = db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t)
+      tagsPromise = db.Tag.findOrCreateTags(videoInfoToUpdate.tags, t)
     }
 
     return tagsPromise
@@ -274,12 +316,12 @@ function updateVideo (req: express.Request, res: express.Response) {
           transaction: t
         }
 
-        if (videoInfosToUpdate.name !== undefined) videoInstance.set('name', videoInfosToUpdate.name)
-        if (videoInfosToUpdate.category !== undefined) videoInstance.set('category', videoInfosToUpdate.category)
-        if (videoInfosToUpdate.licence !== undefined) videoInstance.set('licence', videoInfosToUpdate.licence)
-        if (videoInfosToUpdate.language !== undefined) videoInstance.set('language', videoInfosToUpdate.language)
-        if (videoInfosToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfosToUpdate.nsfw)
-        if (videoInfosToUpdate.description !== undefined) videoInstance.set('description', videoInfosToUpdate.description)
+        if (videoInfoToUpdate.name !== undefined) videoInstance.set('name', videoInfoToUpdate.name)
+        if (videoInfoToUpdate.category !== undefined) videoInstance.set('category', videoInfoToUpdate.category)
+        if (videoInfoToUpdate.licence !== undefined) videoInstance.set('licence', videoInfoToUpdate.licence)
+        if (videoInfoToUpdate.language !== undefined) videoInstance.set('language', videoInfoToUpdate.language)
+        if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw)
+        if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
 
         return videoInstance.save(options).then(() => tagInstances)
       })
@@ -302,7 +344,7 @@ function updateVideo (req: express.Request, res: express.Response) {
       })
   })
   .then(() => {
-    logger.info('Video with name %s updated.', videoInstance.name)
+    logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid)
   })
   .catch(err => {
     logger.debug('Cannot update the video.', err)
@@ -310,7 +352,7 @@ function updateVideo (req: express.Request, res: express.Response) {
     // Force fields we want to update
     // If the transaction is retried, sequelize will think the object has not changed
     // So it will skip the SQL request, even if the last one was ROLLBACKed!
-    Object.keys(videoFieldsSave).forEach(function (key) {
+    Object.keys(videoFieldsSave).forEach(key => {
       const value = videoFieldsSave[key]
       videoInstance.set(key, value)
     })
@@ -319,7 +361,7 @@ function updateVideo (req: express.Request, res: express.Response) {
   })
 }
 
-function getVideo (req: express.Request, res: express.Response, next: express.NextFunction) {
+function getVideo (req: express.Request, res: express.Response) {
   const videoInstance = res.locals.video
 
   if (videoInstance.isOwned()) {
@@ -345,12 +387,12 @@ function getVideo (req: express.Request, res: express.Response, next: express.Ne
   }
 
   // Do not wait the view system
-  res.json(videoInstance.toFormatedJSON())
+  res.json(videoInstance.toFormattedJSON())
 }
 
 function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
   db.Video.listForApi(req.query.start, req.query.count, req.query.sort)
-    .then(result => res.json(getFormatedObjects(result.data, result.total)))
+    .then(result => res.json(getFormattedObjects(result.data, result.total)))
     .catch(err => next(err))
 }
 
@@ -358,7 +400,10 @@ function removeVideo (req: express.Request, res: express.Response, next: express
   const videoInstance = res.locals.video
 
   videoInstance.destroy()
-    .then(() => res.type('json').status(204).end())
+    .then(() => {
+      logger.info('Video with name %s and uuid %s deleted.', videoInstance.name, videoInstance.uuid)
+      res.type('json').status(204).end()
+    })
     .catch(err => {
       logger.error('Errors when removed the video.', err)
       return next(err)
@@ -367,6 +412,6 @@ function removeVideo (req: express.Request, res: express.Response, next: express
 
 function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
   db.Video.searchAndPopulateAuthorAndPodAndTags(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort)
-    .then(result => res.json(getFormatedObjects(result.data, result.total)))
+    .then(result => res.json(getFormattedObjects(result.data, result.total)))
     .catch(err => next(err))
 }