Server: generate magnet uri on the fly
authorChocobozzz <florian.bigard@gmail.com>
Fri, 11 Nov 2016 14:20:03 +0000 (15:20 +0100)
committerChocobozzz <florian.bigard@gmail.com>
Wed, 16 Nov 2016 19:29:26 +0000 (20:29 +0100)
server/controllers/api/videos.js
server/helpers/custom-validators/videos.js
server/initializers/constants.js
server/models/video.js

index 2c9e4940ece24bba1e87a6fe947a964b71108833..daf452573f05cce6e78cc4fedfa470e59d108ef2 100644 (file)
@@ -110,7 +110,7 @@ function addVideo (req, res, next) {
     function renameVideoFile (video, callback) {
       const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR
       const source = path.join(videoDir, videoFile.filename)
-      const destination = path.join(videoDir, video.getFilename())
+      const destination = path.join(videoDir, video.getVideoFilename())
 
       fs.rename(source, destination, function (err) {
         return callback(err, video)
@@ -118,7 +118,7 @@ function addVideo (req, res, next) {
     },
 
     function insertIntoDB (video, callback) {
-      video.save(function (err, video, videoFile) {
+      video.save(function (err, video) {
         // Assert there are only one argument sent to the next function (video)
         return callback(err, video)
       })
index c4c59808f9b775bf817b9c138b214e8542be8f8c..166158ef3700b13906ff3d7592c4817037384069 100644 (file)
@@ -13,7 +13,7 @@ const videosValidators = {
   isVideoDateValid,
   isVideoDescriptionValid,
   isVideoDurationValid,
-  isVideoMagnetUriValid,
+  isVideoMagnetValid,
   isVideoNameValid,
   isVideoPodUrlValid,
   isVideoTagsValid,
@@ -31,7 +31,7 @@ function isEachRemoteVideosValid (requests) {
         isVideoDateValid(video.createdDate) &&
         isVideoDescriptionValid(video.description) &&
         isVideoDurationValid(video.duration) &&
-        isVideoMagnetUriValid(video.magnetUri) &&
+        isVideoMagnetValid(video.magnetUri) &&
         isVideoNameValid(video.name) &&
         isVideoPodUrlValid(video.podUrl) &&
         isVideoTagsValid(video.tags) &&
@@ -62,8 +62,8 @@ function isVideoDurationValid (value) {
   return validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
 }
 
-function isVideoMagnetUriValid (value) {
-  return validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.MAGNET_URI)
+function isVideoMagnetValid (value) {
+  return validator.isLength(value.infoHash, VIDEOS_CONSTRAINTS_FIELDS.MAGNET.XT)
 }
 
 function isVideoNameValid (value) {
index a50eb2f662fed5930ac6c089b488d48caa92d1c2..55129fa3e3a057e9671659797cd733ef47354f1b 100644 (file)
@@ -66,7 +66,9 @@ const CONSTRAINTS_FIELDS = {
   VIDEOS: {
     NAME: { min: 3, max: 50 }, // Length
     DESCRIPTION: { min: 3, max: 250 }, // Length
-    MAGNET_URI: { min: 10 }, // Length
+    MAGNET: {
+      XT: { min: 10 } // Length
+    },
     DURATION: { min: 1, max: 7200 }, // Number
     TAGS: { min: 1, max: 3 }, // Number of total tags
     TAG: { min: 2, max: 10 }, // Length
@@ -131,13 +133,18 @@ const REQUEST_ENDPOINTS = {
 
 // ---------------------------------------------------------------------------
 
+const REMOTE_SCHEME = {
+  HTTP: 'https',
+  WS: 'WS'
+}
+
 // Password encryption
 const BCRYPT_SALT_SIZE = 10
 
 // Express static paths (router)
 const STATIC_PATHS = {
-  PREVIEWS: '/static/previews',
-  THUMBNAILS: '/static/thumbnails',
+  PREVIEWS: '/static/previews/',
+  THUMBNAILS: '/static/thumbnails/',
   TORRENTS: '/static/torrents/',
   WEBSEED: '/static/webseed/'
 }
@@ -161,6 +168,8 @@ if (isTestInstance() === true) {
   CONSTRAINTS_FIELDS.VIDEOS.DURATION.max = 14
   FRIEND_SCORE.BASE = 20
   REQUESTS_INTERVAL = 10000
+  REMOTE_SCHEME.HTTP = 'http'
+  REMOTE_SCHEME.WS = 'ws'
   STATIC_MAX_AGE = 0
 }
 
@@ -177,12 +186,13 @@ module.exports = {
   OAUTH_LIFETIME,
   PAGINATION_COUNT_DEFAULT,
   PODS_SCORE,
+  PREVIEWS_SIZE,
+  REMOTE_SCHEME,
   REQUEST_ENDPOINTS,
   REQUESTS_IN_PARALLEL,
   REQUESTS_INTERVAL,
   REQUESTS_LIMIT,
   RETRY_REQUESTS,
-  PREVIEWS_SIZE,
   SEARCHABLE_COLUMNS,
   SORTABLE_COLUMNS,
   STATIC_MAX_AGE,
index 6cffa87afa206054c7d7d7f304fcd0ae44d0e13b..19136ba2544fc96897e51f4fdcd0f2ab3f2918fd 100644 (file)
@@ -3,10 +3,10 @@
 const createTorrent = require('create-torrent')
 const ffmpeg = require('fluent-ffmpeg')
 const fs = require('fs')
+const magnetUtil = require('magnet-uri')
 const parallel = require('async/parallel')
 const parseTorrent = require('parse-torrent')
 const pathUtils = require('path')
-const magnet = require('magnet-uri')
 const mongoose = require('mongoose')
 
 const constants = require('../initializers/constants')
@@ -25,7 +25,9 @@ const VideoSchema = mongoose.Schema({
   },
   remoteId: mongoose.Schema.Types.ObjectId,
   description: String,
-  magnetUri: String,
+  magnet: {
+    infoHash: String
+  },
   podUrl: String,
   author: String,
   duration: Number,
@@ -39,7 +41,6 @@ const VideoSchema = mongoose.Schema({
 
 VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid)
 VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid)
-VideoSchema.path('magnetUri').validate(customVideosValidators.isVideoMagnetUriValid)
 VideoSchema.path('podUrl').validate(customVideosValidators.isVideoPodUrlValid)
 VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid)
 VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid)
@@ -51,8 +52,10 @@ VideoSchema.path('thumbnail').validate(function (value) {
 VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid)
 
 VideoSchema.methods = {
-  getFilename,
-  getJPEGName,
+  generateMagnetUri,
+  getVideoFilename,
+  getThumbnailName,
+  getPreviewName,
   getTorrentName,
   isOwned,
   toFormatedJSON,
@@ -103,8 +106,8 @@ VideoSchema.pre('save', function (next) {
   const tasks = []
 
   if (video.isOwned()) {
-    const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getFilename())
-    this.podUrl = constants.CONFIG.WEBSERVER.URL
+    const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
+    this.podUrl = constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT
 
     tasks.push(
       // TODO: refractoring
@@ -114,7 +117,7 @@ VideoSchema.pre('save', function (next) {
             [ constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
           ],
           urlList: [
-            constants.CONFIG.WEBSERVER.URL + constants.STATIC_PATHS.WEBSEED + video.getFilename()
+            constants.CONFIG.WEBSERVER.URL + constants.STATIC_PATHS.WEBSEED + video.getVideoFilename()
           ]
         }
 
@@ -125,9 +128,9 @@ VideoSchema.pre('save', function (next) {
             if (err) return callback(err)
 
             const parsedTorrent = parseTorrent(torrent)
-            parsedTorrent.xs = video.podUrl + constants.STATIC_PATHS.TORRENTS + video.getTorrentName()
-            video.magnetUri = magnet.encode(parsedTorrent)
+            video.magnet.infoHash = parsedTorrent.infoHash
 
+            console.log(parsedTorrent)
             callback(null)
           })
         })
@@ -150,16 +153,57 @@ mongoose.model('Video', VideoSchema)
 
 // ------------------------------ METHODS ------------------------------
 
-function getFilename () {
-  return this._id + this.extname
+function generateMagnetUri () {
+  let baseUrlHttp, baseUrlWs
+
+  if (this.isOwned()) {
+    baseUrlHttp = constants.CONFIG.WEBSERVER.URL
+    baseUrlWs = constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT
+  } else {
+    baseUrlHttp = constants.REMOTE_SCHEME.HTTP + this.podUrl
+    baseUrlWs = constants.REMOTE_SCHEME.WS + this.podUrl
+  }
+
+  const xs = baseUrlHttp + constants.STATIC_PATHS.TORRENTS + this.getTorrentName()
+  const announce = baseUrlWs + '/tracker/socket'
+  const urlList = [ baseUrlHttp + constants.STATIC_PATHS.WEBSEED + this.getVideoFilename() ]
+
+  const magnetHash = {
+    xs,
+    announce,
+    urlList,
+    infoHash: this.magnet.infoHash,
+    name: this.name
+  }
+
+  return magnetUtil.encode(magnetHash)
 }
 
-function getJPEGName () {
+function getVideoFilename () {
+  if (this.isOwned()) return this._id + this.extname
+
+  return this.remoteId + this.extname
+}
+
+function getThumbnailName () {
+  // We always have a copy of the thumbnail
   return this._id + '.jpg'
 }
 
+function getPreviewName () {
+  const extension = '.jpg'
+
+  if (this.isOwned()) return this._id + extension
+
+  return this.remoteId + extension
+}
+
 function getTorrentName () {
-  return this._id + '.torrent'
+  const extension = '.torrent'
+
+  if (this.isOwned()) return this._id + extension
+
+  return this.remoteId + extension
 }
 
 function isOwned () {
@@ -171,13 +215,13 @@ function toFormatedJSON () {
     id: this._id,
     name: this.name,
     description: this.description,
-    podUrl: this.podUrl.replace(/^https?:\/\//, ''),
+    podUrl: this.podUrl,
     isLocal: this.isOwned(),
-    magnetUri: this.magnetUri,
+    magnetUri: this.generateMagnetUri(),
     author: this.author,
     duration: this.duration,
     tags: this.tags,
-    thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getJPEGName(),
+    thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getThumbnailName(),
     createdDate: this.createdDate
   }
 
@@ -188,7 +232,7 @@ function toRemoteJSON (callback) {
   const self = this
 
   // Convert thumbnail to base64
-  const thumbnailPath = pathUtils.join(constants.CONFIG.STORAGE.THUMBNAILS_DIR, this.getJPEGName())
+  const thumbnailPath = pathUtils.join(constants.CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
   fs.readFile(thumbnailPath, function (err, thumbnailData) {
     if (err) {
       logger.error('Cannot read the thumbnail of the video')
@@ -198,7 +242,7 @@ function toRemoteJSON (callback) {
     const remoteVideo = {
       name: self.name,
       description: self.description,
-      magnetUri: self.magnetUri,
+      magnet: self.magnet,
       remoteId: self._id,
       author: self.author,
       duration: self.duration,
@@ -267,11 +311,11 @@ function search (value, field, start, count, sort, callback) {
 // ---------------------------------------------------------------------------
 
 function removeThumbnail (video, callback) {
-  fs.unlink(constants.CONFIG.STORAGE.THUMBNAILS_DIR + video.getJPEGName(), callback)
+  fs.unlink(constants.CONFIG.STORAGE.THUMBNAILS_DIR + video.getThumbnailName(), callback)
 }
 
 function removeFile (video, callback) {
-  fs.unlink(constants.CONFIG.STORAGE.VIDEOS_DIR + video.getFilename(), callback)
+  fs.unlink(constants.CONFIG.STORAGE.VIDEOS_DIR + video.getVideoFilename(), callback)
 }
 
 function removeTorrent (video, callback) {
@@ -280,22 +324,21 @@ function removeTorrent (video, callback) {
 
 function removePreview (video, callback) {
   // Same name than video thumnail
-  // TODO: refractoring
-  fs.unlink(constants.CONFIG.STORAGE.PREVIEWS_DIR + video.getJPEGName(), callback)
+  fs.unlink(constants.CONFIG.STORAGE.PREVIEWS_DIR + video.getPreviewName(), callback)
 }
 
 function createPreview (video, videoPath, callback) {
-  generateImage(video, videoPath, constants.CONFIG.STORAGE.PREVIEWS_DIR, callback)
+  generateImage(video, videoPath, constants.CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName(), callback)
 }
 
 function createThumbnail (video, videoPath, callback) {
-  generateImage(video, videoPath, constants.CONFIG.STORAGE.THUMBNAILS_DIR, constants.THUMBNAILS_SIZE, callback)
+  generateImage(video, videoPath, constants.CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName(), constants.THUMBNAILS_SIZE, callback)
 }
 
 function generateThumbnailFromBase64 (video, thumbnailData, callback) {
   // Creating the thumbnail for this remote video)
 
-  const thumbnailName = video.getJPEGName()
+  const thumbnailName = video.getThumbnailName()
   const thumbnailPath = constants.CONFIG.STORAGE.THUMBNAILS_DIR + thumbnailName
   fs.writeFile(thumbnailPath, thumbnailData, { encoding: 'base64' }, function (err) {
     if (err) return callback(err)
@@ -304,10 +347,9 @@ function generateThumbnailFromBase64 (video, thumbnailData, callback) {
   })
 }
 
-function generateImage (video, videoPath, folder, size, callback) {
-  const filename = video.getJPEGName()
+function generateImage (video, videoPath, folder, imageName, size, callback) {
   const options = {
-    filename,
+    filename: imageName,
     count: 1,
     folder
   }
@@ -321,7 +363,7 @@ function generateImage (video, videoPath, folder, size, callback) {
   ffmpeg(videoPath)
     .on('error', callback)
     .on('end', function () {
-      callback(null, filename)
+      callback(null, imageName)
     })
     .thumbnail(options)
 }