Federate video update

pull/128/head
Chocobozzz 2017-11-16 15:55:01 +01:00
parent 20494f1221
commit d7d5611c8a
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
13 changed files with 102 additions and 26 deletions

View File

@ -25,12 +25,8 @@ function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChan
async function videoActivityObjectToDBAttributes ( async function videoActivityObjectToDBAttributes (
videoChannel: VideoChannelInstance, videoChannel: VideoChannelInstance,
videoObject: VideoTorrentObject, videoObject: VideoTorrentObject
t: Sequelize.Transaction
) { ) {
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 duration = videoObject.duration.replace(/[^\d]+/, '')
const videoData: VideoAttributes = { const videoData: VideoAttributes = {
name: videoObject.name, name: videoObject.name,

View File

@ -1,12 +1,12 @@
import * as Bluebird from 'bluebird'
import { VideoTorrentObject } from '../../../shared' import { VideoTorrentObject } from '../../../shared'
import { ActivityAdd } from '../../../shared/models/activitypub/activity' 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 { database as db } from '../../initializers'
import { AccountInstance } from '../../models/account/account-interface' 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 { VideoChannelInstance } from '../../models/video/video-channel-interface'
import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
async function processAddActivity (activity: ActivityAdd) { async function processAddActivity (activity: ActivityAdd) {
const activityObject = activity.object 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.') 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) const video = db.Video.build(videoData)
// Don't block on request // Don't block on request

View File

@ -69,7 +69,7 @@ function addRemoteVideoAbuse (account: AccountInstance, videoAbuseToCreateData:
logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object) logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object)
return db.sequelize.transaction(async t => { 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) { if (!video) {
logger.warn('Unknown video %s for remote video abuse.', videoAbuseToCreateData.object) logger.warn('Unknown video %s for remote video abuse.', videoAbuseToCreateData.object)
return return

View File

@ -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) { if (videoObject !== undefined) {
return processDeleteVideo(account, videoObject) return processDeleteVideo(account, videoObject)
} }

View File

@ -50,14 +50,14 @@ async function updateRemoteVideo (account: AccountInstance, videoAttributesToUpd
transaction: t 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) throw new Error('Video ' + videoAttributesToUpdate.id + ' not found.')
if (videoInstance.VideoChannel.Account.id !== account.id) { if (videoInstance.VideoChannel.Account.id !== account.id) {
throw new Error('Account ' + account.url + ' does not own video channel ' + videoInstance.VideoChannel.url) 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('name', videoData.name)
videoInstance.set('category', videoData.category) videoInstance.set('category', videoData.category)
videoInstance.set('licence', videoData.licence) videoInstance.set('licence', videoData.licence)

View File

@ -24,13 +24,19 @@ async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Se
const videoChannelObject = videoChannel.toActivityPubObject() const videoChannelObject = videoChannel.toActivityPubObject()
const data = await updateActivityData(videoChannel.url, videoChannel.Account, videoChannelObject) 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) { async function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
const data = await deleteActivityData(videoChannel.url, videoChannel.Account) 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) { 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 videoObject = video.toActivityPubObject()
const data = await updateActivityData(video.url, video.VideoChannel.Account, videoObject) 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) { async function sendDeleteVideo (video: VideoInstance, t: Sequelize.Transaction) {
const data = await deleteActivityData(video.url, video.VideoChannel.Account) 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) { async function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transaction) {

View File

@ -187,13 +187,13 @@ async function createListAcceptedFollowForApiQuery (
let query = 'SELECT ' + selection + ' FROM "Accounts" ' + let query = 'SELECT ' + selection + ' FROM "Accounts" ' +
'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' + 'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' +
'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."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 (start !== undefined) query += 'LIMIT ' + start
if (count !== undefined) query += ', ' + count if (count !== undefined) query += ', ' + count
const options = { const options = {
bind: { accountIds: accountIds.join(',') }, bind: { accountIds },
type: Sequelize.QueryTypes.SELECT type: Sequelize.QueryTypes.SELECT
} }
tasks.push(AccountFollow['sequelize'].query(query, options)) tasks.push(AccountFollow['sequelize'].query(query, options))

View File

@ -1,11 +1,14 @@
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 { VideoChannelInstance } from './video-channel-interface' import { VideoChannelInstance } from './video-channel-interface'
export namespace VideoChannelShareMethods { export namespace VideoChannelShareMethods {
export type LoadAccountsByShare = (videoChannelId: number) => Bluebird<AccountInstance[]>
} }
export interface VideoChannelShareClass { export interface VideoChannelShareClass {
loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare
} }
export interface VideoChannelShareAttributes { export interface VideoChannelShareAttributes {

View File

@ -1,9 +1,10 @@
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import { addMethodsToModel } from '../utils' 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<VideoChannelShareInstance, VideoChannelShareAttributes> let VideoChannelShare: Sequelize.Model<VideoChannelShareInstance, VideoChannelShareAttributes>
let loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare
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',
@ -21,7 +22,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
) )
const classMethods = [ const classMethods = [
associate associate,
loadAccountsByShare
] ]
addMethodsToModel(VideoChannelShare, classMethods) addMethodsToModel(VideoChannelShare, classMethods)
@ -47,3 +49,20 @@ function associate (models) {
onDelete: 'cascade' 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))
}

View File

@ -56,7 +56,7 @@ export namespace VideoMethods {
export type Load = (id: number) => Bluebird<VideoInstance> export type Load = (id: number) => Bluebird<VideoInstance>
export type LoadByUUID = (uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance> export type LoadByUUID = (uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance>
export type LoadByUrl = (url: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance> export type LoadByUrlAndPopulateAccount = (url: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance>
export type LoadLocalVideoByUUID = (uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance> export type LoadLocalVideoByUUID = (uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance>
export type LoadByHostAndUUID = (fromHost: string, uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance> export type LoadByHostAndUUID = (fromHost: string, uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance>
export type LoadAndPopulateAccount = (id: number) => Bluebird<VideoInstance> export type LoadAndPopulateAccount = (id: number) => Bluebird<VideoInstance>
@ -82,7 +82,7 @@ export interface VideoClass {
loadAndPopulateAccountAndServerAndTags: VideoMethods.LoadAndPopulateAccountAndServerAndTags loadAndPopulateAccountAndServerAndTags: VideoMethods.LoadAndPopulateAccountAndServerAndTags
loadByHostAndUUID: VideoMethods.LoadByHostAndUUID loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
loadByUUID: VideoMethods.LoadByUUID loadByUUID: VideoMethods.LoadByUUID
loadByUrl: VideoMethods.LoadByUrl loadByUrlAndPopulateAccount: VideoMethods.LoadByUrlAndPopulateAccount
loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL
loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID
loadByUUIDAndPopulateAccountAndServerAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndServerAndTags loadByUUIDAndPopulateAccountAndServerAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndServerAndTags

View File

@ -1,11 +1,14 @@
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 interface VideoShareClass { export interface VideoShareClass {
loadAccountsByShare: VideoShareMethods.LoadAccountsByShare
} }
export interface VideoShareAttributes { export interface VideoShareAttributes {

View File

@ -1,9 +1,10 @@
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import { addMethodsToModel } from '../utils' import { addMethodsToModel } from '../utils'
import { VideoShareAttributes, VideoShareInstance } from './video-share-interface' import { VideoShareAttributes, VideoShareInstance, VideoShareMethods } from './video-share-interface'
let VideoShare: Sequelize.Model<VideoShareInstance, VideoShareAttributes> let VideoShare: Sequelize.Model<VideoShareInstance, VideoShareAttributes>
let loadAccountsByShare: VideoShareMethods.LoadAccountsByShare
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',
@ -21,7 +22,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
) )
const classMethods = [ const classMethods = [
associate associate,
loadAccountsByShare
] ]
addMethodsToModel(VideoShare, classMethods) addMethodsToModel(VideoShare, classMethods)
@ -47,3 +49,20 @@ function associate (models) {
onDelete: 'cascade' 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))
}

View File

@ -84,6 +84,7 @@ let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
let listOwnedAndPopulateAccountAndTags: VideoMethods.ListOwnedAndPopulateAccountAndTags let listOwnedAndPopulateAccountAndTags: VideoMethods.ListOwnedAndPopulateAccountAndTags
let listOwnedByAccount: VideoMethods.ListOwnedByAccount let listOwnedByAccount: VideoMethods.ListOwnedByAccount
let load: VideoMethods.Load let load: VideoMethods.Load
let loadByUrlAndPopulateAccount: VideoMethods.LoadByUrlAndPopulateAccount
let loadByUUID: VideoMethods.LoadByUUID let loadByUUID: VideoMethods.LoadByUUID
let loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL let loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL
let loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID let loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID
@ -271,6 +272,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
listOwnedAndPopulateAccountAndTags, listOwnedAndPopulateAccountAndTags,
listOwnedByAccount, listOwnedByAccount,
load, load,
loadByUrlAndPopulateAccount,
loadAndPopulateAccount, loadAndPopulateAccount,
loadAndPopulateAccountAndServerAndTags, loadAndPopulateAccountAndServerAndTags,
loadByHostAndUUID, loadByHostAndUUID,
@ -936,6 +938,25 @@ loadByUUID = function (uuid: string, t?: Sequelize.Transaction) {
return Video.findOne(query) return Video.findOne(query)
} }
loadByUrlAndPopulateAccount = function (url: string, t?: Sequelize.Transaction) {
const query: Sequelize.FindOptions<VideoAttributes> = {
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) { loadByUUIDOrURL = function (uuid: string, url: string, t?: Sequelize.Transaction) {
const query: Sequelize.FindOptions<VideoAttributes> = { const query: Sequelize.FindOptions<VideoAttributes> = {
where: { where: {