import { join } from 'path'
import * as request from 'request'
import * as sequelize from 'sequelize'
+import { VideoLiveModel } from '@server/models/video/video-live'
import {
ActivityHashTagObject,
ActivityMagnetUrlObject,
ActivitypubHttpFetcherPayload,
ActivityTagObject,
ActivityUrlObject,
- ActivityVideoUrlObject,
- VideoState
+ ActivityVideoUrlObject
} from '../../../shared/index'
-import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
+import { VideoObject } from '../../../shared/models/activitypub/objects'
import { VideoPrivacy } from '../../../shared/models/videos'
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
} from '../../initializers/constants'
import { sequelizeTypescript } from '../../initializers/database'
import { AccountVideoRateModel } from '../../models/account/account-video-rate'
-import { TagModel } from '../../models/video/tag'
import { VideoModel } from '../../models/video/video'
import { VideoCaptionModel } from '../../models/video/video-caption'
import { VideoCommentModel } from '../../models/video/video-comment'
import { ActorFollowScoreCache } from '../files-cache'
import { JobQueue } from '../job-queue'
import { Notifier } from '../notifier'
+import { PeerTubeSocket } from '../peertube-socket'
import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail'
import { setVideoTags } from '../video'
import { autoBlacklistVideoIfNeeded } from '../video-blacklist'
import { addVideoShares, shareVideoByServerAndChannel } from './share'
import { addVideoComments } from './video-comments'
import { createRates } from './video-rates'
+import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) {
const video = videoArg as MVideoAP
// Check this is not a blacklisted video, or unfederated blacklisted video
(video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) &&
// Check the video is public/unlisted and published
- video.hasPrivacyForFederation() && video.state === VideoState.PUBLISHED
+ video.hasPrivacyForFederation() && video.hasStateForFederation()
) {
// Fetch more attributes that we will need to serialize in AP object
if (isArray(video.VideoCaptions) === false) {
}
}
-async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request.RequestResponse, videoObject: VideoTorrentObject }> {
+async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request.RequestResponse, videoObject: VideoObject }> {
const options = {
uri: videoUrl,
method: 'GET',
return body.description ? body.description : ''
}
-function getOrCreateVideoChannelFromVideoObject (videoObject: VideoTorrentObject) {
+function getOrCreateVideoChannelFromVideoObject (videoObject: VideoObject) {
const channel = videoObject.attributedTo.find(a => a.type === 'Group')
if (!channel) throw new Error('Cannot find associated video channel to video ' + videoObject.url)
thumbnail: 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)
const jobPayloads: ActivitypubHttpFetcherPayload[] = []
async function updateVideoFromAP (options: {
video: MVideoAccountLightBlacklistAllFiles
- videoObject: VideoTorrentObject
+ videoObject: VideoObject
account: MAccountIdActor
channel: MChannelDefault
overrideTo?: string[]
}) {
const { video, videoObject, account, channel, overrideTo } = options
- logger.debug('Updating remote video "%s".', options.videoObject.uuid, { account, channel })
+ logger.debug('Updating remote video "%s".', options.videoObject.uuid, { videoObject: options.videoObject, account, channel })
let videoFieldsSave: any
const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE
video.privacy = videoData.privacy
video.channelId = videoData.channelId
video.views = videoData.views
+ video.isLive = videoData.isLive
+
+ // Ensures we update the updated video attribute
+ video.changed('updatedAt', true)
const videoUpdated = await video.save(sequelizeOptions) as MVideoFullLight
await Promise.all(videoCaptionsPromises)
}
+ {
+ // Create or update existing live
+ if (video.isLive) {
+ const [ videoLive ] = await VideoLiveModel.upsert({
+ saveReplay: videoObject.liveSaveReplay,
+ permanentLive: videoObject.permanentLive,
+ videoId: video.id
+ }, { transaction: t, returning: true })
+
+ videoUpdated.VideoLive = videoLive
+ } else { // Delete existing live if it exists
+ await VideoLiveModel.destroy({
+ where: {
+ videoId: video.id
+ },
+ transaction: t
+ })
+
+ videoUpdated.VideoLive = null
+ }
+ }
+
return videoUpdated
})
transaction: undefined
})
- if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) // Notify our users?
+ // Notify our users?
+ if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated)
+
+ if (videoUpdated.isLive) {
+ PeerTubeSocket.Instance.sendVideoLiveNewState(videoUpdated)
+ PeerTubeSocket.Instance.sendVideoViewsUpdate(videoUpdated)
+ }
logger.info('Remote video with uuid %s updated', videoObject.uuid)
try {
const { response, videoObject } = await fetchRemoteVideo(video.url)
- if (response.statusCode === 404) {
+ if (response.statusCode === HttpStatusCode.NOT_FOUND_404) {
logger.info('Cannot refresh remote video %s: video does not exist anymore. Deleting it.', video.url)
// Video does not exist anymore
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)
const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to)
videoCreated.VideoFiles = videoFiles
+ if (videoCreated.isLive) {
+ const videoLive = new VideoLiveModel({
+ streamKey: null,
+ saveReplay: videoObject.liveSaveReplay,
+ permanentLive: videoObject.permanentLive,
+ videoId: videoCreated.id
+ })
+
+ videoCreated.VideoLive = await videoLive.save({ transaction: t })
+ }
+
const autoBlacklisted = await autoBlacklistVideoIfNeeded({
video: videoCreated,
user: undefined,
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)
? VideoPrivacy.PUBLIC
: VideoPrivacy.UNLISTED
commentsEnabled: videoObject.commentsEnabled,
downloadEnabled: videoObject.downloadEnabled,
waitTranscoding: videoObject.waitTranscoding,
+ isLive: videoObject.isLiveBroadcast,
state: videoObject.state,
channelId: videoChannel.id,
duration: parseInt(duration, 10),
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[]
if (playlistUrls.length === 0) return []
return attributes
}
-function getThumbnailFromIcons (videoObject: VideoTorrentObject) {
+function getThumbnailFromIcons (videoObject: VideoObject) {
let validIcons = videoObject.icon.filter(i => i.width > THUMBNAILS_SIZE.minWidth)
// Fallback if there are not valid icons
if (validIcons.length === 0) validIcons = videoObject.icon
return minBy(validIcons, 'width')
}
-function getPreviewFromIcons (videoObject: VideoTorrentObject) {
+function getPreviewFromIcons (videoObject: VideoObject) {
const validIcons = videoObject.icon.filter(i => i.width > PREVIEWS_SIZE.minWidth)
// FIXME: don't put a fallback here for compatibility with PeerTube <2.2