public/stylesheets/global.css
public/stylesheets/vendor
uploads
+thumbnails
certs: 'certs/'
uploads: 'uploads/'
logs: 'logs/'
+ thumbnails: 'thumbnails/'
network:
friends: []
certs: 'test1/certs/'
uploads: 'test1/uploads/'
logs: 'test1/logs/'
+ thumbnails: 'test1/thumbnails/'
network:
friends:
certs: 'test2/certs/'
uploads: 'test2/uploads/'
logs: 'test2/logs/'
+ thumbnails: 'test2/thumbnails/'
network:
friends:
certs: 'test3/certs/'
uploads: 'test3/uploads/'
logs: 'test3/logs/'
+ thumbnails: 'test3/thumbnails/'
network:
friends:
certs: 'test4/certs/'
uploads: 'test4/uploads/'
logs: 'test4/logs/'
+ thumbnails: 'test4/thumbnails/'
network:
friends:
certs: 'test5/certs/'
uploads: 'test5/uploads/'
logs: 'test5/logs/'
+ thumbnails: 'test5/thumbnails/'
network:
friends:
certs: 'test6/certs/'
uploads: 'test6/uploads/'
logs: 'test6/logs/'
+ thumbnails: 'test6/thumbnails/'
network:
friends:
res.sendStatus(404)
})
+// Thumbnails path for express
+const thumbnails_physical_path = path.join(__dirname, config.get('storage.thumbnails'))
+app.use(constants.THUMBNAILS_STATIC_PATH, express.static(thumbnails_physical_path, { maxAge: 0 }))
+
// Client application
app.use('/*', function (req, res, next) {
res.sendFile(path.join(__dirname, 'client/index.html'))
'use strict'
const express = require('express')
-const fs = require('fs')
const logger = require('../../../helpers/logger')
const friends = require('../../../lib/friends')
const middleware = require('../../../middlewares')
const cacheMiddleware = middleware.cache
-const peertubeCrypto = require('../../../helpers/peertubeCrypto')
const Pods = require('../../../models/pods')
const reqValidator = middleware.reqValidators.pods
const secureMiddleware = middleware.secure
const secureRequest = middleware.reqValidators.remote.secureRequest
+const videos = require('../../../lib/videos')
const Videos = require('../../../models/videos')
const router = express.Router()
Pods.add(informations, function (err) {
if (err) return next(err)
- Videos.addRemotes(informations.videos)
+ // Create the remote videos from the new pod
+ videos.createRemoteVideos(informations.videos, function (err) {
+ if (err) logger.error('Cannot create remote videos.', { error: err })
+ })
- fs.readFile(peertubeCrypto.getCertDir() + 'peertube.pub', 'utf8', function (err, cert) {
+ friends.getMyCertificate(function (err, cert) {
if (err) {
logger.error('Cannot read cert file.')
return next(err)
Pods.remove(url, function (err) {
if (err) return next(err)
- Videos.removeAllRemotesOf(url, function (err) {
- if (err) logger.error('Cannot remove all remote videos of %s.', url)
- else logger.info('%s pod removed.', url)
+ Videos.listFromUrl(url, function (err, videos_list) {
+ if (err) {
+ logger.error('Cannot list videos from url.', { error: err })
+ next(err)
+ }
- res.type('json').status(204).end()
+ videos.removeRemoteVideos(videos_list, function (err) {
+ if (err) {
+ logger.error('Cannot remove remote videos.', { error: err })
+ next(err)
+ }
+
+ res.type('json').status(204).end()
+ })
})
})
}
const secureMiddleware = middleware.secure
const cacheMiddleware = middleware.cache
const reqValidator = middleware.reqValidators.remote
-const videos = require('../../../models/videos')
+const logger = require('../../../helpers/logger')
+const Videos = require('../../../models/videos')
+const videos = require('../../../lib/videos')
const router = express.Router()
// ---------------------------------------------------------------------------
function addRemoteVideos (req, res, next) {
- videos.addRemotes(req.body.data, function (err, videos) {
- if (err) return next(err)
+ const videos_to_create = req.body.data
+ videos.createRemoteVideos(videos_to_create, function (err, remote_videos) {
+ if (err) {
+ logger.error('Cannot create remote videos.', { error: err })
+ return next(err)
+ }
- res.json(videos)
+ res.type('json').status(201).end()
})
}
function removeRemoteVideo (req, res, next) {
- const url = req.body.signature.url
+ const fromUrl = req.body.signature.url
const magnetUris = map(req.body.data, 'magnetUri')
- videos.removeRemotesOfByMagnetUris(url, magnetUris, function (err) {
- if (err) return next(err)
+ Videos.listFromUrlAndMagnets(fromUrl, magnetUris, function (err, videos_list) {
+ if (err) {
+ logger.error('Cannot list videos from url and magnets.', { error: err })
+ return next(err)
+ }
- res.type('json').status(204).end()
+ videos.removeRemoteVideos(videos_list, function (err) {
+ if (err) {
+ logger.error('Cannot remove remote videos.', { error: err })
+ return next(err)
+ }
+
+ res.type('json').status(204).end()
+ })
})
}
'use strict'
const config = require('config')
-const crypto = require('crypto')
const express = require('express')
+const fs = require('fs')
+const path = require('path')
const multer = require('multer')
+const constants = require('../../../initializers/constants')
const logger = require('../../../helpers/logger')
const friends = require('../../../lib/friends')
const middleware = require('../../../middlewares')
const oAuth2 = require('../../../middlewares/oauth2')
const cacheMiddleware = middleware.cache
const reqValidator = middleware.reqValidators.videos
+const utils = require('../../../helpers/utils')
const Videos = require('../../../models/videos') // model
const videos = require('../../../lib/videos')
const webtorrent = require('../../../lib/webtorrent')
if (file.mimetype === 'video/webm') extension = 'webm'
else if (file.mimetype === 'video/mp4') extension = 'mp4'
else if (file.mimetype === 'video/ogg') extension = 'ogv'
- crypto.pseudoRandomBytes(16, function (err, raw) {
- const fieldname = err ? undefined : raw.toString('hex')
+ utils.generateRandomString(16, function (err, random_string) {
+ const fieldname = err ? undefined : random_string
cb(null, fieldname + '.' + extension)
})
}
})
const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCount: 1 }])
+const thumbnailsDir = path.join(__dirname, '..', '..', '..', '..', config.get('storage.thumbnails'))
router.get('/', cacheMiddleware.cache(false), listVideos)
router.post('/', oAuth2.authenticate, reqFiles, reqValidator.videosAdd, cacheMiddleware.cache(false), addVideo)
videos.getVideoDuration(video_file.path, function (err, duration) {
if (err) {
// TODO: unseed the video
- logger.error('Cannot retrieve metadata of the file')
+ logger.error('Cannot retrieve metadata of the file.')
return next(err)
}
- const video_data = {
- name: video_infos.name,
- namePath: video_file.filename,
- description: video_infos.description,
- magnetUri: torrent.magnetURI,
- author: res.locals.oauth.token.user.username,
- duration: duration
- }
-
- Videos.add(video_data, function (err) {
+ videos.getVideoThumbnail(video_file.path, function (err, thumbnail_name) {
if (err) {
- // TODO unseed the video
- logger.error('Cannot insert this video in the database.')
+ // TODO: unseed the video
+ logger.error('Cannot make a thumbnail of the video file.')
return next(err)
}
- // Now we'll add the video's meta data to our friends
- friends.addVideoToFriends(video_data)
+ const video_data = {
+ name: video_infos.name,
+ namePath: video_file.filename,
+ description: video_infos.description,
+ magnetUri: torrent.magnetURI,
+ author: res.locals.oauth.token.user.username,
+ duration: duration,
+ thumbnail: thumbnail_name
+ }
- // TODO : include Location of the new video -> 201
- res.type('json').status(204).end()
+ Videos.add(video_data, function (err) {
+ if (err) {
+ // TODO unseed the video
+ logger.error('Cannot insert this video in the database.')
+ return next(err)
+ }
+
+ fs.readFile(thumbnailsDir + thumbnail_name, function (err, data) {
+ if (err) {
+ // TODO: remove video?
+ logger.error('Cannot read the thumbnail of the video')
+ return next(err)
+ }
+
+ // Set the image in base64
+ video_data.thumbnail_base64 = new Buffer(data).toString('base64')
+ // Now we'll add the video's meta data to our friends
+ friends.addVideoToFriends(video_data)
+
+ // TODO : include Location of the new video -> 201
+ res.type('json').status(204).end()
+ })
+ })
})
})
})
Videos.removeOwned(req.params.id, function (err) {
if (err) return next(err)
- const params = {
- name: video.name,
- magnetUri: video.magnetUri
- }
+ videos.removeVideosDataFromDisk([ video ], function (err) {
+ if (err) logger.error('Cannot remove video data from disk.', { video: video })
+
+ const params = {
+ name: video.name,
+ magnetUri: video.magnetUri
+ }
- friends.removeVideoToFriends(params)
- res.type('json').status(204).end()
+ friends.removeVideoToFriends(params)
+ res.type('json').status(204).end()
+ })
})
})
})
isLocal: videos.getVideoState(video_obj).owned,
magnetUri: video_obj.magnetUri,
author: video_obj.author,
- duration: video_obj.duration
+ duration: video_obj.duration,
+ thumbnail_path: constants.THUMBNAILS_STATIC_PATH + '/' + video_obj.thumbnail
}
return formated_video
'use strict'
+const crypto = require('crypto')
+
const logger = require('./logger')
const utils = {
- cleanForExit: cleanForExit
+ cleanForExit: cleanForExit,
+ generateRandomString: generateRandomString
+}
+
+function generateRandomString (size, callback) {
+ crypto.pseudoRandomBytes(size, function (err, raw) {
+ if (err) return callback(err)
+
+ callback(null, raw.toString('hex'))
+ })
}
function cleanForExit (webtorrent_process) {
// Number of retries we make for the make retry requests (to friends...)
let REQUEST_RETRIES = 10
+// Videos thumbnail size
+const THUMBNAILS_SIZE = '200x110'
+
+// Path for access to thumbnails with express router
+const THUMBNAILS_STATIC_PATH = '/static/thumbnails'
+
// Special constants for a test instance
if (isTestInstance() === true) {
FRIEND_BASE_SCORE = 20
FRIEND_BASE_SCORE: FRIEND_BASE_SCORE,
INTERVAL: INTERVAL,
PODS_SCORE: PODS_SCORE,
- REQUEST_RETRIES: REQUEST_RETRIES
+ REQUEST_RETRIES: REQUEST_RETRIES,
+ THUMBNAILS_SIZE: THUMBNAILS_SIZE,
+ THUMBNAILS_STATIC_PATH: THUMBNAILS_STATIC_PATH
}
// ---------------------------------------------------------------------------
const Pods = require('../models/pods')
const requestsScheduler = require('../lib/requestsScheduler')
const requests = require('../helpers/requests')
+const videos = require('../lib/videos')
const Videos = require('../models/videos')
const http = config.get('webserver.https') ? 'https' : 'http'
const pods = {
addVideoToFriends: addVideoToFriends,
hasFriends: hasFriends,
+ getMyCertificate: getMyCertificate,
makeFriends: makeFriends,
quitFriends: quitFriends,
removeVideoToFriends: removeVideoToFriends
})
}
+function getMyCertificate (callback) {
+ fs.readFile(peertubeCrypto.getCertDir() + 'peertube.pub', 'utf8', callback)
+}
+
function makeFriends (callback) {
const pods_score = {}
logger.info('Make friends!')
- fs.readFile(peertubeCrypto.getCertDir() + 'peertube.pub', 'utf8', function (err, cert) {
+ getMyCertificate(function (err, cert) {
if (err) {
logger.error('Cannot read public cert.')
return callback(err)
const urls = config.get('network.friends')
- async.each(urls, function (url, callback) {
- computeForeignPodsList(url, pods_score, callback)
+ async.each(urls, function (url, callback_each) {
+ computeForeignPodsList(url, pods_score, callback_each)
}, function (err) {
if (err) return callback(err)
logger.info('Broke friends, so sad :(')
- Videos.removeAllRemotes(function (err) {
+ Videos.listFromRemotes(function (err, videos_list) {
if (err) return callback(err)
- logger.info('Removed all remote videos.')
- callback(null)
+ videos.removeRemoteVideos(videos_list, function (err) {
+ if (err) {
+ logger.error('Cannot remove remote videos.', { error: err })
+ return callback(err)
+ }
+
+ logger.info('Removed all remote videos.')
+ callback(null)
+ })
})
})
})
if (err) return callback(err)
if (foreign_pods_list.length === 0) return callback()
- async.each(foreign_pods_list, function (foreign_pod, callback_each) {
+ foreign_pods_list.forEach(function (foreign_pod) {
const foreign_url = foreign_pod.url
if (pods_score[foreign_url]) pods_score[foreign_url]++
else pods_score[foreign_url] = 1
-
- callback_each()
- }, function () {
- callback()
})
+
+ callback()
})
}
logger.error('Error with adding %s pod.', pod.url, { error: err })
return callback_each_request()
}
-
- Videos.addRemotes(body.videos, function (err) {
+ console.log('hihi')
+ videos.createRemoteVideos(body.videos, function (err) {
if (err) {
logger.error('Error with adding videos of pod.', pod.url, { error: err })
return callback_each_request()
}
+ console.log('kik')
+
logger.debug('Adding remote videos from %s.', pod.url, { videos: body.videos })
return callback_each_request()
})
const Pods = require('../models/pods')
const Requests = require('../models/requests')
const requests = require('../helpers/requests')
+const videos = require('../lib/videos')
const Videos = require('../models/videos')
let timer = null
requests.makeMultipleRetryRequest(params, pods, callbackEachPodFinished, callbackAllPodsFinished)
function callbackEachPodFinished (err, response, body, url, pod, callback_each_pod_finished) {
- if (err || (response.statusCode !== 200 && response.statusCode !== 204)) {
+ if (err || (response.statusCode !== 200 && response.statusCode !== 201 && response.statusCode !== 204)) {
bad_pods.push(pod._id)
logger.error('Error sending secure request to %s pod.', url, { error: err || new Error('Status code not 20x') })
} else {
const urls = map(pods, 'url')
const ids = map(pods, '_id')
- Videos.removeAllRemotesOf(urls, function (err, r) {
+ Videos.listFromUrls(urls, function (err, videos_list) {
if (err) {
- logger.error('Cannot remove videos from a pod that we removing.', { error: err })
+ logger.error('Cannot list videos urls.', { error: err, urls: urls })
} else {
- const videos_removed = r.result.n
- logger.info('Removed %d videos.', videos_removed)
+ videos.removeRemoteVideos(videos_list, function (err) {
+ if (err) logger.error('Cannot remove remote videos.', { error: err })
+ })
}
Pods.removeAllByIds(ids, function (err, r) {
const async = require('async')
const config = require('config')
const ffmpeg = require('fluent-ffmpeg')
+const fs = require('fs')
+const map = require('lodash/map')
const pathUtils = require('path')
-const webtorrent = require('../lib/webtorrent')
+const constants = require('../initializers/constants')
const logger = require('../helpers/logger')
+const utils = require('../helpers/utils')
const Videos = require('../models/videos')
+const webtorrent = require('../lib/webtorrent')
const uploadDir = pathUtils.join(__dirname, '..', '..', config.get('storage.uploads'))
+const thumbnailsDir = pathUtils.join(__dirname, '..', '..', config.get('storage.thumbnails'))
const videos = {
+ createRemoteVideos: createRemoteVideos,
getVideoDuration: getVideoDuration,
getVideoState: getVideoState,
+ getVideoThumbnail: getVideoThumbnail,
+ removeVideosDataFromDisk: removeVideosDataFromDisk,
+ removeRemoteVideos: removeRemoteVideos,
seed: seed,
seedAllExisting: seedAllExisting
}
+function createRemoteVideos (videos, callback) {
+ // Create the remote videos from the new pod
+ createRemoteVideoObjects(videos, function (err, remote_videos) {
+ if (err) return callback(err)
+
+ Videos.addRemotes(remote_videos, callback)
+ })
+}
+
function getVideoDuration (video_path, callback) {
ffmpeg.ffprobe(video_path, function (err, metadata) {
if (err) return callback(err)
return { exist: exist, owned: owned }
}
+function getVideoThumbnail (video_path, callback) {
+ const filename = pathUtils.basename(video_path) + '.jpg'
+ ffmpeg(video_path)
+ .on('error', callback)
+ .on('end', function () {
+ callback(null, filename)
+ })
+ .thumbnail({
+ count: 1,
+ folder: thumbnailsDir,
+ size: constants.THUMBNAILS_SIZE,
+ filename: filename
+ })
+}
+
+// Remove video datas from disk (video file, thumbnail...)
+function removeVideosDataFromDisk (videos, callback) {
+ async.each(videos, function (video, callback_each) {
+ fs.unlink(thumbnailsDir + video.thumbnail, function (err) {
+ if (err) logger.error('Cannot remove the video thumbnail')
+
+ if (getVideoState(video).owned === true) {
+ fs.unlink(uploadDir + video.namePath, function (err) {
+ if (err) {
+ logger.error('Cannot remove this video file.')
+ return callback_each(err)
+ }
+
+ callback_each(null)
+ })
+ } else {
+ callback_each(null)
+ }
+ })
+ }, callback)
+}
+
+function removeRemoteVideos (videos, callback) {
+ Videos.removeByIds(map(videos, '_id'), function (err) {
+ if (err) return callback(err)
+
+ removeVideosDataFromDisk(videos, callback)
+ })
+}
+
function seed (path, callback) {
logger.info('Seeding %s...', path)
// ---------------------------------------------------------------------------
module.exports = videos
+
+// ---------------------------------------------------------------------------
+
+function createRemoteVideoObjects (videos, callback) {
+ const remote_videos = []
+
+ async.each(videos, function (video, callback_each) {
+ // Creating the thumbnail for this remote video
+ utils.generateRandomString(16, function (err, random_string) {
+ if (err) return callback_each(err)
+
+ const thumbnail_name = random_string + '.jpg'
+ createThumbnailFromBase64(thumbnail_name, video.thumbnail_base64, function (err) {
+ if (err) return callback_each(err)
+
+ const params = {
+ name: video.name,
+ description: video.description,
+ magnetUri: video.magnetUri,
+ podUrl: video.podUrl,
+ duration: video.duration,
+ thumbnail: thumbnail_name
+ }
+ remote_videos.push(params)
+
+ callback_each(null)
+ })
+ })
+ },
+ function (err) {
+ if (err) return callback(err)
+
+ callback(null, remote_videos)
+ })
+}
+
+function createThumbnailFromBase64 (thumbnail_name, data, callback) {
+ fs.writeFile(thumbnailsDir + thumbnail_name, data, { encoding: 'base64' }, callback)
+}
'use strict'
-const async = require('async')
const config = require('config')
-const dz = require('dezalgo')
-const fs = require('fs')
const mongoose = require('mongoose')
-const path = require('path')
const logger = require('../helpers/logger')
const http = config.get('webserver.https') === true ? 'https' : 'http'
const host = config.get('webserver.host')
const port = config.get('webserver.port')
-const uploadDir = path.join(__dirname, '..', '..', config.get('storage.uploads'))
// ---------------------------------------------------------------------------
magnetUri: String,
podUrl: String,
author: String,
- duration: Number
+ duration: Number,
+ thumbnail: String
})
const VideosDB = mongoose.model('videos', videosSchema)
addRemotes: addRemotes,
get: get,
list: list,
+ listFromUrl: listFromUrl,
+ listFromUrls: listFromUrls,
+ listFromUrlAndMagnets: listFromUrlAndMagnets,
+ listFromRemotes: listFromRemotes,
listOwned: listOwned,
removeOwned: removeOwned,
- removeAllRemotes: removeAllRemotes,
- removeAllRemotesOf: removeAllRemotesOf,
- removeRemotesOfByMagnetUris: removeRemotesOfByMagnetUris,
+ removeByIds: removeByIds,
search: search
}
})
}
-// TODO: avoid doublons
function addRemotes (videos, callback) {
- if (!callback) callback = function () {}
-
- const to_add = []
-
- async.each(videos, function (video, callback_each) {
- callback_each = dz(callback_each)
- logger.debug('Add remote video from pod: %s', video.podUrl)
-
- const params = {
- name: video.name,
- namePath: null,
- description: video.description,
- magnetUri: video.magnetUri,
- podUrl: video.podUrl,
- duration: video.duration
- }
-
- to_add.push(params)
-
- callback_each()
- }, function () {
- VideosDB.create(to_add, function (err, videos) {
- if (err) {
- logger.error('Cannot insert this remote video.')
- return callback(err)
- }
-
- return callback(null, videos)
- })
+ videos.forEach(function (video) {
+ // Ensure they are remote videos
+ video.namePath = null
})
+
+ VideosDB.create(videos, callback)
}
function get (id, callback) {
})
}
+function listFromUrl (fromUrl, callback) {
+ VideosDB.find({ podUrl: fromUrl }, callback)
+}
+
+function listFromUrls (fromUrls, callback) {
+ VideosDB.find({ podUrl: { $in: fromUrls } }, callback)
+}
+
+function listFromUrlAndMagnets (fromUrl, magnets, callback) {
+ VideosDB.find({ podUrl: fromUrl, magnetUri: { $in: magnets } }, callback)
+}
+
+function listFromRemotes (callback) {
+ VideosDB.find({ namePath: null }, callback)
+}
+
function listOwned (callback) {
// If namePath is not null this is *our* video
VideosDB.find({ namePath: { $ne: null } }, function (err, videos_list) {
})
}
+// Return the video in the callback
function removeOwned (id, callback) {
- VideosDB.findByIdAndRemove(id, function (err, video) {
- if (err) {
- logger.error('Cannot remove the torrent.')
- return callback(err)
- }
-
- fs.unlink(uploadDir + video.namePath, function (err) {
- if (err) {
- logger.error('Cannot remove this video file.')
- return callback(err)
- }
-
- callback(null)
- })
- })
-}
-
-function removeAllRemotes (callback) {
- VideosDB.remove({ namePath: null }, callback)
-}
-
-function removeAllRemotesOf (fromUrl, callback) {
- VideosDB.remove({ podUrl: fromUrl }, callback)
+ VideosDB.findByIdAndRemove(id, callback)
}
// Use the magnet Uri because the _id field is not the same on different servers
-function removeRemotesOfByMagnetUris (fromUrl, magnetUris, callback) {
- if (callback === undefined) callback = function () {}
-
- VideosDB.find({ magnetUri: { $in: magnetUris } }, function (err, videos) {
- if (err || !videos) {
- logger.error('Cannot find the torrent URI of these remote videos.')
- return callback(err)
- }
-
- const to_remove = []
- async.each(videos, function (video, callback_async) {
- callback_async = dz(callback_async)
-
- if (video.podUrl !== fromUrl) {
- logger.error('The pod %s has not the rights on the video of %s.', fromUrl, video.podUrl)
- } else {
- to_remove.push(video._id)
- }
-
- callback_async()
- }, function () {
- VideosDB.remove({ _id: { $in: to_remove } }, function (err) {
- if (err) {
- logger.error('Cannot remove the remote videos.')
- return callback(err)
- }
-
- logger.info('Removed remote videos from %s.', fromUrl)
- callback(null)
- })
- })
- })
+function removeByIds (ids, callback) {
+ VideosDB.remove({ _id: { $in: ids } }, callback)
}
function search (name, callback) {
expect(videos).to.be.an('array')
expect(videos.length).to.equal(4)
- const video1 = videos[2]
+ // We not sure about the order of the two last uploads
+ let video1 = null
+ let video2 = null
+ if (videos[2].name === 'my super name for pod 3') {
+ video1 = videos[2]
+ video2 = videos[3]
+ } else {
+ video1 = videos[3]
+ video2 = videos[2]
+ }
+
expect(video1.name).to.equal('my super name for pod 3')
expect(video1.description).to.equal('my super description for pod 3')
expect(video1.podUrl).to.equal('http://localhost:9003')
expect(video1.magnetUri).to.exist
expect(video1.duration).to.equal(5)
- const video2 = videos[3]
expect(video2.name).to.equal('my super name for pod 3-2')
expect(video2.description).to.equal('my super description for pod 3-2')
expect(video2.podUrl).to.equal('http://localhost:9003')