Handle live federation
This commit is contained in:
parent
1ef65f4c03
commit
de6310b2fc
10 changed files with 30 additions and 26 deletions
|
@ -55,7 +55,6 @@ import {
|
||||||
videosUpdateValidator
|
videosUpdateValidator
|
||||||
} from '../../../middlewares'
|
} from '../../../middlewares'
|
||||||
import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update'
|
import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update'
|
||||||
import { TagModel } from '../../../models/video/tag'
|
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
import { VideoFileModel } from '../../../models/video/video-file'
|
import { VideoFileModel } from '../../../models/video/video-file'
|
||||||
import { abuseVideoRouter } from './abuse'
|
import { abuseVideoRouter } from './abuse'
|
||||||
|
|
|
@ -62,6 +62,7 @@ function sanitizeAndCheckVideoTorrentObject (video: any) {
|
||||||
if (!isBooleanValid(video.waitTranscoding)) video.waitTranscoding = false
|
if (!isBooleanValid(video.waitTranscoding)) video.waitTranscoding = false
|
||||||
if (!isBooleanValid(video.downloadEnabled)) video.downloadEnabled = true
|
if (!isBooleanValid(video.downloadEnabled)) video.downloadEnabled = true
|
||||||
if (!isBooleanValid(video.commentsEnabled)) video.commentsEnabled = false
|
if (!isBooleanValid(video.commentsEnabled)) video.commentsEnabled = false
|
||||||
|
if (!isBooleanValid(video.isLiveBroadcast)) video.isLiveBroadcast = false
|
||||||
|
|
||||||
return isActivityPubUrlValid(video.id) &&
|
return isActivityPubUrlValid(video.id) &&
|
||||||
isVideoNameValid(video.name) &&
|
isVideoNameValid(video.name) &&
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { isRedundancyAccepted } from '@server/lib/redundancy'
|
import { isRedundancyAccepted } from '@server/lib/redundancy'
|
||||||
import { ActivityCreate, CacheFileObject, VideoTorrentObject } from '../../../../shared'
|
import { ActivityCreate, CacheFileObject, VideoObject } from '../../../../shared'
|
||||||
import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
|
import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
|
||||||
import { VideoCommentObject } from '../../../../shared/models/activitypub/objects/video-comment-object'
|
import { VideoCommentObject } from '../../../../shared/models/activitypub/objects/video-comment-object'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
|
@ -52,7 +52,7 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processCreateVideo (activity: ActivityCreate, notify: boolean) {
|
async function processCreateVideo (activity: ActivityCreate, notify: boolean) {
|
||||||
const videoToCreateData = activity.object as VideoTorrentObject
|
const videoToCreateData = activity.object as VideoObject
|
||||||
|
|
||||||
const syncParam = { likes: false, dislikes: false, shares: false, comments: false, thumbnail: true, refreshVideo: false }
|
const syncParam = { likes: false, dislikes: false, shares: false, comments: false, thumbnail: true, refreshVideo: false }
|
||||||
const { video, created } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoToCreateData, syncParam })
|
const { video, created } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoToCreateData, syncParam })
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ActivityUpdate, CacheFileObject, VideoTorrentObject } from '../../../../shared/models/activitypub'
|
import { ActivityUpdate, CacheFileObject, VideoObject } from '../../../../shared/models/activitypub'
|
||||||
import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor'
|
import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor'
|
||||||
import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
|
@ -55,7 +55,7 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpdate) {
|
async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpdate) {
|
||||||
const videoObject = activity.object as VideoTorrentObject
|
const videoObject = activity.object as VideoObject
|
||||||
|
|
||||||
if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
|
if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
|
||||||
logger.debug('Video sent by update is not valid.', { videoObject })
|
logger.debug('Video sent by update is not valid.', { videoObject })
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
ActivityVideoUrlObject,
|
ActivityVideoUrlObject,
|
||||||
VideoState
|
VideoState
|
||||||
} from '../../../shared/index'
|
} from '../../../shared/index'
|
||||||
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
|
import { VideoObject } from '../../../shared/models/activitypub/objects'
|
||||||
import { VideoPrivacy } from '../../../shared/models/videos'
|
import { VideoPrivacy } from '../../../shared/models/videos'
|
||||||
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
|
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
|
||||||
import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
|
import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
|
||||||
|
@ -38,7 +38,6 @@ import {
|
||||||
} from '../../initializers/constants'
|
} from '../../initializers/constants'
|
||||||
import { sequelizeTypescript } from '../../initializers/database'
|
import { sequelizeTypescript } from '../../initializers/database'
|
||||||
import { AccountVideoRateModel } from '../../models/account/account-video-rate'
|
import { AccountVideoRateModel } from '../../models/account/account-video-rate'
|
||||||
import { TagModel } from '../../models/video/tag'
|
|
||||||
import { VideoModel } from '../../models/video/video'
|
import { VideoModel } from '../../models/video/video'
|
||||||
import { VideoCaptionModel } from '../../models/video/video-caption'
|
import { VideoCaptionModel } from '../../models/video/video-caption'
|
||||||
import { VideoCommentModel } from '../../models/video/video-comment'
|
import { VideoCommentModel } from '../../models/video/video-comment'
|
||||||
|
@ -104,7 +103,7 @@ async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request.RequestResponse, videoObject: VideoTorrentObject }> {
|
async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request.RequestResponse, videoObject: VideoObject }> {
|
||||||
const options = {
|
const options = {
|
||||||
uri: videoUrl,
|
uri: videoUrl,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -136,7 +135,7 @@ async function fetchRemoteVideoDescription (video: MVideoAccountLight) {
|
||||||
return body.description ? body.description : ''
|
return body.description ? body.description : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOrCreateVideoChannelFromVideoObject (videoObject: VideoTorrentObject) {
|
function getOrCreateVideoChannelFromVideoObject (videoObject: VideoObject) {
|
||||||
const channel = videoObject.attributedTo.find(a => a.type === 'Group')
|
const channel = videoObject.attributedTo.find(a => a.type === 'Group')
|
||||||
if (!channel) throw new Error('Cannot find associated video channel to video ' + videoObject.url)
|
if (!channel) throw new Error('Cannot find associated video channel to video ' + videoObject.url)
|
||||||
|
|
||||||
|
@ -155,7 +154,7 @@ type SyncParam = {
|
||||||
thumbnail: boolean
|
thumbnail: boolean
|
||||||
refreshVideo?: boolean
|
refreshVideo?: boolean
|
||||||
}
|
}
|
||||||
async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) {
|
async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoObject, syncParam: SyncParam) {
|
||||||
logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid)
|
logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid)
|
||||||
|
|
||||||
const jobPayloads: ActivitypubHttpFetcherPayload[] = []
|
const jobPayloads: ActivitypubHttpFetcherPayload[] = []
|
||||||
|
@ -294,7 +293,7 @@ async function getOrCreateVideoAndAccountAndChannel (
|
||||||
|
|
||||||
async function updateVideoFromAP (options: {
|
async function updateVideoFromAP (options: {
|
||||||
video: MVideoAccountLightBlacklistAllFiles
|
video: MVideoAccountLightBlacklistAllFiles
|
||||||
videoObject: VideoTorrentObject
|
videoObject: VideoObject
|
||||||
account: MAccountIdActor
|
account: MAccountIdActor
|
||||||
channel: MChannelDefault
|
channel: MChannelDefault
|
||||||
overrideTo?: string[]
|
overrideTo?: string[]
|
||||||
|
@ -538,7 +537,7 @@ function isAPHashTagObject (url: any): url is ActivityHashTagObject {
|
||||||
return url && url.type === 'Hashtag'
|
return url && url.type === 'Hashtag'
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAccountLight, waitThumbnail = false) {
|
async function createVideo (videoObject: VideoObject, channel: MChannelAccountLight, waitThumbnail = false) {
|
||||||
logger.debug('Adding remote video %s.', videoObject.id)
|
logger.debug('Adding remote video %s.', videoObject.id)
|
||||||
|
|
||||||
const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to)
|
const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to)
|
||||||
|
@ -632,7 +631,7 @@ async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAc
|
||||||
return { autoBlacklisted, videoCreated }
|
return { autoBlacklisted, videoCreated }
|
||||||
}
|
}
|
||||||
|
|
||||||
function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObject: VideoTorrentObject, to: string[] = []) {
|
function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObject: VideoObject, to: string[] = []) {
|
||||||
const privacy = to.includes(ACTIVITY_PUB.PUBLIC)
|
const privacy = to.includes(ACTIVITY_PUB.PUBLIC)
|
||||||
? VideoPrivacy.PUBLIC
|
? VideoPrivacy.PUBLIC
|
||||||
: VideoPrivacy.UNLISTED
|
: VideoPrivacy.UNLISTED
|
||||||
|
@ -664,6 +663,7 @@ function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObjec
|
||||||
commentsEnabled: videoObject.commentsEnabled,
|
commentsEnabled: videoObject.commentsEnabled,
|
||||||
downloadEnabled: videoObject.downloadEnabled,
|
downloadEnabled: videoObject.downloadEnabled,
|
||||||
waitTranscoding: videoObject.waitTranscoding,
|
waitTranscoding: videoObject.waitTranscoding,
|
||||||
|
isLive: videoObject.isLiveBroadcast,
|
||||||
state: videoObject.state,
|
state: videoObject.state,
|
||||||
channelId: videoChannel.id,
|
channelId: videoChannel.id,
|
||||||
duration: parseInt(duration, 10),
|
duration: parseInt(duration, 10),
|
||||||
|
@ -732,7 +732,7 @@ function videoFileActivityUrlToDBAttributes (
|
||||||
return attributes
|
return attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
function streamingPlaylistActivityUrlToDBAttributes (video: MVideoId, videoObject: VideoTorrentObject, videoFiles: MVideoFile[]) {
|
function streamingPlaylistActivityUrlToDBAttributes (video: MVideoId, videoObject: VideoObject, videoFiles: MVideoFile[]) {
|
||||||
const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[]
|
const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[]
|
||||||
if (playlistUrls.length === 0) return []
|
if (playlistUrls.length === 0) return []
|
||||||
|
|
||||||
|
@ -766,7 +766,7 @@ function streamingPlaylistActivityUrlToDBAttributes (video: MVideoId, videoObjec
|
||||||
return attributes
|
return attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
function getThumbnailFromIcons (videoObject: VideoTorrentObject) {
|
function getThumbnailFromIcons (videoObject: VideoObject) {
|
||||||
let validIcons = videoObject.icon.filter(i => i.width > THUMBNAILS_SIZE.minWidth)
|
let validIcons = videoObject.icon.filter(i => i.width > THUMBNAILS_SIZE.minWidth)
|
||||||
// Fallback if there are not valid icons
|
// Fallback if there are not valid icons
|
||||||
if (validIcons.length === 0) validIcons = videoObject.icon
|
if (validIcons.length === 0) validIcons = videoObject.icon
|
||||||
|
@ -774,7 +774,7 @@ function getThumbnailFromIcons (videoObject: VideoTorrentObject) {
|
||||||
return minBy(validIcons, 'width')
|
return minBy(validIcons, 'width')
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPreviewFromIcons (videoObject: VideoTorrentObject) {
|
function getPreviewFromIcons (videoObject: VideoObject) {
|
||||||
const validIcons = videoObject.icon.filter(i => i.width > PREVIEWS_SIZE.minWidth)
|
const validIcons = videoObject.icon.filter(i => i.width > PREVIEWS_SIZE.minWidth)
|
||||||
|
|
||||||
// FIXME: don't put a fallback here for compatibility with PeerTube <2.2
|
// FIXME: don't put a fallback here for compatibility with PeerTube <2.2
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
MVideoAccountLightBlacklistAllFiles
|
MVideoAccountLightBlacklistAllFiles
|
||||||
} from '@server/types/models'
|
} from '@server/types/models'
|
||||||
import { ActivityCreate } from '../../shared/models/activitypub'
|
import { ActivityCreate } from '../../shared/models/activitypub'
|
||||||
import { VideoTorrentObject } from '../../shared/models/activitypub/objects'
|
import { VideoObject } from '../../shared/models/activitypub/objects'
|
||||||
import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object'
|
import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object'
|
||||||
import { VideoCreate, VideoImportCreate } from '../../shared/models/videos'
|
import { VideoCreate, VideoImportCreate } from '../../shared/models/videos'
|
||||||
import { VideoCommentCreate } from '../../shared/models/videos/video-comment.model'
|
import { VideoCommentCreate } from '../../shared/models/videos/video-comment.model'
|
||||||
|
@ -62,7 +62,7 @@ function isLocalVideoCommentReplyAccepted (_object: {
|
||||||
|
|
||||||
function isRemoteVideoAccepted (_object: {
|
function isRemoteVideoAccepted (_object: {
|
||||||
activity: ActivityCreate
|
activity: ActivityCreate
|
||||||
videoAP: VideoTorrentObject
|
videoAP: VideoObject
|
||||||
byActor: ActorModel
|
byActor: ActorModel
|
||||||
}): AcceptResult {
|
}): AcceptResult {
|
||||||
return { accepted: true }
|
return { accepted: true }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Video, VideoDetails } from '../../../shared/models/videos'
|
import { Video, VideoDetails } from '../../../shared/models/videos'
|
||||||
import { VideoModel } from './video'
|
import { VideoModel } from './video'
|
||||||
import { ActivityTagObject, ActivityUrlObject, VideoTorrentObject } from '../../../shared/models/activitypub/objects'
|
import { ActivityTagObject, ActivityUrlObject, VideoObject } from '../../../shared/models/activitypub/objects'
|
||||||
import { MIMETYPES, WEBSERVER } from '../../initializers/constants'
|
import { MIMETYPES, WEBSERVER } from '../../initializers/constants'
|
||||||
import { VideoCaptionModel } from './video-caption'
|
import { VideoCaptionModel } from './video-caption'
|
||||||
import {
|
import {
|
||||||
|
@ -262,7 +262,7 @@ function addVideoFilesInAPAcc (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function videoModelToActivityPubObject (video: MVideoAP): VideoTorrentObject {
|
function videoModelToActivityPubObject (video: MVideoAP): VideoObject {
|
||||||
const { baseUrlHttp, baseUrlWs } = video.getBaseUrls()
|
const { baseUrlHttp, baseUrlWs } = video.getBaseUrls()
|
||||||
if (!video.Tags) video.Tags = []
|
if (!video.Tags) video.Tags = []
|
||||||
|
|
||||||
|
@ -351,6 +351,7 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoTorrentObject {
|
||||||
views: video.views,
|
views: video.views,
|
||||||
sensitive: video.nsfw,
|
sensitive: video.nsfw,
|
||||||
waitTranscoding: video.waitTranscoding,
|
waitTranscoding: video.waitTranscoding,
|
||||||
|
isLiveBroadcast: video.isLive,
|
||||||
state: video.state,
|
state: video.state,
|
||||||
commentsEnabled: video.commentsEnabled,
|
commentsEnabled: video.commentsEnabled,
|
||||||
downloadEnabled: video.downloadEnabled,
|
downloadEnabled: video.downloadEnabled,
|
||||||
|
|
|
@ -31,7 +31,7 @@ import { getServerActor } from '@server/models/application/application'
|
||||||
import { ModelCache } from '@server/models/model-cache'
|
import { ModelCache } from '@server/models/model-cache'
|
||||||
import { VideoFile } from '@shared/models/videos/video-file.model'
|
import { VideoFile } from '@shared/models/videos/video-file.model'
|
||||||
import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared'
|
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 { Video, VideoDetails } from '../../../shared/models/videos'
|
||||||
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
|
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
|
||||||
import { VideoFilter } from '../../../shared/models/videos/video-query.type'
|
import { VideoFilter } from '../../../shared/models/videos/video-query.type'
|
||||||
|
@ -1763,7 +1763,7 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
return videoFilesModelToFormattedJSON(this, baseUrlHttp, baseUrlWs, files)
|
return videoFilesModelToFormattedJSON(this, baseUrlHttp, baseUrlWs, files)
|
||||||
}
|
}
|
||||||
|
|
||||||
toActivityPubObject (this: MVideoAP): VideoTorrentObject {
|
toActivityPubObject (this: MVideoAP): VideoObject {
|
||||||
return videoModelToActivityPubObject(this)
|
return videoModelToActivityPubObject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ActivityPubActor } from './activitypub-actor'
|
import { ActivityPubActor } from './activitypub-actor'
|
||||||
import { ActivityPubSignature } from './activitypub-signature'
|
import { ActivityPubSignature } from './activitypub-signature'
|
||||||
import { ActivityFlagReasonObject, CacheFileObject, VideoTorrentObject } from './objects'
|
import { ActivityFlagReasonObject, CacheFileObject, VideoObject } from './objects'
|
||||||
import { AbuseObject } from './objects/abuse-object'
|
import { AbuseObject } from './objects/abuse-object'
|
||||||
import { DislikeObject } from './objects/dislike-object'
|
import { DislikeObject } from './objects/dislike-object'
|
||||||
import { APObject } from './objects/object.model'
|
import { APObject } from './objects/object.model'
|
||||||
|
@ -53,12 +53,12 @@ export interface BaseActivity {
|
||||||
|
|
||||||
export interface ActivityCreate extends BaseActivity {
|
export interface ActivityCreate extends BaseActivity {
|
||||||
type: 'Create'
|
type: 'Create'
|
||||||
object: VideoTorrentObject | AbuseObject | ViewObject | DislikeObject | VideoCommentObject | CacheFileObject | PlaylistObject
|
object: VideoObject | AbuseObject | ViewObject | DislikeObject | VideoCommentObject | CacheFileObject | PlaylistObject
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ActivityUpdate extends BaseActivity {
|
export interface ActivityUpdate extends BaseActivity {
|
||||||
type: 'Update'
|
type: 'Update'
|
||||||
object: VideoTorrentObject | ActivityPubActor | CacheFileObject | PlaylistObject
|
object: VideoObject | ActivityPubActor | CacheFileObject | PlaylistObject
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ActivityDelete extends BaseActivity {
|
export interface ActivityDelete extends BaseActivity {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
} from './common-objects'
|
} from './common-objects'
|
||||||
import { VideoState } from '../../videos'
|
import { VideoState } from '../../videos'
|
||||||
|
|
||||||
export interface VideoTorrentObject {
|
export interface VideoObject {
|
||||||
type: 'Video'
|
type: 'Video'
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
|
@ -19,7 +19,10 @@ export interface VideoTorrentObject {
|
||||||
language: ActivityIdentifierObject
|
language: ActivityIdentifierObject
|
||||||
subtitleLanguage: ActivityIdentifierObject[]
|
subtitleLanguage: ActivityIdentifierObject[]
|
||||||
views: number
|
views: number
|
||||||
|
|
||||||
sensitive: boolean
|
sensitive: boolean
|
||||||
|
isLiveBroadcast: boolean
|
||||||
|
|
||||||
commentsEnabled: boolean
|
commentsEnabled: boolean
|
||||||
downloadEnabled: boolean
|
downloadEnabled: boolean
|
||||||
waitTranscoding: boolean
|
waitTranscoding: boolean
|
||||||
|
|
Loading…
Reference in a new issue