aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/users.ts30
-rw-r--r--server/helpers/youtube-dl.ts2
-rw-r--r--server/initializers/constants.ts1
-rw-r--r--server/lib/job-queue/handlers/video-import.ts2
-rw-r--r--server/middlewares/validators/sort.ts3
-rw-r--r--server/models/video/video-import.ts82
-rw-r--r--server/models/video/video.ts8
7 files changed, 116 insertions, 12 deletions
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 {
29 usersUpdateValidator, 29 usersUpdateValidator,
30 usersVideoRatingValidator 30 usersVideoRatingValidator
31} from '../../middlewares' 31} from '../../middlewares'
32import { usersAskResetPasswordValidator, usersResetPasswordValidator, videosSortValidator } from '../../middlewares/validators' 32import {
33 usersAskResetPasswordValidator,
34 usersResetPasswordValidator,
35 videoImportsSortValidator,
36 videosSortValidator
37} from '../../middlewares/validators'
33import { AccountVideoRateModel } from '../../models/account/account-video-rate' 38import { AccountVideoRateModel } from '../../models/account/account-video-rate'
34import { UserModel } from '../../models/account/user' 39import { UserModel } from '../../models/account/user'
35import { OAuthTokenModel } from '../../models/oauth/oauth-token' 40import { OAuthTokenModel } from '../../models/oauth/oauth-token'
@@ -40,6 +45,7 @@ import { UserVideoQuota } from '../../../shared/models/users/user-video-quota.mo
40import { updateAvatarValidator } from '../../middlewares/validators/avatar' 45import { updateAvatarValidator } from '../../middlewares/validators/avatar'
41import { updateActorAvatarFile } from '../../lib/avatar' 46import { updateActorAvatarFile } from '../../lib/avatar'
42import { auditLoggerFactory, UserAuditView } from '../../helpers/audit-logger' 47import { auditLoggerFactory, UserAuditView } from '../../helpers/audit-logger'
48import { VideoImportModel } from '../../models/video/video-import'
43 49
44const auditLogger = auditLoggerFactory('users') 50const auditLogger = auditLoggerFactory('users')
45 51
@@ -62,6 +68,16 @@ usersRouter.get('/me/video-quota-used',
62 asyncMiddleware(getUserVideoQuotaUsed) 68 asyncMiddleware(getUserVideoQuotaUsed)
63) 69)
64 70
71
72usersRouter.get('/me/videos/imports',
73 authenticate,
74 paginationValidator,
75 videoImportsSortValidator,
76 setDefaultSort,
77 setDefaultPagination,
78 asyncMiddleware(getUserVideoImports)
79)
80
65usersRouter.get('/me/videos', 81usersRouter.get('/me/videos',
66 authenticate, 82 authenticate,
67 paginationValidator, 83 paginationValidator,
@@ -178,6 +194,18 @@ async function getUserVideos (req: express.Request, res: express.Response, next:
178 return res.json(getFormattedObjects(resultList.data, resultList.total, { additionalAttributes })) 194 return res.json(getFormattedObjects(resultList.data, resultList.total, { additionalAttributes }))
179} 195}
180 196
197async function getUserVideoImports (req: express.Request, res: express.Response, next: express.NextFunction) {
198 const user = res.locals.oauth.token.User as UserModel
199 const resultList = await VideoImportModel.listUserVideoImportsForApi(
200 user.Account.id,
201 req.query.start as number,
202 req.query.count as number,
203 req.query.sort
204 )
205
206 return res.json(getFormattedObjects(resultList.data, resultList.total))
207}
208
181async function createUser (req: express.Request, res: express.Response) { 209async function createUser (req: express.Request, res: express.Response) {
182 const body: UserCreate = req.body 210 const body: UserCreate = req.body
183 const userToCreate = new UserModel({ 211 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) {
95} 95}
96 96
97function descriptionTruncation (description: string) { 97function descriptionTruncation (description: string) {
98 if (!description) return undefined 98 if (!description || description.length < CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.min) return undefined
99 99
100 return truncate(description, { 100 return truncate(description, {
101 'length': CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max, 101 '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 = {
37 VIDEO_ABUSES: [ 'id', 'createdAt' ], 37 VIDEO_ABUSES: [ 'id', 'createdAt' ],
38 VIDEO_CHANNELS: [ 'id', 'name', 'updatedAt', 'createdAt' ], 38 VIDEO_CHANNELS: [ 'id', 'name', 'updatedAt', 'createdAt' ],
39 VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes' ], 39 VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes' ],
40 VIDEO_IMPORTS: [ 'createdAt' ],
40 VIDEO_COMMENT_THREADS: [ 'createdAt' ], 41 VIDEO_COMMENT_THREADS: [ 'createdAt' ],
41 BLACKLISTS: [ 'id', 'name', 'duration', 'views', 'likes', 'dislikes', 'uuid', 'createdAt' ], 42 BLACKLISTS: [ 'id', 'name', 'duration', 'views', 'likes', 'dislikes', 'uuid', 'createdAt' ],
42 FOLLOWERS: [ 'createdAt' ], 43 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) {
35 35
36 // Get information about this video 36 // Get information about this video
37 const { videoFileResolution } = await getVideoFileResolution(tempVideoPath) 37 const { videoFileResolution } = await getVideoFileResolution(tempVideoPath)
38 const fps = await getVideoFileFPS(tempVideoPath) 38 const fps = await getVideoFileFPS(tempVideoPath + 's')
39 const stats = await statPromise(tempVideoPath) 39 const stats = await statPromise(tempVideoPath)
40 const duration = await getDurationFromVideoFile(tempVideoPath) 40 const duration = await getDurationFromVideoFile(tempVideoPath)
41 41
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)
8const SORTABLE_VIDEO_ABUSES_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_ABUSES) 8const SORTABLE_VIDEO_ABUSES_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_ABUSES)
9const SORTABLE_VIDEOS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS) 9const SORTABLE_VIDEOS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS)
10const SORTABLE_VIDEOS_SEARCH_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS_SEARCH) 10const SORTABLE_VIDEOS_SEARCH_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS_SEARCH)
11const SORTABLE_VIDEO_IMPORTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_IMPORTS)
11const SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_COMMENT_THREADS) 12const SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_COMMENT_THREADS)
12const SORTABLE_BLACKLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.BLACKLISTS) 13const SORTABLE_BLACKLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.BLACKLISTS)
13const SORTABLE_VIDEO_CHANNELS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_CHANNELS) 14const SORTABLE_VIDEO_CHANNELS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_CHANNELS)
@@ -19,6 +20,7 @@ const accountsSortValidator = checkSort(SORTABLE_ACCOUNTS_COLUMNS)
19const jobsSortValidator = checkSort(SORTABLE_JOBS_COLUMNS) 20const jobsSortValidator = checkSort(SORTABLE_JOBS_COLUMNS)
20const videoAbusesSortValidator = checkSort(SORTABLE_VIDEO_ABUSES_COLUMNS) 21const videoAbusesSortValidator = checkSort(SORTABLE_VIDEO_ABUSES_COLUMNS)
21const videosSortValidator = checkSort(SORTABLE_VIDEOS_COLUMNS) 22const videosSortValidator = checkSort(SORTABLE_VIDEOS_COLUMNS)
23const videoImportsSortValidator = checkSort(SORTABLE_VIDEO_IMPORTS_COLUMNS)
22const videosSearchSortValidator = checkSort(SORTABLE_VIDEOS_SEARCH_COLUMNS) 24const videosSearchSortValidator = checkSort(SORTABLE_VIDEOS_SEARCH_COLUMNS)
23const videoCommentThreadsSortValidator = checkSort(SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS) 25const videoCommentThreadsSortValidator = checkSort(SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS)
24const blacklistSortValidator = checkSort(SORTABLE_BLACKLISTS_COLUMNS) 26const blacklistSortValidator = checkSort(SORTABLE_BLACKLISTS_COLUMNS)
@@ -32,6 +34,7 @@ export {
32 usersSortValidator, 34 usersSortValidator,
33 videoAbusesSortValidator, 35 videoAbusesSortValidator,
34 videoChannelsSortValidator, 36 videoChannelsSortValidator,
37 videoImportsSortValidator,
35 videosSearchSortValidator, 38 videosSearchSortValidator,
36 videosSortValidator, 39 videosSortValidator,
37 blacklistSortValidator, 40 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 @@
1import { 1import {
2 AfterUpdate,
2 AllowNull, 3 AllowNull,
3 BelongsTo, 4 BelongsTo,
4 Column, 5 Column,
@@ -12,13 +13,14 @@ import {
12 Table, 13 Table,
13 UpdatedAt 14 UpdatedAt
14} from 'sequelize-typescript' 15} from 'sequelize-typescript'
15import { CONSTRAINTS_FIELDS } from '../../initializers' 16import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers'
16import { throwIfNotValid } from '../utils' 17import { getSort, throwIfNotValid } from '../utils'
17import { VideoModel } from './video' 18import { VideoModel } from './video'
18import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports' 19import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports'
19import { VideoImport, VideoImportState } from '../../../shared' 20import { VideoImport, VideoImportState } from '../../../shared'
20import { VideoChannelModel } from './video-channel' 21import { VideoChannelModel } from './video-channel'
21import { AccountModel } from '../account/account' 22import { AccountModel } from '../account/account'
23import { TagModel } from './tag'
22 24
23@DefaultScope({ 25@DefaultScope({
24 include: [ 26 include: [
@@ -35,6 +37,10 @@ import { AccountModel } from '../account/account'
35 required: true 37 required: true
36 } 38 }
37 ] 39 ]
40 },
41 {
42 model: () => TagModel,
43 required: false
38 } 44 }
39 ] 45 ]
40 } 46 }
@@ -79,27 +85,89 @@ export class VideoImportModel extends Model<VideoImportModel> {
79 85
80 @BelongsTo(() => VideoModel, { 86 @BelongsTo(() => VideoModel, {
81 foreignKey: { 87 foreignKey: {
82 allowNull: false 88 allowNull: true
83 }, 89 },
84 onDelete: 'CASCADE' 90 onDelete: 'set null'
85 }) 91 })
86 Video: VideoModel 92 Video: VideoModel
87 93
94 @AfterUpdate
95 static deleteVideoIfFailed (instance: VideoImportModel, options) {
96 if (instance.state === VideoImportState.FAILED) {
97 return instance.Video.destroy({ transaction: options.transaction })
98 }
99
100 return undefined
101 }
102
88 static loadAndPopulateVideo (id: number) { 103 static loadAndPopulateVideo (id: number) {
89 return VideoImportModel.findById(id) 104 return VideoImportModel.findById(id)
90 } 105 }
91 106
107 static listUserVideoImportsForApi (accountId: number, start: number, count: number, sort: string) {
108 const query = {
109 offset: start,
110 limit: count,
111 order: getSort(sort),
112 include: [
113 {
114 model: VideoModel,
115 required: true,
116 include: [
117 {
118 model: VideoChannelModel,
119 required: true,
120 include: [
121 {
122 model: AccountModel,
123 required: true,
124 where: {
125 id: accountId
126 }
127 }
128 ]
129 },
130 {
131 model: TagModel,
132 required: false
133 }
134 ]
135 }
136 ]
137 }
138
139 return VideoImportModel.unscoped()
140 .findAndCountAll(query)
141 .then(({ rows, count }) => {
142 return {
143 data: rows,
144 total: count
145 }
146 })
147 }
148
92 toFormattedJSON (): VideoImport { 149 toFormattedJSON (): VideoImport {
93 const videoFormatOptions = { 150 const videoFormatOptions = {
94 additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true } 151 additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true }
95 } 152 }
96 const video = Object.assign(this.Video.toFormattedJSON(videoFormatOptions), { 153 const video = this.Video
97 tags: this.Video.Tags.map(t => t.name) 154 ? Object.assign(this.Video.toFormattedJSON(videoFormatOptions), {
98 }) 155 tags: this.Video.Tags.map(t => t.name)
156 })
157 : undefined
99 158
100 return { 159 return {
101 targetUrl: this.targetUrl, 160 targetUrl: this.targetUrl,
161 state: {
162 id: this.state,
163 label: VideoImportModel.getStateLabel(this.state)
164 },
165 updatedAt: this.updatedAt.toISOString(),
166 createdAt: this.createdAt.toISOString(),
102 video 167 video
103 } 168 }
104 } 169 }
170 private static getStateLabel (id: number) {
171 return VIDEO_IMPORT_STATES[id] || 'Unknown'
172 }
105} 173}
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<VideoModel> {
1569 removeThumbnail () { 1569 removeThumbnail () {
1570 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) 1570 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
1571 return unlinkPromise(thumbnailPath) 1571 return unlinkPromise(thumbnailPath)
1572 .catch(err => logger.warn('Cannot delete thumbnail %s.', thumbnailPath, { err }))
1572 } 1573 }
1573 1574
1574 removePreview () { 1575 removePreview () {
1575 // Same name than video thumbnail 1576 const previewPath = join(CONFIG.STORAGE.PREVIEWS_DIR + this.getPreviewName())
1576 return unlinkPromise(CONFIG.STORAGE.PREVIEWS_DIR + this.getPreviewName()) 1577 return unlinkPromise(previewPath)
1578 .catch(err => logger.warn('Cannot delete preview %s.', previewPath, { err }))
1577 } 1579 }
1578 1580
1579 removeFile (videoFile: VideoFileModel) { 1581 removeFile (videoFile: VideoFileModel) {
1580 const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) 1582 const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
1581 return unlinkPromise(filePath) 1583 return unlinkPromise(filePath)
1584 .catch(err => logger.warn('Cannot delete file %s.', filePath, { err }))
1582 } 1585 }
1583 1586
1584 removeTorrent (videoFile: VideoFileModel) { 1587 removeTorrent (videoFile: VideoFileModel) {
1585 const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) 1588 const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
1586 return unlinkPromise(torrentPath) 1589 return unlinkPromise(torrentPath)
1590 .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err }))
1587 } 1591 }
1588 1592
1589 getActivityStreamDuration () { 1593 getActivityStreamDuration () {