Refractoring and add thumbnails support (without tests)
[oweals/peertube.git] / server / lib / friends.js
1 'use strict'
2
3 const async = require('async')
4 const config = require('config')
5 const fs = require('fs')
6 const request = require('request')
7
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')
16
17 const http = config.get('webserver.https') ? 'https' : 'http'
18 const host = config.get('webserver.host')
19 const port = config.get('webserver.port')
20
21 const pods = {
22   addVideoToFriends: addVideoToFriends,
23   hasFriends: hasFriends,
24   getMyCertificate: getMyCertificate,
25   makeFriends: makeFriends,
26   quitFriends: quitFriends,
27   removeVideoToFriends: removeVideoToFriends
28 }
29
30 function addVideoToFriends (video) {
31   // To avoid duplicates
32   const id = video.name + video.magnetUri
33   // ensure namePath is null
34   video.namePath = null
35   requestsScheduler.addRequest(id, 'add', video)
36 }
37
38 function hasFriends (callback) {
39   Pods.count(function (err, count) {
40     if (err) return callback(err)
41
42     const has_friends = (count !== 0)
43     callback(null, has_friends)
44   })
45 }
46
47 function getMyCertificate (callback) {
48   fs.readFile(peertubeCrypto.getCertDir() + 'peertube.pub', 'utf8', callback)
49 }
50
51 function makeFriends (callback) {
52   const pods_score = {}
53
54   logger.info('Make friends!')
55   getMyCertificate(function (err, cert) {
56     if (err) {
57       logger.error('Cannot read public cert.')
58       return callback(err)
59     }
60
61     const urls = config.get('network.friends')
62
63     async.each(urls, function (url, callback_each) {
64       computeForeignPodsList(url, pods_score, callback_each)
65     }, function (err) {
66       if (err) return callback(err)
67
68       logger.debug('Pods scores computed.', { pods_score: pods_score })
69       const pods_list = computeWinningPods(urls, pods_score)
70       logger.debug('Pods that we keep.', { pods_to_keep: pods_list })
71
72       makeRequestsToWinningPods(cert, pods_list, callback)
73     })
74   })
75 }
76
77 function quitFriends (callback) {
78   // Stop pool requests
79   requestsScheduler.deactivate()
80   // Flush pool requests
81   requestsScheduler.forceSend()
82
83   Pods.list(function (err, pods) {
84     if (err) return callback(err)
85
86     const request = {
87       method: 'POST',
88       path: '/api/' + constants.API_VERSION + '/pods/remove',
89       sign: true,
90       encrypt: true,
91       data: {
92         url: 'me' // Fake data
93       }
94     }
95
96     // Announce we quit them
97     requests.makeMultipleRetryRequest(request, pods, function () {
98       Pods.removeAll(function (err) {
99         requestsScheduler.activate()
100
101         if (err) return callback(err)
102
103         logger.info('Broke friends, so sad :(')
104
105         Videos.listFromRemotes(function (err, videos_list) {
106           if (err) return callback(err)
107
108           videos.removeRemoteVideos(videos_list, function (err) {
109             if (err) {
110               logger.error('Cannot remove remote videos.', { error: err })
111               return callback(err)
112             }
113
114             logger.info('Removed all remote videos.')
115             callback(null)
116           })
117         })
118       })
119     })
120   })
121 }
122
123 function removeVideoToFriends (video) {
124   // To avoid duplicates
125   const id = video.name + video.magnetUri
126   requestsScheduler.addRequest(id, 'remove', video)
127 }
128
129 // ---------------------------------------------------------------------------
130
131 module.exports = pods
132
133 // ---------------------------------------------------------------------------
134
135 function computeForeignPodsList (url, pods_score, callback) {
136   // Let's give 1 point to the pod we ask the friends list
137   pods_score[url] = 1
138
139   getForeignPodsList(url, function (err, foreign_pods_list) {
140     if (err) return callback(err)
141     if (foreign_pods_list.length === 0) return callback()
142
143     foreign_pods_list.forEach(function (foreign_pod) {
144       const foreign_url = foreign_pod.url
145
146       if (pods_score[foreign_url]) pods_score[foreign_url]++
147       else pods_score[foreign_url] = 1
148     })
149
150     callback()
151   })
152 }
153
154 function computeWinningPods (urls, pods_score) {
155   // Build the list of pods to add
156   // Only add a pod if it exists in more than a half base pods
157   const pods_list = []
158   const base_score = urls.length / 2
159   Object.keys(pods_score).forEach(function (pod) {
160     if (pods_score[pod] > base_score) pods_list.push({ url: pod })
161   })
162
163   return pods_list
164 }
165
166 function getForeignPodsList (url, callback) {
167   const path = '/api/' + constants.API_VERSION + '/pods'
168
169   request.get(url + path, function (err, response, body) {
170     if (err) return callback(err)
171
172     callback(null, JSON.parse(body))
173   })
174 }
175
176 function makeRequestsToWinningPods (cert, pods_list, callback) {
177   // Stop pool requests
178   requestsScheduler.deactivate()
179   // Flush pool requests
180   requestsScheduler.forceSend()
181
182   // Get the list of our videos to send to our new friends
183   Videos.listOwned(function (err, videos_list) {
184     if (err) {
185       logger.error('Cannot get the list of videos we own.')
186       return callback(err)
187     }
188
189     const data = {
190       url: http + '://' + host + ':' + port,
191       publicKey: cert,
192       videos: videos_list
193     }
194
195     requests.makeMultipleRetryRequest(
196       { method: 'POST', path: '/api/' + constants.API_VERSION + '/pods/', data: data },
197
198       pods_list,
199
200       function eachRequest (err, response, body, url, pod, callback_each_request) {
201         // We add the pod if it responded correctly with its public certificate
202         if (!err && response.statusCode === 200) {
203           Pods.add({ url: pod.url, publicKey: body.cert, score: constants.FRIEND_BASE_SCORE }, function (err) {
204             if (err) {
205               logger.error('Error with adding %s pod.', pod.url, { error: err })
206               return callback_each_request()
207             }
208             console.log('hihi')
209             videos.createRemoteVideos(body.videos, function (err) {
210               if (err) {
211                 logger.error('Error with adding videos of pod.', pod.url, { error: err })
212                 return callback_each_request()
213               }
214
215               console.log('kik')
216
217               logger.debug('Adding remote videos from %s.', pod.url, { videos: body.videos })
218               return callback_each_request()
219             })
220           })
221         } else {
222           logger.error('Error with adding %s pod.', pod.url, { error: err || new Error('Status not 200') })
223           return callback_each_request()
224         }
225       },
226
227       function endRequests (err) {
228         // Now we made new friends, we can re activate the pool of requests
229         requestsScheduler.activate()
230
231         if (err) {
232           logger.error('There was some errors when we wanted to make friends.')
233           return callback(err)
234         }
235
236         logger.debug('makeRequestsToWinningPods finished.')
237         return callback(null)
238       }
239     )
240   })
241 }