Add shares forward and collection on videos/video channels
This commit is contained in:
parent
74bb2cb834
commit
4e50b6a1c9
|
@ -1,22 +1,26 @@
|
||||||
// Intercept ActivityPub client requests
|
// Intercept ActivityPub client requests
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
|
import { pageToStartAndCount } from '../../helpers'
|
||||||
|
import { activityPubCollectionPagination } from '../../helpers/activitypub'
|
||||||
|
|
||||||
import { database as db } from '../../initializers'
|
import { database as db } from '../../initializers'
|
||||||
import { executeIfActivityPub, localAccountValidator } from '../../middlewares'
|
|
||||||
import { pageToStartAndCount } from '../../helpers'
|
|
||||||
import { AccountInstance, VideoChannelInstance } from '../../models'
|
|
||||||
import { activityPubCollectionPagination } from '../../helpers/activitypub'
|
|
||||||
import { ACTIVITY_PUB, CONFIG } from '../../initializers/constants'
|
import { ACTIVITY_PUB, CONFIG } from '../../initializers/constants'
|
||||||
|
import { buildVideoChannelAnnounceToFollowers } from '../../lib/activitypub/send/send-announce'
|
||||||
|
import { buildVideoAnnounceToFollowers } from '../../lib/index'
|
||||||
|
import { executeIfActivityPub, localAccountValidator } from '../../middlewares'
|
||||||
import { asyncMiddleware } from '../../middlewares/async'
|
import { asyncMiddleware } from '../../middlewares/async'
|
||||||
import { videosGetValidator } from '../../middlewares/validators/videos'
|
import { videoChannelsGetValidator, videoChannelsShareValidator } from '../../middlewares/validators/video-channels'
|
||||||
|
import { videosGetValidator, videosShareValidator } from '../../middlewares/validators/videos'
|
||||||
|
import { AccountInstance, VideoChannelInstance } from '../../models'
|
||||||
|
import { VideoChannelShareInstance } from '../../models/video/video-channel-share-interface'
|
||||||
import { VideoInstance } from '../../models/video/video-interface'
|
import { VideoInstance } from '../../models/video/video-interface'
|
||||||
import { videoChannelsGetValidator } from '../../middlewares/validators/video-channels'
|
import { VideoShareInstance } from '../../models/video/video-share-interface'
|
||||||
|
|
||||||
const activityPubClientRouter = express.Router()
|
const activityPubClientRouter = express.Router()
|
||||||
|
|
||||||
activityPubClientRouter.get('/account/:name',
|
activityPubClientRouter.get('/account/:name',
|
||||||
executeIfActivityPub(localAccountValidator),
|
executeIfActivityPub(localAccountValidator),
|
||||||
executeIfActivityPub(asyncMiddleware(accountController))
|
executeIfActivityPub(accountController)
|
||||||
)
|
)
|
||||||
|
|
||||||
activityPubClientRouter.get('/account/:name/followers',
|
activityPubClientRouter.get('/account/:name/followers',
|
||||||
|
@ -31,7 +35,12 @@ activityPubClientRouter.get('/account/:name/following',
|
||||||
|
|
||||||
activityPubClientRouter.get('/videos/watch/:id',
|
activityPubClientRouter.get('/videos/watch/:id',
|
||||||
executeIfActivityPub(videosGetValidator),
|
executeIfActivityPub(videosGetValidator),
|
||||||
executeIfActivityPub(asyncMiddleware(videoController))
|
executeIfActivityPub(videoController)
|
||||||
|
)
|
||||||
|
|
||||||
|
activityPubClientRouter.get('/videos/watch/:id/announces/:accountId',
|
||||||
|
executeIfActivityPub(asyncMiddleware(videosShareValidator)),
|
||||||
|
executeIfActivityPub(asyncMiddleware(videoAnnounceController))
|
||||||
)
|
)
|
||||||
|
|
||||||
activityPubClientRouter.get('/video-channels/:id',
|
activityPubClientRouter.get('/video-channels/:id',
|
||||||
|
@ -39,6 +48,11 @@ activityPubClientRouter.get('/video-channels/:id',
|
||||||
executeIfActivityPub(asyncMiddleware(videoChannelController))
|
executeIfActivityPub(asyncMiddleware(videoChannelController))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
activityPubClientRouter.get('/video-channels/:id/announces/:accountId',
|
||||||
|
executeIfActivityPub(asyncMiddleware(videoChannelsShareValidator)),
|
||||||
|
executeIfActivityPub(asyncMiddleware(videoChannelAnnounceController))
|
||||||
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -47,7 +61,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function accountController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function accountController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const account: AccountInstance = res.locals.account
|
const account: AccountInstance = res.locals.account
|
||||||
|
|
||||||
return res.json(account.toActivityPubObject()).end()
|
return res.json(account.toActivityPubObject()).end()
|
||||||
|
@ -77,12 +91,26 @@ async function accountFollowingController (req: express.Request, res: express.Re
|
||||||
return res.json(activityPubResult)
|
return res.json(activityPubResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function videoController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function videoController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const video: VideoInstance = res.locals.video
|
const video: VideoInstance = res.locals.video
|
||||||
|
|
||||||
return res.json(video.toActivityPubObject())
|
return res.json(video.toActivityPubObject())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function videoAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
const share = res.locals.videoShare as VideoShareInstance
|
||||||
|
const object = await buildVideoAnnounceToFollowers(share.Account, res.locals.video, undefined)
|
||||||
|
|
||||||
|
return res.json(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function videoChannelAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
const share = res.locals.videoChannelShare as VideoChannelShareInstance
|
||||||
|
const object = await buildVideoChannelAnnounceToFollowers(share.Account, share.VideoChannel, undefined)
|
||||||
|
|
||||||
|
return res.json(object)
|
||||||
|
}
|
||||||
|
|
||||||
async function videoChannelController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function videoChannelController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const videoChannel: VideoChannelInstance = res.locals.videoChannel
|
const videoChannel: VideoChannelInstance = res.locals.videoChannel
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import * as Promise from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import 'express-validator'
|
import 'express-validator'
|
||||||
import * as validator from 'validator'
|
import * as validator from 'validator'
|
||||||
|
@ -11,33 +11,45 @@ function isAccountNameValid (value: string) {
|
||||||
return isUserUsernameValid(value)
|
return isUserUsernameValid(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkVideoAccountExists (id: string, res: express.Response, callback: () => void) {
|
function checkAccountIdExists (id: number | string, res: express.Response, callback: (err: Error, account: AccountInstance) => any) {
|
||||||
let promise: Promise<AccountInstance>
|
let promise: Bluebird<AccountInstance>
|
||||||
if (validator.isInt(id)) {
|
|
||||||
|
if (validator.isInt('' + id)) {
|
||||||
promise = db.Account.load(+id)
|
promise = db.Account.load(+id)
|
||||||
} else { // UUID
|
} else { // UUID
|
||||||
promise = db.Account.loadByUUID(id)
|
promise = db.Account.loadByUUID('' + id)
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.then(account => {
|
return checkAccountExists(promise, res, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkLocalAccountNameExists (name: string, res: express.Response, callback: (err: Error, account: AccountInstance) => any) {
|
||||||
|
const p = db.Account.loadLocalByName(name)
|
||||||
|
|
||||||
|
return checkAccountExists(p, res, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAccountExists (p: Bluebird<AccountInstance>, res: express.Response, callback: (err: Error, account: AccountInstance) => any) {
|
||||||
|
p.then(account => {
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return res.status(404)
|
return res.status(404)
|
||||||
.json({ error: 'Video account not found' })
|
.send({ error: 'Account not found' })
|
||||||
.end()
|
.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
res.locals.account = account
|
res.locals.account = account
|
||||||
callback()
|
return callback(null, account)
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
logger.error('Error in video account request validator.', err)
|
|
||||||
return res.sendStatus(500)
|
|
||||||
})
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error('Error in account request validator.', err)
|
||||||
|
return res.sendStatus(500)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
checkVideoAccountExists,
|
checkAccountIdExists,
|
||||||
|
checkLocalAccountNameExists,
|
||||||
isAccountNameValid
|
isAccountNameValid
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import * as Promise from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import * as validator from 'validator'
|
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import 'express-validator'
|
import 'express-validator'
|
||||||
import 'multer'
|
import 'multer'
|
||||||
|
import * as validator from 'validator'
|
||||||
|
|
||||||
import { database as db, CONSTRAINTS_FIELDS } from '../../initializers'
|
import { CONSTRAINTS_FIELDS, database as db } from '../../initializers'
|
||||||
import { VideoChannelInstance } from '../../models'
|
import { VideoChannelInstance } from '../../models'
|
||||||
import { logger } from '../logger'
|
import { logger } from '../logger'
|
||||||
import { exists } from './misc'
|
|
||||||
import { isActivityPubUrlValid } from './index'
|
import { isActivityPubUrlValid } from './index'
|
||||||
|
import { exists } from './misc'
|
||||||
|
|
||||||
const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS
|
const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ function isVideoChannelNameValid (value: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkVideoChannelExists (id: string, res: express.Response, callback: () => void) {
|
function checkVideoChannelExists (id: string, res: express.Response, callback: () => void) {
|
||||||
let promise: Promise<VideoChannelInstance>
|
let promise: Bluebird<VideoChannelInstance>
|
||||||
if (validator.isInt(id)) {
|
if (validator.isInt(id)) {
|
||||||
promise = db.VideoChannel.loadAndPopulateAccount(+id)
|
promise = db.VideoChannel.loadAndPopulateAccount(+id)
|
||||||
} else { // UUID
|
} else { // UUID
|
||||||
|
@ -48,11 +48,32 @@ function checkVideoChannelExists (id: string, res: express.Response, callback: (
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function isVideoChannelExistsPromise (id: string, res: express.Response) {
|
||||||
|
let videoChannel: VideoChannelInstance
|
||||||
|
if (validator.isInt(id)) {
|
||||||
|
videoChannel = await db.VideoChannel.loadAndPopulateAccount(+id)
|
||||||
|
} else { // UUID
|
||||||
|
videoChannel = await db.VideoChannel.loadByUUIDAndPopulateAccount(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!videoChannel) {
|
||||||
|
res.status(404)
|
||||||
|
.json({ error: 'Video channel not found' })
|
||||||
|
.end()
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
res.locals.videoChannel = videoChannel
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
isVideoChannelDescriptionValid,
|
isVideoChannelDescriptionValid,
|
||||||
isVideoChannelNameValid,
|
|
||||||
checkVideoChannelExists,
|
checkVideoChannelExists,
|
||||||
|
isVideoChannelNameValid,
|
||||||
|
isVideoChannelExistsPromise,
|
||||||
isVideoChannelUrlValid
|
isVideoChannelUrlValid
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,6 +130,27 @@ function checkVideoExists (id: string, res: Response, callback: () => void) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function isVideoExistsPromise (id: string, res: Response) {
|
||||||
|
let video: VideoInstance
|
||||||
|
|
||||||
|
if (validator.isInt(id)) {
|
||||||
|
video = await db.Video.loadAndPopulateAccountAndServerAndTags(+id)
|
||||||
|
} else { // UUID
|
||||||
|
video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!video) {
|
||||||
|
res.status(404)
|
||||||
|
.json({ error: 'Video not found' })
|
||||||
|
.end()
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
res.locals.video = video
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -152,5 +173,6 @@ export {
|
||||||
isVideoPrivacyValid,
|
isVideoPrivacyValid,
|
||||||
isVideoFileResolutionValid,
|
isVideoFileResolutionValid,
|
||||||
isVideoFileSizeValid,
|
isVideoFileSizeValid,
|
||||||
checkVideoExists
|
checkVideoExists,
|
||||||
|
isVideoExistsPromise
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import * as magnetUtil from 'magnet-uri'
|
import * as magnetUtil from 'magnet-uri'
|
||||||
import { VideoTorrentObject } from '../../../../shared'
|
import { VideoTorrentObject } from '../../../../shared'
|
||||||
import { VideoChannelObject } from '../../../../shared/models/activitypub/objects/video-channel-object'
|
import { VideoChannelObject } from '../../../../shared/models/activitypub/objects/video-channel-object'
|
||||||
|
import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
|
||||||
import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos'
|
import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos'
|
||||||
|
import { doRequest } from '../../../helpers/requests'
|
||||||
|
import { database as db } from '../../../initializers'
|
||||||
import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers/constants'
|
import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers/constants'
|
||||||
import { AccountInstance } from '../../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
||||||
import { VideoFileAttributes } from '../../../models/video/video-file-interface'
|
import { VideoFileAttributes } from '../../../models/video/video-file-interface'
|
||||||
import { VideoAttributes, VideoInstance } from '../../../models/video/video-interface'
|
import { VideoAttributes, VideoInstance } from '../../../models/video/video-interface'
|
||||||
import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
|
import { getOrCreateAccountAndServer } from '../account'
|
||||||
|
|
||||||
function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) {
|
function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) {
|
||||||
return {
|
return {
|
||||||
|
@ -97,10 +100,60 @@ function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoO
|
||||||
return attributes
|
return attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function addVideoShares (instance: VideoInstance, shares: string[]) {
|
||||||
|
for (const share of shares) {
|
||||||
|
// Fetch url
|
||||||
|
const json = await doRequest({
|
||||||
|
uri: share,
|
||||||
|
json: true
|
||||||
|
})
|
||||||
|
const actor = json['actor']
|
||||||
|
if (!actor) continue
|
||||||
|
|
||||||
|
const account = await getOrCreateAccountAndServer(actor)
|
||||||
|
|
||||||
|
const entry = {
|
||||||
|
accountId: account.id,
|
||||||
|
videoId: instance.id
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.VideoShare.findOrCreate({
|
||||||
|
where: entry,
|
||||||
|
defaults: entry
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addVideoChannelShares (instance: VideoChannelInstance, shares: string[]) {
|
||||||
|
for (const share of shares) {
|
||||||
|
// Fetch url
|
||||||
|
const json = await doRequest({
|
||||||
|
uri: share,
|
||||||
|
json: true
|
||||||
|
})
|
||||||
|
const actor = json['actor']
|
||||||
|
if (!actor) continue
|
||||||
|
|
||||||
|
const account = await getOrCreateAccountAndServer(actor)
|
||||||
|
|
||||||
|
const entry = {
|
||||||
|
accountId: account.id,
|
||||||
|
videoChannelId: instance.id
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.VideoChannelShare.findOrCreate({
|
||||||
|
where: entry,
|
||||||
|
defaults: entry
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
videoFileActivityUrlToDBAttributes,
|
videoFileActivityUrlToDBAttributes,
|
||||||
videoActivityObjectToDBAttributes,
|
videoActivityObjectToDBAttributes,
|
||||||
videoChannelActivityObjectToDBAttributes
|
videoChannelActivityObjectToDBAttributes,
|
||||||
|
addVideoChannelShares,
|
||||||
|
addVideoShares
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { VideoInstance } from '../../../models/video/video-interface'
|
||||||
import { getOrCreateAccountAndServer } from '../account'
|
import { getOrCreateAccountAndServer } from '../account'
|
||||||
import { getOrCreateVideoChannel } from '../video-channels'
|
import { getOrCreateVideoChannel } from '../video-channels'
|
||||||
import { generateThumbnailFromUrl } from '../videos'
|
import { generateThumbnailFromUrl } from '../videos'
|
||||||
import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
|
import { addVideoShares, videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
|
||||||
|
|
||||||
async function processAddActivity (activity: ActivityAdd) {
|
async function processAddActivity (activity: ActivityAdd) {
|
||||||
const activityObject = activity.object
|
const activityObject = activity.object
|
||||||
|
@ -37,12 +37,10 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processAddVideo (
|
async function processAddVideo (account: AccountInstance,
|
||||||
account: AccountInstance,
|
activity: ActivityAdd,
|
||||||
activity: ActivityAdd,
|
videoChannel: VideoChannelInstance,
|
||||||
videoChannel: VideoChannelInstance,
|
videoToCreateData: VideoTorrentObject) {
|
||||||
videoToCreateData: VideoTorrentObject
|
|
||||||
) {
|
|
||||||
const options = {
|
const options = {
|
||||||
arguments: [ account, activity, videoChannel, videoToCreateData ],
|
arguments: [ account, activity, videoChannel, videoToCreateData ],
|
||||||
errorMessage: 'Cannot insert the remote video with many retries.'
|
errorMessage: 'Cannot insert the remote video with many retries.'
|
||||||
|
@ -59,6 +57,10 @@ async function processAddVideo (
|
||||||
await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike')
|
await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (videoToCreateData.shares && Array.isArray(videoToCreateData.shares.orderedItems)) {
|
||||||
|
await addVideoShares(video, videoToCreateData.shares.orderedItems)
|
||||||
|
}
|
||||||
|
|
||||||
return video
|
return video
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,34 +1,23 @@
|
||||||
import { ActivityAnnounce } from '../../../../shared/models/activitypub/activity'
|
import { ActivityAdd, ActivityAnnounce, ActivityCreate } from '../../../../shared/models/activitypub/activity'
|
||||||
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { database as db } from '../../../initializers/index'
|
import { database as db } from '../../../initializers/index'
|
||||||
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { VideoInstance } from '../../../models/index'
|
import { VideoInstance } from '../../../models/index'
|
||||||
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
||||||
|
import { getOrCreateAccountAndServer } from '../account'
|
||||||
|
import { forwardActivity } from '../send/misc'
|
||||||
import { processAddActivity } from './process-add'
|
import { processAddActivity } from './process-add'
|
||||||
import { processCreateActivity } from './process-create'
|
import { processCreateActivity } from './process-create'
|
||||||
import { getOrCreateAccountAndServer } from '../account'
|
|
||||||
|
|
||||||
async function processAnnounceActivity (activity: ActivityAnnounce) {
|
async function processAnnounceActivity (activity: ActivityAnnounce) {
|
||||||
const announcedActivity = activity.object
|
const announcedActivity = activity.object
|
||||||
const accountAnnouncer = await getOrCreateAccountAndServer(activity.actor)
|
const accountAnnouncer = await getOrCreateAccountAndServer(activity.actor)
|
||||||
|
|
||||||
if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'VideoChannel') {
|
if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'VideoChannel') {
|
||||||
// Add share entry
|
return processVideoChannelShare(accountAnnouncer, activity)
|
||||||
const videoChannel: VideoChannelInstance = await processCreateActivity(announcedActivity)
|
|
||||||
await db.VideoChannelShare.create({
|
|
||||||
accountId: accountAnnouncer.id,
|
|
||||||
videoChannelId: videoChannel.id
|
|
||||||
})
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
} else if (announcedActivity.type === 'Add' && announcedActivity.object.type === 'Video') {
|
} else if (announcedActivity.type === 'Add' && announcedActivity.object.type === 'Video') {
|
||||||
// Add share entry
|
return processVideoShare(accountAnnouncer, activity)
|
||||||
const video: VideoInstance = await processAddActivity(announcedActivity)
|
|
||||||
await db.VideoShare.create({
|
|
||||||
accountId: accountAnnouncer.id,
|
|
||||||
videoId: video.id
|
|
||||||
})
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.warn(
|
logger.warn(
|
||||||
|
@ -44,3 +33,78 @@ async function processAnnounceActivity (activity: ActivityAnnounce) {
|
||||||
export {
|
export {
|
||||||
processAnnounceActivity
|
processAnnounceActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function processVideoChannelShare (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) {
|
||||||
|
const options = {
|
||||||
|
arguments: [ accountAnnouncer, activity ],
|
||||||
|
errorMessage: 'Cannot share the video channel with many retries.'
|
||||||
|
}
|
||||||
|
|
||||||
|
return retryTransactionWrapper(shareVideoChannel, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function shareVideoChannel (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) {
|
||||||
|
const announcedActivity = activity.object as ActivityCreate
|
||||||
|
|
||||||
|
return db.sequelize.transaction(async t => {
|
||||||
|
// Add share entry
|
||||||
|
const videoChannel: VideoChannelInstance = await processCreateActivity(announcedActivity)
|
||||||
|
const share = {
|
||||||
|
accountId: accountAnnouncer.id,
|
||||||
|
videoChannelId: videoChannel.id
|
||||||
|
}
|
||||||
|
|
||||||
|
const [ , created ] = await db.VideoChannelShare.findOrCreate({
|
||||||
|
where: share,
|
||||||
|
defaults: share,
|
||||||
|
transaction: t
|
||||||
|
})
|
||||||
|
|
||||||
|
if (videoChannel.isOwned() && created === true) {
|
||||||
|
// Don't resend the activity to the sender
|
||||||
|
const exceptions = [ accountAnnouncer ]
|
||||||
|
await forwardActivity(activity, t, exceptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function processVideoShare (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) {
|
||||||
|
const options = {
|
||||||
|
arguments: [ accountAnnouncer, activity ],
|
||||||
|
errorMessage: 'Cannot share the video with many retries.'
|
||||||
|
}
|
||||||
|
|
||||||
|
return retryTransactionWrapper(shareVideo, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareVideo (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) {
|
||||||
|
const announcedActivity = activity.object as ActivityAdd
|
||||||
|
|
||||||
|
return db.sequelize.transaction(async t => {
|
||||||
|
// Add share entry
|
||||||
|
const video: VideoInstance = await processAddActivity(announcedActivity)
|
||||||
|
|
||||||
|
const share = {
|
||||||
|
accountId: accountAnnouncer.id,
|
||||||
|
videoId: video.id
|
||||||
|
}
|
||||||
|
|
||||||
|
const [ , created ] = await db.VideoShare.findOrCreate({
|
||||||
|
where: share,
|
||||||
|
defaults: share,
|
||||||
|
transaction: t
|
||||||
|
})
|
||||||
|
|
||||||
|
if (video.isOwned() && created === true) {
|
||||||
|
// Don't resend the activity to the sender
|
||||||
|
const exceptions = [ accountAnnouncer ]
|
||||||
|
await forwardActivity(activity, t, exceptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { getOrCreateAccountAndServer } from '../account'
|
import { getOrCreateAccountAndServer } from '../account'
|
||||||
import { forwardActivity } from '../send/misc'
|
import { forwardActivity } from '../send/misc'
|
||||||
import { getVideoChannelActivityPubUrl } from '../url'
|
import { getVideoChannelActivityPubUrl } from '../url'
|
||||||
import { videoChannelActivityObjectToDBAttributes } from './misc'
|
import { addVideoChannelShares, videoChannelActivityObjectToDBAttributes } from './misc'
|
||||||
|
|
||||||
async function processCreateActivity (activity: ActivityCreate) {
|
async function processCreateActivity (activity: ActivityCreate) {
|
||||||
const activityObject = activity.object
|
const activityObject = activity.object
|
||||||
|
@ -92,13 +92,19 @@ async function processCreateView (byAccount: AccountInstance, activity: Activity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processCreateVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
|
async function processCreateVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
|
||||||
const options = {
|
const options = {
|
||||||
arguments: [ account, videoChannelToCreateData ],
|
arguments: [ account, videoChannelToCreateData ],
|
||||||
errorMessage: 'Cannot insert the remote video channel with many retries.'
|
errorMessage: 'Cannot insert the remote video channel with many retries.'
|
||||||
}
|
}
|
||||||
|
|
||||||
return retryTransactionWrapper(addRemoteVideoChannel, options)
|
const videoChannel = await retryTransactionWrapper(addRemoteVideoChannel, options)
|
||||||
|
|
||||||
|
if (videoChannelToCreateData.shares && Array.isArray(videoChannelToCreateData.shares.orderedItems)) {
|
||||||
|
await addVideoChannelShares(videoChannel, videoChannelToCreateData.shares.orderedItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
return videoChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
|
function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
|
import { Activity } from '../../../../shared/models/activitypub/activity'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { ACTIVITY_PUB, database as db } from '../../../initializers'
|
import { ACTIVITY_PUB, database as db } from '../../../initializers'
|
||||||
import { AccountInstance } from '../../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
|
import { VideoChannelInstance } from '../../../models/index'
|
||||||
|
import { VideoInstance } from '../../../models/video/video-interface'
|
||||||
import {
|
import {
|
||||||
activitypubHttpJobScheduler,
|
activitypubHttpJobScheduler,
|
||||||
ActivityPubHttpPayload
|
ActivityPubHttpPayload
|
||||||
} from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler'
|
} from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler'
|
||||||
import { VideoInstance } from '../../../models/video/video-interface'
|
|
||||||
import { Activity } from '../../../../shared/models/activitypub/activity'
|
|
||||||
|
|
||||||
async function forwardActivity (
|
async function forwardActivity (
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
|
@ -85,9 +86,16 @@ function getOriginVideoAudience (video: VideoInstance, accountsInvolvedInVideo:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoFollowersAudience (accountsInvolvedInVideo: AccountInstance[]) {
|
function getOriginVideoChannelAudience (videoChannel: VideoChannelInstance, accountsInvolved: AccountInstance[]) {
|
||||||
return {
|
return {
|
||||||
to: accountsInvolvedInVideo.map(a => a.followersUrl),
|
to: [ videoChannel.Account.url ],
|
||||||
|
cc: accountsInvolved.map(a => a.followersUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getObjectFollowersAudience (accountsInvolvedInObject: AccountInstance[]) {
|
||||||
|
return {
|
||||||
|
to: accountsInvolvedInObject.map(a => a.followersUrl),
|
||||||
cc: []
|
cc: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +107,13 @@ async function getAccountsInvolvedInVideo (video: VideoInstance) {
|
||||||
return accountsToForwardView
|
return accountsToForwardView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getAccountsInvolvedInVideoChannel (videoChannel: VideoChannelInstance) {
|
||||||
|
const accountsToForwardView = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id)
|
||||||
|
accountsToForwardView.push(videoChannel.Account)
|
||||||
|
|
||||||
|
return accountsToForwardView
|
||||||
|
}
|
||||||
|
|
||||||
async function getAudience (accountSender: AccountInstance, isPublic = true) {
|
async function getAudience (accountSender: AccountInstance, isPublic = true) {
|
||||||
const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls()
|
const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls()
|
||||||
|
|
||||||
|
@ -131,10 +146,12 @@ async function computeFollowerUris (toAccountFollower: AccountInstance[], follow
|
||||||
|
|
||||||
export {
|
export {
|
||||||
broadcastToFollowers,
|
broadcastToFollowers,
|
||||||
|
getOriginVideoChannelAudience,
|
||||||
unicastTo,
|
unicastTo,
|
||||||
getAudience,
|
getAudience,
|
||||||
getOriginVideoAudience,
|
getOriginVideoAudience,
|
||||||
getAccountsInvolvedInVideo,
|
getAccountsInvolvedInVideo,
|
||||||
getVideoFollowersAudience,
|
getAccountsInvolvedInVideoChannel,
|
||||||
|
getObjectFollowersAudience,
|
||||||
forwardActivity
|
forwardActivity
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,96 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAdd } from '../../../../shared/index'
|
import { ActivityAdd } from '../../../../shared/index'
|
||||||
import { ActivityAnnounce, ActivityCreate } from '../../../../shared/models/activitypub/activity'
|
import { ActivityAnnounce, ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub/activity'
|
||||||
import { AccountInstance, VideoInstance } from '../../../models'
|
import { AccountInstance, VideoInstance } from '../../../models'
|
||||||
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
||||||
import { getAnnounceActivityPubUrl } from '../url'
|
import { getAnnounceActivityPubUrl } from '../url'
|
||||||
import { broadcastToFollowers } from './misc'
|
import {
|
||||||
|
broadcastToFollowers,
|
||||||
|
getAccountsInvolvedInVideo,
|
||||||
|
getAccountsInvolvedInVideoChannel,
|
||||||
|
getAudience,
|
||||||
|
getObjectFollowersAudience,
|
||||||
|
getOriginVideoAudience,
|
||||||
|
getOriginVideoChannelAudience,
|
||||||
|
unicastTo
|
||||||
|
} from './misc'
|
||||||
import { addActivityData } from './send-add'
|
import { addActivityData } from './send-add'
|
||||||
import { createActivityData } from './send-create'
|
import { createActivityData } from './send-create'
|
||||||
|
|
||||||
async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
async function buildVideoAnnounceToFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
||||||
const url = getAnnounceActivityPubUrl(video.url, byAccount)
|
const url = getAnnounceActivityPubUrl(video.url, byAccount)
|
||||||
|
|
||||||
const videoChannel = video.VideoChannel
|
const videoChannel = video.VideoChannel
|
||||||
const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject())
|
const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject())
|
||||||
|
|
||||||
const data = await announceActivityData(url, byAccount, announcedActivity)
|
const accountsToForwardView = await getAccountsInvolvedInVideo(video)
|
||||||
|
const audience = getObjectFollowersAudience(accountsToForwardView)
|
||||||
|
const data = await announceActivityData(url, byAccount, announcedActivity, audience)
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendVideoAnnounceToFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
||||||
|
const data = await buildVideoAnnounceToFollowers(byAccount, video, t)
|
||||||
|
|
||||||
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendVideoChannelAnnounce (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) {
|
async function sendVideoAnnounceToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
||||||
|
const url = getAnnounceActivityPubUrl(video.url, byAccount)
|
||||||
|
|
||||||
|
const videoChannel = video.VideoChannel
|
||||||
|
const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject())
|
||||||
|
|
||||||
|
const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video)
|
||||||
|
const audience = getOriginVideoAudience(video, accountsInvolvedInVideo)
|
||||||
|
const data = await createActivityData(url, byAccount, announcedActivity, audience)
|
||||||
|
|
||||||
|
return unicastTo(data, byAccount, videoChannel.Account.sharedInboxUrl, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildVideoChannelAnnounceToFollowers (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) {
|
||||||
const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount)
|
const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount)
|
||||||
const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject())
|
const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject())
|
||||||
|
|
||||||
const data = await announceActivityData(url, byAccount, announcedActivity)
|
const accountsToForwardView = await getAccountsInvolvedInVideoChannel(videoChannel)
|
||||||
|
const audience = getObjectFollowersAudience(accountsToForwardView)
|
||||||
|
const data = await announceActivityData(url, byAccount, announcedActivity, audience)
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendVideoChannelAnnounceToFollowers (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) {
|
||||||
|
const data = await buildVideoChannelAnnounceToFollowers(byAccount, videoChannel, t)
|
||||||
|
|
||||||
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function announceActivityData (url: string, byAccount: AccountInstance, object: ActivityCreate | ActivityAdd) {
|
async function sendVideoChannelAnnounceToOrigin (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) {
|
||||||
|
const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount)
|
||||||
|
const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject())
|
||||||
|
|
||||||
|
const accountsInvolvedInVideo = await getAccountsInvolvedInVideoChannel(videoChannel)
|
||||||
|
const audience = getOriginVideoChannelAudience(videoChannel, accountsInvolvedInVideo)
|
||||||
|
const data = await createActivityData(url, byAccount, announcedActivity, audience)
|
||||||
|
|
||||||
|
return unicastTo(data, byAccount, videoChannel.Account.sharedInboxUrl, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function announceActivityData (
|
||||||
|
url: string,
|
||||||
|
byAccount: AccountInstance,
|
||||||
|
object: ActivityCreate | ActivityAdd,
|
||||||
|
audience?: ActivityAudience
|
||||||
|
) {
|
||||||
|
if (!audience) {
|
||||||
|
audience = await getAudience(byAccount)
|
||||||
|
}
|
||||||
|
|
||||||
const activity: ActivityAnnounce = {
|
const activity: ActivityAnnounce = {
|
||||||
type: 'Announce',
|
type: 'Announce',
|
||||||
|
to: audience.to,
|
||||||
|
cc: audience.cc,
|
||||||
id: url,
|
id: url,
|
||||||
actor: byAccount.url,
|
actor: byAccount.url,
|
||||||
object
|
object
|
||||||
|
@ -40,7 +102,11 @@ async function announceActivityData (url: string, byAccount: AccountInstance, ob
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
sendVideoAnnounce,
|
sendVideoAnnounceToFollowers,
|
||||||
sendVideoChannelAnnounce,
|
sendVideoChannelAnnounceToFollowers,
|
||||||
announceActivityData
|
sendVideoAnnounceToOrigin,
|
||||||
|
sendVideoChannelAnnounceToOrigin,
|
||||||
|
announceActivityData,
|
||||||
|
buildVideoAnnounceToFollowers,
|
||||||
|
buildVideoChannelAnnounceToFollowers
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
getAccountsInvolvedInVideo,
|
getAccountsInvolvedInVideo,
|
||||||
getAudience,
|
getAudience,
|
||||||
getOriginVideoAudience,
|
getOriginVideoAudience,
|
||||||
getVideoFollowersAudience,
|
getObjectFollowersAudience,
|
||||||
unicastTo
|
unicastTo
|
||||||
} from './misc'
|
} from './misc'
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ async function sendCreateViewToVideoFollowers (byAccount: AccountInstance, video
|
||||||
const viewActivity = createViewActivityData(byAccount, video)
|
const viewActivity = createViewActivityData(byAccount, video)
|
||||||
|
|
||||||
const accountsToForwardView = await getAccountsInvolvedInVideo(video)
|
const accountsToForwardView = await getAccountsInvolvedInVideo(video)
|
||||||
const audience = getVideoFollowersAudience(accountsToForwardView)
|
const audience = getObjectFollowersAudience(accountsToForwardView)
|
||||||
const data = await createActivityData(url, byAccount, viewActivity, audience)
|
const data = await createActivityData(url, byAccount, viewActivity, audience)
|
||||||
|
|
||||||
// Use the server account to send the view, because it could be an unregistered account
|
// Use the server account to send the view, because it could be an unregistered account
|
||||||
|
@ -72,7 +72,7 @@ async function sendCreateDislikeToVideoFollowers (byAccount: AccountInstance, vi
|
||||||
const dislikeActivity = createDislikeActivityData(byAccount, video)
|
const dislikeActivity = createDislikeActivityData(byAccount, video)
|
||||||
|
|
||||||
const accountsToForwardView = await getAccountsInvolvedInVideo(video)
|
const accountsToForwardView = await getAccountsInvolvedInVideo(video)
|
||||||
const audience = getVideoFollowersAudience(accountsToForwardView)
|
const audience = getObjectFollowersAudience(accountsToForwardView)
|
||||||
const data = await createActivityData(url, byAccount, dislikeActivity, audience)
|
const data = await createActivityData(url, byAccount, dislikeActivity, audience)
|
||||||
|
|
||||||
const followersException = [ byAccount ]
|
const followersException = [ byAccount ]
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
getAccountsInvolvedInVideo,
|
getAccountsInvolvedInVideo,
|
||||||
getAudience,
|
getAudience,
|
||||||
getOriginVideoAudience,
|
getOriginVideoAudience,
|
||||||
getVideoFollowersAudience,
|
getObjectFollowersAudience,
|
||||||
unicastTo
|
unicastTo
|
||||||
} from './misc'
|
} from './misc'
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ async function sendLikeToVideoFollowers (byAccount: AccountInstance, video: Vide
|
||||||
const url = getVideoLikeActivityPubUrl(byAccount, video)
|
const url = getVideoLikeActivityPubUrl(byAccount, video)
|
||||||
|
|
||||||
const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video)
|
const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video)
|
||||||
const audience = getVideoFollowersAudience(accountsInvolvedInVideo)
|
const audience = getObjectFollowersAudience(accountsInvolvedInVideo)
|
||||||
const data = await likeActivityData(url, byAccount, video, audience)
|
const data = await likeActivityData(url, byAccount, video, audience)
|
||||||
|
|
||||||
const toAccountsFollowers = await getAccountsInvolvedInVideo(video)
|
const toAccountsFollowers = await getAccountsInvolvedInVideo(video)
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { AccountInstance } from '../../../models'
|
||||||
import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
|
import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
|
||||||
import { VideoInstance } from '../../../models/video/video-interface'
|
import { VideoInstance } from '../../../models/video/video-interface'
|
||||||
import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
|
import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
|
||||||
import { broadcastToFollowers, getAccountsInvolvedInVideo, getAudience, getVideoFollowersAudience, unicastTo } from './misc'
|
import { broadcastToFollowers, getAccountsInvolvedInVideo, getAudience, getObjectFollowersAudience, unicastTo } from './misc'
|
||||||
import { createActivityData, createDislikeActivityData } from './send-create'
|
import { createActivityData, createDislikeActivityData } from './send-create'
|
||||||
import { followActivityData } from './send-follow'
|
import { followActivityData } from './send-follow'
|
||||||
import { likeActivityData } from './send-like'
|
import { likeActivityData } from './send-like'
|
||||||
|
@ -43,7 +43,7 @@ async function sendUndoLikeToVideoFollowers (byAccount: AccountInstance, video:
|
||||||
const undoUrl = getUndoActivityPubUrl(likeUrl)
|
const undoUrl = getUndoActivityPubUrl(likeUrl)
|
||||||
|
|
||||||
const toAccountsFollowers = await getAccountsInvolvedInVideo(video)
|
const toAccountsFollowers = await getAccountsInvolvedInVideo(video)
|
||||||
const audience = getVideoFollowersAudience(toAccountsFollowers)
|
const audience = getObjectFollowersAudience(toAccountsFollowers)
|
||||||
const object = await likeActivityData(likeUrl, byAccount, video)
|
const object = await likeActivityData(likeUrl, byAccount, video)
|
||||||
const data = await undoActivityData(undoUrl, byAccount, object, audience)
|
const data = await undoActivityData(undoUrl, byAccount, object, audience)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { getServerAccount } from '../../helpers/utils'
|
||||||
import { database as db } from '../../initializers'
|
import { database as db } from '../../initializers'
|
||||||
import { VideoChannelInstance } from '../../models/index'
|
import { VideoChannelInstance } from '../../models/index'
|
||||||
import { VideoInstance } from '../../models/video/video-interface'
|
import { VideoInstance } from '../../models/video/video-interface'
|
||||||
import { sendVideoAnnounce, sendVideoChannelAnnounce } from './send/send-announce'
|
import { sendVideoAnnounceToFollowers, sendVideoChannelAnnounceToFollowers } from './send/send-announce'
|
||||||
|
|
||||||
async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Transaction) {
|
async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Transaction) {
|
||||||
const serverAccount = await getServerAccount()
|
const serverAccount = await getServerAccount()
|
||||||
|
@ -13,7 +13,7 @@ async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t:
|
||||||
videoChannelId: videoChannel.id
|
videoChannelId: videoChannel.id
|
||||||
}, { transaction: t })
|
}, { transaction: t })
|
||||||
|
|
||||||
return sendVideoChannelAnnounce(serverAccount, videoChannel, t)
|
return sendVideoChannelAnnounceToFollowers(serverAccount, videoChannel, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function shareVideoByServer (video: VideoInstance, t: Transaction) {
|
async function shareVideoByServer (video: VideoInstance, t: Transaction) {
|
||||||
|
@ -24,7 +24,7 @@ async function shareVideoByServer (video: VideoInstance, t: Transaction) {
|
||||||
videoId: video.id
|
videoId: video.id
|
||||||
}, { transaction: t })
|
}, { transaction: t })
|
||||||
|
|
||||||
return sendVideoAnnounce(serverAccount, video, t)
|
return sendVideoAnnounceToFollowers(serverAccount, video, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -22,37 +22,37 @@ function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseInstance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoViewActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) {
|
function getVideoViewActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) {
|
||||||
return video.url + '#views/' + byAccount.uuid + '/' + new Date().toISOString()
|
return video.url + '/views/' + byAccount.uuid + '/' + new Date().toISOString()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoLikeActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) {
|
function getVideoLikeActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) {
|
||||||
return byAccount.url + '#likes/' + video.id
|
return byAccount.url + '/likes/' + video.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoDislikeActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) {
|
function getVideoDislikeActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) {
|
||||||
return byAccount.url + '#dislikes/' + video.id
|
return byAccount.url + '/dislikes/' + video.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) {
|
function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) {
|
||||||
const me = accountFollow.AccountFollower
|
const me = accountFollow.AccountFollower
|
||||||
const following = accountFollow.AccountFollowing
|
const following = accountFollow.AccountFollowing
|
||||||
|
|
||||||
return me.url + '#follows/' + following.id
|
return me.url + '/follows/' + following.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowInstance) {
|
function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowInstance) {
|
||||||
const follower = accountFollow.AccountFollower
|
const follower = accountFollow.AccountFollower
|
||||||
const me = accountFollow.AccountFollowing
|
const me = accountFollow.AccountFollowing
|
||||||
|
|
||||||
return follower.url + '#accepts/follows/' + me.id
|
return follower.url + '/accepts/follows/' + me.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountInstance) {
|
function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountInstance) {
|
||||||
return originalUrl + '#announces/' + byAccount.id
|
return originalUrl + '/announces/' + byAccount.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) {
|
function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) {
|
||||||
return originalUrl + '#updates/' + updatedAt
|
return originalUrl + '/updates/' + updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUndoActivityPubUrl (originalUrl: string) {
|
function getUndoActivityPubUrl (originalUrl: string) {
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
import { Request, Response, NextFunction } from 'express'
|
import { Request, Response, NextFunction, RequestHandler } from 'express'
|
||||||
|
import { eachSeries } from 'async'
|
||||||
|
|
||||||
// Syntactic sugar to avoid try/catch in express controllers
|
// Syntactic sugar to avoid try/catch in express controllers
|
||||||
// Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
|
// Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
|
||||||
function asyncMiddleware (fn: (req: Request, res: Response, next: NextFunction) => Promise<any>) {
|
function asyncMiddleware (fun: RequestHandler | RequestHandler[]) {
|
||||||
return (req: Request, res: Response, next: NextFunction) => {
|
return (req: Request, res: Response, next: NextFunction) => {
|
||||||
return Promise.resolve(fn(req, res, next))
|
if (Array.isArray(fun) === true) {
|
||||||
|
return eachSeries(fun as RequestHandler[], (f, cb) => {
|
||||||
|
Promise.resolve(f(req, res, cb))
|
||||||
|
.catch(next)
|
||||||
|
}, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve((fun as RequestHandler)(req, res, next))
|
||||||
.catch(next)
|
.catch(next)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { param } from 'express-validator/check'
|
import { param } from 'express-validator/check'
|
||||||
import { logger } from '../../helpers'
|
import { logger } from '../../helpers'
|
||||||
import { isAccountNameValid } from '../../helpers/custom-validators/accounts'
|
import { checkLocalAccountNameExists, isAccountNameValid } from '../../helpers/custom-validators/accounts'
|
||||||
import { database as db } from '../../initializers/database'
|
|
||||||
import { AccountInstance } from '../../models'
|
|
||||||
import { checkErrors } from './utils'
|
import { checkErrors } from './utils'
|
||||||
|
|
||||||
const localAccountValidator = [
|
const localAccountValidator = [
|
||||||
|
@ -13,7 +11,7 @@ const localAccountValidator = [
|
||||||
logger.debug('Checking localAccountValidator parameters', { parameters: req.params })
|
logger.debug('Checking localAccountValidator parameters', { parameters: req.params })
|
||||||
|
|
||||||
checkErrors(req, res, () => {
|
checkErrors(req, res, () => {
|
||||||
checkLocalAccountExists(req.params.name, res, next)
|
checkLocalAccountNameExists(req.params.name, res, next)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -23,23 +21,3 @@ const localAccountValidator = [
|
||||||
export {
|
export {
|
||||||
localAccountValidator
|
localAccountValidator
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function checkLocalAccountExists (name: string, res: express.Response, callback: (err: Error, account: AccountInstance) => void) {
|
|
||||||
db.Account.loadLocalByName(name)
|
|
||||||
.then(account => {
|
|
||||||
if (!account) {
|
|
||||||
return res.status(404)
|
|
||||||
.send({ error: 'Account not found' })
|
|
||||||
.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
res.locals.account = account
|
|
||||||
return callback(null, account)
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
logger.error('Error in account request validator.', err)
|
|
||||||
return res.sendStatus(500)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,8 +14,22 @@ function checkErrors (req: express.Request, res: express.Response, next: express
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function areValidationErrors (req: express.Request, res: express.Response) {
|
||||||
|
const errors = validationResult(req)
|
||||||
|
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
logger.warn('Incorrect request parameters', { path: req.originalUrl, err: errors.mapped() })
|
||||||
|
res.status(400).json({ errors: errors.mapped() })
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
checkErrors
|
checkErrors,
|
||||||
|
areValidationErrors
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { body, param } from 'express-validator/check'
|
import { body, param } from 'express-validator/check'
|
||||||
import { UserRight } from '../../../shared'
|
import { UserRight } from '../../../shared'
|
||||||
import { checkVideoAccountExists } from '../../helpers/custom-validators/accounts'
|
import { checkAccountIdExists } from '../../helpers/custom-validators/accounts'
|
||||||
import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels'
|
import { isIdValid } from '../../helpers/custom-validators/misc'
|
||||||
import { checkVideoChannelExists, isIdOrUUIDValid } from '../../helpers/index'
|
import {
|
||||||
|
checkVideoChannelExists,
|
||||||
|
isVideoChannelDescriptionValid,
|
||||||
|
isVideoChannelExistsPromise,
|
||||||
|
isVideoChannelNameValid
|
||||||
|
} from '../../helpers/custom-validators/video-channels'
|
||||||
|
import { isIdOrUUIDValid } from '../../helpers/index'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { database as db } from '../../initializers'
|
import { database as db } from '../../initializers'
|
||||||
import { UserInstance } from '../../models'
|
import { UserInstance } from '../../models'
|
||||||
import { checkErrors } from './utils'
|
import { areValidationErrors, checkErrors } from './utils'
|
||||||
|
|
||||||
const listVideoAccountChannelsValidator = [
|
const listVideoAccountChannelsValidator = [
|
||||||
param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
|
param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
|
||||||
|
@ -16,7 +22,7 @@ const listVideoAccountChannelsValidator = [
|
||||||
logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body })
|
logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body })
|
||||||
|
|
||||||
checkErrors(req, res, () => {
|
checkErrors(req, res, () => {
|
||||||
checkVideoAccountExists(req.params.accountId, res, next)
|
checkAccountIdExists(req.params.accountId, res, next)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -90,6 +96,28 @@ const videoChannelsGetValidator = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const videoChannelsShareValidator = [
|
||||||
|
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
|
||||||
|
param('accountId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid account id'),
|
||||||
|
|
||||||
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
|
logger.debug('Checking videoChannelShare parameters', { parameters: req.params })
|
||||||
|
|
||||||
|
if (areValidationErrors(req, res)) return
|
||||||
|
if (!await isVideoChannelExistsPromise(req.params.id, res)) return
|
||||||
|
|
||||||
|
const share = await db.VideoChannelShare.load(res.locals.video.id, req.params.accountId)
|
||||||
|
if (!share) {
|
||||||
|
return res.status(404)
|
||||||
|
.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
res.locals.videoChannelShare = share
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -97,7 +125,8 @@ export {
|
||||||
videoChannelsAddValidator,
|
videoChannelsAddValidator,
|
||||||
videoChannelsUpdateValidator,
|
videoChannelsUpdateValidator,
|
||||||
videoChannelsRemoveValidator,
|
videoChannelsRemoveValidator,
|
||||||
videoChannelsGetValidator
|
videoChannelsGetValidator,
|
||||||
|
videoChannelsShareValidator
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -24,7 +24,8 @@ import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers'
|
||||||
import { database as db } from '../../initializers/database'
|
import { database as db } from '../../initializers/database'
|
||||||
import { UserInstance } from '../../models/account/user-interface'
|
import { UserInstance } from '../../models/account/user-interface'
|
||||||
import { authenticate } from '../oauth'
|
import { authenticate } from '../oauth'
|
||||||
import { checkErrors } from './utils'
|
import { areValidationErrors, checkErrors } from './utils'
|
||||||
|
import { isVideoExistsPromise } from '../../helpers/index'
|
||||||
|
|
||||||
const videosAddValidator = [
|
const videosAddValidator = [
|
||||||
body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage(
|
body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage(
|
||||||
|
@ -230,6 +231,28 @@ const videoRateValidator = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const videosShareValidator = [
|
||||||
|
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
|
||||||
|
param('accountId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid account id'),
|
||||||
|
|
||||||
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
|
logger.debug('Checking videoShare parameters', { parameters: req.params })
|
||||||
|
|
||||||
|
if (areValidationErrors(req, res)) return
|
||||||
|
if (!await isVideoExistsPromise(req.params.id, res)) return
|
||||||
|
|
||||||
|
const share = await db.VideoShare.load(req.params.accountId, res.locals.video.id)
|
||||||
|
if (!share) {
|
||||||
|
return res.status(404)
|
||||||
|
.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
res.locals.videoShare = share
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -238,6 +261,7 @@ export {
|
||||||
videosGetValidator,
|
videosGetValidator,
|
||||||
videosRemoveValidator,
|
videosRemoveValidator,
|
||||||
videosSearchValidator,
|
videosSearchValidator,
|
||||||
|
videosShareValidator,
|
||||||
|
|
||||||
videoAbuseReportValidator,
|
videoAbuseReportValidator,
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { VideoChannelObject } from '../../../shared/models/activitypub/objects/v
|
||||||
import { VideoChannel as FormattedVideoChannel } from '../../../shared/models/videos/video-channel.model'
|
import { VideoChannel as FormattedVideoChannel } from '../../../shared/models/videos/video-channel.model'
|
||||||
import { AccountInstance } from '../account/account-interface'
|
import { AccountInstance } from '../account/account-interface'
|
||||||
import { VideoInstance } from './video-interface'
|
import { VideoInstance } from './video-interface'
|
||||||
|
import { VideoChannelShareInstance } from './video-channel-share-interface'
|
||||||
|
|
||||||
export namespace VideoChannelMethods {
|
export namespace VideoChannelMethods {
|
||||||
export type ToFormattedJSON = (this: VideoChannelInstance) => FormattedVideoChannel
|
export type ToFormattedJSON = (this: VideoChannelInstance) => FormattedVideoChannel
|
||||||
|
@ -47,6 +48,7 @@ export interface VideoChannelAttributes {
|
||||||
|
|
||||||
Account?: AccountInstance
|
Account?: AccountInstance
|
||||||
Videos?: VideoInstance[]
|
Videos?: VideoInstance[]
|
||||||
|
VideoChannelShares?: VideoChannelShareInstance[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoChannelInstance extends VideoChannelClass, VideoChannelAttributes, Sequelize.Instance<VideoChannelAttributes> {
|
export interface VideoChannelInstance extends VideoChannelClass, VideoChannelAttributes, Sequelize.Instance<VideoChannelAttributes> {
|
||||||
|
|
|
@ -5,10 +5,12 @@ import { VideoChannelInstance } from './video-channel-interface'
|
||||||
|
|
||||||
export namespace VideoChannelShareMethods {
|
export namespace VideoChannelShareMethods {
|
||||||
export type LoadAccountsByShare = (videoChannelId: number) => Bluebird<AccountInstance[]>
|
export type LoadAccountsByShare = (videoChannelId: number) => Bluebird<AccountInstance[]>
|
||||||
|
export type Load = (accountId: number, videoId: number) => Bluebird<VideoChannelShareInstance>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoChannelShareClass {
|
export interface VideoChannelShareClass {
|
||||||
loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare
|
loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare
|
||||||
|
load: VideoChannelShareMethods.Load
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoChannelShareAttributes {
|
export interface VideoChannelShareAttributes {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { VideoChannelShareAttributes, VideoChannelShareInstance, VideoChannelSha
|
||||||
|
|
||||||
let VideoChannelShare: Sequelize.Model<VideoChannelShareInstance, VideoChannelShareAttributes>
|
let VideoChannelShare: Sequelize.Model<VideoChannelShareInstance, VideoChannelShareAttributes>
|
||||||
let loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare
|
let loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare
|
||||||
|
let load: VideoChannelShareMethods.Load
|
||||||
|
|
||||||
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
|
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
|
||||||
VideoChannelShare = sequelize.define<VideoChannelShareInstance, VideoChannelShareAttributes>('VideoChannelShare',
|
VideoChannelShare = sequelize.define<VideoChannelShareInstance, VideoChannelShareAttributes>('VideoChannelShare',
|
||||||
|
@ -23,6 +24,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
|
||||||
|
|
||||||
const classMethods = [
|
const classMethods = [
|
||||||
associate,
|
associate,
|
||||||
|
load,
|
||||||
loadAccountsByShare
|
loadAccountsByShare
|
||||||
]
|
]
|
||||||
addMethodsToModel(VideoChannelShare, classMethods)
|
addMethodsToModel(VideoChannelShare, classMethods)
|
||||||
|
@ -50,6 +52,19 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
load = function (accountId: number, videoChannelId: number) {
|
||||||
|
return VideoChannelShare.findOne({
|
||||||
|
where: {
|
||||||
|
accountId,
|
||||||
|
videoChannelId
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
VideoChannelShare['sequelize'].models.Account,
|
||||||
|
VideoChannelShare['sequelize'].models.VideoChannel
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
loadAccountsByShare = function (videoChannelId: number) {
|
loadAccountsByShare = function (videoChannelId: number) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { sendDeleteVideoChannel } from '../../lib/activitypub/send/send-delete'
|
||||||
|
|
||||||
import { addMethodsToModel, getSort } from '../utils'
|
import { addMethodsToModel, getSort } from '../utils'
|
||||||
import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface'
|
import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface'
|
||||||
|
import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
|
||||||
|
import { activityPubCollection } from '../../helpers/activitypub'
|
||||||
|
|
||||||
let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes>
|
let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes>
|
||||||
let toFormattedJSON: VideoChannelMethods.ToFormattedJSON
|
let toFormattedJSON: VideoChannelMethods.ToFormattedJSON
|
||||||
|
@ -139,6 +141,18 @@ toFormattedJSON = function (this: VideoChannelInstance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
toActivityPubObject = function (this: VideoChannelInstance) {
|
toActivityPubObject = function (this: VideoChannelInstance) {
|
||||||
|
let sharesObject
|
||||||
|
if (Array.isArray(this.VideoChannelShares)) {
|
||||||
|
const shares: string[] = []
|
||||||
|
|
||||||
|
for (const videoChannelShare of this.VideoChannelShares) {
|
||||||
|
const shareUrl = getAnnounceActivityPubUrl(this.url, videoChannelShare.Account)
|
||||||
|
shares.push(shareUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
sharesObject = activityPubCollection(shares)
|
||||||
|
}
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
type: 'VideoChannel' as 'VideoChannel',
|
type: 'VideoChannel' as 'VideoChannel',
|
||||||
id: this.url,
|
id: this.url,
|
||||||
|
@ -146,7 +160,8 @@ toActivityPubObject = function (this: VideoChannelInstance) {
|
||||||
content: this.description,
|
content: this.description,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
published: this.createdAt.toISOString(),
|
published: this.createdAt.toISOString(),
|
||||||
updated: this.updatedAt.toISOString()
|
updated: this.updatedAt.toISOString(),
|
||||||
|
shares: sharesObject
|
||||||
}
|
}
|
||||||
|
|
||||||
return json
|
return json
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
|
import * as Bluebird from 'bluebird'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import { AccountInstance } from '../account/account-interface'
|
import { AccountInstance } from '../account/account-interface'
|
||||||
import { VideoInstance } from './video-interface'
|
import { VideoInstance } from './video-interface'
|
||||||
import * as Bluebird from 'bluebird'
|
|
||||||
|
|
||||||
export namespace VideoShareMethods {
|
export namespace VideoShareMethods {
|
||||||
export type LoadAccountsByShare = (videoChannelId: number) => Bluebird<AccountInstance[]>
|
export type LoadAccountsByShare = (videoId: number) => Bluebird<AccountInstance[]>
|
||||||
|
export type Load = (accountId: number, videoId: number) => Bluebird<VideoShareInstance>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoShareClass {
|
export interface VideoShareClass {
|
||||||
loadAccountsByShare: VideoShareMethods.LoadAccountsByShare
|
loadAccountsByShare: VideoShareMethods.LoadAccountsByShare
|
||||||
|
load: VideoShareMethods.Load
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoShareAttributes {
|
export interface VideoShareAttributes {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { VideoShareAttributes, VideoShareInstance, VideoShareMethods } from './v
|
||||||
|
|
||||||
let VideoShare: Sequelize.Model<VideoShareInstance, VideoShareAttributes>
|
let VideoShare: Sequelize.Model<VideoShareInstance, VideoShareAttributes>
|
||||||
let loadAccountsByShare: VideoShareMethods.LoadAccountsByShare
|
let loadAccountsByShare: VideoShareMethods.LoadAccountsByShare
|
||||||
|
let load: VideoShareMethods.Load
|
||||||
|
|
||||||
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
|
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
|
||||||
VideoShare = sequelize.define<VideoShareInstance, VideoShareAttributes>('VideoShare',
|
VideoShare = sequelize.define<VideoShareInstance, VideoShareAttributes>('VideoShare',
|
||||||
|
@ -23,7 +24,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
|
||||||
|
|
||||||
const classMethods = [
|
const classMethods = [
|
||||||
associate,
|
associate,
|
||||||
loadAccountsByShare
|
loadAccountsByShare,
|
||||||
|
load
|
||||||
]
|
]
|
||||||
addMethodsToModel(VideoShare, classMethods)
|
addMethodsToModel(VideoShare, classMethods)
|
||||||
|
|
||||||
|
@ -50,6 +52,18 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
load = function (accountId: number, videoId: number) {
|
||||||
|
return VideoShare.findOne({
|
||||||
|
where: {
|
||||||
|
accountId,
|
||||||
|
videoId
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
VideoShare['sequelize'].models.Account
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
loadAccountsByShare = function (videoId: number) {
|
loadAccountsByShare = function (videoId: number) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import { isVideoNameValid, isVideoLicenceValid, isVideoNSFWValid, isVideoDescrip
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { generateImageFromVideoFile, transcode, getVideoFileHeight } from '../../helpers/ffmpeg-utils'
|
import { generateImageFromVideoFile, transcode, getVideoFileHeight } from '../../helpers/ffmpeg-utils'
|
||||||
import { createTorrentPromise, writeFilePromise, unlinkPromise, renamePromise, statPromise } from '../../helpers/core-utils'
|
import { createTorrentPromise, writeFilePromise, unlinkPromise, renamePromise, statPromise } from '../../helpers/core-utils'
|
||||||
|
import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
|
||||||
|
|
||||||
let Video: Sequelize.Model<VideoInstance, VideoAttributes>
|
let Video: Sequelize.Model<VideoInstance, VideoAttributes>
|
||||||
let getOriginalFile: VideoMethods.GetOriginalFile
|
let getOriginalFile: VideoMethods.GetOriginalFile
|
||||||
|
@ -573,6 +574,18 @@ toActivityPubObject = function (this: VideoInstance) {
|
||||||
dislikesObject = activityPubCollection(dislikes)
|
dislikesObject = activityPubCollection(dislikes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let sharesObject
|
||||||
|
if (Array.isArray(this.VideoShares)) {
|
||||||
|
const shares: string[] = []
|
||||||
|
|
||||||
|
for (const videoShare of this.VideoShares) {
|
||||||
|
const shareUrl = getAnnounceActivityPubUrl(this.url, videoShare.Account)
|
||||||
|
shares.push(shareUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
sharesObject = activityPubCollection(shares)
|
||||||
|
}
|
||||||
|
|
||||||
const url = []
|
const url = []
|
||||||
for (const file of this.VideoFiles) {
|
for (const file of this.VideoFiles) {
|
||||||
url.push({
|
url.push({
|
||||||
|
@ -630,7 +643,8 @@ toActivityPubObject = function (this: VideoInstance) {
|
||||||
},
|
},
|
||||||
url,
|
url,
|
||||||
likes: likesObject,
|
likes: likesObject,
|
||||||
dislikes: dislikesObject
|
dislikes: dislikesObject,
|
||||||
|
shares: sharesObject
|
||||||
}
|
}
|
||||||
|
|
||||||
return videoObject
|
return videoObject
|
||||||
|
@ -823,7 +837,8 @@ listAllAndSharedByAccountForOutbox = function (accountId: number, start: number,
|
||||||
accountId
|
accountId
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
include: [ Video['sequelize'].models.Account ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: Video['sequelize'].models.VideoChannel,
|
model: Video['sequelize'].models.VideoChannel,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { ActivityPubOrderedCollection } from '../activitypub-ordered-collection'
|
||||||
|
|
||||||
export interface VideoChannelObject {
|
export interface VideoChannelObject {
|
||||||
type: 'VideoChannel'
|
type: 'VideoChannel'
|
||||||
id: string
|
id: string
|
||||||
|
@ -7,4 +9,5 @@ export interface VideoChannelObject {
|
||||||
published: string
|
published: string
|
||||||
updated: string
|
updated: string
|
||||||
actor?: string
|
actor?: string
|
||||||
|
shares?: ActivityPubOrderedCollection<string>
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,4 +27,5 @@ export interface VideoTorrentObject {
|
||||||
actor?: string
|
actor?: string
|
||||||
likes?: ActivityPubOrderedCollection<string>
|
likes?: ActivityPubOrderedCollection<string>
|
||||||
dislikes?: ActivityPubOrderedCollection<string>
|
dislikes?: ActivityPubOrderedCollection<string>
|
||||||
|
shares?: ActivityPubOrderedCollection<string>
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue