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 poolRequests = require('../lib/poolRequests')
13 const requests = require('../helpers/requests')
14 const Videos = require('../models/videos')
16 const http = config.get('webserver.https') ? 'https' : 'http'
17 const host = config.get('webserver.host')
18 const port = config.get('webserver.port')
21 addVideoToFriends: addVideoToFriends,
22 hasFriends: hasFriends,
23 makeFriends: makeFriends,
24 quitFriends: quitFriends,
25 removeVideoToFriends: removeVideoToFriends
28 function addVideoToFriends (video) {
29 // To avoid duplicates
30 const id = video.name + video.magnetUri
31 // ensure namePath is null
33 poolRequests.addRequest(id, 'add', video)
36 function hasFriends (callback) {
37 Pods.count(function (err, count) {
38 if (err) return callback(err)
40 const has_friends = (count !== 0)
41 callback(null, has_friends)
45 function makeFriends (callback) {
48 logger.info('Make friends!')
49 fs.readFile(peertubeCrypto.getCertDir() + 'peertube.pub', 'utf8', function (err, cert) {
51 logger.error('Cannot read public cert.')
55 const urls = config.get('network.friends')
57 async.each(urls, function (url, callback) {
58 computeForeignPodsList(url, pods_score, callback)
60 if (err) return callback(err)
62 logger.debug('Pods scores computed.', { pods_score: pods_score })
63 const pods_list = computeWinningPods(urls, pods_score)
64 logger.debug('Pods that we keep.', { pods_to_keep: pods_list })
66 makeRequestsToWinningPods(cert, pods_list, callback)
71 function quitFriends (callback) {
73 poolRequests.deactivate()
74 // Flush pool requests
75 poolRequests.forceSend()
77 Pods.list(function (err, pods) {
78 if (err) return callback(err)
82 path: '/api/' + constants.API_VERSION + '/pods/remove',
86 url: 'me' // Fake data
90 // Announce we quit them
91 requests.makeMultipleRetryRequest(request, pods, function () {
92 Pods.removeAll(function (err) {
93 poolRequests.activate()
95 if (err) return callback(err)
97 logger.info('Broke friends, so sad :(')
99 Videos.removeAllRemotes(function (err) {
100 if (err) return callback(err)
102 logger.info('Removed all remote videos.')
110 function removeVideoToFriends (video) {
111 // To avoid duplicates
112 const id = video.name + video.magnetUri
113 poolRequests.addRequest(id, 'remove', video)
116 // ---------------------------------------------------------------------------
118 module.exports = pods
120 // ---------------------------------------------------------------------------
122 function computeForeignPodsList (url, pods_score, callback) {
123 // Let's give 1 point to the pod we ask the friends list
126 getForeignPodsList(url, function (err, foreign_pods_list) {
127 if (err) return callback(err)
128 if (foreign_pods_list.length === 0) return callback()
130 async.each(foreign_pods_list, function (foreign_pod, callback_each) {
131 const foreign_url = foreign_pod.url
133 if (pods_score[foreign_url]) pods_score[foreign_url]++
134 else pods_score[foreign_url] = 1
143 function computeWinningPods (urls, pods_score) {
144 // Build the list of pods to add
145 // Only add a pod if it exists in more than a half base pods
147 const base_score = urls.length / 2
148 Object.keys(pods_score).forEach(function (pod) {
149 if (pods_score[pod] > base_score) pods_list.push({ url: pod })
155 function getForeignPodsList (url, callback) {
156 const path = '/api/' + constants.API_VERSION + '/pods'
158 request.get(url + path, function (err, response, body) {
159 if (err) return callback(err)
161 callback(null, JSON.parse(body))
165 function makeRequestsToWinningPods (cert, pods_list, callback) {
166 // Stop pool requests
167 poolRequests.deactivate()
168 // Flush pool requests
169 poolRequests.forceSend()
171 // Get the list of our videos to send to our new friends
172 Videos.listOwned(function (err, videos_list) {
174 logger.error('Cannot get the list of videos we own.')
179 url: http + '://' + host + ':' + port,
184 requests.makeMultipleRetryRequest(
185 { method: 'POST', path: '/api/' + constants.API_VERSION + '/pods/', data: data },
189 function eachRequest (err, response, body, url, pod, callback_each_request) {
190 // We add the pod if it responded correctly with its public certificate
191 if (!err && response.statusCode === 200) {
192 Pods.add({ url: pod.url, publicKey: body.cert, score: constants.FRIEND_BASE_SCORE }, function (err) {
194 logger.error('Error with adding %s pod.', pod.url, { error: err })
195 return callback_each_request()
198 Videos.addRemotes(body.videos, function (err) {
200 logger.error('Error with adding videos of pod.', pod.url, { error: err })
201 return callback_each_request()
204 logger.debug('Adding remote videos from %s.', pod.url, { videos: body.videos })
205 return callback_each_request()
209 logger.error('Error with adding %s pod.', pod.url, { error: err || new Error('Status not 200') })
210 return callback_each_request()
214 function endRequests (err) {
215 // Now we made new friends, we can re activate the pool of requests
216 poolRequests.activate()
219 logger.error('There was some errors when we wanted to make friends.')
223 logger.debug('makeRequestsToWinningPods finished.')
224 return callback(null)