From d7d5611c8a23de9b483f0437ad3469afef7b8805 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 16 Nov 2017 15:55:01 +0100 Subject: [PATCH] Federate video update --- server/lib/activitypub/misc.ts | 6 +---- server/lib/activitypub/process-add.ts | 13 +++++++---- server/lib/activitypub/process-create.ts | 2 +- server/lib/activitypub/process-delete.ts | 2 +- server/lib/activitypub/process-update.ts | 4 ++-- server/lib/activitypub/send-request.ts | 20 ++++++++++++---- server/models/account/account-follow.ts | 4 ++-- .../video/video-channel-share-interface.ts | 3 +++ server/models/video/video-channel-share.ts | 23 +++++++++++++++++-- server/models/video/video-interface.ts | 4 ++-- server/models/video/video-share-interface.ts | 3 +++ server/models/video/video-share.ts | 23 +++++++++++++++++-- server/models/video/video.ts | 21 +++++++++++++++++ 13 files changed, 102 insertions(+), 26 deletions(-) diff --git a/server/lib/activitypub/misc.ts b/server/lib/activitypub/misc.ts index 13838fc4c..f853d742e 100644 --- a/server/lib/activitypub/misc.ts +++ b/server/lib/activitypub/misc.ts @@ -25,12 +25,8 @@ function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChan async function videoActivityObjectToDBAttributes ( videoChannel: VideoChannelInstance, - videoObject: VideoTorrentObject, - t: Sequelize.Transaction + videoObject: VideoTorrentObject ) { - const videoFromDatabase = await db.Video.loadByUUIDOrURL(videoObject.uuid, videoObject.id, t) - if (videoFromDatabase) throw new Error('Video with this UUID/Url already exists.') - const duration = videoObject.duration.replace(/[^\d]+/, '') const videoData: VideoAttributes = { name: videoObject.name, diff --git a/server/lib/activitypub/process-add.ts b/server/lib/activitypub/process-add.ts index df7139d46..72c5b1932 100644 --- a/server/lib/activitypub/process-add.ts +++ b/server/lib/activitypub/process-add.ts @@ -1,12 +1,12 @@ +import * as Bluebird from 'bluebird' import { VideoTorrentObject } from '../../../shared' import { ActivityAdd } from '../../../shared/models/activitypub/activity' -import { generateThumbnailFromUrl, logger, retryTransactionWrapper, getOrCreateAccount } from '../../helpers' +import { generateThumbnailFromUrl, getOrCreateAccount, logger, retryTransactionWrapper } from '../../helpers' +import { getOrCreateVideoChannel } from '../../helpers/activitypub' 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' +import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' async function processAddActivity (activity: ActivityAdd) { const activityObject = activity.object @@ -51,7 +51,10 @@ function addRemoteVideo (account: AccountInstance, videoChannel: VideoChannelIns if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.') - const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, t) + const videoFromDatabase = await db.Video.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t) + if (videoFromDatabase) throw new Error('Video with this UUID/Url already exists.') + + const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData) const video = db.Video.build(videoData) // Don't block on request diff --git a/server/lib/activitypub/process-create.ts b/server/lib/activitypub/process-create.ts index c4706a66b..faea3f48a 100644 --- a/server/lib/activitypub/process-create.ts +++ b/server/lib/activitypub/process-create.ts @@ -69,7 +69,7 @@ function addRemoteVideoAbuse (account: AccountInstance, videoAbuseToCreateData: logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object) return db.sequelize.transaction(async t => { - const video = await db.Video.loadByUrl(videoAbuseToCreateData.object, t) + const video = await db.Video.loadByUrlAndPopulateAccount(videoAbuseToCreateData.object, t) if (!video) { logger.warn('Unknown video %s for remote video abuse.', videoAbuseToCreateData.object) return diff --git a/server/lib/activitypub/process-delete.ts b/server/lib/activitypub/process-delete.ts index 377df432d..d4487d2cc 100644 --- a/server/lib/activitypub/process-delete.ts +++ b/server/lib/activitypub/process-delete.ts @@ -15,7 +15,7 @@ async function processDeleteActivity (activity: ActivityDelete) { } { - let videoObject = await db.Video.loadByUrl(activity.id) + let videoObject = await db.Video.loadByUrlAndPopulateAccount(activity.id) if (videoObject !== undefined) { return processDeleteVideo(account, videoObject) } diff --git a/server/lib/activitypub/process-update.ts b/server/lib/activitypub/process-update.ts index cd8a4b8e2..4aefd1b9b 100644 --- a/server/lib/activitypub/process-update.ts +++ b/server/lib/activitypub/process-update.ts @@ -50,14 +50,14 @@ async function updateRemoteVideo (account: AccountInstance, videoAttributesToUpd transaction: t } - const videoInstance = await db.Video.loadByUrl(videoAttributesToUpdate.id, t) + const videoInstance = await db.Video.loadByUrlAndPopulateAccount(videoAttributesToUpdate.id, t) if (!videoInstance) throw new Error('Video ' + videoAttributesToUpdate.id + ' not found.') if (videoInstance.VideoChannel.Account.id !== account.id) { throw new Error('Account ' + account.url + ' does not own video channel ' + videoInstance.VideoChannel.url) } - const videoData = await videoActivityObjectToDBAttributes(videoInstance.VideoChannel, videoAttributesToUpdate, t) + const videoData = await videoActivityObjectToDBAttributes(videoInstance.VideoChannel, videoAttributesToUpdate) videoInstance.set('name', videoData.name) videoInstance.set('category', videoData.category) videoInstance.set('licence', videoData.licence) diff --git a/server/lib/activitypub/send-request.ts b/server/lib/activitypub/send-request.ts index f9b72f2a8..d5d07011a 100644 --- a/server/lib/activitypub/send-request.ts +++ b/server/lib/activitypub/send-request.ts @@ -24,13 +24,19 @@ async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Se const videoChannelObject = videoChannel.toActivityPubObject() const data = await updateActivityData(videoChannel.url, videoChannel.Account, videoChannelObject) - return broadcastToFollowers(data, [ videoChannel.Account ], t) + const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id) + accountsInvolved.push(videoChannel.Account) + + return broadcastToFollowers(data, accountsInvolved, t) } async function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) { const data = await deleteActivityData(videoChannel.url, videoChannel.Account) - return broadcastToFollowers(data, [ videoChannel.Account ], t) + const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id) + accountsInvolved.push(videoChannel.Account) + + return broadcastToFollowers(data, accountsInvolved, t) } async function sendAddVideo (video: VideoInstance, t: Sequelize.Transaction) { @@ -44,13 +50,19 @@ async function sendUpdateVideo (video: VideoInstance, t: Sequelize.Transaction) const videoObject = video.toActivityPubObject() const data = await updateActivityData(video.url, video.VideoChannel.Account, videoObject) - return broadcastToFollowers(data, [ video.VideoChannel.Account ], t) + const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id) + accountsInvolved.push(video.VideoChannel.Account) + + return broadcastToFollowers(data, accountsInvolved, t) } async function sendDeleteVideo (video: VideoInstance, t: Sequelize.Transaction) { const data = await deleteActivityData(video.url, video.VideoChannel.Account) - return broadcastToFollowers(data, [ video.VideoChannel.Account ], t) + const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id) + accountsInvolved.push(video.VideoChannel.Account) + + return broadcastToFollowers(data, accountsInvolved, t) } async function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transaction) { diff --git a/server/models/account/account-follow.ts b/server/models/account/account-follow.ts index 8a7474c9d..cc9b7c42b 100644 --- a/server/models/account/account-follow.ts +++ b/server/models/account/account-follow.ts @@ -187,13 +187,13 @@ async function createListAcceptedFollowForApiQuery ( let query = 'SELECT ' + selection + ' FROM "Accounts" ' + 'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' + 'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."id" ' + - 'WHERE "Accounts"."id" IN ($accountIds) AND "AccountFollows"."state" = \'accepted\' ' + 'WHERE "Accounts"."id" = ANY ($accountIds) AND "AccountFollows"."state" = \'accepted\' ' if (start !== undefined) query += 'LIMIT ' + start if (count !== undefined) query += ', ' + count const options = { - bind: { accountIds: accountIds.join(',') }, + bind: { accountIds }, type: Sequelize.QueryTypes.SELECT } tasks.push(AccountFollow['sequelize'].query(query, options)) diff --git a/server/models/video/video-channel-share-interface.ts b/server/models/video/video-channel-share-interface.ts index 9ac6d7b23..8bb531af2 100644 --- a/server/models/video/video-channel-share-interface.ts +++ b/server/models/video/video-channel-share-interface.ts @@ -1,11 +1,14 @@ +import * as Bluebird from 'bluebird' import * as Sequelize from 'sequelize' import { AccountInstance } from '../account/account-interface' import { VideoChannelInstance } from './video-channel-interface' export namespace VideoChannelShareMethods { + export type LoadAccountsByShare = (videoChannelId: number) => Bluebird } export interface VideoChannelShareClass { + loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare } export interface VideoChannelShareAttributes { diff --git a/server/models/video/video-channel-share.ts b/server/models/video/video-channel-share.ts index b6199279f..01f84c806 100644 --- a/server/models/video/video-channel-share.ts +++ b/server/models/video/video-channel-share.ts @@ -1,9 +1,10 @@ import * as Sequelize from 'sequelize' import { addMethodsToModel } from '../utils' -import { VideoChannelShareAttributes, VideoChannelShareInstance } from './video-channel-share-interface' +import { VideoChannelShareAttributes, VideoChannelShareInstance, VideoChannelShareMethods } from './video-channel-share-interface' let VideoChannelShare: Sequelize.Model +let loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { VideoChannelShare = sequelize.define('VideoChannelShare', @@ -21,7 +22,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da ) const classMethods = [ - associate + associate, + loadAccountsByShare ] addMethodsToModel(VideoChannelShare, classMethods) @@ -47,3 +49,20 @@ function associate (models) { onDelete: 'cascade' }) } + +loadAccountsByShare = function (videoChannelId: number) { + const query = { + where: { + videoChannelId + }, + include: [ + { + model: VideoChannelShare['sequelize'].models.Account, + required: true + } + ] + } + + return VideoChannelShare.findAll(query) + .then(res => res.map(r => r.Account)) +} diff --git a/server/models/video/video-interface.ts b/server/models/video/video-interface.ts index 4df33f801..9f29c842c 100644 --- a/server/models/video/video-interface.ts +++ b/server/models/video/video-interface.ts @@ -56,7 +56,7 @@ export namespace VideoMethods { export type Load = (id: number) => Bluebird export type LoadByUUID = (uuid: string, t?: Sequelize.Transaction) => Bluebird - export type LoadByUrl = (url: string, t?: Sequelize.Transaction) => Bluebird + export type LoadByUrlAndPopulateAccount = (url: string, t?: Sequelize.Transaction) => Bluebird export type LoadLocalVideoByUUID = (uuid: string, t?: Sequelize.Transaction) => Bluebird export type LoadByHostAndUUID = (fromHost: string, uuid: string, t?: Sequelize.Transaction) => Bluebird export type LoadAndPopulateAccount = (id: number) => Bluebird @@ -82,7 +82,7 @@ export interface VideoClass { loadAndPopulateAccountAndServerAndTags: VideoMethods.LoadAndPopulateAccountAndServerAndTags loadByHostAndUUID: VideoMethods.LoadByHostAndUUID loadByUUID: VideoMethods.LoadByUUID - loadByUrl: VideoMethods.LoadByUrl + loadByUrlAndPopulateAccount: VideoMethods.LoadByUrlAndPopulateAccount loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID loadByUUIDAndPopulateAccountAndServerAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndServerAndTags diff --git a/server/models/video/video-share-interface.ts b/server/models/video/video-share-interface.ts index 7928b9a9c..569568842 100644 --- a/server/models/video/video-share-interface.ts +++ b/server/models/video/video-share-interface.ts @@ -1,11 +1,14 @@ import * as Sequelize from 'sequelize' import { AccountInstance } from '../account/account-interface' import { VideoInstance } from './video-interface' +import * as Bluebird from 'bluebird' export namespace VideoShareMethods { + export type LoadAccountsByShare = (videoChannelId: number) => Bluebird } export interface VideoShareClass { + loadAccountsByShare: VideoShareMethods.LoadAccountsByShare } export interface VideoShareAttributes { diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts index 358491fd2..22ac31a4a 100644 --- a/server/models/video/video-share.ts +++ b/server/models/video/video-share.ts @@ -1,9 +1,10 @@ import * as Sequelize from 'sequelize' import { addMethodsToModel } from '../utils' -import { VideoShareAttributes, VideoShareInstance } from './video-share-interface' +import { VideoShareAttributes, VideoShareInstance, VideoShareMethods } from './video-share-interface' let VideoShare: Sequelize.Model +let loadAccountsByShare: VideoShareMethods.LoadAccountsByShare export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { VideoShare = sequelize.define('VideoShare', @@ -21,7 +22,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da ) const classMethods = [ - associate + associate, + loadAccountsByShare ] addMethodsToModel(VideoShare, classMethods) @@ -47,3 +49,20 @@ function associate (models) { onDelete: 'cascade' }) } + +loadAccountsByShare = function (videoId: number) { + const query = { + where: { + videoId + }, + include: [ + { + model: VideoShare['sequelize'].models.Account, + required: true + } + ] + } + + return VideoShare.findAll(query) + .then(res => res.map(r => r.Account)) +} diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 64ee7ae34..5b0377c2e 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -84,6 +84,7 @@ let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID let listOwnedAndPopulateAccountAndTags: VideoMethods.ListOwnedAndPopulateAccountAndTags let listOwnedByAccount: VideoMethods.ListOwnedByAccount let load: VideoMethods.Load +let loadByUrlAndPopulateAccount: VideoMethods.LoadByUrlAndPopulateAccount let loadByUUID: VideoMethods.LoadByUUID let loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL let loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID @@ -271,6 +272,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da listOwnedAndPopulateAccountAndTags, listOwnedByAccount, load, + loadByUrlAndPopulateAccount, loadAndPopulateAccount, loadAndPopulateAccountAndServerAndTags, loadByHostAndUUID, @@ -936,6 +938,25 @@ loadByUUID = function (uuid: string, t?: Sequelize.Transaction) { return Video.findOne(query) } +loadByUrlAndPopulateAccount = function (url: string, t?: Sequelize.Transaction) { + const query: Sequelize.FindOptions = { + where: { + url + }, + include: [ + Video['sequelize'].models.VideoFile, + { + model: Video['sequelize'].models.VideoChannel, + include: [ Video['sequelize'].models.Account ] + } + ] + } + + if (t !== undefined) query.transaction = t + + return Video.findOne(query) +} + loadByUUIDOrURL = function (uuid: string, url: string, t?: Sequelize.Transaction) { const query: Sequelize.FindOptions = { where: { -- 2.41.0