UpdatedAt
} from 'sequelize-typescript'
import { buildNSFWFilter } from '@server/helpers/express-utils'
-import { getPrivaciesForFederation, isPrivacyForFederation } from '@server/helpers/video'
+import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
+import { LiveManager } from '@server/lib/live-manager'
import { getHLSDirectory, getTorrentFileName, getTorrentFilePath, getVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
import { getServerActor } from '@server/models/application/application'
import { ModelCache } from '@server/models/model-cache'
import { VideoFile } from '@shared/models/videos/video-file.model'
import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared'
-import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
+import { VideoObject } from '../../../shared/models/activitypub/objects'
import { Video, VideoDetails } from '../../../shared/models/videos'
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
import { VideoFilter } from '../../../shared/models/videos/video-query.type'
MVideoWithRights
} from '../../types/models'
import { MThumbnail } from '../../types/models/video/thumbnail'
-import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileRedundanciesOpt } from '../../types/models/video/video-file'
+import { MVideoFile, MVideoFileRedundanciesOpt, MVideoFileStreamingPlaylistVideo } from '../../types/models/video/video-file'
import { VideoAbuseModel } from '../abuse/video-abuse'
import { AccountModel } from '../account/account'
import { AccountVideoRateModel } from '../account/account-video-rate'
videoModelToFormattedJSON
} from './video-format-utils'
import { VideoImportModel } from './video-import'
+import { VideoLiveModel } from './video-live'
import { VideoPlaylistElementModel } from './video-playlist-element'
import { buildListQuery, BuildVideosQueryOptions, wrapForAPIResults } from './video-query-builder'
import { VideoShareModel } from './video-share'
import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
import { VideoTagModel } from './video-tag'
import { VideoViewModel } from './video-view'
-import { stream } from 'winston'
export enum ScopeNames {
AVAILABLE_FOR_LIST_IDS = 'AVAILABLE_FOR_LIST_IDS',
WITH_STREAMING_PLAYLISTS = 'WITH_STREAMING_PLAYLISTS',
WITH_USER_ID = 'WITH_USER_ID',
WITH_IMMUTABLE_ATTRIBUTES = 'WITH_IMMUTABLE_ATTRIBUTES',
- WITH_THUMBNAILS = 'WITH_THUMBNAILS'
+ WITH_THUMBNAILS = 'WITH_THUMBNAILS',
+ WITH_LIVE = 'WITH_LIVE'
}
export type ForAPIOptions = {
}
]
},
+ [ScopeNames.WITH_LIVE]: {
+ include: [
+ {
+ model: VideoLiveModel.unscoped(),
+ required: false
+ }
+ ]
+ },
[ScopeNames.WITH_USER_ID]: {
include: [
{
@Column
remote: boolean
+ @AllowNull(false)
+ @Default(false)
+ @Column
+ isLive: boolean
+
@AllowNull(false)
@Is('VideoUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
@Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max))
})
VideoBlacklist: VideoBlacklistModel
+ @HasOne(() => VideoLiveModel, {
+ foreignKey: {
+ name: 'videoId',
+ allowNull: false
+ },
+ onDelete: 'cascade'
+ })
+ VideoLive: VideoLiveModel
+
@HasOne(() => VideoImportModel, {
foreignKey: {
name: 'videoId',
return undefined
}
+ @BeforeDestroy
+ static stopLiveIfNeeded (instance: VideoModel) {
+ if (!instance.isLive) return
+
+ logger.info('Stopping live of video %s after video deletion.', instance.uuid)
+
+ return LiveManager.Instance.stopSessionOf(instance.id)
+ }
+
@BeforeDestroy
static invalidateCache (instance: VideoModel) {
ModelCache.Instance.invalidateCache('video', instance.id)
}
]
},
+ {
+ model: VideoStreamingPlaylistModel.unscoped(),
+ required: false,
+ include: [
+ {
+ model: VideoFileModel,
+ required: false
+ }
+ ]
+ },
+ VideoLiveModel.unscoped(),
VideoFileModel,
TagModel
]
})
}
+ static listPublishedLiveIds () {
+ const options = {
+ attributes: [ 'id' ],
+ where: {
+ isLive: true,
+ state: VideoState.PUBLISHED
+ }
+ }
+
+ return VideoModel.findAll(options)
+ .map(v => v.id)
+ }
+
static listUserVideosForApi (
accountId: number,
start: number,
return VideoModel.getAvailableForApi(queryOptions)
}
+ static countLocalLives () {
+ const options = {
+ where: {
+ remote: false,
+ isLive: true
+ }
+ }
+
+ return VideoModel.count(options)
+ }
+
+ static countLivesOfAccount (accountId: number) {
+ const options = {
+ where: {
+ remote: false,
+ isLive: true
+ },
+ include: [
+ {
+ required: true,
+ model: VideoChannelModel.unscoped(),
+ where: {
+ accountId
+ }
+ }
+ ]
+ }
+
+ return VideoModel.count(options)
+ }
+
static load (id: number | string, t?: Transaction): Bluebird<MVideoThumbnail> {
const where = buildWhereIdOrUUID(id)
const options = {
ScopeNames.WITH_SCHEDULED_UPDATE,
ScopeNames.WITH_WEBTORRENT_FILES,
ScopeNames.WITH_STREAMING_PLAYLISTS,
- ScopeNames.WITH_THUMBNAILS
+ ScopeNames.WITH_THUMBNAILS,
+ ScopeNames.WITH_LIVE
]
if (userId) {
ScopeNames.WITH_ACCOUNT_DETAILS,
ScopeNames.WITH_SCHEDULED_UPDATE,
ScopeNames.WITH_THUMBNAILS,
+ ScopeNames.WITH_LIVE,
{ method: [ ScopeNames.WITH_WEBTORRENT_FILES, true ] },
{ method: [ ScopeNames.WITH_STREAMING_PLAYLISTS, true ] }
]
const thumbnailsDone = new Set<number>()
const historyDone = new Set<number>()
const videoFilesDone = new Set<number>()
- const videoStreamingPlaylistsDone = new Set<number>()
const videos: VideoModel[] = []
videoFilesDone.add(row.VideoFiles.id)
}
- if (row.VideoFiles?.id && !videoFilesDone.has(row.VideoFiles.id)) {
- const videoFileModel = new VideoFileModel(pick(row.VideoFiles, videoFileKeys))
- videoModel.VideoFiles.push(videoFileModel)
-
- videoFilesDone.add(row.VideoFiles.id)
- }
-
if (row.VideoStreamingPlaylists?.id && !videoStreamingPlaylistMemo[row.VideoStreamingPlaylists.id]) {
const streamingPlaylist = new VideoStreamingPlaylistModel(pick(row.VideoStreamingPlaylists, videoStreamingPlaylistKeys))
streamingPlaylist.VideoFiles = []
const { baseUrlHttp, baseUrlWs } = this.getBaseUrls()
let files: MVideoFileRedundanciesOpt[] = []
- logger.info('coucou', { files })
-
if (Array.isArray(this.VideoFiles)) {
files = files.concat(this.VideoFiles)
}
files = files.concat(p.VideoFiles || [])
}
- logger.info('coucou', { files, video: this.VideoStreamingPlaylists })
-
return videoFilesModelToFormattedJSON(this, baseUrlHttp, baseUrlWs, files)
}
- toActivityPubObject (this: MVideoAP): VideoTorrentObject {
+ toActivityPubObject (this: MVideoAP): VideoObject {
return videoModelToActivityPubObject(this)
}
return isPrivacyForFederation(this.privacy)
}
+ hasStateForFederation () {
+ return isStateForFederation(this.state)
+ }
+
isNewVideo (newPrivacy: VideoPrivacy) {
return this.hasPrivacyForFederation() === false && isPrivacyForFederation(newPrivacy) === true
}