Server shares user videos

This commit is contained in:
Chocobozzz 2017-11-16 15:22:39 +01:00
parent efc32059d9
commit 20494f1221
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
16 changed files with 215 additions and 111 deletions

View File

@ -4,10 +4,13 @@ import * as express from 'express'
import { database as db } from '../../initializers'
import { executeIfActivityPub, localAccountValidator } from '../../middlewares'
import { pageToStartAndCount } from '../../helpers'
import { AccountInstance } from '../../models'
import { AccountInstance, VideoChannelInstance } from '../../models'
import { activityPubCollectionPagination } from '../../helpers/activitypub'
import { ACTIVITY_PUB } from '../../initializers/constants'
import { asyncMiddleware } from '../../middlewares/async'
import { videosGetValidator } from '../../middlewares/validators/videos'
import { VideoInstance } from '../../models/video/video-interface'
import { videoChannelsGetValidator } from '../../middlewares/validators/video-channels'
const activityPubClientRouter = express.Router()
@ -26,6 +29,16 @@ activityPubClientRouter.get('/account/:name/following',
executeIfActivityPub(asyncMiddleware(accountFollowingController))
)
activityPubClientRouter.get('/videos/watch/:id',
executeIfActivityPub(videosGetValidator),
executeIfActivityPub(asyncMiddleware(videoController))
)
activityPubClientRouter.get('/video-channels/:id',
executeIfActivityPub(videoChannelsGetValidator),
executeIfActivityPub(asyncMiddleware(videoChannelController))
)
// ---------------------------------------------------------------------------
export {
@ -63,3 +76,15 @@ async function accountFollowingController (req: express.Request, res: express.Re
return res.json(activityPubResult)
}
async function videoController (req: express.Request, res: express.Response, next: express.NextFunction) {
const video: VideoInstance = res.locals.video
return res.json(video.toActivityPubObject())
}
async function videoChannelController (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoChannel: VideoChannelInstance = res.locals.videoChannel
return res.json(videoChannel.toActivityPubObject())
}

View File

@ -10,7 +10,7 @@ import {
paginationValidator,
setPagination,
setVideoChannelsSort,
videoChannelGetValidator,
videoChannelsGetValidator,
videoChannelsAddValidator,
videoChannelsRemoveValidator,
videoChannelsSortValidator,
@ -53,7 +53,7 @@ videoChannelRouter.delete('/channels/:id',
)
videoChannelRouter.get('/channels/:id',
videoChannelGetValidator,
videoChannelsGetValidator,
asyncMiddleware(getVideoChannel)
)

View File

@ -4,13 +4,18 @@ import * as Sequelize from 'sequelize'
import * as url from 'url'
import { ActivityIconObject } from '../../shared/index'
import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor'
import { VideoChannelObject } from '../../shared/models/activitypub/objects/video-channel-object'
import { ResultList } from '../../shared/models/result-list.model'
import { database as db, REMOTE_SCHEME } from '../initializers'
import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants'
import { sendAnnounce } from '../lib/activitypub/send-request'
import { videoChannelActivityObjectToDBAttributes } from '../lib/activitypub/misc'
import { sendVideoAnnounce } from '../lib/activitypub/send-request'
import { sendVideoChannelAnnounce } from '../lib/index'
import { AccountInstance } from '../models/account/account-interface'
import { VideoChannelInstance } from '../models/video/video-channel-interface'
import { VideoInstance } from '../models/video/video-interface'
import { isRemoteAccountValid } from './custom-validators'
import { isVideoChannelObjectValid } from './custom-validators/activitypub/videos'
import { logger } from './logger'
import { doRequest, doRequestAndSaveToFile } from './requests'
import { getServerAccount } from './utils'
@ -34,7 +39,7 @@ async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t:
videoChannelId: videoChannel.id
}, { transaction: t })
return sendAnnounce(serverAccount, videoChannel, t)
return sendVideoChannelAnnounce(serverAccount, videoChannel, t)
}
async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transaction) {
@ -45,7 +50,7 @@ async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transactio
videoId: video.id
}, { transaction: t })
return sendAnnounce(serverAccount, video, t)
return sendVideoAnnounce(serverAccount, video, t)
}
function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account' | 'videoAbuse', id: string) {
@ -66,13 +71,27 @@ async function getOrCreateAccount (accountUrl: string) {
if (res === undefined) throw new Error('Cannot fetch remote account.')
// Save our new account in database
const account = res.account
await account.save()
account = await res.account.save()
}
return account
}
async function getOrCreateVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) {
let videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl)
// We don't have this account in our database, fetch it on remote
if (!videoChannel) {
videoChannel = await fetchRemoteVideoChannel(ownerAccount, videoChannelUrl)
if (videoChannel === undefined) throw new Error('Cannot fetch remote video channel.')
// Save our new video channel in database
await videoChannel.save()
}
return videoChannel
}
async function fetchRemoteAccountAndCreateServer (accountUrl: string) {
const options = {
uri: accountUrl,
@ -131,6 +150,38 @@ async function fetchRemoteAccountAndCreateServer (accountUrl: string) {
return { account, server }
}
async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) {
const options = {
uri: videoChannelUrl,
method: 'GET',
headers: {
'Accept': ACTIVITY_PUB_ACCEPT_HEADER
}
}
logger.info('Fetching remote video channel %s.', videoChannelUrl)
let requestResult
try {
requestResult = await doRequest(options)
} catch (err) {
logger.warn('Cannot fetch remote video channel %s.', videoChannelUrl, err)
return undefined
}
const videoChannelJSON: VideoChannelObject = JSON.parse(requestResult.body)
if (isVideoChannelObjectValid(videoChannelJSON) === false) {
logger.debug('Remote video channel JSON is not valid.', { videoChannelJSON })
return undefined
}
const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount)
const videoChannel = db.VideoChannel.build(videoChannelAttributes)
videoChannel.Account = ownerAccount
return videoChannel
}
function fetchRemoteVideoPreview (video: VideoInstance) {
// FIXME: use url
const host = video.VideoChannel.Account.Server.host
@ -200,7 +251,8 @@ export {
fetchRemoteVideoPreview,
fetchRemoteVideoDescription,
shareVideoChannelByServer,
shareVideoByServer
shareVideoByServer,
getOrCreateVideoChannel
}
// ---------------------------------------------------------------------------

View File

@ -2,8 +2,7 @@ import * as validator from 'validator'
import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './account'
import { isActivityPubUrlValid } from './misc'
import {
isVideoAnnounceValid,
isVideoChannelAnnounceValid,
isAnnounceValid,
isVideoChannelCreateActivityValid,
isVideoChannelDeleteActivityValid,
isVideoChannelUpdateActivityValid,
@ -37,8 +36,7 @@ function isActivityValid (activity: any) {
isAccountFollowActivityValid(activity) ||
isAccountAcceptActivityValid(activity) ||
isVideoFlagValid(activity) ||
isVideoAnnounceValid(activity) ||
isVideoChannelAnnounceValid(activity)
isAnnounceValid(activity)
}
// ---------------------------------------------------------------------------

View File

@ -21,7 +21,7 @@ function isActivityPubUrlValid (url: string) {
}
function isBaseActivityValid (activity: any, type: string) {
return Array.isArray(activity['@context']) &&
return (activity['@context'] === undefined || Array.isArray(activity['@context'])) &&
activity.type === type &&
isActivityPubUrlValid(activity.id) &&
isActivityPubUrlValid(activity.actor) &&

View File

@ -39,6 +39,7 @@ function isActivityPubVideoDurationValid (value: string) {
function isVideoTorrentObjectValid (video: any) {
return video.type === 'Video' &&
isActivityPubUrlValid(video.id) &&
isVideoNameValid(video.name) &&
isActivityPubVideoDurationValid(video.duration) &&
isUUIDValid(video.uuid) &&
@ -62,14 +63,12 @@ function isVideoFlagValid (activity: any) {
isActivityPubUrlValid(activity.object)
}
function isVideoAnnounceValid (activity: any) {
function isAnnounceValid (activity: any) {
return isBaseActivityValid(activity, 'Announce') &&
isVideoTorrentObjectValid(activity.object)
}
function isVideoChannelAnnounceValid (activity: any) {
return isBaseActivityValid(activity, 'Announce') &&
isVideoChannelObjectValid(activity.object)
(
isVideoChannelCreateActivityValid(activity.object) ||
isVideoTorrentAddActivityValid(activity.object)
)
}
function isVideoChannelCreateActivityValid (activity: any) {
@ -88,8 +87,11 @@ function isVideoChannelDeleteActivityValid (activity: any) {
function isVideoChannelObjectValid (videoChannel: any) {
return videoChannel.type === 'VideoChannel' &&
isActivityPubUrlValid(videoChannel.id) &&
isVideoChannelNameValid(videoChannel.name) &&
isVideoChannelDescriptionValid(videoChannel.description) &&
isVideoChannelDescriptionValid(videoChannel.content) &&
isDateValid(videoChannel.published) &&
isDateValid(videoChannel.updated) &&
isUUIDValid(videoChannel.uuid)
}
@ -103,8 +105,8 @@ export {
isVideoChannelDeleteActivityValid,
isVideoTorrentDeleteActivityValid,
isVideoFlagValid,
isVideoAnnounceValid,
isVideoChannelAnnounceValid
isAnnounceValid,
isVideoChannelObjectValid
}
// ---------------------------------------------------------------------------
@ -148,8 +150,20 @@ function setValidRemoteVideoUrls (video: any) {
function isRemoteVideoUrlValid (url: any) {
return url.type === 'Link' &&
ACTIVITY_PUB.VIDEO_URL_MIME_TYPES.indexOf(url.mimeType) !== -1 &&
isVideoUrlValid(url.url) &&
validator.isInt(url.width + '', { min: 0 }) &&
validator.isInt(url.size + '', { min: 0 })
(
ACTIVITY_PUB.URL_MIME_TYPES.VIDEO.indexOf(url.mimeType) !== -1 &&
isVideoUrlValid(url.url) &&
validator.isInt(url.width + '', { min: 0 }) &&
validator.isInt(url.size + '', { min: 0 })
) ||
(
ACTIVITY_PUB.URL_MIME_TYPES.TORRENT.indexOf(url.mimeType) !== -1 &&
isVideoUrlValid(url.url) &&
validator.isInt(url.width + '', { min: 0 })
) ||
(
ACTIVITY_PUB.URL_MIME_TYPES.MAGNET.indexOf(url.mimeType) !== -1 &&
validator.isLength(url.url, { min: 5 }) &&
validator.isInt(url.width + '', { min: 0 })
)
}

View File

@ -1,10 +1,10 @@
// TODO: import from ES6 when retry typing file will include errorFilter function
import * as retry from 'async/retry'
import * as Bluebird from 'bluebird'
import { logger } from './logger'
type RetryTransactionWrapperOptions = { errorMessage: string, arguments?: any[] }
function retryTransactionWrapper (functionToRetry: (...args) => Promise<any>, options: RetryTransactionWrapperOptions) {
function retryTransactionWrapper (functionToRetry: (...args) => Promise<any> | Bluebird<any>, options: RetryTransactionWrapperOptions) {
const args = options.arguments ? options.arguments : []
return transactionRetryer(callback => {
@ -13,8 +13,8 @@ function retryTransactionWrapper (functionToRetry: (...args) => Promise<any>, op
.catch(err => callback(err))
})
.catch(err => {
// Do not throw the error, continue the process
logger.error(options.errorMessage, err)
throw err
})
}
@ -28,7 +28,7 @@ function transactionRetryer (func: Function) {
logger.debug('Maybe retrying the transaction function.', { willRetry })
return willRetry
}
}, func, err => err ? rej(err) : res())
}, func, (err, data) => err ? rej(err) : res(data))
})
}

View File

@ -227,13 +227,11 @@ const ACTIVITY_PUB_ACCEPT_HEADER = 'application/ld+json; profile="https://www.w3
const ACTIVITY_PUB = {
COLLECTION_ITEMS_PER_PAGE: 10,
VIDEO_URL_MIME_TYPES: [
'video/mp4',
'video/webm',
'video/ogg',
'application/x-bittorrent',
'application/x-bittorrent;x-scheme-handler/magnet'
]
URL_MIME_TYPES: {
VIDEO: [ 'video/mp4', 'video/webm', 'video/ogg' ], // TODO: Merge with VIDEO_MIMETYPE_EXT
TORRENT: [ 'application/x-bittorrent' ],
MAGNET: [ 'application/x-bittorrent;x-scheme-handler/magnet' ]
}
}
// ---------------------------------------------------------------------------

View File

@ -7,6 +7,21 @@ import { VIDEO_MIMETYPE_EXT } from '../../initializers/constants'
import { VideoChannelInstance } from '../../models/video/video-channel-interface'
import { VideoFileAttributes } from '../../models/video/video-file-interface'
import { VideoAttributes, VideoInstance } from '../../models/video/video-interface'
import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object'
import { AccountInstance } from '../../models/account/account-interface'
function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) {
return {
name: videoChannelObject.name,
description: videoChannelObject.content,
uuid: videoChannelObject.uuid,
url: videoChannelObject.id,
createdAt: new Date(videoChannelObject.published),
updatedAt: new Date(videoChannelObject.updated),
remote: true,
accountId: account.id
}
}
async function videoActivityObjectToDBAttributes (
videoChannel: VideoChannelInstance,
@ -45,26 +60,32 @@ async function videoActivityObjectToDBAttributes (
}
function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoObject: VideoTorrentObject) {
const fileUrls = videoObject.url
.filter(u => Object.keys(VIDEO_MIMETYPE_EXT).indexOf(u.mimeType) !== -1 && u.url.startsWith('video/'))
const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT)
const fileUrls = videoObject.url.filter(u => {
return mimeTypes.indexOf(u.mimeType) !== -1 && u.mimeType.startsWith('video/')
})
if (fileUrls.length === 0) {
throw new Error('Cannot find video files for ' + videoCreated.url)
}
const attributes: VideoFileAttributes[] = []
for (const url of fileUrls) {
for (const fileUrl of fileUrls) {
// Fetch associated magnet uri
const magnet = videoObject.url
.find(u => {
return u.mimeType === 'application/x-bittorrent;x-scheme-handler/magnet' && u.width === url.width
})
if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + url.url)
const magnet = videoObject.url.find(u => {
return u.mimeType === 'application/x-bittorrent;x-scheme-handler/magnet' && u.width === fileUrl.width
})
if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + fileUrl.url)
const parsed = magnetUtil.decode(magnet.url)
if (!parsed || isVideoFileInfoHashValid(parsed.infoHash) === false) throw new Error('Cannot parse magnet URI ' + magnet.url)
const attribute = {
extname: VIDEO_MIMETYPE_EXT[url.mimeType],
extname: VIDEO_MIMETYPE_EXT[fileUrl.mimeType],
infoHash: parsed.infoHash,
resolution: url.width,
size: url.size,
resolution: fileUrl.width,
size: fileUrl.size,
videoId: videoCreated.id
}
attributes.push(attribute)
@ -77,5 +98,6 @@ function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoO
export {
videoFileActivityUrlToDBAttributes,
videoActivityObjectToDBAttributes
videoActivityObjectToDBAttributes,
videoChannelActivityObjectToDBAttributes
}

View File

@ -5,6 +5,8 @@ import { database as db } from '../../initializers'
import { AccountInstance } from '../../models/account/account-interface'
import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
import Bluebird = require('bluebird')
import { getOrCreateVideoChannel } from '../../helpers/activitypub'
import { VideoChannelInstance } from '../../models/video/video-channel-interface'
async function processAddActivity (activity: ActivityAdd) {
const activityObject = activity.object
@ -12,7 +14,10 @@ async function processAddActivity (activity: ActivityAdd) {
const account = await getOrCreateAccount(activity.actor)
if (activityType === 'Video') {
return processAddVideo(account, activity.id, activityObject as VideoTorrentObject)
const videoChannelUrl = activity.target
const videoChannel = await getOrCreateVideoChannel(account, videoChannelUrl)
return processAddVideo(account, videoChannel, activityObject as VideoTorrentObject)
}
logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id })
@ -27,16 +32,16 @@ export {
// ---------------------------------------------------------------------------
function processAddVideo (account: AccountInstance, videoChannelUrl: string, video: VideoTorrentObject) {
function processAddVideo (account: AccountInstance, videoChannel: VideoChannelInstance, video: VideoTorrentObject) {
const options = {
arguments: [ account, videoChannelUrl, video ],
arguments: [ account, videoChannel, video ],
errorMessage: 'Cannot insert the remote video with many retries.'
}
return retryTransactionWrapper(addRemoteVideo, options)
}
async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string, videoToCreateData: VideoTorrentObject) {
function addRemoteVideo (account: AccountInstance, videoChannel: VideoChannelInstance, videoToCreateData: VideoTorrentObject) {
logger.debug('Adding remote video %s.', videoToCreateData.url)
return db.sequelize.transaction(async t => {
@ -44,9 +49,6 @@ async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string
transaction: t
}
const videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl, t)
if (!videoChannel) throw new Error('Video channel not found.')
if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.')
const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, t)
@ -59,8 +61,11 @@ async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string
const videoCreated = await video.save(sequelizeOptions)
const videoFileAttributes = await videoFileActivityUrlToDBAttributes(videoCreated, videoToCreateData)
if (videoFileAttributes.length === 0) {
throw new Error('Cannot find valid files for video %s ' + videoToCreateData.url)
}
const tasks: Bluebird<any>[] = videoFileAttributes.map(f => db.VideoFile.create(f))
const tasks: Bluebird<any>[] = videoFileAttributes.map(f => db.VideoFile.create(f, { transaction: t }))
await Promise.all(tasks)
const tags = videoToCreateData.tag.map(t => t.name)
@ -71,5 +76,4 @@ async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string
return videoCreated
})
}

View File

@ -10,38 +10,33 @@ import { VideoChannelInstance } from '../../models/video/video-channel-interface
import { VideoInstance } from '../../models/index'
async function processAnnounceActivity (activity: ActivityAnnounce) {
const activityType = activity.object.type
const announcedActivity = activity.object
const accountAnnouncer = await getOrCreateAccount(activity.actor)
if (activityType === 'VideoChannel') {
const activityCreate = Object.assign(activity, {
type: 'Create' as 'Create',
actor: activity.object.actor,
object: activity.object as VideoChannelObject
})
if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'VideoChannel') {
// Add share entry
const videoChannel: VideoChannelInstance = await processCreateActivity(activityCreate)
const videoChannel: VideoChannelInstance = await processCreateActivity(announcedActivity)
await db.VideoChannelShare.create({
accountId: accountAnnouncer.id,
videoChannelId: videoChannel.id
})
} else if (activityType === 'Video') {
const activityAdd = Object.assign(activity, {
type: 'Add' as 'Add',
actor: activity.object.actor,
object: activity.object as VideoTorrentObject
})
return undefined
} else if (announcedActivity.type === 'Add' && announcedActivity.object.type === 'Video') {
// Add share entry
const video: VideoInstance = await processAddActivity(activityAdd)
const video: VideoInstance = await processAddActivity(announcedActivity)
await db.VideoShare.create({
accountId: accountAnnouncer.id,
videoId: video.id
})
return undefined
}
logger.warn('Unknown activity object type %s when announcing activity.', activityType, { activity: activity.id })
logger.warn(
'Unknown activity object type %s -> %s when announcing activity.', announcedActivity.type, announcedActivity.object.type,
{ activity: activity.id }
)
return Promise.resolve(undefined)
}

View File

@ -4,6 +4,7 @@ import { logger, retryTransactionWrapper } from '../../helpers'
import { getActivityPubUrl, getOrCreateAccount } from '../../helpers/activitypub'
import { database as db } from '../../initializers'
import { AccountInstance } from '../../models/account/account-interface'
import { videoChannelActivityObjectToDBAttributes } from './misc'
async function processCreateActivity (activity: ActivityCreate) {
const activityObject = activity.object
@ -37,23 +38,14 @@ function processCreateVideoChannel (account: AccountInstance, videoChannelToCrea
return retryTransactionWrapper(addRemoteVideoChannel, options)
}
async function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid)
return db.sequelize.transaction(async t => {
let videoChannel = await db.VideoChannel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t)
if (videoChannel) throw new Error('Video channel with this URL/UUID already exists.')
const videoChannelData = {
name: videoChannelToCreateData.name,
description: videoChannelToCreateData.content,
uuid: videoChannelToCreateData.uuid,
createdAt: new Date(videoChannelToCreateData.published),
updatedAt: new Date(videoChannelToCreateData.updated),
remote: true,
accountId: account.id
}
const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account)
videoChannel = db.VideoChannel.build(videoChannelData)
videoChannel.url = getActivityPubUrl('videoChannel', videoChannel.uuid)
@ -73,7 +65,7 @@ function processCreateVideoAbuse (account: AccountInstance, videoAbuseToCreateDa
return retryTransactionWrapper(addRemoteVideoAbuse, options)
}
async function addRemoteVideoAbuse (account: AccountInstance, videoAbuseToCreateData: VideoAbuseObject) {
function addRemoteVideoAbuse (account: AccountInstance, videoAbuseToCreateData: VideoAbuseObject) {
logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object)
return db.sequelize.transaction(async t => {

View File

@ -59,24 +59,21 @@ async function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transac
return broadcastToFollowers(data, [ account ], t)
}
async function sendAnnounce (byAccount: AccountInstance, instance: VideoInstance | VideoChannelInstance, t: Sequelize.Transaction) {
const object = instance.toActivityPubObject()
async function sendVideoChannelAnnounce (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
const url = getActivityPubUrl('videoChannel', videoChannel.uuid) + '#announce'
const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject(), true)
let url = ''
let objectActorUrl: string
if ((instance as any).VideoChannel !== undefined) {
objectActorUrl = (instance as VideoInstance).VideoChannel.Account.url
url = getActivityPubUrl('video', instance.uuid) + '#announce'
} else {
objectActorUrl = (instance as VideoChannelInstance).Account.url
url = getActivityPubUrl('videoChannel', instance.uuid) + '#announce'
}
const data = await announceActivityData(url, byAccount, announcedActivity)
return broadcastToFollowers(data, [ byAccount ], t)
}
const objectWithActor = Object.assign(object, {
actor: objectActorUrl
})
async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Sequelize.Transaction) {
const url = getActivityPubUrl('video', video.uuid) + '#announce'
const data = await announceActivityData(url, byAccount, objectWithActor)
const videoChannel = video.VideoChannel
const announcedActivity = await addActivityData(url, videoChannel.Account, videoChannel.url, video.toActivityPubObject(), true)
const data = await announceActivityData(url, byAccount, announcedActivity)
return broadcastToFollowers(data, [ byAccount ], t)
}
@ -117,7 +114,8 @@ export {
sendAccept,
sendFollow,
sendVideoAbuse,
sendAnnounce
sendVideoChannelAnnounce,
sendVideoAnnounce
}
// ---------------------------------------------------------------------------
@ -159,7 +157,7 @@ async function getPublicActivityTo (account: AccountInstance) {
return inboxUrls.concat('https://www.w3.org/ns/activitystreams#Public')
}
async function createActivityData (url: string, byAccount: AccountInstance, object: any) {
async function createActivityData (url: string, byAccount: AccountInstance, object: any, raw = false) {
const to = await getPublicActivityTo(byAccount)
const base = {
type: 'Create',
@ -169,6 +167,8 @@ async function createActivityData (url: string, byAccount: AccountInstance, obje
object
}
if (raw === true) return base
return buildSignedActivity(byAccount, base)
}
@ -195,7 +195,7 @@ async function deleteActivityData (url: string, byAccount: AccountInstance) {
return buildSignedActivity(byAccount, base)
}
async function addActivityData (url: string, byAccount: AccountInstance, target: string, object: any) {
async function addActivityData (url: string, byAccount: AccountInstance, target: string, object: any, raw = false) {
const to = await getPublicActivityTo(byAccount)
const base = {
type: 'Add',
@ -206,6 +206,8 @@ async function addActivityData (url: string, byAccount: AccountInstance, target:
target
}
if (raw === true) return base
return buildSignedActivity(byAccount, base)
}

View File

@ -83,7 +83,7 @@ const videoChannelsRemoveValidator = [
}
]
const videoChannelGetValidator = [
const videoChannelsGetValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
@ -102,7 +102,7 @@ export {
videoChannelsAddValidator,
videoChannelsUpdateValidator,
videoChannelsRemoveValidator,
videoChannelGetValidator
videoChannelsGetValidator
}
// ---------------------------------------------------------------------------

View File

@ -264,7 +264,8 @@ loadByUrl = function (url: string, t?: Sequelize.Transaction) {
const query: Sequelize.FindOptions<VideoChannelAttributes> = {
where: {
url
}
},
include: [ VideoChannel['sequelize'].models.Account ]
}
if (t !== undefined) query.transaction = t

View File

@ -24,6 +24,7 @@ export interface ActivityCreate extends BaseActivity {
export interface ActivityAdd extends BaseActivity {
type: 'Add'
target: string
object: VideoTorrentObject
}
@ -52,5 +53,5 @@ export interface ActivityAccept extends BaseActivity {
export interface ActivityAnnounce extends BaseActivity {
type: 'Announce'
object: VideoChannelObject | VideoTorrentObject
object: ActivityCreate | ActivityAdd
}