From ed31c059851a30bd5ba9999f8ecb3822d576b9f4 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 2 Aug 2018 17:48:50 +0200 Subject: Add ability to list video imports --- server/controllers/api/users.ts | 30 +++++++++- server/helpers/youtube-dl.ts | 2 +- server/initializers/constants.ts | 1 + server/lib/job-queue/handlers/video-import.ts | 2 +- server/middlewares/validators/sort.ts | 3 + server/models/video/video-import.ts | 82 ++++++++++++++++++++++++--- server/models/video/video.ts | 8 ++- 7 files changed, 116 insertions(+), 12 deletions(-) (limited to 'server') diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts index dbe736bff..6e5f9913e 100644 --- a/server/controllers/api/users.ts +++ b/server/controllers/api/users.ts @@ -29,7 +29,12 @@ import { usersUpdateValidator, usersVideoRatingValidator } from '../../middlewares' -import { usersAskResetPasswordValidator, usersResetPasswordValidator, videosSortValidator } from '../../middlewares/validators' +import { + usersAskResetPasswordValidator, + usersResetPasswordValidator, + videoImportsSortValidator, + videosSortValidator +} from '../../middlewares/validators' import { AccountVideoRateModel } from '../../models/account/account-video-rate' import { UserModel } from '../../models/account/user' import { OAuthTokenModel } from '../../models/oauth/oauth-token' @@ -40,6 +45,7 @@ import { UserVideoQuota } from '../../../shared/models/users/user-video-quota.mo import { updateAvatarValidator } from '../../middlewares/validators/avatar' import { updateActorAvatarFile } from '../../lib/avatar' import { auditLoggerFactory, UserAuditView } from '../../helpers/audit-logger' +import { VideoImportModel } from '../../models/video/video-import' const auditLogger = auditLoggerFactory('users') @@ -62,6 +68,16 @@ usersRouter.get('/me/video-quota-used', asyncMiddleware(getUserVideoQuotaUsed) ) + +usersRouter.get('/me/videos/imports', + authenticate, + paginationValidator, + videoImportsSortValidator, + setDefaultSort, + setDefaultPagination, + asyncMiddleware(getUserVideoImports) +) + usersRouter.get('/me/videos', authenticate, paginationValidator, @@ -178,6 +194,18 @@ async function getUserVideos (req: express.Request, res: express.Response, next: return res.json(getFormattedObjects(resultList.data, resultList.total, { additionalAttributes })) } +async function getUserVideoImports (req: express.Request, res: express.Response, next: express.NextFunction) { + const user = res.locals.oauth.token.User as UserModel + const resultList = await VideoImportModel.listUserVideoImportsForApi( + user.Account.id, + req.query.start as number, + req.query.count as number, + req.query.sort + ) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} + async function createUser (req: express.Request, res: express.Response) { const body: UserCreate = req.body const userToCreate = new UserModel({ diff --git a/server/helpers/youtube-dl.ts b/server/helpers/youtube-dl.ts index 74d3e213b..43156bb22 100644 --- a/server/helpers/youtube-dl.ts +++ b/server/helpers/youtube-dl.ts @@ -95,7 +95,7 @@ function titleTruncation (title: string) { } function descriptionTruncation (description: string) { - if (!description) return undefined + if (!description || description.length < CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.min) return undefined return truncate(description, { 'length': CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max, diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index cc363d4f2..feb45e4d0 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -37,6 +37,7 @@ const SORTABLE_COLUMNS = { VIDEO_ABUSES: [ 'id', 'createdAt' ], VIDEO_CHANNELS: [ 'id', 'name', 'updatedAt', 'createdAt' ], VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes' ], + VIDEO_IMPORTS: [ 'createdAt' ], VIDEO_COMMENT_THREADS: [ 'createdAt' ], BLACKLISTS: [ 'id', 'name', 'duration', 'views', 'likes', 'dislikes', 'uuid', 'createdAt' ], FOLLOWERS: [ 'createdAt' ], diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts index 2f219e986..5a7722153 100644 --- a/server/lib/job-queue/handlers/video-import.ts +++ b/server/lib/job-queue/handlers/video-import.ts @@ -35,7 +35,7 @@ async function processVideoImport (job: Bull.Job) { // Get information about this video const { videoFileResolution } = await getVideoFileResolution(tempVideoPath) - const fps = await getVideoFileFPS(tempVideoPath) + const fps = await getVideoFileFPS(tempVideoPath + 's') const stats = await statPromise(tempVideoPath) const duration = await getDurationFromVideoFile(tempVideoPath) diff --git a/server/middlewares/validators/sort.ts b/server/middlewares/validators/sort.ts index 00bde548c..d85611773 100644 --- a/server/middlewares/validators/sort.ts +++ b/server/middlewares/validators/sort.ts @@ -8,6 +8,7 @@ const SORTABLE_JOBS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.JOBS) const SORTABLE_VIDEO_ABUSES_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_ABUSES) const SORTABLE_VIDEOS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS) const SORTABLE_VIDEOS_SEARCH_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS_SEARCH) +const SORTABLE_VIDEO_IMPORTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_IMPORTS) const SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_COMMENT_THREADS) const SORTABLE_BLACKLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.BLACKLISTS) const SORTABLE_VIDEO_CHANNELS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_CHANNELS) @@ -19,6 +20,7 @@ const accountsSortValidator = checkSort(SORTABLE_ACCOUNTS_COLUMNS) const jobsSortValidator = checkSort(SORTABLE_JOBS_COLUMNS) const videoAbusesSortValidator = checkSort(SORTABLE_VIDEO_ABUSES_COLUMNS) const videosSortValidator = checkSort(SORTABLE_VIDEOS_COLUMNS) +const videoImportsSortValidator = checkSort(SORTABLE_VIDEO_IMPORTS_COLUMNS) const videosSearchSortValidator = checkSort(SORTABLE_VIDEOS_SEARCH_COLUMNS) const videoCommentThreadsSortValidator = checkSort(SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS) const blacklistSortValidator = checkSort(SORTABLE_BLACKLISTS_COLUMNS) @@ -32,6 +34,7 @@ export { usersSortValidator, videoAbusesSortValidator, videoChannelsSortValidator, + videoImportsSortValidator, videosSearchSortValidator, videosSortValidator, blacklistSortValidator, diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts index 89eeafd6a..6b8a16b65 100644 --- a/server/models/video/video-import.ts +++ b/server/models/video/video-import.ts @@ -1,4 +1,5 @@ import { + AfterUpdate, AllowNull, BelongsTo, Column, @@ -12,13 +13,14 @@ import { Table, UpdatedAt } from 'sequelize-typescript' -import { CONSTRAINTS_FIELDS } from '../../initializers' -import { throwIfNotValid } from '../utils' +import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers' +import { getSort, throwIfNotValid } from '../utils' import { VideoModel } from './video' import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports' import { VideoImport, VideoImportState } from '../../../shared' import { VideoChannelModel } from './video-channel' import { AccountModel } from '../account/account' +import { TagModel } from './tag' @DefaultScope({ include: [ @@ -35,6 +37,10 @@ import { AccountModel } from '../account/account' required: true } ] + }, + { + model: () => TagModel, + required: false } ] } @@ -79,27 +85,89 @@ export class VideoImportModel extends Model { @BelongsTo(() => VideoModel, { foreignKey: { - allowNull: false + allowNull: true }, - onDelete: 'CASCADE' + onDelete: 'set null' }) Video: VideoModel + @AfterUpdate + static deleteVideoIfFailed (instance: VideoImportModel, options) { + if (instance.state === VideoImportState.FAILED) { + return instance.Video.destroy({ transaction: options.transaction }) + } + + return undefined + } + static loadAndPopulateVideo (id: number) { return VideoImportModel.findById(id) } + static listUserVideoImportsForApi (accountId: number, start: number, count: number, sort: string) { + const query = { + offset: start, + limit: count, + order: getSort(sort), + include: [ + { + model: VideoModel, + required: true, + include: [ + { + model: VideoChannelModel, + required: true, + include: [ + { + model: AccountModel, + required: true, + where: { + id: accountId + } + } + ] + }, + { + model: TagModel, + required: false + } + ] + } + ] + } + + return VideoImportModel.unscoped() + .findAndCountAll(query) + .then(({ rows, count }) => { + return { + data: rows, + total: count + } + }) + } + toFormattedJSON (): VideoImport { const videoFormatOptions = { additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true } } - const video = Object.assign(this.Video.toFormattedJSON(videoFormatOptions), { - tags: this.Video.Tags.map(t => t.name) - }) + const video = this.Video + ? Object.assign(this.Video.toFormattedJSON(videoFormatOptions), { + tags: this.Video.Tags.map(t => t.name) + }) + : undefined return { targetUrl: this.targetUrl, + state: { + id: this.state, + label: VideoImportModel.getStateLabel(this.state) + }, + updatedAt: this.updatedAt.toISOString(), + createdAt: this.createdAt.toISOString(), video } } + private static getStateLabel (id: number) { + return VIDEO_IMPORT_STATES[id] || 'Unknown' + } } diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 459fcb31e..f32010014 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -1569,21 +1569,25 @@ export class VideoModel extends Model { removeThumbnail () { const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) return unlinkPromise(thumbnailPath) + .catch(err => logger.warn('Cannot delete thumbnail %s.', thumbnailPath, { err })) } removePreview () { - // Same name than video thumbnail - return unlinkPromise(CONFIG.STORAGE.PREVIEWS_DIR + this.getPreviewName()) + const previewPath = join(CONFIG.STORAGE.PREVIEWS_DIR + this.getPreviewName()) + return unlinkPromise(previewPath) + .catch(err => logger.warn('Cannot delete preview %s.', previewPath, { err })) } removeFile (videoFile: VideoFileModel) { const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) return unlinkPromise(filePath) + .catch(err => logger.warn('Cannot delete file %s.', filePath, { err })) } removeTorrent (videoFile: VideoFileModel) { const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) return unlinkPromise(torrentPath) + .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) } getActivityStreamDuration () { -- cgit v1.2.3