From 6a94a109b4b89a97fe7bfeded3125fb7aad2ac3b Mon Sep 17 00:00:00 2001
From: Chocobozzz <florian.bigard@gmail.com>
Date: Fri, 11 Nov 2016 11:52:24 +0100
Subject: [PATCH] Server: add video preview

---
 .gitignore                       |  1 +
 config/default.yaml              |  1 +
 server.js                        |  7 ++++++-
 server/initializers/checker.js   |  2 +-
 server/initializers/constants.js |  4 ++++
 server/models/video.js           | 27 ++++++++++++++++++++++++++-
 6 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/.gitignore b/.gitignore
index bd43f0bd1c..28dec58f3d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
 /uploads/
 /videos/
 /thumbnails/
+/previews/
 /certs/
 /logs/
 /torrents/
diff --git a/config/default.yaml b/config/default.yaml
index ad27b4eb89..90f4b94665 100644
--- a/config/default.yaml
+++ b/config/default.yaml
@@ -16,5 +16,6 @@ storage:
   certs: 'certs/'
   videos: 'videos/'
   logs: 'logs/'
+  previews: 'previews/'
   thumbnails: 'thumbnails/'
   torrents: 'torrents/'
diff --git a/server.js b/server.js
index d3d3661add..16e27e852c 100644
--- a/server.js
+++ b/server.js
@@ -71,7 +71,8 @@ const apiRoute = '/api/' + constants.API_VERSION
 app.use(apiRoute, routes.api)
 app.use('/', routes.client)
 
-// Static files
+// Static client files
+// TODO: move in client
 app.use('/client', express.static(path.join(__dirname, '/client/dist'), { maxAge: constants.STATIC_MAX_AGE }))
 // 404 for static files not found
 app.use('/client/*', function (req, res, next) {
@@ -89,6 +90,10 @@ app.use(constants.STATIC_PATHS.WEBSEED, cors(), express.static(videosPhysicalPat
 const thumbnailsPhysicalPath = constants.CONFIG.STORAGE.THUMBNAILS_DIR
 app.use(constants.STATIC_PATHS.THUMBNAILS, express.static(thumbnailsPhysicalPath, { maxAge: constants.STATIC_MAX_AGE }))
 
+// Video previews path for express
+const previewsPhysicalPath = constants.CONFIG.STORAGE.PREVIEWS_DIR
+app.use(constants.STATIC_PATHS.PREVIEWS, express.static(previewsPhysicalPath, { maxAge: constants.STATIC_MAX_AGE }))
+
 // Always serve index client page
 app.use('/*', function (req, res, next) {
   res.sendFile(path.join(__dirname, './client/dist/index.html'))
diff --git a/server/initializers/checker.js b/server/initializers/checker.js
index dad8525fa7..aea013fa9a 100644
--- a/server/initializers/checker.js
+++ b/server/initializers/checker.js
@@ -30,7 +30,7 @@ function checkMissedConfig () {
   const required = [ 'listen.port',
     'webserver.https', 'webserver.hostname', 'webserver.port',
     'database.hostname', 'database.port', 'database.suffix',
-    'storage.certs', 'storage.videos', 'storage.logs', 'storage.thumbnails'
+    'storage.certs', 'storage.videos', 'storage.logs', 'storage.thumbnails', 'storage.previews'
   ]
   const miss = []
 
diff --git a/server/initializers/constants.js b/server/initializers/constants.js
index d345776ff2..a50eb2f662 100644
--- a/server/initializers/constants.js
+++ b/server/initializers/constants.js
@@ -44,6 +44,7 @@ const CONFIG = {
     LOG_DIR: path.join(__dirname, '..', '..', config.get('storage.logs')),
     VIDEOS_DIR: path.join(__dirname, '..', '..', config.get('storage.videos')),
     THUMBNAILS_DIR: path.join(__dirname, '..', '..', config.get('storage.thumbnails')),
+    PREVIEWS_DIR: path.join(__dirname, '..', '..', config.get('storage.previews')),
     TORRENTS_DIR: path.join(__dirname, '..', '..', config.get('storage.torrents'))
   },
   WEBSERVER: {
@@ -135,6 +136,7 @@ const BCRYPT_SALT_SIZE = 10
 
 // Express static paths (router)
 const STATIC_PATHS = {
+  PREVIEWS: '/static/previews',
   THUMBNAILS: '/static/thumbnails',
   TORRENTS: '/static/torrents/',
   WEBSEED: '/static/webseed/'
@@ -145,6 +147,7 @@ let STATIC_MAX_AGE = '30d'
 
 // Videos thumbnail size
 const THUMBNAILS_SIZE = '200x110'
+const PREVIEWS_SIZE = '640x480'
 
 const USER_ROLES = {
   ADMIN: 'admin',
@@ -179,6 +182,7 @@ module.exports = {
   REQUESTS_INTERVAL,
   REQUESTS_LIMIT,
   RETRY_REQUESTS,
+  PREVIEWS_SIZE,
   SEARCHABLE_COLUMNS,
   SORTABLE_COLUMNS,
   STATIC_MAX_AGE,
diff --git a/server/models/video.js b/server/models/video.js
index 673ccabf8e..bfa1fca154 100644
--- a/server/models/video.js
+++ b/server/models/video.js
@@ -82,6 +82,9 @@ VideoSchema.pre('remove', function (next) {
       },
       function (callback) {
         removeTorrent(video, callback)
+      },
+      function (callback) {
+        removePreview(video, callback)
       }
     )
   }
@@ -125,6 +128,9 @@ VideoSchema.pre('save', function (next) {
       },
       function (callback) {
         createThumbnail(videoPath, callback)
+      },
+      function (callback) {
+        createPreview(videoPath, callback)
       }
     )
 
@@ -261,11 +267,30 @@ function removeFile (video, callback) {
   fs.unlink(constants.CONFIG.STORAGE.VIDEOS_DIR + video.filename, callback)
 }
 
-// Maybe the torrent is not seeded, but we catch the error to don't stop the removing process
 function removeTorrent (video, callback) {
   fs.unlink(constants.CONFIG.STORAGE.TORRENTS_DIR + video.filename + '.torrent', callback)
 }
 
+function removePreview (video, callback) {
+  // Same name than video thumnail
+  // TODO: refractoring
+  fs.unlink(constants.CONFIG.STORAGE.PREVIEWS_DIR + video.thumbnail, callback)
+}
+
+function createPreview (videoPath, callback) {
+  const filename = pathUtils.basename(videoPath) + '.jpg'
+  ffmpeg(videoPath)
+    .on('error', callback)
+    .on('end', function () {
+      callback(null, filename)
+    })
+    .thumbnail({
+      count: 1,
+      folder: constants.CONFIG.STORAGE.PREVIEWS_DIR,
+      filename: filename
+    })
+}
+
 function createThumbnail (videoPath, callback) {
   const filename = pathUtils.basename(videoPath) + '.jpg'
   ffmpeg(videoPath)
-- 
GitLab