Add live notification tests
This commit is contained in:
parent
68e70a745b
commit
bd54ad1953
11 changed files with 286 additions and 37 deletions
|
@ -189,6 +189,7 @@
|
||||||
"@types/redis": "^2.8.5",
|
"@types/redis": "^2.8.5",
|
||||||
"@types/request": "^2.0.3",
|
"@types/request": "^2.0.3",
|
||||||
"@types/socket.io": "^2.1.2",
|
"@types/socket.io": "^2.1.2",
|
||||||
|
"@types/socket.io-client": "^1.4.34",
|
||||||
"@types/supertest": "^2.0.3",
|
"@types/supertest": "^2.0.3",
|
||||||
"@types/validator": "^13.0.0",
|
"@types/validator": "^13.0.0",
|
||||||
"@types/webtorrent": "^0.107.0",
|
"@types/webtorrent": "^0.107.0",
|
||||||
|
@ -211,6 +212,7 @@
|
||||||
"marked-man": "^0.7.0",
|
"marked-man": "^0.7.0",
|
||||||
"mocha": "^8.0.1",
|
"mocha": "^8.0.1",
|
||||||
"nodemon": "^2.0.1",
|
"nodemon": "^2.0.1",
|
||||||
|
"socket.io-client": "^2.3.1",
|
||||||
"source-map-support": "^0.5.0",
|
"source-map-support": "^0.5.0",
|
||||||
"supertest": "^4.0.2",
|
"supertest": "^4.0.2",
|
||||||
"swagger-cli": "^4.0.2",
|
"swagger-cli": "^4.0.2",
|
||||||
|
|
|
@ -244,7 +244,7 @@ class LiveManager {
|
||||||
size: -1,
|
size: -1,
|
||||||
extname: '.ts',
|
extname: '.ts',
|
||||||
infoHash: null,
|
infoHash: null,
|
||||||
fps: -1,
|
fps,
|
||||||
videoStreamingPlaylistId: playlist.id
|
videoStreamingPlaylistId: playlist.id
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
logger.error('Cannot create file for live streaming.', { err })
|
logger.error('Cannot create file for live streaming.', { err })
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { UserNotificationModelForApi } from '@server/types/models/user'
|
||||||
import { LiveVideoEventPayload, LiveVideoEventType } from '@shared/models'
|
import { LiveVideoEventPayload, LiveVideoEventType } from '@shared/models'
|
||||||
import { logger } from '../helpers/logger'
|
import { logger } from '../helpers/logger'
|
||||||
import { authenticateSocket } from '../middlewares'
|
import { authenticateSocket } from '../middlewares'
|
||||||
|
import { isIdValid } from '@server/helpers/custom-validators/misc'
|
||||||
|
|
||||||
class PeerTubeSocket {
|
class PeerTubeSocket {
|
||||||
|
|
||||||
|
@ -39,8 +40,17 @@ class PeerTubeSocket {
|
||||||
|
|
||||||
this.liveVideosNamespace = io.of('/live-videos')
|
this.liveVideosNamespace = io.of('/live-videos')
|
||||||
.on('connection', socket => {
|
.on('connection', socket => {
|
||||||
socket.on('subscribe', ({ videoId }) => socket.join(videoId))
|
socket.on('subscribe', ({ videoId }) => {
|
||||||
socket.on('unsubscribe', ({ videoId }) => socket.leave(videoId))
|
if (!isIdValid(videoId)) return
|
||||||
|
|
||||||
|
socket.join(videoId)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('unsubscribe', ({ videoId }) => {
|
||||||
|
if (!isIdValid(videoId)) return
|
||||||
|
|
||||||
|
socket.leave(videoId)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -329,6 +329,10 @@ export class VideoFileModel extends Model<VideoFileModel> {
|
||||||
return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname]
|
return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isLive () {
|
||||||
|
return this.size === -1
|
||||||
|
}
|
||||||
|
|
||||||
hasSameUniqueKeysThan (other: MVideoFile) {
|
hasSameUniqueKeysThan (other: MVideoFile) {
|
||||||
return this.fps === other.fps &&
|
return this.fps === other.fps &&
|
||||||
this.resolution === other.resolution &&
|
this.resolution === other.resolution &&
|
||||||
|
|
|
@ -199,6 +199,7 @@ function videoFilesModelToFormattedJSON (
|
||||||
const video = extractVideo(model)
|
const video = extractVideo(model)
|
||||||
|
|
||||||
return [ ...videoFiles ]
|
return [ ...videoFiles ]
|
||||||
|
.filter(f => !f.isLive())
|
||||||
.sort(sortByResolutionDesc)
|
.sort(sortByResolutionDesc)
|
||||||
.map(videoFile => {
|
.map(videoFile => {
|
||||||
return {
|
return {
|
||||||
|
@ -225,7 +226,9 @@ function addVideoFilesInAPAcc (
|
||||||
baseUrlWs: string,
|
baseUrlWs: string,
|
||||||
files: MVideoFile[]
|
files: MVideoFile[]
|
||||||
) {
|
) {
|
||||||
const sortedFiles = [ ...files ].sort(sortByResolutionDesc)
|
const sortedFiles = [ ...files ]
|
||||||
|
.filter(f => !f.isLive())
|
||||||
|
.sort(sortByResolutionDesc)
|
||||||
|
|
||||||
for (const file of sortedFiles) {
|
for (const file of sortedFiles) {
|
||||||
acc.push({
|
acc.push({
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export * from './live-constraints'
|
import './live-constraints'
|
||||||
export * from './live-save-replay'
|
import './live-save-replay'
|
||||||
export * from './live'
|
import './live'
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
|
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import { LiveVideo, LiveVideoCreate, User, VideoDetails, VideoPrivacy } from '@shared/models'
|
import { getLiveNotificationSocket } from '@shared/extra-utils/socket/socket-io'
|
||||||
|
import { LiveVideo, LiveVideoCreate, User, Video, VideoDetails, VideoPrivacy, VideoState, VideoStreamingPlaylistType } from '@shared/models'
|
||||||
import {
|
import {
|
||||||
addVideoToBlacklist,
|
addVideoToBlacklist,
|
||||||
|
checkLiveCleanup,
|
||||||
|
checkResolutionsInMasterPlaylist,
|
||||||
cleanupTests,
|
cleanupTests,
|
||||||
createLive,
|
createLive,
|
||||||
createUser,
|
createUser,
|
||||||
|
@ -13,19 +16,23 @@ import {
|
||||||
getLive,
|
getLive,
|
||||||
getMyUserInformation,
|
getMyUserInformation,
|
||||||
getVideo,
|
getVideo,
|
||||||
|
getVideoIdFromUUID,
|
||||||
getVideosList,
|
getVideosList,
|
||||||
makeRawRequest,
|
makeRawRequest,
|
||||||
removeVideo,
|
removeVideo,
|
||||||
sendRTMPStream,
|
sendRTMPStream,
|
||||||
|
sendRTMPStreamInVideo,
|
||||||
ServerInfo,
|
ServerInfo,
|
||||||
setAccessTokensToServers,
|
setAccessTokensToServers,
|
||||||
setDefaultVideoChannel,
|
setDefaultVideoChannel,
|
||||||
|
stopFfmpeg,
|
||||||
testFfmpegStreamError,
|
testFfmpegStreamError,
|
||||||
testImage,
|
testImage,
|
||||||
updateCustomSubConfig,
|
updateCustomSubConfig,
|
||||||
updateLive,
|
updateLive,
|
||||||
userLogin,
|
userLogin,
|
||||||
waitJobs
|
waitJobs,
|
||||||
|
waitUntilLiveStarts
|
||||||
} from '../../../../shared/extra-utils'
|
} from '../../../../shared/extra-utils'
|
||||||
|
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
@ -234,12 +241,12 @@ describe('Test live', function () {
|
||||||
async function createLiveWrapper () {
|
async function createLiveWrapper () {
|
||||||
const liveAttributes = {
|
const liveAttributes = {
|
||||||
name: 'user live',
|
name: 'user live',
|
||||||
channelId: userChannelId,
|
channelId: servers[0].videoChannel.id,
|
||||||
privacy: VideoPrivacy.PUBLIC,
|
privacy: VideoPrivacy.PUBLIC,
|
||||||
saveReplay: false
|
saveReplay: false
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await createLive(servers[0].url, userAccessToken, liveAttributes)
|
const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes)
|
||||||
const uuid = res.body.video.uuid
|
const uuid = res.body.video.uuid
|
||||||
|
|
||||||
const resLive = await getLive(servers[0].url, servers[0].accessToken, uuid)
|
const resLive = await getLive(servers[0].url, servers[0].accessToken, uuid)
|
||||||
|
@ -295,42 +302,226 @@ describe('Test live', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Live transcoding', function () {
|
describe('Live transcoding', function () {
|
||||||
|
let liveVideoId: string
|
||||||
|
|
||||||
|
async function createLiveWrapper (saveReplay: boolean) {
|
||||||
|
const liveAttributes = {
|
||||||
|
name: 'live video',
|
||||||
|
channelId: servers[0].videoChannel.id,
|
||||||
|
privacy: VideoPrivacy.PUBLIC,
|
||||||
|
saveReplay
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes)
|
||||||
|
return res.body.video.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testVideoResolutions (liveVideoId: string, resolutions: number[]) {
|
||||||
|
for (const server of servers) {
|
||||||
|
const resList = await getVideosList(server.url)
|
||||||
|
const videos: Video[] = resList.body.data
|
||||||
|
|
||||||
|
expect(videos.find(v => v.uuid === liveVideoId)).to.exist
|
||||||
|
|
||||||
|
const resVideo = await getVideo(server.url, liveVideoId)
|
||||||
|
const video: VideoDetails = resVideo.body
|
||||||
|
|
||||||
|
expect(video.streamingPlaylists).to.have.lengthOf(1)
|
||||||
|
|
||||||
|
const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS)
|
||||||
|
expect(hlsPlaylist).to.exist
|
||||||
|
|
||||||
|
// Only finite files are displayed
|
||||||
|
expect(hlsPlaylist.files).to.have.lengthOf(0)
|
||||||
|
|
||||||
|
await checkResolutionsInMasterPlaylist(hlsPlaylist.playlistUrl, resolutions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateConf (resolutions: number[]) {
|
||||||
|
return updateCustomSubConfig(servers[0].url, servers[0].accessToken, {
|
||||||
|
live: {
|
||||||
|
enabled: true,
|
||||||
|
allowReplay: true,
|
||||||
|
maxDuration: null,
|
||||||
|
transcoding: {
|
||||||
|
enabled: true,
|
||||||
|
resolutions: {
|
||||||
|
'240p': resolutions.includes(240),
|
||||||
|
'360p': resolutions.includes(360),
|
||||||
|
'480p': resolutions.includes(480),
|
||||||
|
'720p': resolutions.includes(720),
|
||||||
|
'1080p': resolutions.includes(1080),
|
||||||
|
'2160p': resolutions.includes(2160)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
before(async function () {
|
||||||
|
await updateConf([])
|
||||||
|
})
|
||||||
|
|
||||||
it('Should enable transcoding without additional resolutions', async function () {
|
it('Should enable transcoding without additional resolutions', async function () {
|
||||||
// enable
|
this.timeout(30000)
|
||||||
// stream
|
|
||||||
// wait federation + test
|
|
||||||
|
|
||||||
|
liveVideoId = await createLiveWrapper(false)
|
||||||
|
|
||||||
|
const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId)
|
||||||
|
await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoId)
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await testVideoResolutions(liveVideoId, [ 720 ])
|
||||||
|
|
||||||
|
await stopFfmpeg(command)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should enable transcoding with some resolutions', async function () {
|
it('Should enable transcoding with some resolutions', async function () {
|
||||||
// enable
|
this.timeout(30000)
|
||||||
// stream
|
|
||||||
// wait federation + test
|
const resolutions = [ 240, 480 ]
|
||||||
|
await updateConf(resolutions)
|
||||||
|
liveVideoId = await createLiveWrapper(false)
|
||||||
|
|
||||||
|
const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId)
|
||||||
|
await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoId)
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await testVideoResolutions(liveVideoId, resolutions)
|
||||||
|
|
||||||
|
await stopFfmpeg(command)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should enable transcoding with some resolutions and correctly save them', async function () {
|
it('Should enable transcoding with some resolutions and correctly save them', async function () {
|
||||||
// enable
|
this.timeout(60000)
|
||||||
// stream
|
|
||||||
// end stream
|
const resolutions = [ 240, 360, 720 ]
|
||||||
// wait federation + test
|
await updateConf(resolutions)
|
||||||
|
liveVideoId = await createLiveWrapper(true)
|
||||||
|
|
||||||
|
const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId)
|
||||||
|
await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoId)
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await testVideoResolutions(liveVideoId, resolutions)
|
||||||
|
|
||||||
|
await stopFfmpeg(command)
|
||||||
|
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
for (const server of servers) {
|
||||||
|
const resVideo = await getVideo(server.url, liveVideoId)
|
||||||
|
const video: VideoDetails = resVideo.body
|
||||||
|
|
||||||
|
expect(video.duration).to.be.greaterThan(1)
|
||||||
|
expect(video.files).to.have.lengthOf(0)
|
||||||
|
|
||||||
|
const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS)
|
||||||
|
|
||||||
|
expect(hlsPlaylist.files).to.have.lengthOf(resolutions.length)
|
||||||
|
|
||||||
|
for (const resolution of resolutions) {
|
||||||
|
const file = hlsPlaylist.files.find(f => f.resolution.id === resolution)
|
||||||
|
|
||||||
|
expect(file).to.exist
|
||||||
|
expect(file.fps).to.equal(25)
|
||||||
|
expect(file.size).to.be.greaterThan(1)
|
||||||
|
|
||||||
|
await makeRawRequest(file.torrentUrl, 200)
|
||||||
|
await makeRawRequest(file.fileUrl, 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should correctly have cleaned up the live files', async function () {
|
it('Should correctly have cleaned up the live files', async function () {
|
||||||
// check files
|
this.timeout(30000)
|
||||||
|
|
||||||
|
await checkLiveCleanup(servers[0], liveVideoId, [ 240, 360, 720 ])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Live socket messages', function () {
|
describe('Live socket messages', function () {
|
||||||
|
|
||||||
it('Should correctly send a message when the live starts', async function () {
|
async function createLiveWrapper () {
|
||||||
// local
|
const liveAttributes = {
|
||||||
// federation
|
name: 'live video',
|
||||||
|
channelId: servers[0].videoChannel.id,
|
||||||
|
privacy: VideoPrivacy.PUBLIC
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes)
|
||||||
|
return res.body.video.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
it('Should correctly send a message when the live starts and ends', async function () {
|
||||||
|
this.timeout(60000)
|
||||||
|
|
||||||
|
const localStateChanges: VideoState[] = []
|
||||||
|
const remoteStateChanges: VideoState[] = []
|
||||||
|
|
||||||
|
const liveVideoUUID = await createLiveWrapper()
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
{
|
||||||
|
const videoId = await getVideoIdFromUUID(servers[0].url, liveVideoUUID)
|
||||||
|
|
||||||
|
const localSocket = getLiveNotificationSocket(servers[0].url)
|
||||||
|
localSocket.on('state-change', data => localStateChanges.push(data.state))
|
||||||
|
localSocket.emit('subscribe', { videoId })
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const videoId = await getVideoIdFromUUID(servers[1].url, liveVideoUUID)
|
||||||
|
|
||||||
|
const remoteSocket = getLiveNotificationSocket(servers[1].url)
|
||||||
|
remoteSocket.on('state-change', data => remoteStateChanges.push(data.state))
|
||||||
|
remoteSocket.emit('subscribe', { videoId })
|
||||||
|
}
|
||||||
|
|
||||||
|
const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID)
|
||||||
|
await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID)
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
for (const stateChanges of [ localStateChanges, remoteStateChanges ]) {
|
||||||
|
expect(stateChanges).to.have.lengthOf(1)
|
||||||
|
expect(stateChanges[0]).to.equal(VideoState.PUBLISHED)
|
||||||
|
}
|
||||||
|
|
||||||
|
await stopFfmpeg(command)
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
for (const stateChanges of [ localStateChanges, remoteStateChanges ]) {
|
||||||
|
expect(stateChanges).to.have.lengthOf(2)
|
||||||
|
expect(stateChanges[1]).to.equal(VideoState.LIVE_ENDED)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should correctly send a message when the live ends', async function () {
|
it('Should not receive a notification after unsubscribe', async function () {
|
||||||
// local
|
this.timeout(60000)
|
||||||
// federation
|
|
||||||
|
const stateChanges: VideoState[] = []
|
||||||
|
|
||||||
|
const liveVideoUUID = await createLiveWrapper()
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
const videoId = await getVideoIdFromUUID(servers[0].url, liveVideoUUID)
|
||||||
|
|
||||||
|
const socket = getLiveNotificationSocket(servers[0].url)
|
||||||
|
socket.on('state-change', data => stateChanges.push(data.state))
|
||||||
|
socket.emit('subscribe', { videoId })
|
||||||
|
|
||||||
|
const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID)
|
||||||
|
await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID)
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
expect(stateChanges).to.have.lengthOf(1)
|
||||||
|
socket.emit('unsubscribe', { videoId })
|
||||||
|
|
||||||
|
await stopFfmpeg(command)
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
expect(stateChanges).to.have.lengthOf(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||||
|
|
||||||
import * as chai from 'chai'
|
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
|
import * as chai from 'chai'
|
||||||
|
import { join } from 'path'
|
||||||
import {
|
import {
|
||||||
checkDirectoryIsEmpty,
|
checkDirectoryIsEmpty,
|
||||||
|
checkResolutionsInMasterPlaylist,
|
||||||
checkSegmentHash,
|
checkSegmentHash,
|
||||||
checkTmpIsEmpty,
|
checkTmpIsEmpty,
|
||||||
cleanupTests,
|
cleanupTests,
|
||||||
|
@ -23,7 +25,6 @@ import {
|
||||||
} from '../../../../shared/extra-utils'
|
} from '../../../../shared/extra-utils'
|
||||||
import { VideoDetails } from '../../../../shared/models/videos'
|
import { VideoDetails } from '../../../../shared/models/videos'
|
||||||
import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type'
|
import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type'
|
||||||
import { join } from 'path'
|
|
||||||
import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants'
|
import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants'
|
||||||
|
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
@ -66,16 +67,12 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const res = await getPlaylist(hlsPlaylist.playlistUrl)
|
await checkResolutionsInMasterPlaylist(hlsPlaylist.playlistUrl, resolutions)
|
||||||
|
|
||||||
|
const res = await getPlaylist(hlsPlaylist.playlistUrl)
|
||||||
const masterPlaylist = res.text
|
const masterPlaylist = res.text
|
||||||
|
|
||||||
for (const resolution of resolutions) {
|
for (const resolution of resolutions) {
|
||||||
const reg = new RegExp(
|
|
||||||
'#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',FRAME-RATE=\\d+,CODECS="avc1.64001f,mp4a.40.2"'
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(masterPlaylist).to.match(reg)
|
|
||||||
expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
|
expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
|
||||||
expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
|
expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,13 @@ function getUserNotificationSocket (serverUrl: string, accessToken: string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLiveNotificationSocket (serverUrl: string) {
|
||||||
|
return io(serverUrl + '/live-videos')
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getUserNotificationSocket
|
getUserNotificationSocket,
|
||||||
|
getLiveNotificationSocket
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,11 +41,26 @@ async function checkSegmentHash (
|
||||||
expect(sha256(res2.body)).to.equal(sha256Server)
|
expect(sha256(res2.body)).to.equal(sha256Server)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkResolutionsInMasterPlaylist (playlistUrl: string, resolutions: number[]) {
|
||||||
|
const res = await getPlaylist(playlistUrl)
|
||||||
|
|
||||||
|
const masterPlaylist = res.text
|
||||||
|
|
||||||
|
for (const resolution of resolutions) {
|
||||||
|
const reg = new RegExp(
|
||||||
|
'#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',(FRAME-RATE=\\d+,)?CODECS="avc1.64001f,mp4a.40.2"'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(masterPlaylist).to.match(reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getPlaylist,
|
getPlaylist,
|
||||||
getSegment,
|
getSegment,
|
||||||
|
checkResolutionsInMasterPlaylist,
|
||||||
getSegmentSha256,
|
getSegmentSha256,
|
||||||
checkSegmentHash
|
checkSegmentHash
|
||||||
}
|
}
|
||||||
|
|
22
yarn.lock
22
yarn.lock
|
@ -796,6 +796,11 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/socket.io-client@^1.4.34":
|
||||||
|
version "1.4.34"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/socket.io-client/-/socket.io-client-1.4.34.tgz#8ca5f5732a9ad92b79aba71083cda5e5821e3ed9"
|
||||||
|
integrity sha512-Lzia5OTQFJZJ5R4HsEEldywiiqT9+W2rDbyHJiiTGqOcju89sCsQ8aUXDljY6Ls33wKZZGC0bfMhr/VpOyjtXg==
|
||||||
|
|
||||||
"@types/socket.io@^2.1.2":
|
"@types/socket.io@^2.1.2":
|
||||||
version "2.1.11"
|
version "2.1.11"
|
||||||
resolved "https://registry.yarnpkg.com/@types/socket.io/-/socket.io-2.1.11.tgz#e0d6759880e5f9818d5297a3328b36641bae996b"
|
resolved "https://registry.yarnpkg.com/@types/socket.io/-/socket.io-2.1.11.tgz#e0d6759880e5f9818d5297a3328b36641bae996b"
|
||||||
|
@ -6910,6 +6915,23 @@ socket.io-client@2.3.0:
|
||||||
socket.io-parser "~3.3.0"
|
socket.io-parser "~3.3.0"
|
||||||
to-array "0.1.4"
|
to-array "0.1.4"
|
||||||
|
|
||||||
|
socket.io-client@^2.3.1:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.1.tgz#91a4038ef4d03c19967bb3c646fec6e0eaa78cff"
|
||||||
|
integrity sha512-YXmXn3pA8abPOY//JtYxou95Ihvzmg8U6kQyolArkIyLd0pgVhrfor/iMsox8cn07WCOOvvuJ6XKegzIucPutQ==
|
||||||
|
dependencies:
|
||||||
|
backo2 "1.0.2"
|
||||||
|
component-bind "1.0.0"
|
||||||
|
component-emitter "~1.3.0"
|
||||||
|
debug "~3.1.0"
|
||||||
|
engine.io-client "~3.4.0"
|
||||||
|
has-binary2 "~1.0.2"
|
||||||
|
indexof "0.0.1"
|
||||||
|
parseqs "0.0.6"
|
||||||
|
parseuri "0.0.6"
|
||||||
|
socket.io-parser "~3.3.0"
|
||||||
|
to-array "0.1.4"
|
||||||
|
|
||||||
socket.io-parser@~3.3.0:
|
socket.io-parser@~3.3.0:
|
||||||
version "3.3.1"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199"
|
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199"
|
||||||
|
|
Loading…
Reference in a new issue