tasks.push(p)
}
- await Promise.all(tasks)
+ // Don't make the client wait the tasks
+ Promise.all(tasks)
+ .catch(err => {
+ logger.error('Error in follow.', err)
+ })
return res.status(204).end()
}
import * as validator from 'validator'
-
-import {
- ACTIVITY_PUB
-} from '../../../initializers'
-import { isDateValid, isUUIDValid } from '../misc'
+import { ACTIVITY_PUB } from '../../../initializers'
+import { exists, isDateValid, isUUIDValid } from '../misc'
+import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
import {
- isVideoViewsValid,
- isVideoNSFWValid,
- isVideoTruncatedDescriptionValid,
isVideoDurationValid,
isVideoNameValid,
+ isVideoNSFWValid,
isVideoTagValid,
- isVideoUrlValid
+ isVideoTruncatedDescriptionValid,
+ isVideoUrlValid,
+ isVideoViewsValid
} from '../videos'
-import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
-import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
+import { isBaseActivityValid } from './misc'
function isVideoTorrentAddActivityValid (activity: any) {
return isBaseActivityValid(activity, 'Add') &&
return isBaseActivityValid(activity, 'Delete')
}
+function isActivityPubVideoDurationValid (value: string) {
+ // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
+ return exists(value) &&
+ typeof value === 'string' &&
+ value.startsWith('PT') &&
+ value.endsWith('S') &&
+ isVideoDurationValid(value.replace(/[^0-9]+/, ''))
+}
+
function isVideoTorrentObjectValid (video: any) {
return video.type === 'Video' &&
isVideoNameValid(video.name) &&
- isVideoDurationValid(video.duration) &&
+ isActivityPubVideoDurationValid(video.duration) &&
isUUIDValid(video.uuid) &&
setValidRemoteTags(video) &&
isRemoteIdentifierValid(video.category) &&
return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
}
+function isVideoDurationValid (value: string) {
+ return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
+}
+
function isVideoTruncatedDescriptionValid (value: string) {
return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
}
return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION)
}
-function isVideoDurationValid (value: string) {
- // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
- return exists(value) &&
- typeof value === 'string' &&
- value.startsWith('PT') &&
- value.endsWith('S') &&
- validator.isInt(value.replace(/[^0-9]+/, ''), VIDEOS_CONSTRAINTS_FIELDS.DURATION)
-}
-
function isVideoNameValid (value: string) {
return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
}
isVideoNSFWValid,
isVideoTruncatedDescriptionValid,
isVideoDescriptionValid,
- isVideoDurationValid,
isVideoFileInfoHashValid,
isVideoNameValid,
isVideoTagsValid,
isVideoFileSizeValid,
isVideoPrivacyValid,
isRemoteVideoPrivacyValid,
+ isVideoDurationValid,
isVideoFileResolutionValid,
checkVideoExists,
isVideoTagValid,
import { Activity } from '../../../shared'
import { VideoAbuseInstance } from '../../models/video/video-abuse-interface'
import { getActivityPubUrl } from '../../helpers/activitypub'
+import { logger } from '../../helpers/logger'
async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
const videoChannelObject = videoChannel.toActivityPubObject()
// ---------------------------------------------------------------------------
async function broadcastToFollowers (data: any, fromAccount: AccountInstance, t: Sequelize.Transaction) {
- const result = await db.AccountFollow.listAcceptedFollowerUrlsForApi(fromAccount.id, 0)
+ const result = await db.AccountFollow.listAcceptedFollowerUrlsForApi(fromAccount.id)
+ if (result.data.length === 0) {
+ logger.info('Not broadcast because of 0 followers.')
+ return
+ }
const jobPayload = {
uris: result.data,
return Promise.resolve()
}
-async function onSuccess (jobId: number) {
+function onSuccess (jobId: number) {
logger.info('Job %d is a success.', jobId)
+ return Promise.resolve()
}
// ---------------------------------------------------------------------------
return Promise.resolve()
}
-async function onSuccess (jobId: number) {
+function onSuccess (jobId: number) {
logger.info('Job %d is a success.', jobId)
+ return Promise.resolve()
}
// ---------------------------------------------------------------------------
export interface JobHandler<P, T> {
process (data: object, jobId: number): Promise<T>
onError (err: Error, jobId: number)
- onSuccess (jobId: number, jobResult: T, jobScheduler: JobScheduler<P, T>)
+ onSuccess (jobId: number, jobResult: T, jobScheduler: JobScheduler<P, T>): Promise<any>
}
type JobQueueCallback = (err: Error) => void
try {
await job.save()
- jobHandler.onSuccess(job.id, jobResult, this)
+ await jobHandler.onSuccess(job.id, jobResult, this)
} catch (err) {
this.cannotSaveJobError(err)
}
await sendAddVideo(video, undefined)
const originalFileHeight = await videoDatabase.getOriginalFileHeight()
- // Create transcoding jobs if there are enabled resolutions
+ // Create transcoding jobs if there are enabled resolutions
const resolutionsEnabled = computeResolutionsToTranscode(originalFileHeight)
logger.info(
'Resolutions computed for video %s and origin file height of %d.', videoDatabase.uuid, originalFileHeight,
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 ListAcceptedFollowerUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList<string> >
- export type ListAcceptedFollowingUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList<string> >
+ export type ListAcceptedFollowerUrlsForApi = (id: number, start?: number, count?: number) => Promise< ResultList<string> >
+ export type ListAcceptedFollowingUrlsForApi = (id: number, start?: number, count?: number) => Promise< ResultList<string> >
}
export interface AccountFollowClass {
})
}
-listAcceptedFollowerUrlsForApi = function (id: number, start: number, count?: number) {
- return createListAcceptedFollowForApiQuery('followers', id, start, count)
+listAcceptedFollowerUrlsForApi = function (accountId: number, start?: number, count?: number) {
+ return createListAcceptedFollowForApiQuery('followers', accountId, start, count)
}
-listAcceptedFollowingUrlsForApi = function (id: number, start: number, count?: number) {
- return createListAcceptedFollowForApiQuery('following', id, start, count)
+listAcceptedFollowingUrlsForApi = function (accountId: number, start?: number, count?: number) {
+ return createListAcceptedFollowForApiQuery('following', accountId, start, count)
}
// ------------------------------ UTILS ------------------------------
-async function createListAcceptedFollowForApiQuery (type: 'followers' | 'following', id: number, start: number, count?: number) {
+async function createListAcceptedFollowForApiQuery (type: 'followers' | 'following', accountId: number, start?: number, count?: number) {
let firstJoin: string
let secondJoin: string
secondJoin = 'targetAccountId'
}
- const selections = [ '"Followers"."url" AS "url"', 'COUNT(*) AS "total"' ]
+ const selections = [ '"Follows"."url" AS "url"', 'COUNT(*) AS "total"' ]
const tasks: Promise<any>[] = []
for (const selection of selections) {
- let query = 'SELECT ' + selection + ' FROM "Account" ' +
- 'INNER JOIN "AccountFollow" ON "AccountFollow"."' + firstJoin + '" = "Account"."id" ' +
- 'INNER JOIN "Account" AS "Follows" ON "Followers"."id" = "Follows"."' + secondJoin + '" ' +
- 'WHERE "Account"."id" = $id AND "AccountFollow"."state" = \'accepted\' ' +
- 'LIMIT ' + start
+ 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" = $accountId AND "AccountFollows"."state" = \'accepted\' '
+ if (start !== undefined) query += 'LIMIT ' + start
if (count !== undefined) query += ', ' + count
const options = {
- bind: { id },
+ bind: { accountId },
type: Sequelize.QueryTypes.SELECT
}
tasks.push(AccountFollow['sequelize'].query(query, options))
name: 'targetAccountId',
allowNull: false
},
+ as: 'followers',
onDelete: 'cascade'
})
}
onDelete: 'cascade'
})
- Video.belongsTo(models.VideoChannel, {
+ Video.belongsTo(models.Video, {
foreignKey: {
name: 'parentId',
allowNull: true
include: [
{
model: Video['sequelize'].models.VideoChannel,
+ required: true,
include: [
{
model: Video['sequelize'].models.Account,
+ required: true,
include: [
{
model: Video['sequelize'].models.Server,