aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video.js
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/video.js')
-rw-r--r--server/models/video.js146
1 files changed, 76 insertions, 70 deletions
diff --git a/server/models/video.js b/server/models/video.js
index bfa1fca15..6cffa87af 100644
--- a/server/models/video.js
+++ b/server/models/video.js
@@ -13,14 +13,17 @@ const constants = require('../initializers/constants')
13const customVideosValidators = require('../helpers/custom-validators').videos 13const customVideosValidators = require('../helpers/custom-validators').videos
14const logger = require('../helpers/logger') 14const logger = require('../helpers/logger')
15const modelUtils = require('./utils') 15const modelUtils = require('./utils')
16const utils = require('../helpers/utils')
17 16
18// --------------------------------------------------------------------------- 17// ---------------------------------------------------------------------------
19 18
20// TODO: add indexes on searchable columns 19// TODO: add indexes on searchable columns
21const VideoSchema = mongoose.Schema({ 20const VideoSchema = mongoose.Schema({
22 name: String, 21 name: String,
23 filename: String, 22 extname: {
23 type: String,
24 enum: [ '.mp4', '.webm', '.ogv' ]
25 },
26 remoteId: mongoose.Schema.Types.ObjectId,
24 description: String, 27 description: String,
25 magnetUri: String, 28 magnetUri: String,
26 podUrl: String, 29 podUrl: String,
@@ -48,6 +51,9 @@ VideoSchema.path('thumbnail').validate(function (value) {
48VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid) 51VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid)
49 52
50VideoSchema.methods = { 53VideoSchema.methods = {
54 getFilename,
55 getJPEGName,
56 getTorrentName,
51 isOwned, 57 isOwned,
52 toFormatedJSON, 58 toFormatedJSON,
53 toRemoteJSON 59 toRemoteJSON
@@ -56,7 +62,7 @@ VideoSchema.methods = {
56VideoSchema.statics = { 62VideoSchema.statics = {
57 getDurationFromFile, 63 getDurationFromFile,
58 listForApi, 64 listForApi,
59 listByUrlAndMagnet, 65 listByUrlAndRemoteId,
60 listByUrl, 66 listByUrl,
61 listOwned, 67 listOwned,
62 listOwnedByAuthor, 68 listOwnedByAuthor,
@@ -97,7 +103,7 @@ VideoSchema.pre('save', function (next) {
97 const tasks = [] 103 const tasks = []
98 104
99 if (video.isOwned()) { 105 if (video.isOwned()) {
100 const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.filename) 106 const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getFilename())
101 this.podUrl = constants.CONFIG.WEBSERVER.URL 107 this.podUrl = constants.CONFIG.WEBSERVER.URL
102 108
103 tasks.push( 109 tasks.push(
@@ -108,18 +114,18 @@ VideoSchema.pre('save', function (next) {
108 [ constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT + '/tracker/socket' ] 114 [ constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
109 ], 115 ],
110 urlList: [ 116 urlList: [
111 constants.CONFIG.WEBSERVER.URL + constants.STATIC_PATHS.WEBSEED + video.filename 117 constants.CONFIG.WEBSERVER.URL + constants.STATIC_PATHS.WEBSEED + video.getFilename()
112 ] 118 ]
113 } 119 }
114 120
115 createTorrent(videoPath, options, function (err, torrent) { 121 createTorrent(videoPath, options, function (err, torrent) {
116 if (err) return callback(err) 122 if (err) return callback(err)
117 123
118 fs.writeFile(constants.CONFIG.STORAGE.TORRENTS_DIR + video.filename + '.torrent', torrent, function (err) { 124 fs.writeFile(constants.CONFIG.STORAGE.TORRENTS_DIR + video.getTorrentName(), torrent, function (err) {
119 if (err) return callback(err) 125 if (err) return callback(err)
120 126
121 const parsedTorrent = parseTorrent(torrent) 127 const parsedTorrent = parseTorrent(torrent)
122 parsedTorrent.xs = video.podUrl + constants.STATIC_PATHS.TORRENTS + video.filename + '.torrent' 128 parsedTorrent.xs = video.podUrl + constants.STATIC_PATHS.TORRENTS + video.getTorrentName()
123 video.magnetUri = magnet.encode(parsedTorrent) 129 video.magnetUri = magnet.encode(parsedTorrent)
124 130
125 callback(null) 131 callback(null)
@@ -127,28 +133,16 @@ VideoSchema.pre('save', function (next) {
127 }) 133 })
128 }, 134 },
129 function (callback) { 135 function (callback) {
130 createThumbnail(videoPath, callback) 136 createThumbnail(video, videoPath, callback)
131 }, 137 },
132 function (callback) { 138 function (callback) {
133 createPreview(videoPath, callback) 139 createPreview(video, videoPath, callback)
134 } 140 }
135 ) 141 )
136 142
137 parallel(tasks, function (err, results) { 143 parallel(tasks, next)
138 if (err) return next(err)
139
140 video.thumbnail = results[1]
141
142 return next()
143 })
144 } else { 144 } else {
145 generateThumbnailFromBase64(video.thumbnail, function (err, thumbnailName) { 145 generateThumbnailFromBase64(video, video.thumbnail, next)
146 if (err) return next(err)
147
148 video.thumbnail = thumbnailName
149
150 return next()
151 })
152 } 146 }
153}) 147})
154 148
@@ -156,8 +150,20 @@ mongoose.model('Video', VideoSchema)
156 150
157// ------------------------------ METHODS ------------------------------ 151// ------------------------------ METHODS ------------------------------
158 152
153function getFilename () {
154 return this._id + this.extname
155}
156
157function getJPEGName () {
158 return this._id + '.jpg'
159}
160
161function getTorrentName () {
162 return this._id + '.torrent'
163}
164
159function isOwned () { 165function isOwned () {
160 return this.filename !== null 166 return this.remoteId === null
161} 167}
162 168
163function toFormatedJSON () { 169function toFormatedJSON () {
@@ -171,7 +177,7 @@ function toFormatedJSON () {
171 author: this.author, 177 author: this.author,
172 duration: this.duration, 178 duration: this.duration,
173 tags: this.tags, 179 tags: this.tags,
174 thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.thumbnail, 180 thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getJPEGName(),
175 createdDate: this.createdDate 181 createdDate: this.createdDate
176 } 182 }
177 183
@@ -182,7 +188,8 @@ function toRemoteJSON (callback) {
182 const self = this 188 const self = this
183 189
184 // Convert thumbnail to base64 190 // Convert thumbnail to base64
185 fs.readFile(pathUtils.join(constants.CONFIG.STORAGE.THUMBNAILS_DIR, this.thumbnail), function (err, thumbnailData) { 191 const thumbnailPath = pathUtils.join(constants.CONFIG.STORAGE.THUMBNAILS_DIR, this.getJPEGName())
192 fs.readFile(thumbnailPath, function (err, thumbnailData) {
186 if (err) { 193 if (err) {
187 logger.error('Cannot read the thumbnail of the video') 194 logger.error('Cannot read the thumbnail of the video')
188 return callback(err) 195 return callback(err)
@@ -192,7 +199,7 @@ function toRemoteJSON (callback) {
192 name: self.name, 199 name: self.name,
193 description: self.description, 200 description: self.description,
194 magnetUri: self.magnetUri, 201 magnetUri: self.magnetUri,
195 filename: null, 202 remoteId: self._id,
196 author: self.author, 203 author: self.author,
197 duration: self.duration, 204 duration: self.duration,
198 thumbnailBase64: new Buffer(thumbnailData).toString('base64'), 205 thumbnailBase64: new Buffer(thumbnailData).toString('base64'),
@@ -220,8 +227,8 @@ function listForApi (start, count, sort, callback) {
220 return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback) 227 return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback)
221} 228}
222 229
223function listByUrlAndMagnet (fromUrl, magnetUri, callback) { 230function listByUrlAndRemoteId (fromUrl, remoteId, callback) {
224 this.find({ podUrl: fromUrl, magnetUri: magnetUri }, callback) 231 this.find({ podUrl: fromUrl, remoteId: remoteId }, callback)
225} 232}
226 233
227function listByUrl (fromUrl, callback) { 234function listByUrl (fromUrl, callback) {
@@ -229,16 +236,16 @@ function listByUrl (fromUrl, callback) {
229} 236}
230 237
231function listOwned (callback) { 238function listOwned (callback) {
232 // If filename is not null this is *our* video 239 // If remoteId is null this is *our* video
233 this.find({ filename: { $ne: null } }, callback) 240 this.find({ remoteId: null }, callback)
234} 241}
235 242
236function listOwnedByAuthor (author, callback) { 243function listOwnedByAuthor (author, callback) {
237 this.find({ filename: { $ne: null }, author: author }, callback) 244 this.find({ remoteId: null, author: author }, callback)
238} 245}
239 246
240function listRemotes (callback) { 247function listRemotes (callback) {
241 this.find({ filename: null }, callback) 248 this.find({ remoteId: { $ne: null } }, callback)
242} 249}
243 250
244function load (id, callback) { 251function load (id, callback) {
@@ -260,62 +267,61 @@ function search (value, field, start, count, sort, callback) {
260// --------------------------------------------------------------------------- 267// ---------------------------------------------------------------------------
261 268
262function removeThumbnail (video, callback) { 269function removeThumbnail (video, callback) {
263 fs.unlink(constants.CONFIG.STORAGE.THUMBNAILS_DIR + video.thumbnail, callback) 270 fs.unlink(constants.CONFIG.STORAGE.THUMBNAILS_DIR + video.getJPEGName(), callback)
264} 271}
265 272
266function removeFile (video, callback) { 273function removeFile (video, callback) {
267 fs.unlink(constants.CONFIG.STORAGE.VIDEOS_DIR + video.filename, callback) 274 fs.unlink(constants.CONFIG.STORAGE.VIDEOS_DIR + video.getFilename(), callback)
268} 275}
269 276
270function removeTorrent (video, callback) { 277function removeTorrent (video, callback) {
271 fs.unlink(constants.CONFIG.STORAGE.TORRENTS_DIR + video.filename + '.torrent', callback) 278 fs.unlink(constants.CONFIG.STORAGE.TORRENTS_DIR + video.getTorrentName(), callback)
272} 279}
273 280
274function removePreview (video, callback) { 281function removePreview (video, callback) {
275 // Same name than video thumnail 282 // Same name than video thumnail
276 // TODO: refractoring 283 // TODO: refractoring
277 fs.unlink(constants.CONFIG.STORAGE.PREVIEWS_DIR + video.thumbnail, callback) 284 fs.unlink(constants.CONFIG.STORAGE.PREVIEWS_DIR + video.getJPEGName(), callback)
278} 285}
279 286
280function createPreview (videoPath, callback) { 287function createPreview (video, videoPath, callback) {
281 const filename = pathUtils.basename(videoPath) + '.jpg' 288 generateImage(video, videoPath, constants.CONFIG.STORAGE.PREVIEWS_DIR, callback)
282 ffmpeg(videoPath)
283 .on('error', callback)
284 .on('end', function () {
285 callback(null, filename)
286 })
287 .thumbnail({
288 count: 1,
289 folder: constants.CONFIG.STORAGE.PREVIEWS_DIR,
290 filename: filename
291 })
292} 289}
293 290
294function createThumbnail (videoPath, callback) { 291function createThumbnail (video, videoPath, callback) {
295 const filename = pathUtils.basename(videoPath) + '.jpg' 292 generateImage(video, videoPath, constants.CONFIG.STORAGE.THUMBNAILS_DIR, constants.THUMBNAILS_SIZE, callback)
296 ffmpeg(videoPath)
297 .on('error', callback)
298 .on('end', function () {
299 callback(null, filename)
300 })
301 .thumbnail({
302 count: 1,
303 folder: constants.CONFIG.STORAGE.THUMBNAILS_DIR,
304 size: constants.THUMBNAILS_SIZE,
305 filename: filename
306 })
307} 293}
308 294
309function generateThumbnailFromBase64 (data, callback) { 295function generateThumbnailFromBase64 (video, thumbnailData, callback) {
310 // Creating the thumbnail for this remote video 296 // Creating the thumbnail for this remote video)
311 utils.generateRandomString(16, function (err, randomString) { 297
298 const thumbnailName = video.getJPEGName()
299 const thumbnailPath = constants.CONFIG.STORAGE.THUMBNAILS_DIR + thumbnailName
300 fs.writeFile(thumbnailPath, thumbnailData, { encoding: 'base64' }, function (err) {
312 if (err) return callback(err) 301 if (err) return callback(err)
313 302
314 const thumbnailName = randomString + '.jpg' 303 return callback(null, thumbnailName)
315 fs.writeFile(constants.CONFIG.STORAGE.THUMBNAILS_DIR + thumbnailName, data, { encoding: 'base64' }, function (err) { 304 })
316 if (err) return callback(err) 305}
306
307function generateImage (video, videoPath, folder, size, callback) {
308 const filename = video.getJPEGName()
309 const options = {
310 filename,
311 count: 1,
312 folder
313 }
317 314
318 return callback(null, thumbnailName) 315 if (!callback) {
316 callback = size
317 } else {
318 options.size = size
319 }
320
321 ffmpeg(videoPath)
322 .on('error', callback)
323 .on('end', function () {
324 callback(null, filename)
319 }) 325 })
320 }) 326 .thumbnail(options)
321} 327}