aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/sass/application.scss2
-rw-r--r--client/src/sass/include/_fonts.scss2
-rw-r--r--server/controllers/api/server/follows.ts5
-rw-r--r--server/controllers/api/server/redundancy.ts6
-rw-r--r--server/lib/redundancy.ts9
-rw-r--r--server/models/redundancy/video-redundancy.ts41
-rw-r--r--server/tests/api/server/redundancy.ts69
7 files changed, 116 insertions, 18 deletions
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss
index 0029c22ef..40a9ed231 100644
--- a/client/src/sass/application.scss
+++ b/client/src/sass/application.scss
@@ -1,4 +1,4 @@
1$icon-font-path: '../../node_modules/@neos21/bootstrap3-glyphicons/assets/fonts/'; 1$icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
2@import '_bootstrap'; 2@import '_bootstrap';
3 3
4@import '_variables'; 4@import '_variables';
diff --git a/client/src/sass/include/_fonts.scss b/client/src/sass/include/_fonts.scss
index 61717e6f5..d4694a747 100644
--- a/client/src/sass/include/_fonts.scss
+++ b/client/src/sass/include/_fonts.scss
@@ -1,4 +1,4 @@
1$FontPathSourceSansPro: '../../node_modules/npm-font-source-sans-pro/fonts'; 1$FontPathSourceSansPro: '~npm-font-source-sans-pro/fonts';
2 2
3@font-face { 3@font-face {
4 font-family: 'Source Sans Pro'; 4 font-family: 'Source Sans Pro';
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts
index a4eae6b45..d62400e42 100644
--- a/server/controllers/api/server/follows.ts
+++ b/server/controllers/api/server/follows.ts
@@ -17,6 +17,7 @@ import {
17import { followersSortValidator, followingSortValidator, followValidator } from '../../../middlewares/validators' 17import { followersSortValidator, followingSortValidator, followValidator } from '../../../middlewares/validators'
18import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 18import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
19import { JobQueue } from '../../../lib/job-queue' 19import { JobQueue } from '../../../lib/job-queue'
20import { removeRedundancyOf } from '../../../lib/redundancy'
20 21
21const serverFollowsRouter = express.Router() 22const serverFollowsRouter = express.Router()
22serverFollowsRouter.get('/following', 23serverFollowsRouter.get('/following',
@@ -101,6 +102,10 @@ async function removeFollow (req: express.Request, res: express.Response, next:
101 server.redundancyAllowed = false 102 server.redundancyAllowed = false
102 await server.save({ transaction: t }) 103 await server.save({ transaction: t })
103 104
105 // Async, could be long
106 removeRedundancyOf(server.id)
107 .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, err))
108
104 await follow.destroy({ transaction: t }) 109 await follow.destroy({ transaction: t })
105 }) 110 })
106 111
diff --git a/server/controllers/api/server/redundancy.ts b/server/controllers/api/server/redundancy.ts
index 4216b9e35..4140c4991 100644
--- a/server/controllers/api/server/redundancy.ts
+++ b/server/controllers/api/server/redundancy.ts
@@ -3,6 +3,8 @@ import { UserRight } from '../../../../shared/models/users'
3import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares' 3import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares'
4import { updateServerRedundancyValidator } from '../../../middlewares/validators/redundancy' 4import { updateServerRedundancyValidator } from '../../../middlewares/validators/redundancy'
5import { ServerModel } from '../../../models/server/server' 5import { ServerModel } from '../../../models/server/server'
6import { removeRedundancyOf } from '../../../lib/redundancy'
7import { logger } from '../../../helpers/logger'
6 8
7const serverRedundancyRouter = express.Router() 9const serverRedundancyRouter = express.Router()
8 10
@@ -28,5 +30,9 @@ async function updateRedundancy (req: express.Request, res: express.Response, ne
28 30
29 await server.save() 31 await server.save()
30 32
33 // Async, could be long
34 removeRedundancyOf(server.id)
35 .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, err))
36
31 return res.sendStatus(204) 37 return res.sendStatus(204)
32} 38}
diff --git a/server/lib/redundancy.ts b/server/lib/redundancy.ts
index 16b122658..04d3ded8f 100644
--- a/server/lib/redundancy.ts
+++ b/server/lib/redundancy.ts
@@ -12,8 +12,17 @@ async function removeVideoRedundancy (videoRedundancy: VideoRedundancyModel, t?:
12 await videoRedundancy.destroy({ transaction: t }) 12 await videoRedundancy.destroy({ transaction: t })
13} 13}
14 14
15async function removeRedundancyOf (serverId: number) {
16 const videosRedundancy = await VideoRedundancyModel.listLocalOfServer(serverId)
17
18 for (const redundancy of videosRedundancy) {
19 await removeVideoRedundancy(redundancy)
20 }
21}
22
15// --------------------------------------------------------------------------- 23// ---------------------------------------------------------------------------
16 24
17export { 25export {
26 removeRedundancyOf,
18 removeVideoRedundancy 27 removeVideoRedundancy
19} 28}
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts
index 3c87ec2c1..e67164802 100644
--- a/server/models/redundancy/video-redundancy.ts
+++ b/server/models/redundancy/video-redundancy.ts
@@ -286,6 +286,47 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
286 return VideoRedundancyModel.scope([ ScopeNames.WITH_VIDEO ]).findAll(query) 286 return VideoRedundancyModel.scope([ ScopeNames.WITH_VIDEO ]).findAll(query)
287 } 287 }
288 288
289 static async listLocalOfServer (serverId: number) {
290 const actor = await getServerActor()
291
292 const query = {
293 where: {
294 actorId: actor.id
295 },
296 include: [
297 {
298 model: VideoFileModel,
299 required: true,
300 include: [
301 {
302 model: VideoModel,
303 required: true,
304 include: [
305 {
306 attributes: [],
307 model: VideoChannelModel.unscoped(),
308 required: true,
309 include: [
310 {
311 attributes: [],
312 model: ActorModel.unscoped(),
313 required: true,
314 where: {
315 serverId
316 }
317 }
318 ]
319 }
320 ]
321 }
322 ]
323 }
324 ]
325 }
326
327 return VideoRedundancyModel.findAll(query)
328 }
329
289 static async getStats (strategy: VideoRedundancyStrategy) { 330 static async getStats (strategy: VideoRedundancyStrategy) {
290 const actor = await getServerActor() 331 const actor = await getServerActor()
291 332
diff --git a/server/tests/api/server/redundancy.ts b/server/tests/api/server/redundancy.ts
index 9b3b1b4ad..e1709891d 100644
--- a/server/tests/api/server/redundancy.ts
+++ b/server/tests/api/server/redundancy.ts
@@ -12,7 +12,7 @@ import {
12 killallServers, makeGetRequest, 12 killallServers, makeGetRequest,
13 root, 13 root,
14 ServerInfo, 14 ServerInfo,
15 setAccessTokensToServers, 15 setAccessTokensToServers, unfollow,
16 uploadVideo, 16 uploadVideo,
17 viewVideo, 17 viewVideo,
18 wait 18 wait
@@ -39,6 +39,8 @@ function checkMagnetWebseeds (file: { magnetUri: string, resolution: { id: numbe
39 const found = parsed.urlList.find(url => url === `${ws}-${file.resolution.id}.mp4`) 39 const found = parsed.urlList.find(url => url === `${ws}-${file.resolution.id}.mp4`)
40 expect(found, `Webseed ${ws} not found in ${file.magnetUri} on server ${server.url}`).to.not.be.undefined 40 expect(found, `Webseed ${ws} not found in ${file.magnetUri} on server ${server.url}`).to.not.be.undefined
41 } 41 }
42
43 expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length)
42} 44}
43 45
44async function runServers (strategy: VideoRedundancyStrategy, additionalParams: any = {}) { 46async function runServers (strategy: VideoRedundancyStrategy, additionalParams: any = {}) {
@@ -136,23 +138,21 @@ async function check2Webseeds (strategy: VideoRedundancyStrategy, videoUUID?: st
136 ] 138 ]
137 139
138 for (const server of servers) { 140 for (const server of servers) {
139 { 141 const res = await getVideo(server.url, videoUUID)
140 const res = await getVideo(server.url, videoUUID)
141 142
142 const video: VideoDetails = res.body 143 const video: VideoDetails = res.body
143 144
144 for (const file of video.files) { 145 for (const file of video.files) {
145 checkMagnetWebseeds(file, webseeds, server) 146 checkMagnetWebseeds(file, webseeds, server)
146 147
147 // Only servers 1 and 2 have the video 148 // Only servers 1 and 2 have the video
148 if (server.serverNumber !== 3) { 149 if (server.serverNumber !== 3) {
149 await makeGetRequest({ 150 await makeGetRequest({
150 url: server.url, 151 url: server.url,
151 statusCodeExpected: 200, 152 statusCodeExpected: 200,
152 path: '/static/webseed/' + `${videoUUID}-${file.resolution.id}.mp4`, 153 path: '/static/webseed/' + `${videoUUID}-${file.resolution.id}.mp4`,
153 contentType: null 154 contentType: null
154 }) 155 })
155 }
156 } 156 }
157 } 157 }
158 } 158 }
@@ -182,6 +182,21 @@ async function enableRedundancyOnServer1 () {
182 expect(server2.following.hostRedundancyAllowed).to.be.true 182 expect(server2.following.hostRedundancyAllowed).to.be.true
183} 183}
184 184
185async function disableRedundancyOnServer1 () {
186 await updateRedundancy(servers[ 0 ].url, servers[ 0 ].accessToken, servers[ 1 ].host, false)
187
188 const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt')
189 const follows: ActorFollow[] = res.body.data
190 const server2 = follows.find(f => f.following.host === 'localhost:9002')
191 const server3 = follows.find(f => f.following.host === 'localhost:9003')
192
193 expect(server3).to.not.be.undefined
194 expect(server3.following.hostRedundancyAllowed).to.be.false
195
196 expect(server2).to.not.be.undefined
197 expect(server2.following.hostRedundancyAllowed).to.be.false
198}
199
185async function cleanServers () { 200async function cleanServers () {
186 killallServers(servers) 201 killallServers(servers)
187} 202}
@@ -217,6 +232,17 @@ describe('Test videos redundancy', function () {
217 await checkStatsWith2Webseed(strategy) 232 await checkStatsWith2Webseed(strategy)
218 }) 233 })
219 234
235 it('Should undo redundancy on server 1 and remove duplicated videos', async function () {
236 this.timeout(40000)
237
238 await disableRedundancyOnServer1()
239
240 await waitJobs(servers)
241 await wait(5000)
242
243 await check1WebSeed(strategy)
244 })
245
220 after(function () { 246 after(function () {
221 return cleanServers() 247 return cleanServers()
222 }) 248 })
@@ -251,6 +277,17 @@ describe('Test videos redundancy', function () {
251 await checkStatsWith2Webseed(strategy) 277 await checkStatsWith2Webseed(strategy)
252 }) 278 })
253 279
280 it('Should unfollow on server 1 and remove duplicated videos', async function () {
281 this.timeout(40000)
282
283 await unfollow(servers[0].url, servers[0].accessToken, servers[1])
284
285 await waitJobs(servers)
286 await wait(5000)
287
288 await check1WebSeed(strategy)
289 })
290
254 after(function () { 291 after(function () {
255 return cleanServers() 292 return cleanServers()
256 }) 293 })