aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-11-13 14:36:30 +0100
committerChocobozzz <me@florianbigard.com>2020-11-13 14:36:30 +0100
commit5c0904fc664e3eb04ac75a9430c1297c2a14f853 (patch)
tree6ede3183976686f113aca6c06ba36b99cfe965f4 /server
parent786b855af726599a9c877eefa6fa8508c36e1aca (diff)
downloadPeerTube-5c0904fc664e3eb04ac75a9430c1297c2a14f853.tar.gz
PeerTube-5c0904fc664e3eb04ac75a9430c1297c2a14f853.tar.zst
PeerTube-5c0904fc664e3eb04ac75a9430c1297c2a14f853.zip
Cleanup lives on server restart
Diffstat (limited to 'server')
-rw-r--r--server/lib/live-manager.ts12
-rw-r--r--server/models/video/video.ts13
-rw-r--r--server/tests/api/live/live.ts82
3 files changed, 105 insertions, 2 deletions
diff --git a/server/lib/live-manager.ts b/server/lib/live-manager.ts
index 2702437c4..fe5b33322 100644
--- a/server/lib/live-manager.ts
+++ b/server/lib/live-manager.ts
@@ -99,6 +99,10 @@ class LiveManager {
99 } 99 }
100 }) 100 })
101 101
102 // Cleanup broken lives, that were terminated by a server restart for example
103 this.handleBrokenLives()
104 .catch(err => logger.error('Cannot handle broken lives.', { err }))
105
102 setInterval(() => this.updateLiveViews(), VIEW_LIFETIME.LIVE) 106 setInterval(() => this.updateLiveViews(), VIEW_LIFETIME.LIVE)
103 } 107 }
104 108
@@ -468,6 +472,14 @@ class LiveManager {
468 } 472 }
469 } 473 }
470 474
475 private async handleBrokenLives () {
476 const videoIds = await VideoModel.listPublishedLiveIds()
477
478 for (const id of videoIds) {
479 await this.onEndTransmuxing(id, true)
480 }
481 }
482
471 static get Instance () { 483 static get Instance () {
472 return this.instance || (this.instance = new this()) 484 return this.instance || (this.instance = new this())
473 } 485 }
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 70839aa89..f3055a494 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -988,6 +988,19 @@ export class VideoModel extends Model<VideoModel> {
988 }) 988 })
989 } 989 }
990 990
991 static listPublishedLiveIds () {
992 const options = {
993 attributes: [ 'id' ],
994 where: {
995 isLive: true,
996 state: VideoState.PUBLISHED
997 }
998 }
999
1000 return VideoModel.findAll(options)
1001 .map(v => v.id)
1002 }
1003
991 static listUserVideosForApi ( 1004 static listUserVideosForApi (
992 accountId: number, 1005 accountId: number,
993 start: number, 1006 start: number,
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts
index 29081c6cc..aa2e1318a 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -2,22 +2,28 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { FfmpegCommand } from 'fluent-ffmpeg'
5import { getLiveNotificationSocket } from '@shared/extra-utils/socket/socket-io' 6import { getLiveNotificationSocket } from '@shared/extra-utils/socket/socket-io'
6import { LiveVideo, LiveVideoCreate, Video, VideoDetails, VideoPrivacy, VideoState, VideoStreamingPlaylistType } from '@shared/models' 7import { LiveVideo, LiveVideoCreate, Video, VideoDetails, VideoPrivacy, VideoState, VideoStreamingPlaylistType } from '@shared/models'
7import { 8import {
8 addVideoToBlacklist, 9 addVideoToBlacklist,
9 checkLiveCleanup, 10 checkLiveCleanup,
11 checkLiveSegmentHash,
10 checkResolutionsInMasterPlaylist, 12 checkResolutionsInMasterPlaylist,
13 checkSegmentHash,
11 cleanupTests, 14 cleanupTests,
12 createLive, 15 createLive,
13 doubleFollow, 16 doubleFollow,
14 flushAndRunMultipleServers, 17 flushAndRunMultipleServers,
15 getLive, 18 getLive,
19 getPlaylist,
16 getVideo, 20 getVideo,
17 getVideoIdFromUUID, 21 getVideoIdFromUUID,
18 getVideosList, 22 getVideosList,
23 killallServers,
19 makeRawRequest, 24 makeRawRequest,
20 removeVideo, 25 removeVideo,
26 reRunServer,
21 sendRTMPStream, 27 sendRTMPStream,
22 sendRTMPStreamInVideo, 28 sendRTMPStreamInVideo,
23 ServerInfo, 29 ServerInfo,
@@ -31,9 +37,9 @@ import {
31 viewVideo, 37 viewVideo,
32 wait, 38 wait,
33 waitJobs, 39 waitJobs,
34 waitUntilLiveStarts 40 waitUntilLiveStarts,
41 waitUntilLog
35} from '../../../../shared/extra-utils' 42} from '../../../../shared/extra-utils'
36import { FfmpegCommand } from 'fluent-ffmpeg'
37 43
38const expect = chai.expect 44const expect = chai.expect
39 45
@@ -316,6 +322,19 @@ describe('Test live', function () {
316 expect(hlsPlaylist.files).to.have.lengthOf(0) 322 expect(hlsPlaylist.files).to.have.lengthOf(0)
317 323
318 await checkResolutionsInMasterPlaylist(hlsPlaylist.playlistUrl, resolutions) 324 await checkResolutionsInMasterPlaylist(hlsPlaylist.playlistUrl, resolutions)
325
326 for (let i = 0; i < resolutions.length; i++) {
327 const segmentName = `${i}-000001.ts`
328 await waitUntilLog(servers[0], `${video.uuid}/${segmentName}`, 1, false)
329
330 const res = await getPlaylist(`${servers[0].url}/static/streaming-playlists/hls/${video.uuid}/${i}.m3u8`)
331 const subPlaylist = res.text
332
333 expect(subPlaylist).to.contain(segmentName)
334
335 const baseUrlAndPath = servers[0].url + '/static/streaming-playlists/hls'
336 await checkLiveSegmentHash(baseUrlAndPath, video.uuid, segmentName, hlsPlaylist)
337 }
319 } 338 }
320 } 339 }
321 340
@@ -580,6 +599,65 @@ describe('Test live', function () {
580 }) 599 })
581 }) 600 })
582 601
602 describe('After a server restart', function () {
603 let liveVideoId: string
604 let liveVideoReplayId: string
605
606 async function createLiveWrapper (saveReplay: boolean) {
607 const liveAttributes = {
608 name: 'live video',
609 channelId: servers[0].videoChannel.id,
610 privacy: VideoPrivacy.PUBLIC,
611 saveReplay
612 }
613
614 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes)
615 return res.body.video.uuid
616 }
617
618 before(async function () {
619 this.timeout(60000)
620
621 liveVideoId = await createLiveWrapper(false)
622 liveVideoReplayId = await createLiveWrapper(true)
623
624 await Promise.all([
625 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId),
626 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoReplayId)
627 ])
628
629 await Promise.all([
630 waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoId),
631 waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoReplayId)
632 ])
633
634 await killallServers([ servers[0] ])
635 await reRunServer(servers[0])
636
637 await wait(5000)
638 })
639
640 it('Should cleanup lives', async function () {
641 this.timeout(60000)
642
643 const res = await getVideo(servers[0].url, liveVideoId)
644 const video: VideoDetails = res.body
645
646 expect(video.state.id).to.equal(VideoState.LIVE_ENDED)
647 })
648
649 it('Should save a live replay', async function () {
650 this.timeout(60000)
651
652 await waitJobs(servers)
653
654 const res = await getVideo(servers[0].url, liveVideoReplayId)
655 const video: VideoDetails = res.body
656
657 expect(video.state.id).to.equal(VideoState.PUBLISHED)
658 })
659 })
660
583 after(async function () { 661 after(async function () {
584 await cleanupTests(servers) 662 await cleanupTests(servers)
585 }) 663 })