From 8e10cf1a5a438a00e5f7e0691cb830769867cffc Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 15 Nov 2017 16:28:35 +0100 Subject: [PATCH] Fix video upload and videos list --- server/controllers/api/server/follows.ts | 6 +++- .../custom-validators/activitypub/videos.ts | 30 +++++++++++-------- server/helpers/custom-validators/videos.ts | 15 ++++------ server/lib/activitypub/send-request.ts | 7 ++++- .../http-request-broadcast-handler.ts | 3 +- .../http-request-unicast-handler.ts | 3 +- server/lib/jobs/job-scheduler.ts | 4 +-- .../video-file-optimizer-handler.ts | 2 +- .../account/account-follow-interface.ts | 4 +-- server/models/account/account-follow.ts | 24 +++++++-------- server/models/account/account.ts | 1 + server/models/video/video.ts | 4 ++- 12 files changed, 59 insertions(+), 44 deletions(-) diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index c9775ad21..ac8ea87f9 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts @@ -108,7 +108,11 @@ async function follow (req: express.Request, res: express.Response, next: expres 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() } diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts index c9ecf1f3d..a46757397 100644 --- a/server/helpers/custom-validators/activitypub/videos.ts +++ b/server/helpers/custom-validators/activitypub/videos.ts @@ -1,20 +1,17 @@ import * as validator from 'validator' - +import { ACTIVITY_PUB } from '../../../initializers' +import { exists, isDateValid, isUUIDValid } from '../misc' +import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels' import { - ACTIVITY_PUB -} from '../../../initializers' -import { isDateValid, isUUIDValid } from '../misc' -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') && @@ -30,10 +27,19 @@ function isVideoTorrentDeleteActivityValid (activity: any) { 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) && diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts index 1505632da..c97c9a2ad 100644 --- a/server/helpers/custom-validators/videos.ts +++ b/server/helpers/custom-validators/videos.ts @@ -69,6 +69,10 @@ function isVideoNSFWValid (value: any) { 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) } @@ -77,15 +81,6 @@ function isVideoDescriptionValid (value: string) { 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) } @@ -197,7 +192,6 @@ export { isVideoNSFWValid, isVideoTruncatedDescriptionValid, isVideoDescriptionValid, - isVideoDurationValid, isVideoFileInfoHashValid, isVideoNameValid, isVideoTagsValid, @@ -214,6 +208,7 @@ export { isVideoFileSizeValid, isVideoPrivacyValid, isRemoteVideoPrivacyValid, + isVideoDurationValid, isVideoFileResolutionValid, checkVideoExists, isVideoTagValid, diff --git a/server/lib/activitypub/send-request.ts b/server/lib/activitypub/send-request.ts index 1a6cebc03..1dad51828 100644 --- a/server/lib/activitypub/send-request.ts +++ b/server/lib/activitypub/send-request.ts @@ -11,6 +11,7 @@ import { signObject, activityPubContextify } from '../../helpers' 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() @@ -100,7 +101,11 @@ export { // --------------------------------------------------------------------------- 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, diff --git a/server/lib/jobs/http-request-job-scheduler/http-request-broadcast-handler.ts b/server/lib/jobs/http-request-job-scheduler/http-request-broadcast-handler.ts index 2f1d9ee92..ccb008e4d 100644 --- a/server/lib/jobs/http-request-job-scheduler/http-request-broadcast-handler.ts +++ b/server/lib/jobs/http-request-job-scheduler/http-request-broadcast-handler.ts @@ -22,8 +22,9 @@ function onError (err: Error, jobId: number) { return Promise.resolve() } -async function onSuccess (jobId: number) { +function onSuccess (jobId: number) { logger.info('Job %d is a success.', jobId) + return Promise.resolve() } // --------------------------------------------------------------------------- diff --git a/server/lib/jobs/http-request-job-scheduler/http-request-unicast-handler.ts b/server/lib/jobs/http-request-job-scheduler/http-request-unicast-handler.ts index 3a1a7fabf..9e4e73891 100644 --- a/server/lib/jobs/http-request-job-scheduler/http-request-unicast-handler.ts +++ b/server/lib/jobs/http-request-job-scheduler/http-request-unicast-handler.ts @@ -20,8 +20,9 @@ function onError (err: Error, jobId: number) { return Promise.resolve() } -async function onSuccess (jobId: number) { +function onSuccess (jobId: number) { logger.info('Job %d is a success.', jobId) + return Promise.resolve() } // --------------------------------------------------------------------------- diff --git a/server/lib/jobs/job-scheduler.ts b/server/lib/jobs/job-scheduler.ts index b25bb7ab3..73c440279 100644 --- a/server/lib/jobs/job-scheduler.ts +++ b/server/lib/jobs/job-scheduler.ts @@ -9,7 +9,7 @@ import { error } from 'util' export interface JobHandler { process (data: object, jobId: number): Promise onError (err: Error, jobId: number) - onSuccess (jobId: number, jobResult: T, jobScheduler: JobScheduler) + onSuccess (jobId: number, jobResult: T, jobScheduler: JobScheduler): Promise } type JobQueueCallback = (err: Error) => void @@ -127,7 +127,7 @@ class JobScheduler { try { await job.save() - jobHandler.onSuccess(job.id, jobResult, this) + await jobHandler.onSuccess(job.id, jobResult, this) } catch (err) { this.cannotSaveJobError(err) } diff --git a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts index d3ee886e7..f6d9627a5 100644 --- a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts +++ b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts @@ -39,8 +39,8 @@ async function onSuccess (jobId: number, video: VideoInstance, jobScheduler: Job 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, diff --git a/server/models/account/account-follow-interface.ts b/server/models/account/account-follow-interface.ts index 413dad190..54baf45ed 100644 --- a/server/models/account/account-follow-interface.ts +++ b/server/models/account/account-follow-interface.ts @@ -10,8 +10,8 @@ export namespace AccountFollowMethods { export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList > export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList > - export type ListAcceptedFollowerUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList > - export type ListAcceptedFollowingUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList > + export type ListAcceptedFollowerUrlsForApi = (id: number, start?: number, count?: number) => Promise< ResultList > + export type ListAcceptedFollowingUrlsForApi = (id: number, start?: number, count?: number) => Promise< ResultList > } export interface AccountFollowClass { diff --git a/server/models/account/account-follow.ts b/server/models/account/account-follow.ts index c940d7cd4..f457e43e9 100644 --- a/server/models/account/account-follow.ts +++ b/server/models/account/account-follow.ts @@ -146,17 +146,17 @@ listFollowersForApi = function (id: number, start: number, count: number, sort: }) } -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 @@ -168,20 +168,20 @@ async function createListAcceptedFollowForApiQuery (type: 'followers' | 'followi secondJoin = 'targetAccountId' } - const selections = [ '"Followers"."url" AS "url"', 'COUNT(*) AS "total"' ] + const selections = [ '"Follows"."url" AS "url"', 'COUNT(*) AS "total"' ] const tasks: Promise[] = [] 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)) diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 464105261..84461a2eb 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts @@ -263,6 +263,7 @@ function associate (models) { name: 'targetAccountId', allowNull: false }, + as: 'followers', onDelete: 'cascade' }) } diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 86800fb88..b00081f25 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -329,7 +329,7 @@ function associate (models) { onDelete: 'cascade' }) - Video.belongsTo(models.VideoChannel, { + Video.belongsTo(models.Video, { foreignKey: { name: 'parentId', allowNull: true @@ -825,9 +825,11 @@ listForApi = function (start: number, count: number, sort: string) { include: [ { model: Video['sequelize'].models.VideoChannel, + required: true, include: [ { model: Video['sequelize'].models.Account, + required: true, include: [ { model: Video['sequelize'].models.Server,