3 const async = require('async')
4 const config = require('config')
5 const fs = require('fs')
6 const request = require('request')
8 const constants = require('../initializers/constants')
9 const logger = require('../helpers/logger')
10 const peertubeCrypto = require('../helpers/peertubeCrypto')
11 const Pods = require('../models/pods')
12 const requestsScheduler = require('../lib/requestsScheduler')
13 const requests = require('../helpers/requests')
14 const videos = require('../lib/videos')
15 const Videos = require('../models/videos')
17 const http = config.get('webserver.https') ? 'https' : 'http'
18 const host = config.get('webserver.host')
19 const port = config.get('webserver.port')
22 addVideoToFriends: addVideoToFriends,
23 hasFriends: hasFriends,
24 getMyCertificate: getMyCertificate,
25 makeFriends: makeFriends,
26 quitFriends: quitFriends,
27 removeVideoToFriends: removeVideoToFriends,
28 sendOwnedVideosToPod: sendOwnedVideosToPod
31 function addVideoToFriends (video) {
32 // ensure namePath is null
35 requestsScheduler.addRequest('add', video)
38 function hasFriends (callback) {
39 Pods.count(function (err, count) {
40 if (err) return callback(err)
42 const hasFriends = (count !== 0)
43 callback(null, hasFriends)
47 function getMyCertificate (callback) {
48 fs.readFile(peertubeCrypto.getCertDir() + 'peertube.pub', 'utf8', callback)
51 function makeFriends (callback) {
54 logger.info('Make friends!')
55 getMyCertificate(function (err, cert) {
57 logger.error('Cannot read public cert.')
61 const urls = config.get('network.friends')
63 async.eachSeries(urls, function (url, callbackEach) {
64 computeForeignPodsList(url, podsScore, callbackEach)
66 if (err) return callback(err)
68 logger.debug('Pods scores computed.', { podsScore: podsScore })
69 const podsList = computeWinningPods(urls, podsScore)
70 logger.debug('Pods that we keep.', { podsToKeep: podsList })
72 makeRequestsToWinningPods(cert, podsList, callback)
77 function quitFriends (callback) {
79 requestsScheduler.deactivate()
80 // Flush pool requests
81 requestsScheduler.flush()
84 function getPodsList (callbackAsync) {
85 return Pods.list(callbackAsync)
88 function announceIQuitMyFriends (pods, callbackAsync) {
89 const requestParams = {
91 path: '/api/' + constants.API_VERSION + '/pods/remove',
95 // Announce we quit them
96 // We don't care if the request fails
97 // The other pod will exclude us automatically after a while
98 async.eachLimit(pods, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) {
99 requestParams.toPod = pod
100 requests.makeSecureRequest(requestParams, callbackEach)
103 logger.error('Some errors while quitting friends.', { err: err })
104 // Don't stop the process
107 return callbackAsync()
111 function removePodsFromDB (callbackAsync) {
112 Pods.removeAll(function (err) {
113 return callbackAsync(err)
117 function listRemoteVideos (callbackAsync) {
118 logger.info('Broke friends, so sad :(')
120 Videos.listFromRemotes(callbackAsync)
123 function removeTheRemoteVideos (videosList, callbackAsync) {
124 videos.removeRemoteVideos(videosList, function (err) {
126 logger.error('Cannot remove remote videos.', { error: err })
127 return callbackAsync(err)
130 return callbackAsync(null)
134 // Don't forget to re activate the scheduler, even if there was an error
135 requestsScheduler.activate()
137 if (err) return callback(err)
139 logger.info('Removed all remote videos.')
140 return callback(null)
144 function removeVideoToFriends (video) {
145 requestsScheduler.addRequest('remove', video)
148 function sendOwnedVideosToPod (podId) {
149 Videos.listOwned(function (err, videosList) {
151 logger.error('Cannot get the list of videos we own.')
155 videosList.forEach(function (video) {
156 videos.convertVideoToRemote(video, function (err, remoteVideo) {
158 logger.error('Cannot convert video to remote.', { error: err })
159 // Don't break the process
163 requestsScheduler.addRequestTo([ podId ], 'add', remoteVideo)
169 // ---------------------------------------------------------------------------
171 module.exports = pods
173 // ---------------------------------------------------------------------------
175 function computeForeignPodsList (url, podsScore, callback) {
176 getForeignPodsList(url, function (err, foreignPodsList) {
177 if (err) return callback(err)
179 if (!foreignPodsList) foreignPodsList = []
181 // Let's give 1 point to the pod we ask the friends list
182 foreignPodsList.push({ url: url })
184 foreignPodsList.forEach(function (foreignPod) {
185 const foreignPodUrl = foreignPod.url
187 if (podsScore[foreignPodUrl]) podsScore[foreignPodUrl]++
188 else podsScore[foreignPodUrl] = 1
195 function computeWinningPods (urls, podsScore) {
196 // Build the list of pods to add
197 // Only add a pod if it exists in more than a half base pods
199 const baseScore = urls.length / 2
200 Object.keys(podsScore).forEach(function (pod) {
201 if (podsScore[pod] > baseScore) podsList.push({ url: pod })
207 function getForeignPodsList (url, callback) {
208 const path = '/api/' + constants.API_VERSION + '/pods'
210 request.get(url + path, function (err, response, body) {
211 if (err) return callback(err)
213 callback(null, JSON.parse(body))
217 function makeRequestsToWinningPods (cert, podsList, callback) {
218 // Stop pool requests
219 requestsScheduler.deactivate()
220 // Flush pool requests
221 requestsScheduler.forceSend()
223 async.eachLimit(podsList, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) {
225 url: pod.url + '/api/' + constants.API_VERSION + '/pods/',
228 url: http + '://' + host + ':' + port,
233 requests.makeRetryRequest(params, function (err, res, body) {
235 logger.error('Error with adding %s pod.', pod.url, { error: err })
236 // Don't break the process
237 return callbackEach()
240 if (res.statusCode === 200) {
241 Pods.add({ url: pod.url, publicKey: body.cert, score: constants.FRIEND_BASE_SCORE }, function (err, podCreated) {
242 if (err) logger.error('Cannot add friend %s pod.', pod.url)
244 // Add our videos to the request scheduler
245 sendOwnedVideosToPod(podCreated._id)
247 return callbackEach()
250 logger.error('Status not 200 for %s pod.', pod.url)
251 return callbackEach()
254 }, function endRequests () {
255 // Final callback, we've ended all the requests
256 // Now we made new friends, we can re activate the pool of requests
257 requestsScheduler.activate()
259 logger.debug('makeRequestsToWinningPods finished.')