3 const async = require('async')
4 const config = require('config')
5 const fs = require('fs')
6 const mongoose = require('mongoose')
7 const request = require('request')
9 const constants = require('../initializers/constants')
10 const logger = require('../helpers/logger')
11 const peertubeCrypto = require('../helpers/peertubeCrypto')
12 const Pods = require('../models/pods')
13 const requestsScheduler = require('../lib/requestsScheduler')
14 const requests = require('../helpers/requests')
16 const http = config.get('webserver.https') ? 'https' : 'http'
17 const host = config.get('webserver.host')
18 const port = config.get('webserver.port')
19 const Video = mongoose.model('Video')
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 Video.listRemotes(callbackAsync)
123 function removeTheRemoteVideos (videosList, callbackAsync) {
124 async.each(videosList, function (video, callbackEach) {
125 video.remove(callbackEach)
129 // Don't forget to re activate the scheduler, even if there was an error
130 requestsScheduler.activate()
132 if (err) return callback(err)
134 logger.info('Removed all remote videos.')
135 return callback(null)
139 function removeVideoToFriends (video) {
140 requestsScheduler.addRequest('remove', video)
143 function sendOwnedVideosToPod (podId) {
144 Video.listOwned(function (err, videosList) {
146 logger.error('Cannot get the list of videos we own.')
150 videosList.forEach(function (video) {
151 video.toRemoteJSON(function (err, remoteVideo) {
153 logger.error('Cannot convert video to remote.', { error: err })
154 // Don't break the process
158 requestsScheduler.addRequestTo([ podId ], 'add', remoteVideo)
164 // ---------------------------------------------------------------------------
166 module.exports = pods
168 // ---------------------------------------------------------------------------
170 function computeForeignPodsList (url, podsScore, callback) {
171 getForeignPodsList(url, function (err, foreignPodsList) {
172 if (err) return callback(err)
174 if (!foreignPodsList) foreignPodsList = []
176 // Let's give 1 point to the pod we ask the friends list
177 foreignPodsList.push({ url: url })
179 foreignPodsList.forEach(function (foreignPod) {
180 const foreignPodUrl = foreignPod.url
182 if (podsScore[foreignPodUrl]) podsScore[foreignPodUrl]++
183 else podsScore[foreignPodUrl] = 1
190 function computeWinningPods (urls, podsScore) {
191 // Build the list of pods to add
192 // Only add a pod if it exists in more than a half base pods
194 const baseScore = urls.length / 2
195 Object.keys(podsScore).forEach(function (pod) {
196 if (podsScore[pod] > baseScore) podsList.push({ url: pod })
202 function getForeignPodsList (url, callback) {
203 const path = '/api/' + constants.API_VERSION + '/pods'
205 request.get(url + path, function (err, response, body) {
206 if (err) return callback(err)
208 callback(null, JSON.parse(body))
212 function makeRequestsToWinningPods (cert, podsList, callback) {
213 // Stop pool requests
214 requestsScheduler.deactivate()
215 // Flush pool requests
216 requestsScheduler.forceSend()
218 async.eachLimit(podsList, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) {
220 url: pod.url + '/api/' + constants.API_VERSION + '/pods/',
223 url: http + '://' + host + ':' + port,
228 requests.makeRetryRequest(params, function (err, res, body) {
230 logger.error('Error with adding %s pod.', pod.url, { error: err })
231 // Don't break the process
232 return callbackEach()
235 if (res.statusCode === 200) {
236 Pods.add({ url: pod.url, publicKey: body.cert, score: constants.FRIEND_BASE_SCORE }, function (err, podCreated) {
237 if (err) logger.error('Cannot add friend %s pod.', pod.url)
239 // Add our videos to the request scheduler
240 sendOwnedVideosToPod(podCreated._id)
242 return callbackEach()
245 logger.error('Status not 200 for %s pod.', pod.url)
246 return callbackEach()
249 }, function endRequests () {
250 // Final callback, we've ended all the requests
251 // Now we made new friends, we can re activate the pool of requests
252 requestsScheduler.activate()
254 logger.debug('makeRequestsToWinningPods finished.')