import { VideoLiveModel } from '@server/models/video/video-live'
import { MVideoDetails, MVideoFullLight } from '@server/types/models'
import { buildUUID, uuidToShort } from '@shared/extra-utils'
-import { HttpStatusCode, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, VideoState } from '@shared/models'
+import { HttpStatusCode, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, UserRight, VideoState } from '@shared/models'
import { logger } from '../../../helpers/logger'
import { sequelizeTypescript } from '../../../initializers/database'
import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail'
-import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares'
+import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, optionalAuthenticate } from '../../../middlewares'
import { VideoModel } from '../../../models/video/video'
const liveRouter = express.Router()
)
liveRouter.get('/live/:videoId',
- authenticate,
+ optionalAuthenticate,
asyncMiddleware(videoLiveGetValidator),
getLiveVideo
)
function getLiveVideo (req: express.Request, res: express.Response) {
const videoLive = res.locals.videoLive
- return res.json(videoLive.toFormattedJSON())
+ return res.json(videoLive.toFormattedJSON(canSeePrivateLiveInformation(res)))
+}
+
+function canSeePrivateLiveInformation (res: express.Response) {
+ const user = res.locals.oauth?.token.User
+ if (!user) return false
+
+ if (user.hasRight(UserRight.GET_ANY_LIVE)) return true
+
+ const video = res.locals.videoAll
+ return video.VideoChannel.Account.userId === user.id
}
async function updateLiveVideo (req: express.Request, res: express.Response) {
isValidVideoIdParam('videoId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
- logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.params, user: res.locals.oauth.token.User.username })
+ logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.params })
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res, 'all')) return
- // Check if the user who did the request is able to get the live info
- const user = res.locals.oauth.token.User
- if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.GET_ANY_LIVE, res, false)) return
-
const videoLive = await VideoLiveModel.loadByVideoId(res.locals.videoAll.id)
if (!videoLive) {
return res.fail({
return VideoLiveModel.findOne<MVideoLive>(query)
}
- toFormattedJSON (): LiveVideo {
- let rtmpUrl: string = null
- let rtmpsUrl: string = null
+ toFormattedJSON (canSeePrivateInformation: boolean): LiveVideo {
+ let privateInformation: Pick<LiveVideo, 'rtmpUrl' | 'rtmpsUrl' | 'streamKey'> | {} = {}
// If we don't have a stream key, it means this is a remote live so we don't specify the rtmp URL
- if (this.streamKey) {
- if (CONFIG.LIVE.RTMP.ENABLED) rtmpUrl = WEBSERVER.RTMP_URL
- if (CONFIG.LIVE.RTMPS.ENABLED) rtmpsUrl = WEBSERVER.RTMPS_URL
+ // We also display these private information only to the live owne/moderators
+ if (this.streamKey && canSeePrivateInformation === true) {
+ privateInformation = {
+ streamKey: this.streamKey,
+
+ rtmpUrl: CONFIG.LIVE.RTMP.ENABLED
+ ? WEBSERVER.RTMP_URL
+ : null,
+
+ rtmpsUrl: CONFIG.LIVE.RTMPS.ENABLED
+ ? WEBSERVER.RTMPS_URL
+ : null
+ }
}
return {
- rtmpUrl,
- rtmpsUrl,
+ ...privateInformation,
- streamKey: this.streamKey,
permanentLive: this.permanentLive,
saveReplay: this.saveReplay,
latencyMode: this.latencyMode
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import 'mocha'
+import { expect } from 'chai'
import { omit } from 'lodash'
import { buildAbsoluteFixturePath } from '@shared/core-utils'
import { HttpStatusCode, LiveVideoLatencyMode, VideoCreateResult, VideoPrivacy } from '@shared/models'
describe('When getting live information', function () {
- it('Should fail without access token', async function () {
- await command.get({ token: '', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
- })
it('Should fail with a bad access token', async function () {
await command.get({ token: 'toto', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
})
- it('Should fail with access token of another user', async function () {
- await command.get({ token: userAccessToken, videoId: video.id, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+ it('Should not display private information without access token', async function () {
+ const live = await command.get({ token: '', videoId: video.id })
+
+ expect(live.rtmpUrl).to.not.exist
+ expect(live.streamKey).to.not.exist
+ expect(live.latencyMode).to.exist
+ })
+
+ it('Should not display private information with token of another user', async function () {
+ const live = await command.get({ token: userAccessToken, videoId: video.id })
+
+ expect(live.rtmpUrl).to.not.exist
+ expect(live.streamKey).to.not.exist
+ expect(live.latencyMode).to.exist
+ })
+
+ it('Should display private information with appropriate token', async function () {
+ const live = await command.get({ videoId: video.id })
+
+ expect(live.rtmpUrl).to.exist
+ expect(live.streamKey).to.exist
+ expect(live.latencyMode).to.exist
})
it('Should fail with a bad video id', async function () {
export const enum UserRight {
- ALL,
+ ALL = 0,
- MANAGE_USERS,
+ MANAGE_USERS = 1,
- MANAGE_SERVER_FOLLOW,
+ MANAGE_SERVER_FOLLOW = 2,
- MANAGE_LOGS,
+ MANAGE_LOGS = 3,
- MANAGE_DEBUG,
+ MANAGE_DEBUG = 4,
- MANAGE_SERVER_REDUNDANCY,
+ MANAGE_SERVER_REDUNDANCY = 5,
- MANAGE_ABUSES,
+ MANAGE_ABUSES = 6,
- MANAGE_JOBS,
+ MANAGE_JOBS = 7,
- MANAGE_CONFIGURATION,
- MANAGE_INSTANCE_CUSTOM_PAGE,
+ MANAGE_CONFIGURATION = 8,
+ MANAGE_INSTANCE_CUSTOM_PAGE = 9,
- MANAGE_ACCOUNTS_BLOCKLIST,
- MANAGE_SERVERS_BLOCKLIST,
+ MANAGE_ACCOUNTS_BLOCKLIST = 10,
+ MANAGE_SERVERS_BLOCKLIST = 11,
- MANAGE_VIDEO_BLACKLIST,
- MANAGE_ANY_VIDEO_CHANNEL,
+ MANAGE_VIDEO_BLACKLIST = 12,
+ MANAGE_ANY_VIDEO_CHANNEL = 13,
- REMOVE_ANY_VIDEO,
- REMOVE_ANY_VIDEO_PLAYLIST,
- REMOVE_ANY_VIDEO_COMMENT,
+ REMOVE_ANY_VIDEO = 14,
+ REMOVE_ANY_VIDEO_PLAYLIST = 15,
+ REMOVE_ANY_VIDEO_COMMENT = 16,
- UPDATE_ANY_VIDEO,
- UPDATE_ANY_VIDEO_PLAYLIST,
+ UPDATE_ANY_VIDEO = 17,
+ UPDATE_ANY_VIDEO_PLAYLIST = 18,
- GET_ANY_LIVE,
- SEE_ALL_VIDEOS,
- SEE_ALL_COMMENTS,
- CHANGE_VIDEO_OWNERSHIP,
+ GET_ANY_LIVE = 19,
+ SEE_ALL_VIDEOS = 20,
+ SEE_ALL_COMMENTS = 21,
+ CHANGE_VIDEO_OWNERSHIP = 22,
- MANAGE_PLUGINS,
+ MANAGE_PLUGINS = 23,
- MANAGE_VIDEOS_REDUNDANCIES,
+ MANAGE_VIDEOS_REDUNDANCIES = 24,
- MANAGE_VIDEO_FILES,
- RUN_VIDEO_TRANSCODING,
+ MANAGE_VIDEO_FILES = 25,
+ RUN_VIDEO_TRANSCODING = 26,
- MANAGE_VIDEO_IMPORTS
+ MANAGE_VIDEO_IMPORTS = 27
}
import { LiveVideoLatencyMode } from './live-video-latency-mode.enum'
export interface LiveVideo {
- rtmpUrl: string
- rtmpsUrl: string
-
- streamKey: string
+ // If owner
+ rtmpUrl?: string
+ rtmpsUrl?: string
+ streamKey?: string
saveReplay: boolean
permanentLive: boolean
properties:
rtmpUrl:
type: string
+ description: Included in the response if an appropriate token is provided
rtmpsUrl:
type: string
+ description: Included in the response if an appropriate token is provided
streamKey:
type: string
- description: RTMP stream key to use to stream into this live video
+ description: RTMP stream key to use to stream into this live video. Included in the response if an appropriate token is provided
saveReplay:
type: boolean
permanentLive: