mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-06 03:50:26 +02:00
Add Scheduled Lives functionality (#7144)
* Add Scheduled Lives functionality through originallyPublishedAt Implements #6604 by reusing the originallyPublishedAt field of isLive videos to mark "waiting for live" videos as scheduled at a set time. * Hide scheduled lives from Browse Videos page * Add tests for Scheduled Live videos * Make scheduled lives use a dedicated scheduledAt field in the VideoLive table * Plan live schedules to evolve in the future * Use a dedicated table to store live schedules, so we can add multiple schedules in the future and also add a title, description etc. for a specific schedule * Adapt REST API to use an array to store/get live schedules * Add REST API param so it's the client choice to include or not scheduled lives * Export schedules info in user import/export --------- Co-authored-by: Chocobozzz <me@florianbigard.com>
This commit is contained in:
parent
a5c087d3d4
commit
8c9b4abe45
62 changed files with 858 additions and 148 deletions
|
@ -6,7 +6,7 @@ import { ActorModel } from '@server/models/actor/actor.js'
|
|||
import { MActorFull } from '@server/types/models/index.js'
|
||||
import WebFinger from 'webfinger.js'
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
||||
const webfinger = new WebFinger({
|
||||
tls_only: isProdInstance(),
|
||||
uri_fallback: false,
|
||||
|
|
|
@ -17,6 +17,7 @@ import { setVideoTags } from '@server/lib/video.js'
|
|||
import { StoryboardModel } from '@server/models/video/storyboard.js'
|
||||
import { VideoCaptionModel } from '@server/models/video/video-caption.js'
|
||||
import { VideoFileModel } from '@server/models/video/video-file.js'
|
||||
import { VideoLiveScheduleModel } from '@server/models/video/video-live-schedule.js'
|
||||
import { VideoLiveModel } from '@server/models/video/video-live.js'
|
||||
import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist.js'
|
||||
import {
|
||||
|
@ -35,6 +36,7 @@ import {
|
|||
getCaptionAttributesFromObject,
|
||||
getFileAttributesFromUrl,
|
||||
getLiveAttributesFromObject,
|
||||
getLiveSchedulesAttributesFromObject,
|
||||
getPreviewFromIcons,
|
||||
getStoryboardAttributeFromObject,
|
||||
getStreamingPlaylistAttributesFromObject,
|
||||
|
@ -101,7 +103,7 @@ export abstract class APVideoAbstractBuilder {
|
|||
const existingCaptions = await VideoCaptionModel.listVideoCaptions(video.id, t)
|
||||
|
||||
let captionsToCreate = getCaptionAttributesFromObject(video, this.videoObject)
|
||||
.map(a => new VideoCaptionModel(a) as MVideoCaption)
|
||||
.map(a => new VideoCaptionModel(a) as MVideoCaption)
|
||||
|
||||
for (const existingCaption of existingCaptions) {
|
||||
// Only keep captions that do not already exist
|
||||
|
@ -136,7 +138,14 @@ export abstract class APVideoAbstractBuilder {
|
|||
const attributes = getLiveAttributesFromObject(video, this.videoObject)
|
||||
const [ videoLive ] = await VideoLiveModel.upsert(attributes, { transaction, returning: true })
|
||||
|
||||
video.VideoLive = videoLive
|
||||
await VideoLiveScheduleModel.deleteAllOfLiveId(videoLive.id, transaction)
|
||||
videoLive.LiveSchedules = []
|
||||
|
||||
for (const scheduleAttributes of getLiveSchedulesAttributesFromObject(videoLive, this.videoObject)) {
|
||||
const scheduleModel = new VideoLiveScheduleModel(scheduleAttributes)
|
||||
|
||||
videoLive.LiveSchedules.push(await scheduleModel.save({ transaction }))
|
||||
}
|
||||
}
|
||||
|
||||
protected async setWebVideoFiles (video: MVideoFullLight, t: Transaction) {
|
||||
|
|
|
@ -31,7 +31,15 @@ import { VideoCaptionModel } from '@server/models/video/video-caption.js'
|
|||
import { VideoFileModel } from '@server/models/video/video-file.js'
|
||||
import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist.js'
|
||||
import { FilteredModelAttributes } from '@server/types/index.js'
|
||||
import { isStreamingPlaylist, MChannelId, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoId } from '@server/types/models/index.js'
|
||||
import {
|
||||
isStreamingPlaylist,
|
||||
MChannelId,
|
||||
MStreamingPlaylistVideo,
|
||||
MVideo,
|
||||
MVideoFile,
|
||||
MVideoId,
|
||||
MVideoLive
|
||||
} from '@server/types/models/index.js'
|
||||
import { decode as magnetUriDecode } from 'magnet-uri'
|
||||
import { basename, extname } from 'path'
|
||||
import { getDurationFromActivityStream } from '../../activity.js'
|
||||
|
@ -206,6 +214,14 @@ export function getLiveAttributesFromObject (video: MVideoId, videoObject: Video
|
|||
videoId: video.id
|
||||
}
|
||||
}
|
||||
export function getLiveSchedulesAttributesFromObject (live: MVideoLive, videoObject: VideoObject) {
|
||||
const schedules = videoObject.schedules || []
|
||||
|
||||
return schedules.map(s => ({
|
||||
liveVideoId: live.id,
|
||||
startAt: s.startDate
|
||||
}))
|
||||
}
|
||||
|
||||
export function getCaptionAttributesFromObject (video: MVideoId, videoObject: VideoObject) {
|
||||
return videoObject.subtitleLanguage.map(c => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue