Send server announce when users upload a video
This commit is contained in:
parent
d846501818
commit
efc32059d9
22 changed files with 161 additions and 80 deletions
|
@ -257,6 +257,7 @@ export class AuthService {
|
||||||
this.user.save()
|
this.user.save()
|
||||||
|
|
||||||
this.setStatus(AuthStatus.LoggedIn)
|
this.setStatus(AuthStatus.LoggedIn)
|
||||||
|
this.userInformationLoaded.next(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleRefreshToken (obj: UserRefreshToken) {
|
private handleRefreshToken (obj: UserRefreshToken) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ async function accountFollowersController (req: express.Request, res: express.Re
|
||||||
const page = req.params.page || 1
|
const page = req.params.page || 1
|
||||||
const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
|
const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
|
||||||
|
|
||||||
const result = await db.AccountFollow.listAcceptedFollowerUrlsForApi(account.id, start, count)
|
const result = await db.AccountFollow.listAcceptedFollowerUrlsForApi([ account.id ], start, count)
|
||||||
const activityPubResult = activityPubCollectionPagination(req.url, page, result)
|
const activityPubResult = activityPubCollectionPagination(req.url, page, result)
|
||||||
|
|
||||||
return res.json(activityPubResult)
|
return res.json(activityPubResult)
|
||||||
|
@ -58,7 +58,7 @@ async function accountFollowingController (req: express.Request, res: express.Re
|
||||||
const page = req.params.page || 1
|
const page = req.params.page || 1
|
||||||
const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
|
const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
|
||||||
|
|
||||||
const result = await db.AccountFollow.listAcceptedFollowingUrlsForApi(account.id, start, count)
|
const result = await db.AccountFollow.listAcceptedFollowingUrlsForApi([ account.id ], start, count)
|
||||||
const activityPubResult = activityPubCollectionPagination(req.url, page, result)
|
const activityPubResult = activityPubCollectionPagination(req.url, page, result)
|
||||||
|
|
||||||
return res.json(activityPubResult)
|
return res.json(activityPubResult)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as express from 'express'
|
||||||
import { UserRight } from '../../../../shared/models/users/user-right.enum'
|
import { UserRight } from '../../../../shared/models/users/user-right.enum'
|
||||||
import { getFormattedObjects } from '../../../helpers'
|
import { getFormattedObjects } from '../../../helpers'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { getApplicationAccount } from '../../../helpers/utils'
|
import { getServerAccount } from '../../../helpers/utils'
|
||||||
import { getAccountFromWebfinger } from '../../../helpers/webfinger'
|
import { getAccountFromWebfinger } from '../../../helpers/webfinger'
|
||||||
import { SERVER_ACCOUNT_NAME } from '../../../initializers/constants'
|
import { SERVER_ACCOUNT_NAME } from '../../../initializers/constants'
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
|
@ -50,14 +50,14 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function listFollowing (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function listFollowing (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const applicationAccount = await getApplicationAccount()
|
const applicationAccount = await getServerAccount()
|
||||||
const resultList = await db.AccountFollow.listFollowingForApi(applicationAccount.id, req.query.start, req.query.count, req.query.sort)
|
const resultList = await db.AccountFollow.listFollowingForApi(applicationAccount.id, req.query.start, req.query.count, req.query.sort)
|
||||||
|
|
||||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listFollowers (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function listFollowers (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const applicationAccount = await getApplicationAccount()
|
const applicationAccount = await getServerAccount()
|
||||||
const resultList = await db.AccountFollow.listFollowersForApi(applicationAccount.id, req.query.start, req.query.count, req.query.sort)
|
const resultList = await db.AccountFollow.listFollowersForApi(applicationAccount.id, req.query.start, req.query.count, req.query.sort)
|
||||||
|
|
||||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||||
|
@ -65,7 +65,7 @@ async function listFollowers (req: express.Request, res: express.Response, next:
|
||||||
|
|
||||||
async function follow (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function follow (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const hosts = req.body.hosts as string[]
|
const hosts = req.body.hosts as string[]
|
||||||
const fromAccount = await getApplicationAccount()
|
const fromAccount = await getServerAccount()
|
||||||
|
|
||||||
const tasks: Promise<any>[] = []
|
const tasks: Promise<any>[] = []
|
||||||
const accountName = SERVER_ACCOUNT_NAME
|
const accountName = SERVER_ACCOUNT_NAME
|
||||||
|
|
|
@ -12,10 +12,10 @@ import {
|
||||||
resetSequelizeInstance,
|
resetSequelizeInstance,
|
||||||
retryTransactionWrapper
|
retryTransactionWrapper
|
||||||
} from '../../../helpers'
|
} from '../../../helpers'
|
||||||
import { getActivityPubUrl } from '../../../helpers/activitypub'
|
import { getActivityPubUrl, shareVideoByServer } from '../../../helpers/activitypub'
|
||||||
import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers'
|
import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers'
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import { sendAddVideo, sendUpdateVideoChannel } from '../../../lib/activitypub/send-request'
|
import { sendAddVideo, sendUpdateVideo } from '../../../lib/activitypub/send-request'
|
||||||
import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler/transcoding-job-scheduler'
|
import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler/transcoding-job-scheduler'
|
||||||
import {
|
import {
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
|
@ -56,7 +56,7 @@ const storage = multer.diskStorage({
|
||||||
randomString = 'fake-random-string'
|
randomString = 'fake-random-string'
|
||||||
}
|
}
|
||||||
|
|
||||||
cb(null, randomString + '.' + extension)
|
cb(null, randomString + extension)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -237,6 +237,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
|
||||||
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
|
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
|
||||||
|
|
||||||
await sendAddVideo(video, t)
|
await sendAddVideo(video, t)
|
||||||
|
await shareVideoByServer(video, t)
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoUUID)
|
logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoUUID)
|
||||||
|
@ -254,7 +255,7 @@ async function updateVideoRetryWrapper (req: express.Request, res: express.Respo
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateVideo (req: express.Request, res: express.Response) {
|
async function updateVideo (req: express.Request, res: express.Response) {
|
||||||
const videoInstance = res.locals.video
|
const videoInstance: VideoInstance = res.locals.video
|
||||||
const videoFieldsSave = videoInstance.toJSON()
|
const videoFieldsSave = videoInstance.toJSON()
|
||||||
const videoInfoToUpdate: VideoUpdate = req.body
|
const videoInfoToUpdate: VideoUpdate = req.body
|
||||||
const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE
|
const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE
|
||||||
|
@ -284,7 +285,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
||||||
|
|
||||||
// Now we'll update the video's meta data to our friends
|
// Now we'll update the video's meta data to our friends
|
||||||
if (wasPrivateVideo === false) {
|
if (wasPrivateVideo === false) {
|
||||||
await sendUpdateVideoChannel(videoInstance, t)
|
await sendUpdateVideo(videoInstance, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Video is not private anymore, send a create action to remote servers
|
// Video is not private anymore, send a create action to remote servers
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import * as request from 'request'
|
import * as request from 'request'
|
||||||
|
import * as Sequelize from 'sequelize'
|
||||||
import * as url from 'url'
|
import * as url from 'url'
|
||||||
import { ActivityIconObject } from '../../shared/index'
|
import { ActivityIconObject } from '../../shared/index'
|
||||||
import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor'
|
import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor'
|
||||||
import { ResultList } from '../../shared/models/result-list.model'
|
import { ResultList } from '../../shared/models/result-list.model'
|
||||||
import { database as db, REMOTE_SCHEME } from '../initializers'
|
import { database as db, REMOTE_SCHEME } from '../initializers'
|
||||||
import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants'
|
import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants'
|
||||||
|
import { sendAnnounce } from '../lib/activitypub/send-request'
|
||||||
|
import { VideoChannelInstance } from '../models/video/video-channel-interface'
|
||||||
import { VideoInstance } from '../models/video/video-interface'
|
import { VideoInstance } from '../models/video/video-interface'
|
||||||
import { isRemoteAccountValid } from './custom-validators'
|
import { isRemoteAccountValid } from './custom-validators'
|
||||||
import { logger } from './logger'
|
import { logger } from './logger'
|
||||||
import { doRequest, doRequestAndSaveToFile } from './requests'
|
import { doRequest, doRequestAndSaveToFile } from './requests'
|
||||||
|
import { getServerAccount } from './utils'
|
||||||
|
|
||||||
function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) {
|
function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) {
|
||||||
const thumbnailName = video.getThumbnailName()
|
const thumbnailName = video.getThumbnailName()
|
||||||
|
@ -22,6 +26,28 @@ function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObjec
|
||||||
return doRequestAndSaveToFile(options, thumbnailPath)
|
return doRequestAndSaveToFile(options, thumbnailPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
|
||||||
|
const serverAccount = await getServerAccount()
|
||||||
|
|
||||||
|
await db.VideoChannelShare.create({
|
||||||
|
accountId: serverAccount.id,
|
||||||
|
videoChannelId: videoChannel.id
|
||||||
|
}, { transaction: t })
|
||||||
|
|
||||||
|
return sendAnnounce(serverAccount, videoChannel, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transaction) {
|
||||||
|
const serverAccount = await getServerAccount()
|
||||||
|
|
||||||
|
await db.VideoShare.create({
|
||||||
|
accountId: serverAccount.id,
|
||||||
|
videoId: video.id
|
||||||
|
}, { transaction: t })
|
||||||
|
|
||||||
|
return sendAnnounce(serverAccount, video, t)
|
||||||
|
}
|
||||||
|
|
||||||
function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account' | 'videoAbuse', id: string) {
|
function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account' | 'videoAbuse', id: string) {
|
||||||
if (type === 'video') return CONFIG.WEBSERVER.URL + '/videos/watch/' + id
|
if (type === 'video') return CONFIG.WEBSERVER.URL + '/videos/watch/' + id
|
||||||
else if (type === 'videoChannel') return CONFIG.WEBSERVER.URL + '/video-channels/' + id
|
else if (type === 'videoChannel') return CONFIG.WEBSERVER.URL + '/video-channels/' + id
|
||||||
|
@ -172,7 +198,9 @@ export {
|
||||||
generateThumbnailFromUrl,
|
generateThumbnailFromUrl,
|
||||||
getOrCreateAccount,
|
getOrCreateAccount,
|
||||||
fetchRemoteVideoPreview,
|
fetchRemoteVideoPreview,
|
||||||
fetchRemoteVideoDescription
|
fetchRemoteVideoDescription,
|
||||||
|
shareVideoChannelByServer,
|
||||||
|
shareVideoByServer
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -20,9 +20,6 @@ import * as bcrypt from 'bcrypt'
|
||||||
import * as createTorrent from 'create-torrent'
|
import * as createTorrent from 'create-torrent'
|
||||||
import * as rimraf from 'rimraf'
|
import * as rimraf from 'rimraf'
|
||||||
import * as pem from 'pem'
|
import * as pem from 'pem'
|
||||||
import * as jsonld from 'jsonld'
|
|
||||||
import * as jsig from 'jsonld-signatures'
|
|
||||||
jsig.use('jsonld', jsonld)
|
|
||||||
|
|
||||||
function isTestInstance () {
|
function isTestInstance () {
|
||||||
return process.env.NODE_ENV === 'test'
|
return process.env.NODE_ENV === 'test'
|
||||||
|
@ -120,8 +117,6 @@ const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash)
|
||||||
const createTorrentPromise = promisify2<string, any, any>(createTorrent)
|
const createTorrentPromise = promisify2<string, any, any>(createTorrent)
|
||||||
const rimrafPromise = promisify1WithVoid<string>(rimraf)
|
const rimrafPromise = promisify1WithVoid<string>(rimraf)
|
||||||
const statPromise = promisify1<string, Stats>(stat)
|
const statPromise = promisify1<string, Stats>(stat)
|
||||||
const jsonldSignPromise = promisify2<object, { privateKeyPem: string, creator: string }, object>(jsig.sign)
|
|
||||||
const jsonldVerifyPromise = promisify2<object, object, object>(jsig.verify)
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -150,7 +145,5 @@ export {
|
||||||
bcryptHashPromise,
|
bcryptHashPromise,
|
||||||
createTorrentPromise,
|
createTorrentPromise,
|
||||||
rimrafPromise,
|
rimrafPromise,
|
||||||
statPromise,
|
statPromise
|
||||||
jsonldSignPromise,
|
|
||||||
jsonldVerifyPromise
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ function isActivityPubVideoDurationValid (value: string) {
|
||||||
typeof value === 'string' &&
|
typeof value === 'string' &&
|
||||||
value.startsWith('PT') &&
|
value.startsWith('PT') &&
|
||||||
value.endsWith('S') &&
|
value.endsWith('S') &&
|
||||||
isVideoDurationValid(value.replace(/[^0-9]+/, ''))
|
isVideoDurationValid(value.replace(/[^0-9]+/g, ''))
|
||||||
}
|
}
|
||||||
|
|
||||||
function isVideoTorrentObjectValid (video: any) {
|
function isVideoTorrentObjectValid (video: any) {
|
||||||
|
@ -46,13 +46,14 @@ function isVideoTorrentObjectValid (video: any) {
|
||||||
isRemoteIdentifierValid(video.category) &&
|
isRemoteIdentifierValid(video.category) &&
|
||||||
isRemoteIdentifierValid(video.licence) &&
|
isRemoteIdentifierValid(video.licence) &&
|
||||||
isRemoteIdentifierValid(video.language) &&
|
isRemoteIdentifierValid(video.language) &&
|
||||||
isVideoViewsValid(video.video) &&
|
isVideoViewsValid(video.views) &&
|
||||||
isVideoNSFWValid(video.nsfw) &&
|
isVideoNSFWValid(video.nsfw) &&
|
||||||
isDateValid(video.published) &&
|
isDateValid(video.published) &&
|
||||||
isDateValid(video.updated) &&
|
isDateValid(video.updated) &&
|
||||||
isRemoteVideoContentValid(video.mediaType, video.content) &&
|
isRemoteVideoContentValid(video.mediaType, video.content) &&
|
||||||
isRemoteVideoIconValid(video.icon) &&
|
isRemoteVideoIconValid(video.icon) &&
|
||||||
setValidRemoteVideoUrls(video.url)
|
setValidRemoteVideoUrls(video) &&
|
||||||
|
video.url.length !== 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function isVideoFlagValid (activity: any) {
|
function isVideoFlagValid (activity: any) {
|
||||||
|
@ -132,8 +133,8 @@ function isRemoteVideoIconValid (icon: any) {
|
||||||
return icon.type === 'Image' &&
|
return icon.type === 'Image' &&
|
||||||
isVideoUrlValid(icon.url) &&
|
isVideoUrlValid(icon.url) &&
|
||||||
icon.mediaType === 'image/jpeg' &&
|
icon.mediaType === 'image/jpeg' &&
|
||||||
validator.isInt(icon.width, { min: 0 }) &&
|
validator.isInt(icon.width + '', { min: 0 }) &&
|
||||||
validator.isInt(icon.height, { min: 0 })
|
validator.isInt(icon.height + '', { min: 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
function setValidRemoteVideoUrls (video: any) {
|
function setValidRemoteVideoUrls (video: any) {
|
||||||
|
@ -149,6 +150,6 @@ function isRemoteVideoUrlValid (url: any) {
|
||||||
return url.type === 'Link' &&
|
return url.type === 'Link' &&
|
||||||
ACTIVITY_PUB.VIDEO_URL_MIME_TYPES.indexOf(url.mimeType) !== -1 &&
|
ACTIVITY_PUB.VIDEO_URL_MIME_TYPES.indexOf(url.mimeType) !== -1 &&
|
||||||
isVideoUrlValid(url.url) &&
|
isVideoUrlValid(url.url) &&
|
||||||
validator.isInt(url.width, { min: 0 }) &&
|
validator.isInt(url.width + '', { min: 0 }) &&
|
||||||
validator.isInt(url.size, { min: 0 })
|
validator.isInt(url.size + '', { min: 0 })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import * as jsonld from 'jsonld'
|
||||||
import * as jsig from 'jsonld-signatures'
|
import * as jsig from 'jsonld-signatures'
|
||||||
|
jsig.use('jsonld', jsonld)
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PRIVATE_RSA_KEY_SIZE,
|
PRIVATE_RSA_KEY_SIZE,
|
||||||
|
@ -9,9 +11,7 @@ import {
|
||||||
bcryptGenSaltPromise,
|
bcryptGenSaltPromise,
|
||||||
bcryptHashPromise,
|
bcryptHashPromise,
|
||||||
createPrivateKey,
|
createPrivateKey,
|
||||||
getPublicKey,
|
getPublicKey
|
||||||
jsonldSignPromise,
|
|
||||||
jsonldVerifyPromise
|
|
||||||
} from './core-utils'
|
} from './core-utils'
|
||||||
import { logger } from './logger'
|
import { logger } from './logger'
|
||||||
import { AccountInstance } from '../models/account/account-interface'
|
import { AccountInstance } from '../models/account/account-interface'
|
||||||
|
@ -45,7 +45,7 @@ function isSignatureVerified (fromAccount: AccountInstance, signedDocument: obje
|
||||||
publicKeyOwner: publicKeyOwnerObject
|
publicKeyOwner: publicKeyOwnerObject
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonldVerifyPromise(signedDocument, options)
|
return jsig.promises.verify(signedDocument, options)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.error('Cannot check signature.', err)
|
logger.error('Cannot check signature.', err)
|
||||||
return false
|
return false
|
||||||
|
@ -58,7 +58,7 @@ function signObject (byAccount: AccountInstance, data: any) {
|
||||||
creator: byAccount.url
|
creator: byAccount.url
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonldSignPromise(data, options)
|
return jsig.promises.sign(data, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function comparePassword (plainPassword: string, hashPassword: string) {
|
function comparePassword (plainPassword: string, hashPassword: string) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { CONFIG, database as db } from '../initializers'
|
||||||
import { ResultList } from '../../shared'
|
import { ResultList } from '../../shared'
|
||||||
import { VideoResolution } from '../../shared/models/videos/video-resolution.enum'
|
import { VideoResolution } from '../../shared/models/videos/video-resolution.enum'
|
||||||
import { AccountInstance } from '../models/account/account-interface'
|
import { AccountInstance } from '../models/account/account-interface'
|
||||||
|
import { logger } from './logger'
|
||||||
|
|
||||||
function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
return res.type('json').status(400).end()
|
return res.type('json').status(400).end()
|
||||||
|
@ -79,13 +80,18 @@ function resetSequelizeInstance (instance: Sequelize.Instance<any>, savedFields:
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let applicationAccount: AccountInstance
|
let serverAccount: AccountInstance
|
||||||
async function getApplicationAccount () {
|
async function getServerAccount () {
|
||||||
if (applicationAccount === undefined) {
|
if (serverAccount === undefined) {
|
||||||
applicationAccount = await db.Account.loadApplication()
|
serverAccount = await db.Account.loadApplication()
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(applicationAccount)
|
if (!serverAccount) {
|
||||||
|
logger.error('Cannot load server account.')
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(serverAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SortType = { sortModel: any, sortValue: string }
|
type SortType = { sortModel: any, sortValue: string }
|
||||||
|
@ -99,6 +105,6 @@ export {
|
||||||
isSignupAllowed,
|
isSignupAllowed,
|
||||||
computeResolutionsToTranscode,
|
computeResolutionsToTranscode,
|
||||||
resetSequelizeInstance,
|
resetSequelizeInstance,
|
||||||
getApplicationAccount,
|
getServerAccount,
|
||||||
SortType
|
SortType
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,9 +209,9 @@ const VIDEO_PRIVACIES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const VIDEO_MIMETYPE_EXT = {
|
const VIDEO_MIMETYPE_EXT = {
|
||||||
'video/webm': 'webm',
|
'video/webm': '.webm',
|
||||||
'video/ogg': 'ogv',
|
'video/ogg': '.ogv',
|
||||||
'video/mp4': 'mp4'
|
'video/mp4': '.mp4'
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -13,9 +13,9 @@ async function installApplication () {
|
||||||
await db.sequelize.sync()
|
await db.sequelize.sync()
|
||||||
await removeCacheDirectories()
|
await removeCacheDirectories()
|
||||||
await createDirectoriesIfNotExist()
|
await createDirectoriesIfNotExist()
|
||||||
|
await createApplicationIfNotExist()
|
||||||
await createOAuthClientIfNotExist()
|
await createOAuthClientIfNotExist()
|
||||||
await createOAuthAdminIfNotExist()
|
await createOAuthAdminIfNotExist()
|
||||||
await createApplicationIfNotExist()
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Cannot install application.', err)
|
logger.error('Cannot install application.', err)
|
||||||
throw err
|
throw err
|
||||||
|
|
|
@ -28,9 +28,9 @@ async function videoActivityObjectToDBAttributes (
|
||||||
description: videoObject.content,
|
description: videoObject.content,
|
||||||
channelId: videoChannel.id,
|
channelId: videoChannel.id,
|
||||||
duration: parseInt(duration, 10),
|
duration: parseInt(duration, 10),
|
||||||
createdAt: videoObject.published,
|
createdAt: new Date(videoObject.published),
|
||||||
// FIXME: updatedAt does not seems to be considered by Sequelize
|
// FIXME: updatedAt does not seems to be considered by Sequelize
|
||||||
updatedAt: videoObject.updated,
|
updatedAt: new Date(videoObject.updated),
|
||||||
views: videoObject.views,
|
views: videoObject.views,
|
||||||
likes: 0,
|
likes: 0,
|
||||||
dislikes: 0,
|
dislikes: 0,
|
||||||
|
@ -46,7 +46,7 @@ async function videoActivityObjectToDBAttributes (
|
||||||
|
|
||||||
function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoObject: VideoTorrentObject) {
|
function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoObject: VideoTorrentObject) {
|
||||||
const fileUrls = videoObject.url
|
const fileUrls = videoObject.url
|
||||||
.filter(u => Object.keys(VIDEO_MIMETYPE_EXT).indexOf(u.mimeType) !== -1)
|
.filter(u => Object.keys(VIDEO_MIMETYPE_EXT).indexOf(u.mimeType) !== -1 && u.url.startsWith('video/'))
|
||||||
|
|
||||||
const attributes: VideoFileAttributes[] = []
|
const attributes: VideoFileAttributes[] = []
|
||||||
for (const url of fileUrls) {
|
for (const url of fileUrls) {
|
||||||
|
|
|
@ -48,8 +48,8 @@ async function addRemoteVideoChannel (account: AccountInstance, videoChannelToCr
|
||||||
name: videoChannelToCreateData.name,
|
name: videoChannelToCreateData.name,
|
||||||
description: videoChannelToCreateData.content,
|
description: videoChannelToCreateData.content,
|
||||||
uuid: videoChannelToCreateData.uuid,
|
uuid: videoChannelToCreateData.uuid,
|
||||||
createdAt: videoChannelToCreateData.published,
|
createdAt: new Date(videoChannelToCreateData.published),
|
||||||
updatedAt: videoChannelToCreateData.updated,
|
updatedAt: new Date(videoChannelToCreateData.updated),
|
||||||
remote: true,
|
remote: true,
|
||||||
accountId: account.id
|
accountId: account.id
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,46 +17,67 @@ async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Se
|
||||||
const videoChannelObject = videoChannel.toActivityPubObject()
|
const videoChannelObject = videoChannel.toActivityPubObject()
|
||||||
const data = await createActivityData(videoChannel.url, videoChannel.Account, videoChannelObject)
|
const data = await createActivityData(videoChannel.url, videoChannel.Account, videoChannelObject)
|
||||||
|
|
||||||
return broadcastToFollowers(data, videoChannel.Account, t)
|
return broadcastToFollowers(data, [ videoChannel.Account ], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
|
async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
|
||||||
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)
|
return broadcastToFollowers(data, [ videoChannel.Account ], 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)
|
return broadcastToFollowers(data, [ videoChannel.Account ], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendAddVideo (video: VideoInstance, t: Sequelize.Transaction) {
|
async function sendAddVideo (video: VideoInstance, t: Sequelize.Transaction) {
|
||||||
const videoObject = video.toActivityPubObject()
|
const videoObject = video.toActivityPubObject()
|
||||||
const data = await addActivityData(video.url, video.VideoChannel.Account, video.VideoChannel.url, videoObject)
|
const data = await addActivityData(video.url, video.VideoChannel.Account, video.VideoChannel.url, videoObject)
|
||||||
|
|
||||||
return broadcastToFollowers(data, video.VideoChannel.Account, t)
|
return broadcastToFollowers(data, [ video.VideoChannel.Account ], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendUpdateVideo (video: VideoInstance, t: Sequelize.Transaction) {
|
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)
|
return broadcastToFollowers(data, [ video.VideoChannel.Account ], 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)
|
return broadcastToFollowers(data, [ video.VideoChannel.Account ], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transaction) {
|
async function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transaction) {
|
||||||
const data = await deleteActivityData(account.url, account)
|
const data = await deleteActivityData(account.url, account)
|
||||||
|
|
||||||
return broadcastToFollowers(data, account, t)
|
return broadcastToFollowers(data, [ account ], t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendAnnounce (byAccount: AccountInstance, instance: VideoInstance | VideoChannelInstance, t: Sequelize.Transaction) {
|
||||||
|
const object = instance.toActivityPubObject()
|
||||||
|
|
||||||
|
let url = ''
|
||||||
|
let objectActorUrl: string
|
||||||
|
if ((instance as any).VideoChannel !== undefined) {
|
||||||
|
objectActorUrl = (instance as VideoInstance).VideoChannel.Account.url
|
||||||
|
url = getActivityPubUrl('video', instance.uuid) + '#announce'
|
||||||
|
} else {
|
||||||
|
objectActorUrl = (instance as VideoChannelInstance).Account.url
|
||||||
|
url = getActivityPubUrl('videoChannel', instance.uuid) + '#announce'
|
||||||
|
}
|
||||||
|
|
||||||
|
const objectWithActor = Object.assign(object, {
|
||||||
|
actor: objectActorUrl
|
||||||
|
})
|
||||||
|
|
||||||
|
const data = await announceActivityData(url, byAccount, objectWithActor)
|
||||||
|
return broadcastToFollowers(data, [ byAccount ], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendVideoAbuse (
|
async function sendVideoAbuse (
|
||||||
|
@ -95,15 +116,17 @@ export {
|
||||||
sendDeleteAccount,
|
sendDeleteAccount,
|
||||||
sendAccept,
|
sendAccept,
|
||||||
sendFollow,
|
sendFollow,
|
||||||
sendVideoAbuse
|
sendVideoAbuse,
|
||||||
|
sendAnnounce
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function broadcastToFollowers (data: any, fromAccount: AccountInstance, t: Sequelize.Transaction) {
|
async function broadcastToFollowers (data: any, toAccountFollowers: AccountInstance[], t: Sequelize.Transaction) {
|
||||||
const result = await db.AccountFollow.listAcceptedFollowerUrlsForApi(fromAccount.id)
|
const toAccountFollowerIds = toAccountFollowers.map(a => a.id)
|
||||||
|
const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds)
|
||||||
if (result.data.length === 0) {
|
if (result.data.length === 0) {
|
||||||
logger.info('Not broadcast because of 0 followers.')
|
logger.info('Not broadcast because of 0 followers for %s.', toAccountFollowerIds.join(', '))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +209,17 @@ async function addActivityData (url: string, byAccount: AccountInstance, target:
|
||||||
return buildSignedActivity(byAccount, base)
|
return buildSignedActivity(byAccount, base)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function announceActivityData (url: string, byAccount: AccountInstance, object: any) {
|
||||||
|
const base = {
|
||||||
|
type: 'Announce',
|
||||||
|
id: url,
|
||||||
|
actor: byAccount.url,
|
||||||
|
object
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildSignedActivity(byAccount, base)
|
||||||
|
}
|
||||||
|
|
||||||
async function followActivityData (url: string, byAccount: AccountInstance) {
|
async function followActivityData (url: string, byAccount: AccountInstance) {
|
||||||
const base = {
|
const base = {
|
||||||
type: 'Follow',
|
type: 'Follow',
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { VideoInstance } from '../../../models'
|
||||||
import { sendAddVideo } from '../../activitypub/send-request'
|
import { sendAddVideo } from '../../activitypub/send-request'
|
||||||
import { JobScheduler } from '../job-scheduler'
|
import { JobScheduler } from '../job-scheduler'
|
||||||
import { TranscodingJobPayload } from './transcoding-job-scheduler'
|
import { TranscodingJobPayload } from './transcoding-job-scheduler'
|
||||||
|
import { shareVideoByServer } from '../../../helpers/activitypub'
|
||||||
|
|
||||||
async function process (data: TranscodingJobPayload, jobId: number) {
|
async function process (data: TranscodingJobPayload, jobId: number) {
|
||||||
const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
|
const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
|
||||||
|
@ -37,6 +38,7 @@ async function onSuccess (jobId: number, video: VideoInstance, jobScheduler: Job
|
||||||
|
|
||||||
// Now we'll add the video's meta data to our followers
|
// Now we'll add the video's meta data to our followers
|
||||||
await sendAddVideo(video, undefined)
|
await sendAddVideo(video, undefined)
|
||||||
|
await shareVideoByServer(video, undefined)
|
||||||
|
|
||||||
const originalFileHeight = await videoDatabase.getOriginalFileHeight()
|
const originalFileHeight = await videoDatabase.getOriginalFileHeight()
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { logger } from '../helpers'
|
||||||
import { AccountInstance } from '../models'
|
import { AccountInstance } from '../models'
|
||||||
import { VideoChannelCreate } from '../../shared/models'
|
import { VideoChannelCreate } from '../../shared/models'
|
||||||
import { sendCreateVideoChannel } from './activitypub/send-request'
|
import { sendCreateVideoChannel } from './activitypub/send-request'
|
||||||
import { getActivityPubUrl } from '../helpers/activitypub'
|
import { getActivityPubUrl, shareVideoChannelByServer } from '../helpers/activitypub'
|
||||||
|
|
||||||
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) {
|
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) {
|
||||||
const videoChannelData = {
|
const videoChannelData = {
|
||||||
|
@ -25,7 +25,8 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
|
||||||
// Do not forget to add Account information to the created video channel
|
// Do not forget to add Account information to the created video channel
|
||||||
videoChannelCreated.Account = account
|
videoChannelCreated.Account = account
|
||||||
|
|
||||||
sendCreateVideoChannel(videoChannelCreated, t)
|
await sendCreateVideoChannel(videoChannelCreated, t)
|
||||||
|
await shareVideoChannelByServer(videoChannelCreated, t)
|
||||||
|
|
||||||
return videoChannelCreated
|
return videoChannelCreated
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,9 @@ export namespace AccountFollowMethods {
|
||||||
export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
|
export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
|
||||||
export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
|
export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
|
||||||
|
|
||||||
export type ListAcceptedFollowerUrlsForApi = (id: number, start?: number, count?: number) => Promise< ResultList<string> >
|
export type ListAcceptedFollowerUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> >
|
||||||
export type ListAcceptedFollowingUrlsForApi = (id: number, start?: number, count?: number) => Promise< ResultList<string> >
|
export type ListAcceptedFollowingUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> >
|
||||||
|
export type ListAcceptedFollowerSharedInboxUrls = (accountId: number[]) => Promise< ResultList<string> >
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccountFollowClass {
|
export interface AccountFollowClass {
|
||||||
|
@ -21,6 +22,7 @@ export interface AccountFollowClass {
|
||||||
|
|
||||||
listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
|
listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
|
||||||
listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
|
listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
|
||||||
|
listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccountFollowAttributes {
|
export interface AccountFollowAttributes {
|
||||||
|
|
|
@ -11,6 +11,7 @@ let listFollowingForApi: AccountFollowMethods.ListFollowingForApi
|
||||||
let listFollowersForApi: AccountFollowMethods.ListFollowersForApi
|
let listFollowersForApi: AccountFollowMethods.ListFollowersForApi
|
||||||
let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
|
let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
|
||||||
let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
|
let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
|
||||||
|
let listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls
|
||||||
|
|
||||||
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
|
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
|
||||||
AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow',
|
AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow',
|
||||||
|
@ -42,7 +43,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
|
||||||
listFollowingForApi,
|
listFollowingForApi,
|
||||||
listFollowersForApi,
|
listFollowersForApi,
|
||||||
listAcceptedFollowerUrlsForApi,
|
listAcceptedFollowerUrlsForApi,
|
||||||
listAcceptedFollowingUrlsForApi
|
listAcceptedFollowingUrlsForApi,
|
||||||
|
listAcceptedFollowerSharedInboxUrls
|
||||||
]
|
]
|
||||||
addMethodsToModel(AccountFollow, classMethods)
|
addMethodsToModel(AccountFollow, classMethods)
|
||||||
|
|
||||||
|
@ -146,17 +148,27 @@ listFollowersForApi = function (id: number, start: number, count: number, sort:
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
listAcceptedFollowerUrlsForApi = function (accountId: number, start?: number, count?: number) {
|
listAcceptedFollowerUrlsForApi = function (accountIds: number[], start?: number, count?: number) {
|
||||||
return createListAcceptedFollowForApiQuery('followers', accountId, start, count)
|
return createListAcceptedFollowForApiQuery('followers', accountIds, start, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
listAcceptedFollowingUrlsForApi = function (accountId: number, start?: number, count?: number) {
|
listAcceptedFollowerSharedInboxUrls = function (accountIds: number[]) {
|
||||||
return createListAcceptedFollowForApiQuery('following', accountId, start, count)
|
return createListAcceptedFollowForApiQuery('followers', accountIds, undefined, undefined, 'sharedInboxUrl')
|
||||||
|
}
|
||||||
|
|
||||||
|
listAcceptedFollowingUrlsForApi = function (accountIds: number[], start?: number, count?: number) {
|
||||||
|
return createListAcceptedFollowForApiQuery('following', accountIds, start, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------ UTILS ------------------------------
|
// ------------------------------ UTILS ------------------------------
|
||||||
|
|
||||||
async function createListAcceptedFollowForApiQuery (type: 'followers' | 'following', accountId: number, start?: number, count?: number) {
|
async function createListAcceptedFollowForApiQuery (
|
||||||
|
type: 'followers' | 'following',
|
||||||
|
accountIds: number[],
|
||||||
|
start?: number,
|
||||||
|
count?: number,
|
||||||
|
columnUrl = 'url'
|
||||||
|
) {
|
||||||
let firstJoin: string
|
let firstJoin: string
|
||||||
let secondJoin: string
|
let secondJoin: string
|
||||||
|
|
||||||
|
@ -168,20 +180,20 @@ async function createListAcceptedFollowForApiQuery (type: 'followers' | 'followi
|
||||||
secondJoin = 'targetAccountId'
|
secondJoin = 'targetAccountId'
|
||||||
}
|
}
|
||||||
|
|
||||||
const selections = [ '"Follows"."url" AS "url"', 'COUNT(*) AS "total"' ]
|
const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ]
|
||||||
const tasks: Promise<any>[] = []
|
const tasks: Promise<any>[] = []
|
||||||
|
|
||||||
for (const selection of selections) {
|
for (const selection of selections) {
|
||||||
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" = $accountId AND "AccountFollows"."state" = \'accepted\' '
|
'WHERE "Accounts"."id" IN ($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: { accountId },
|
bind: { accountIds: accountIds.join(',') },
|
||||||
type: Sequelize.QueryTypes.SELECT
|
type: Sequelize.QueryTypes.SELECT
|
||||||
}
|
}
|
||||||
tasks.push(AccountFollow['sequelize'].query(query, options))
|
tasks.push(AccountFollow['sequelize'].query(query, options))
|
||||||
|
|
|
@ -153,8 +153,8 @@ toActivityPubObject = function (this: VideoChannelInstance) {
|
||||||
uuid: this.uuid,
|
uuid: this.uuid,
|
||||||
content: this.description,
|
content: this.description,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
published: this.createdAt,
|
published: this.createdAt.toISOString(),
|
||||||
updated: this.updatedAt
|
updated: this.updatedAt.toISOString()
|
||||||
}
|
}
|
||||||
|
|
||||||
return json
|
return json
|
||||||
|
|
|
@ -558,7 +558,7 @@ toActivityPubObject = function (this: VideoInstance) {
|
||||||
for (const file of this.VideoFiles) {
|
for (const file of this.VideoFiles) {
|
||||||
url.push({
|
url.push({
|
||||||
type: 'Link',
|
type: 'Link',
|
||||||
mimeType: 'video/' + file.extname,
|
mimeType: 'video/' + file.extname.replace('.', ''),
|
||||||
url: getVideoFileUrl(this, file, baseUrlHttp),
|
url: getVideoFileUrl(this, file, baseUrlHttp),
|
||||||
width: file.resolution,
|
width: file.resolution,
|
||||||
size: file.size
|
size: file.size
|
||||||
|
@ -601,8 +601,8 @@ toActivityPubObject = function (this: VideoInstance) {
|
||||||
},
|
},
|
||||||
views: this.views,
|
views: this.views,
|
||||||
nsfw: this.nsfw,
|
nsfw: this.nsfw,
|
||||||
published: this.createdAt,
|
published: this.createdAt.toISOString(),
|
||||||
updated: this.updatedAt,
|
updated: this.updatedAt.toISOString(),
|
||||||
mediaType: 'text/markdown',
|
mediaType: 'text/markdown',
|
||||||
content: this.getTruncatedDescription(),
|
content: this.getTruncatedDescription(),
|
||||||
icon: {
|
icon: {
|
||||||
|
|
|
@ -4,7 +4,7 @@ export interface VideoChannelObject {
|
||||||
name: string
|
name: string
|
||||||
content: string
|
content: string
|
||||||
uuid: string
|
uuid: string
|
||||||
published: Date
|
published: string
|
||||||
updated: Date
|
updated: string
|
||||||
actor?: string
|
actor?: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@ export interface VideoTorrentObject {
|
||||||
language: ActivityIdentifierObject
|
language: ActivityIdentifierObject
|
||||||
views: number
|
views: number
|
||||||
nsfw: boolean
|
nsfw: boolean
|
||||||
published: Date
|
published: string
|
||||||
updated: Date
|
updated: string
|
||||||
mediaType: 'text/markdown'
|
mediaType: 'text/markdown'
|
||||||
content: string
|
content: string
|
||||||
icon: ActivityIconObject
|
icon: ActivityIconObject
|
||||||
|
|
Loading…
Add table
Reference in a new issue