import { processCreateActivity, processFlagActivity, processUpdateActivity } from '../../lib'
import { processAcceptActivity } from '../../lib/activitypub/process-accept'
import { processAddActivity } from '../../lib/activitypub/process-add'
+import { processAnnounceActivity } from '../../lib/activitypub/process-announce'
import { processDeleteActivity } from '../../lib/activitypub/process-delete'
import { processFollowActivity } from '../../lib/activitypub/process-follow'
import { asyncMiddleware, checkSignature, localAccountValidator, signatureValidator } from '../../middlewares'
Flag: processFlagActivity,
Delete: processDeleteActivity,
Follow: processFollowActivity,
- Accept: processAcceptActivity
+ Accept: processAcceptActivity,
+ Announce: processAnnounceActivity
}
const inboxRouter = express.Router()
import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './account'
import { isActivityPubUrlValid } from './misc'
import {
+ isVideoAnnounceValid,
+ isVideoChannelAnnounceValid,
isVideoChannelCreateActivityValid,
isVideoChannelDeleteActivityValid,
isVideoChannelUpdateActivityValid,
+ isVideoFlagValid,
isVideoTorrentAddActivityValid,
isVideoTorrentDeleteActivityValid,
isVideoTorrentUpdateActivityValid
isVideoChannelDeleteActivityValid(activity) ||
isAccountDeleteActivityValid(activity) ||
isAccountFollowActivityValid(activity) ||
- isAccountAcceptActivityValid(activity)
+ isAccountAcceptActivityValid(activity) ||
+ isVideoFlagValid(activity) ||
+ isVideoAnnounceValid(activity) ||
+ isVideoChannelAnnounceValid(activity)
}
// ---------------------------------------------------------------------------
import { exists, isDateValid, isUUIDValid } from '../misc'
import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
import {
+ isVideoAbuseReasonValid,
isVideoDurationValid,
isVideoNameValid,
isVideoNSFWValid,
isVideoUrlValid,
isVideoViewsValid
} from '../videos'
-import { isBaseActivityValid } from './misc'
+import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
function isVideoTorrentAddActivityValid (activity: any) {
return isBaseActivityValid(activity, 'Add') &&
setValidRemoteVideoUrls(video.url)
}
+function isVideoFlagValid (activity: any) {
+ return isBaseActivityValid(activity, 'Flag') &&
+ isVideoAbuseReasonValid(activity.content) &&
+ isActivityPubUrlValid(activity.object)
+}
+
+function isVideoAnnounceValid (activity: any) {
+ return isBaseActivityValid(activity, 'Announce') &&
+ isVideoTorrentObjectValid(activity.object)
+}
+
+function isVideoChannelAnnounceValid (activity: any) {
+ return isBaseActivityValid(activity, 'Announce') &&
+ isVideoChannelObjectValid(activity.object)
+}
+
function isVideoChannelCreateActivityValid (activity: any) {
return isBaseActivityValid(activity, 'Create') &&
isVideoChannelObjectValid(activity.object)
isVideoTorrentUpdateActivityValid,
isVideoChannelUpdateActivityValid,
isVideoChannelDeleteActivityValid,
- isVideoTorrentDeleteActivityValid
+ isVideoTorrentDeleteActivityValid,
+ isVideoFlagValid,
+ isVideoAnnounceValid,
+ isVideoChannelAnnounceValid
}
// ---------------------------------------------------------------------------
import { JobModel } from './../models/job/job-interface'
import { AccountModel } from './../models/account/account-interface'
import { ApplicationModel } from './../models/application/application-interface'
+import { VideoChannelShareModel } from '../models/video/video-channel-share-interface'
+import { VideoShareModel } from '../models/video/video-share-interface'
const dbname = CONFIG.DATABASE.DBNAME
const username = CONFIG.DATABASE.USERNAME
User?: UserModel,
VideoAbuse?: VideoAbuseModel,
VideoChannel?: VideoChannelModel,
+ VideoChannelShare?: VideoChannelShareModel,
+ VideoShare?: VideoShareModel,
VideoFile?: VideoFileModel,
BlacklistedVideo?: BlacklistedVideoModel,
VideoTag?: VideoTagModel,
+export * from './process-accept'
+export * from './process-add'
+export * from './process-announce'
export * from './process-create'
+export * from './process-delete'
export * from './process-flag'
+export * from './process-follow'
export * from './process-update'
export * from './send-request'
async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string, videoToCreateData: VideoTorrentObject) {
logger.debug('Adding remote video %s.', videoToCreateData.url)
- await db.sequelize.transaction(async t => {
+ return db.sequelize.transaction(async t => {
const sequelizeOptions = {
transaction: t
}
const tags = videoToCreateData.tag.map(t => t.name)
const tagInstances = await db.Tag.findOrCreateTags(tags, t)
await videoCreated.setTags(tagInstances, sequelizeOptions)
+
+ logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid)
+
+ return videoCreated
})
- logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid)
}
--- /dev/null
+import { ActivityAnnounce } from '../../../shared/models/activitypub/activity'
+import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object'
+import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
+import { logger } from '../../helpers/logger'
+import { processAddActivity } from './process-add'
+import { processCreateActivity } from './process-create'
+import { database as db } from '../../initializers/index'
+import { getOrCreateAccount } from '../../helpers/activitypub'
+import { VideoChannelInstance } from '../../models/video/video-channel-interface'
+import { VideoInstance } from '../../models/index'
+
+async function processAnnounceActivity (activity: ActivityAnnounce) {
+ const activityType = activity.object.type
+ const accountAnnouncer = await getOrCreateAccount(activity.actor)
+
+ if (activityType === 'VideoChannel') {
+ const activityCreate = Object.assign(activity, {
+ type: 'Create' as 'Create',
+ actor: activity.object.actor,
+ object: activity.object as VideoChannelObject
+ })
+
+ // Add share entry
+ const videoChannel: VideoChannelInstance = await processCreateActivity(activityCreate)
+ await db.VideoChannelShare.create({
+ accountId: accountAnnouncer.id,
+ videoChannelId: videoChannel.id
+ })
+ } else if (activityType === 'Video') {
+ const activityAdd = Object.assign(activity, {
+ type: 'Add' as 'Add',
+ actor: activity.object.actor,
+ object: activity.object as VideoTorrentObject
+ })
+
+ // Add share entry
+ const video: VideoInstance = await processAddActivity(activityAdd)
+ await db.VideoShare.create({
+ accountId: accountAnnouncer.id,
+ videoId: video.id
+ })
+ }
+
+ logger.warn('Unknown activity object type %s when announcing activity.', activityType, { activity: activity.id })
+ return Promise.resolve(undefined)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ processAnnounceActivity
+}
async function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid)
- await db.sequelize.transaction(async t => {
+ return db.sequelize.transaction(async t => {
let videoChannel = await db.VideoChannel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t)
if (videoChannel) throw new Error('Video channel with this URL/UUID already exists.')
videoChannel = db.VideoChannel.build(videoChannelData)
videoChannel.url = getActivityPubUrl('videoChannel', videoChannel.uuid)
- await videoChannel.save({ transaction: t })
- })
+ videoChannel = await videoChannel.save({ transaction: t })
+ logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid)
- logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid)
+ return videoChannel
+ })
}
function processCreateVideoAbuse (account: AccountInstance, videoAbuseToCreateData: VideoAbuseObject) {
export * from './video-tag-interface'
export * from './video-file-interface'
export * from './video-interface'
+export * from './video-share-interface'
+export * from './video-channel-share-interface'
--- /dev/null
+import * as Sequelize from 'sequelize'
+import { AccountInstance } from '../account/account-interface'
+import { VideoChannelInstance } from './video-channel-interface'
+
+export namespace VideoChannelShareMethods {
+}
+
+export interface VideoChannelShareClass {
+}
+
+export interface VideoChannelShareAttributes {
+ accountId: number
+ videoChannelId: number
+}
+
+export interface VideoChannelShareInstance
+ extends VideoChannelShareClass, VideoChannelShareAttributes, Sequelize.Instance<VideoChannelShareAttributes> {
+ id: number
+ createdAt: Date
+ updatedAt: Date
+
+ Account?: AccountInstance
+ VideoChannel?: VideoChannelInstance
+}
+
+export interface VideoChannelShareModel
+ extends VideoChannelShareClass, Sequelize.Model<VideoChannelShareInstance, VideoChannelShareAttributes> {}
--- /dev/null
+import * as Sequelize from 'sequelize'
+
+import { addMethodsToModel } from '../utils'
+import { VideoChannelShareAttributes, VideoChannelShareInstance } from './video-channel-share-interface'
+
+let VideoChannelShare: Sequelize.Model<VideoChannelShareInstance, VideoChannelShareAttributes>
+
+export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
+ VideoChannelShare = sequelize.define<VideoChannelShareInstance, VideoChannelShareAttributes>('VideoChannelShare',
+ { },
+ {
+ indexes: [
+ {
+ fields: [ 'accountId' ]
+ },
+ {
+ fields: [ 'videoChannelId' ]
+ }
+ ]
+ }
+ )
+
+ const classMethods = [
+ associate
+ ]
+ addMethodsToModel(VideoChannelShare, classMethods)
+
+ return VideoChannelShare
+}
+
+// ------------------------------ METHODS ------------------------------
+
+function associate (models) {
+ VideoChannelShare.belongsTo(models.Account, {
+ foreignKey: {
+ name: 'accountId',
+ allowNull: false
+ },
+ onDelete: 'cascade'
+ })
+
+ VideoChannelShare.belongsTo(models.VideoChannel, {
+ foreignKey: {
+ name: 'videoChannelId',
+ allowNull: true
+ },
+ onDelete: 'cascade'
+ })
+}
--- /dev/null
+import * as Sequelize from 'sequelize'
+import { AccountInstance } from '../account/account-interface'
+import { VideoInstance } from './video-interface'
+
+export namespace VideoShareMethods {
+}
+
+export interface VideoShareClass {
+}
+
+export interface VideoShareAttributes {
+ accountId: number
+ videoId: number
+}
+
+export interface VideoShareInstance extends VideoShareClass, VideoShareAttributes, Sequelize.Instance<VideoShareAttributes> {
+ id: number
+ createdAt: Date
+ updatedAt: Date
+
+ Account?: AccountInstance
+ Video?: VideoInstance
+}
+
+export interface VideoShareModel extends VideoShareClass, Sequelize.Model<VideoShareInstance, VideoShareAttributes> {}
--- /dev/null
+import * as Sequelize from 'sequelize'
+
+import { addMethodsToModel } from '../utils'
+import { VideoShareAttributes, VideoShareInstance } from './video-share-interface'
+
+let VideoShare: Sequelize.Model<VideoShareInstance, VideoShareAttributes>
+
+export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
+ VideoShare = sequelize.define<VideoShareInstance, VideoShareAttributes>('VideoShare',
+ { },
+ {
+ indexes: [
+ {
+ fields: [ 'accountId' ]
+ },
+ {
+ fields: [ 'videoId' ]
+ }
+ ]
+ }
+ )
+
+ const classMethods = [
+ associate
+ ]
+ addMethodsToModel(VideoShare, classMethods)
+
+ return VideoShare
+}
+
+// ------------------------------ METHODS ------------------------------
+
+function associate (models) {
+ VideoShare.belongsTo(models.Account, {
+ foreignKey: {
+ name: 'accountId',
+ allowNull: false
+ },
+ onDelete: 'cascade'
+ })
+
+ VideoShare.belongsTo(models.Video, {
+ foreignKey: {
+ name: 'videoId',
+ allowNull: true
+ },
+ onDelete: 'cascade'
+ })
+}
},
{
fields: [ 'channelId' ]
- },
- {
- fields: [ 'parentId' ]
}
],
hooks: {
onDelete: 'cascade'
})
- Video.belongsTo(models.Video, {
- foreignKey: {
- name: 'parentId',
- allowNull: true
- },
- onDelete: 'cascade'
- })
-
Video.belongsToMany(models.Tag, {
foreignKey: 'videoId',
through: models.VideoTag,
import { VideoAbuseObject } from './objects/video-abuse-object'
export type Activity = ActivityCreate | ActivityAdd | ActivityUpdate | ActivityFlag |
- ActivityDelete | ActivityFollow | ActivityAccept
+ ActivityDelete | ActivityFollow | ActivityAccept | ActivityAnnounce
// Flag -> report abuse
-export type ActivityType = 'Create' | 'Add' | 'Update' | 'Flag' | 'Delete' | 'Follow' | 'Accept'
+export type ActivityType = 'Create' | 'Add' | 'Update' | 'Flag' | 'Delete' | 'Follow' | 'Accept' | 'Announce'
export interface BaseActivity {
'@context'?: any[]
export interface ActivityAccept extends BaseActivity {
type: 'Accept'
}
+
+export interface ActivityAnnounce extends BaseActivity {
+ type: 'Announce'
+ object: VideoChannelObject | VideoTorrentObject
+}
uuid: string
published: Date
updated: Date
+ actor?: string
}
content: string
icon: ActivityIconObject
url: ActivityUrlObject[]
+ actor?: string
}