Fix video upload and videos list

pull/128/head
Chocobozzz 2017-11-15 16:28:35 +01:00
parent 8e13fa7d09
commit 8e10cf1a5a
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
12 changed files with 59 additions and 44 deletions

View File

@ -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()
}

View File

@ -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) &&

View File

@ -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,

View File

@ -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,

View File

@ -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()
}
// ---------------------------------------------------------------------------

View File

@ -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()
}
// ---------------------------------------------------------------------------

View File

@ -9,7 +9,7 @@ import { error } from 'util'
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
@ -127,7 +127,7 @@ class JobScheduler<P, T> {
try {
await job.save()
jobHandler.onSuccess(job.id, jobResult, this)
await jobHandler.onSuccess(job.id, jobResult, this)
} catch (err) {
this.cannotSaveJobError(err)
}

View File

@ -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,

View File

@ -10,8 +10,8 @@ export namespace AccountFollowMethods {
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 {

View File

@ -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<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))

View File

@ -263,6 +263,7 @@ function associate (models) {
name: 'targetAccountId',
allowNull: false
},
as: 'followers',
onDelete: 'cascade'
})
}

View File

@ -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,