aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2016-07-26 22:30:46 +0200
committerChocobozzz <florian.bigard@gmail.com>2016-07-27 21:21:57 +0200
commit052937db8a8d282eccdbdf38d487ed8d85d3c0a7 (patch)
treeda84e08fbe3569c2122c9b733e22768980750744 /server
parent71d3476b825571376b06498326d138685493fb62 (diff)
downloadPeerTube-052937db8a8d282eccdbdf38d487ed8d85d3c0a7.tar.gz
PeerTube-052937db8a8d282eccdbdf38d487ed8d85d3c0a7.tar.zst
PeerTube-052937db8a8d282eccdbdf38d487ed8d85d3c0a7.zip
First draft using only webseed for server
Diffstat (limited to 'server')
-rw-r--r--server/initializers/constants.js12
-rw-r--r--server/lib/webtorrent-process.js92
-rw-r--r--server/lib/webtorrent.js161
-rw-r--r--server/models/video.js56
4 files changed, 30 insertions, 291 deletions
diff --git a/server/initializers/constants.js b/server/initializers/constants.js
index 467816f2c..e0ea188af 100644
--- a/server/initializers/constants.js
+++ b/server/initializers/constants.js
@@ -49,12 +49,16 @@ const SORTABLE_COLUMNS = {
49 VIDEOS: [ 'name', '-name', 'duration', '-duration', 'createdDate', '-createdDate' ] 49 VIDEOS: [ 'name', '-name', 'duration', '-duration', 'createdDate', '-createdDate' ]
50} 50}
51 51
52// Express static paths (router)
53const STATIC_PATHS = {
54 THUMBNAILS: '/static/thumbnails',
55 TORRENTS: '/static/torrents/',
56 WEBSEED: '/static/webseed/'
57}
58
52// Videos thumbnail size 59// Videos thumbnail size
53const THUMBNAILS_SIZE = '200x110' 60const THUMBNAILS_SIZE = '200x110'
54 61
55// Path for access to thumbnails with express router
56const THUMBNAILS_STATIC_PATH = '/static/thumbnails'
57
58const VIDEOS_CONSTRAINTS_FIELDS = { 62const VIDEOS_CONSTRAINTS_FIELDS = {
59 NAME: { min: 3, max: 50 }, // Length 63 NAME: { min: 3, max: 50 }, // Length
60 DESCRIPTION: { min: 3, max: 250 }, // Length 64 DESCRIPTION: { min: 3, max: 250 }, // Length
@@ -89,8 +93,8 @@ module.exports = {
89 SEARCHABLE_COLUMNS: SEARCHABLE_COLUMNS, 93 SEARCHABLE_COLUMNS: SEARCHABLE_COLUMNS,
90 SEEDS_IN_PARALLEL: SEEDS_IN_PARALLEL, 94 SEEDS_IN_PARALLEL: SEEDS_IN_PARALLEL,
91 SORTABLE_COLUMNS: SORTABLE_COLUMNS, 95 SORTABLE_COLUMNS: SORTABLE_COLUMNS,
96 STATIC_PATHS: STATIC_PATHS,
92 THUMBNAILS_SIZE: THUMBNAILS_SIZE, 97 THUMBNAILS_SIZE: THUMBNAILS_SIZE,
93 THUMBNAILS_STATIC_PATH: THUMBNAILS_STATIC_PATH,
94 VIDEOS_CONSTRAINTS_FIELDS: VIDEOS_CONSTRAINTS_FIELDS 98 VIDEOS_CONSTRAINTS_FIELDS: VIDEOS_CONSTRAINTS_FIELDS
95} 99}
96 100
diff --git a/server/lib/webtorrent-process.js b/server/lib/webtorrent-process.js
deleted file mode 100644
index be7ac5bb4..000000000
--- a/server/lib/webtorrent-process.js
+++ /dev/null
@@ -1,92 +0,0 @@
1'use strict'
2
3const WebTorrent = require('webtorrent')
4const ipc = require('node-ipc')
5
6function webtorrent (args) {
7 if (args.length !== 3) {
8 throw new Error('Wrong arguments number: ' + args.length + '/3')
9 }
10
11 const host = args[1]
12 const port = args[2]
13 const nodeKey = 'webtorrentnode' + port
14 const processKey = 'webtorrentprocess' + port
15
16 ipc.config.silent = true
17 ipc.config.id = processKey
18
19 if (host === 'client' && port === '1') global.WEBTORRENT_ANNOUNCE = []
20 else global.WEBTORRENT_ANNOUNCE = 'ws://' + host + ':' + port + '/tracker/socket'
21 const wt = new WebTorrent({ dht: false })
22
23 function seed (data) {
24 const args = data.args
25 const path = args.path
26 const _id = data._id
27
28 wt.seed(path, { announceList: '' }, function (torrent) {
29 const toSend = {
30 magnetUri: torrent.magnetURI
31 }
32
33 ipc.of[nodeKey].emit(nodeKey + '.seedDone.' + _id, toSend)
34 })
35 }
36
37 function add (data) {
38 const args = data.args
39 const magnetUri = args.magnetUri
40 const _id = data._id
41
42 wt.add(magnetUri, function (torrent) {
43 const toSend = {
44 files: []
45 }
46
47 torrent.files.forEach(function (file) {
48 toSend.files.push({ path: file.path })
49 })
50
51 ipc.of[nodeKey].emit(nodeKey + '.addDone.' + _id, toSend)
52 })
53 }
54
55 function remove (data) {
56 const args = data.args
57 const magnetUri = args.magnetUri
58 const _id = data._id
59
60 try {
61 wt.remove(magnetUri, callback)
62 } catch (err) {
63 console.log('Cannot remove the torrent from WebTorrent.')
64 return callback(null)
65 }
66
67 function callback () {
68 const toSend = {}
69 ipc.of[nodeKey].emit(nodeKey + '.removeDone.' + _id, toSend)
70 }
71 }
72
73 console.log('Configuration: ' + host + ':' + port)
74 console.log('Connecting to IPC...')
75
76 ipc.connectTo(nodeKey, function () {
77 ipc.of[nodeKey].on(processKey + '.seed', seed)
78 ipc.of[nodeKey].on(processKey + '.add', add)
79 ipc.of[nodeKey].on(processKey + '.remove', remove)
80
81 ipc.of[nodeKey].emit(processKey + '.ready')
82 console.log('Ready.')
83 })
84
85 process.on('uncaughtException', function (e) {
86 ipc.of[nodeKey].emit(processKey + '.exception', { exception: e.toString() })
87 })
88}
89
90// ---------------------------------------------------------------------------
91
92module.exports = webtorrent
diff --git a/server/lib/webtorrent.js b/server/lib/webtorrent.js
deleted file mode 100644
index bcd30139e..000000000
--- a/server/lib/webtorrent.js
+++ /dev/null
@@ -1,161 +0,0 @@
1'use strict'
2
3const config = require('config')
4const ipc = require('node-ipc')
5const pathUtils = require('path')
6const spawn = require('electron-spawn')
7
8const logger = require('../helpers/logger')
9
10const electronDebug = config.get('electron.debug')
11let host = config.get('webserver.host')
12let port = config.get('webserver.port')
13let nodeKey = 'webtorrentnode' + port
14let processKey = 'webtorrentprocess' + port
15ipc.config.silent = true
16ipc.config.id = nodeKey
17
18const webtorrent = {
19 add: add,
20 app: null, // Pid of the app
21 create: create,
22 remove: remove,
23 seed: seed,
24 silent: false // Useful for beautiful tests
25}
26
27function create (options, callback) {
28 if (typeof options === 'function') {
29 callback = options
30 options = {}
31 }
32
33 // Override options
34 if (options.host) host = options.host
35 if (options.port) {
36 port = options.port
37 nodeKey = 'webtorrentnode' + port
38 processKey = 'webtorrentprocess' + port
39 ipc.config.id = nodeKey
40 }
41
42 ipc.serve(function () {
43 if (!webtorrent.silent) logger.info('IPC server ready.')
44
45 // Run a timeout of 30s after which we exit the process
46 const timeoutWebtorrentProcess = setTimeout(function () {
47 throw new Error('Timeout : cannot run the webtorrent process. Please ensure you have electron-prebuilt npm package installed with xvfb-run.')
48 }, 30000)
49
50 ipc.server.on(processKey + '.ready', function () {
51 if (!webtorrent.silent) logger.info('Webtorrent process ready.')
52 clearTimeout(timeoutWebtorrentProcess)
53 callback()
54 })
55
56 ipc.server.on(processKey + '.exception', function (data) {
57 throw new Error('Received exception error from webtorrent process : ' + data.exception)
58 })
59
60 const webtorrentProcess = spawn(pathUtils.join(__dirname, 'webtorrent-process.js'), host, port, { detached: true })
61
62 if (electronDebug === true) {
63 webtorrentProcess.stderr.on('data', function (data) {
64 logger.debug('Webtorrent process stderr: ', data.toString())
65 })
66
67 webtorrentProcess.stdout.on('data', function (data) {
68 logger.debug('Webtorrent process:', data.toString())
69 })
70 }
71
72 webtorrent.app = webtorrentProcess
73 })
74
75 ipc.server.start()
76}
77
78function seed (path, callback) {
79 const extension = pathUtils.extname(path)
80 const basename = pathUtils.basename(path, extension)
81 const data = {
82 _id: basename,
83 args: {
84 path: path
85 }
86 }
87
88 if (!webtorrent.silent) logger.debug('Node wants to seed %s.', data._id)
89
90 // Finish signal
91 const eventKey = nodeKey + '.seedDone.' + data._id
92 ipc.server.on(eventKey, function listener (received) {
93 if (!webtorrent.silent) logger.debug('Process seeded torrent %s.', received.magnetUri)
94
95 // This is a fake object, we just use the magnetUri in this project
96 const torrent = {
97 magnetURI: received.magnetUri
98 }
99
100 ipc.server.off(eventKey, '*')
101 callback(torrent)
102 })
103
104 ipc.server.broadcast(processKey + '.seed', data)
105}
106
107function add (magnetUri, callback) {
108 const data = {
109 _id: magnetUri,
110 args: {
111 magnetUri: magnetUri
112 }
113 }
114
115 if (!webtorrent.silent) logger.debug('Node wants to add ' + data._id)
116
117 // Finish signal
118 const eventKey = nodeKey + '.addDone.' + data._id
119 ipc.server.on(eventKey, function (received) {
120 if (!webtorrent.silent) logger.debug('Process added torrent.')
121
122 // This is a fake object, we just use the magnetUri in this project
123 const torrent = {
124 files: received.files
125 }
126
127 ipc.server.off(eventKey, '*')
128 callback(torrent)
129 })
130
131 ipc.server.broadcast(processKey + '.add', data)
132}
133
134function remove (magnetUri, callback) {
135 const data = {
136 _id: magnetUri,
137 args: {
138 magnetUri: magnetUri
139 }
140 }
141
142 if (!webtorrent.silent) logger.debug('Node wants to stop seeding %s.', data._id)
143
144 // Finish signal
145 const eventKey = nodeKey + '.removeDone.' + data._id
146 ipc.server.on(eventKey, function (received) {
147 if (!webtorrent.silent) logger.debug('Process removed torrent %s.', data._id)
148
149 let err = null
150 if (received.err) err = received.err
151
152 ipc.server.off(eventKey, '*')
153 callback(err)
154 })
155
156 ipc.server.broadcast(processKey + '.remove', data)
157}
158
159// ---------------------------------------------------------------------------
160
161module.exports = webtorrent
diff --git a/server/models/video.js b/server/models/video.js
index 396aa505d..14e0df6f2 100644
--- a/server/models/video.js
+++ b/server/models/video.js
@@ -1,24 +1,27 @@
1'use strict' 1'use strict'
2 2
3const config = require('config') 3const config = require('config')
4const eachLimit = require('async/eachLimit') 4const createTorrent = require('create-torrent')
5const ffmpeg = require('fluent-ffmpeg') 5const ffmpeg = require('fluent-ffmpeg')
6const fs = require('fs') 6const fs = require('fs')
7const parallel = require('async/parallel') 7const parallel = require('async/parallel')
8const parseTorrent = require('parse-torrent')
8const pathUtils = require('path') 9const pathUtils = require('path')
10const magnet = require('magnet-uri')
9const mongoose = require('mongoose') 11const mongoose = require('mongoose')
10 12
11const constants = require('../initializers/constants') 13const constants = require('../initializers/constants')
12const customValidators = require('../helpers/custom-validators') 14const customValidators = require('../helpers/custom-validators')
13const logger = require('../helpers/logger') 15const logger = require('../helpers/logger')
14const utils = require('../helpers/utils') 16const utils = require('../helpers/utils')
15const webtorrent = require('../lib/webtorrent')
16 17
17const http = config.get('webserver.https') === true ? 'https' : 'http' 18const http = config.get('webserver.https') === true ? 'https' : 'http'
18const host = config.get('webserver.host') 19const host = config.get('webserver.host')
19const port = config.get('webserver.port') 20const port = config.get('webserver.port')
20const uploadsDir = pathUtils.join(__dirname, '..', '..', config.get('storage.uploads')) 21const uploadsDir = pathUtils.join(__dirname, '..', '..', config.get('storage.uploads'))
21const thumbnailsDir = pathUtils.join(__dirname, '..', '..', config.get('storage.thumbnails')) 22const thumbnailsDir = pathUtils.join(__dirname, '..', '..', config.get('storage.thumbnails'))
23const torrentsDir = pathUtils.join(__dirname, '..', '..', config.get('storage.torrents'))
24const webseedBaseUrl = http + '://' + host + ':' + port + constants.STATIC_PATHS.WEBSEED
22 25
23// --------------------------------------------------------------------------- 26// ---------------------------------------------------------------------------
24 27
@@ -66,8 +69,7 @@ VideoSchema.statics = {
66 listOwned: listOwned, 69 listOwned: listOwned,
67 listRemotes: listRemotes, 70 listRemotes: listRemotes,
68 load: load, 71 load: load,
69 search: search, 72 search: search
70 seedAllExisting: seedAllExisting
71} 73}
72 74
73VideoSchema.pre('remove', function (next) { 75VideoSchema.pre('remove', function (next) {
@@ -103,8 +105,21 @@ VideoSchema.pre('save', function (next) {
103 this.podUrl = http + '://' + host + ':' + port 105 this.podUrl = http + '://' + host + ':' + port
104 106
105 tasks.push( 107 tasks.push(
108 // TODO: refractoring
106 function (callback) { 109 function (callback) {
107 seed(videoPath, callback) 110 createTorrent(videoPath, { announceList: [ [ 'ws://' + host + ':' + port + '/tracker/socket' ] ], urlList: [ webseedBaseUrl + video.filename ] }, function (err, torrent) {
111 if (err) return callback(err)
112
113 fs.writeFile(torrentsDir + video.filename + '.torrent', torrent, function (err) {
114 if (err) return callback(err)
115
116 const parsedTorrent = parseTorrent(torrent)
117 parsedTorrent.xs = video.podUrl + constants.STATIC_PATHS.TORRENTS + video.filename + '.torrent'
118 video.magnetUri = magnet.encode(parsedTorrent)
119
120 callback(null)
121 })
122 })
108 }, 123 },
109 function (callback) { 124 function (callback) {
110 createThumbnail(videoPath, callback) 125 createThumbnail(videoPath, callback)
@@ -114,7 +129,6 @@ VideoSchema.pre('save', function (next) {
114 parallel(tasks, function (err, results) { 129 parallel(tasks, function (err, results) {
115 if (err) return next(err) 130 if (err) return next(err)
116 131
117 video.magnetUri = results[0].magnetURI
118 video.thumbnail = results[1] 132 video.thumbnail = results[1]
119 133
120 return next() 134 return next()
@@ -149,7 +163,7 @@ function toFormatedJSON () {
149 author: this.author, 163 author: this.author,
150 duration: this.duration, 164 duration: this.duration,
151 tags: this.tags, 165 tags: this.tags,
152 thumbnailPath: constants.THUMBNAILS_STATIC_PATH + '/' + this.thumbnail, 166 thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.thumbnail,
153 createdDate: this.createdDate 167 createdDate: this.createdDate
154 } 168 }
155 169
@@ -231,17 +245,6 @@ function search (value, field, start, count, sort, callback) {
231 findWithCount.call(this, query, start, count, sort, callback) 245 findWithCount.call(this, query, start, count, sort, callback)
232} 246}
233 247
234function seedAllExisting (callback) {
235 listOwned.call(this, function (err, videos) {
236 if (err) return callback(err)
237
238 eachLimit(videos, constants.SEEDS_IN_PARALLEL, function (video, callbackEach) {
239 const videoPath = pathUtils.join(uploadsDir, video.filename)
240 seed(videoPath, callbackEach)
241 }, callback)
242 })
243}
244
245// --------------------------------------------------------------------------- 248// ---------------------------------------------------------------------------
246 249
247function findWithCount (query, start, count, sort, callback) { 250function findWithCount (query, start, count, sort, callback) {
@@ -273,12 +276,7 @@ function removeFile (video, callback) {
273 276
274// Maybe the torrent is not seeded, but we catch the error to don't stop the removing process 277// Maybe the torrent is not seeded, but we catch the error to don't stop the removing process
275function removeTorrent (video, callback) { 278function removeTorrent (video, callback) {
276 try { 279 fs.unlink(torrentsDir + video.filename + '.torrent')
277 webtorrent.remove(video.magnetUri, callback)
278 } catch (err) {
279 logger.warn('Cannot remove the torrent from WebTorrent', { err: err })
280 return callback(null)
281 }
282} 280}
283 281
284function createThumbnail (videoPath, callback) { 282function createThumbnail (videoPath, callback) {
@@ -296,16 +294,6 @@ function createThumbnail (videoPath, callback) {
296 }) 294 })
297} 295}
298 296
299function seed (path, callback) {
300 logger.info('Seeding %s...', path)
301
302 webtorrent.seed(path, function (torrent) {
303 logger.info('%s seeded (%s).', path, torrent.magnetURI)
304
305 return callback(null, torrent)
306 })
307}
308
309function generateThumbnailFromBase64 (data, callback) { 297function generateThumbnailFromBase64 (data, callback) {
310 // Creating the thumbnail for this remote video 298 // Creating the thumbnail for this remote video
311 utils.generateRandomString(16, function (err, randomString) { 299 utils.generateRandomString(16, function (err, randomString) {