diff options
Diffstat (limited to 'server')
163 files changed, 1509 insertions, 1292 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index 1b4acc234..1982e171d 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts | |||
@@ -30,7 +30,7 @@ import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } | |||
30 | import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' | 30 | import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' |
31 | import { AccountModel } from '../../models/account/account' | 31 | import { AccountModel } from '../../models/account/account' |
32 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' | 32 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' |
33 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | 33 | import { ActorFollowModel } from '../../models/actor/actor-follow' |
34 | import { VideoModel } from '../../models/video/video' | 34 | import { VideoModel } from '../../models/video/video' |
35 | import { VideoCaptionModel } from '../../models/video/video-caption' | 35 | import { VideoCaptionModel } from '../../models/video/video-caption' |
36 | import { VideoCommentModel } from '../../models/video/video-comment' | 36 | import { VideoCommentModel } from '../../models/video/video-comment' |
diff --git a/server/controllers/activitypub/utils.ts b/server/controllers/activitypub/utils.ts index 599cf48ab..19bdd58eb 100644 --- a/server/controllers/activitypub/utils.ts +++ b/server/controllers/activitypub/utils.ts | |||
@@ -3,7 +3,6 @@ import * as express from 'express' | |||
3 | function activityPubResponse (data: any, res: express.Response) { | 3 | function activityPubResponse (data: any, res: express.Response) { |
4 | return res.type('application/activity+json; charset=utf-8') | 4 | return res.type('application/activity+json; charset=utf-8') |
5 | .json(data) | 5 | .json(data) |
6 | .end() | ||
7 | } | 6 | } |
8 | 7 | ||
9 | export { | 8 | export { |
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 2ddb73519..5ce7adc35 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -18,6 +18,7 @@ const configRouter = express.Router() | |||
18 | const auditLogger = auditLoggerFactory('config') | 18 | const auditLogger = auditLoggerFactory('config') |
19 | 19 | ||
20 | configRouter.get('/about', getAbout) | 20 | configRouter.get('/about', getAbout) |
21 | |||
21 | configRouter.get('/', | 22 | configRouter.get('/', |
22 | asyncMiddleware(getConfig) | 23 | asyncMiddleware(getConfig) |
23 | ) | 24 | ) |
@@ -27,12 +28,14 @@ configRouter.get('/custom', | |||
27 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), | 28 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), |
28 | getCustomConfig | 29 | getCustomConfig |
29 | ) | 30 | ) |
31 | |||
30 | configRouter.put('/custom', | 32 | configRouter.put('/custom', |
31 | authenticate, | 33 | authenticate, |
32 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), | 34 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), |
33 | customConfigUpdateValidator, | 35 | customConfigUpdateValidator, |
34 | asyncMiddleware(updateCustomConfig) | 36 | asyncMiddleware(updateCustomConfig) |
35 | ) | 37 | ) |
38 | |||
36 | configRouter.delete('/custom', | 39 | configRouter.delete('/custom', |
37 | authenticate, | 40 | authenticate, |
38 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), | 41 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), |
@@ -67,13 +70,13 @@ function getAbout (req: express.Request, res: express.Response) { | |||
67 | } | 70 | } |
68 | } | 71 | } |
69 | 72 | ||
70 | return res.json(about).end() | 73 | return res.json(about) |
71 | } | 74 | } |
72 | 75 | ||
73 | function getCustomConfig (req: express.Request, res: express.Response) { | 76 | function getCustomConfig (req: express.Request, res: express.Response) { |
74 | const data = customConfig() | 77 | const data = customConfig() |
75 | 78 | ||
76 | return res.json(data).end() | 79 | return res.json(data) |
77 | } | 80 | } |
78 | 81 | ||
79 | async function deleteCustomConfig (req: express.Request, res: express.Response) { | 82 | async function deleteCustomConfig (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/api/plugins.ts b/server/controllers/api/plugins.ts index a186de010..e18eed332 100644 --- a/server/controllers/api/plugins.ts +++ b/server/controllers/api/plugins.ts | |||
@@ -1,16 +1,18 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getFormattedObjects } from '../../helpers/utils' | 2 | import { logger } from '@server/helpers/logger' |
3 | import { getFormattedObjects } from '@server/helpers/utils' | ||
4 | import { listAvailablePluginsFromIndex } from '@server/lib/plugins/plugin-index' | ||
5 | import { PluginManager } from '@server/lib/plugins/plugin-manager' | ||
3 | import { | 6 | import { |
4 | asyncMiddleware, | 7 | asyncMiddleware, |
5 | authenticate, | 8 | authenticate, |
9 | availablePluginsSortValidator, | ||
6 | ensureUserHasRight, | 10 | ensureUserHasRight, |
7 | paginationValidator, | 11 | paginationValidator, |
12 | pluginsSortValidator, | ||
8 | setDefaultPagination, | 13 | setDefaultPagination, |
9 | setDefaultSort | 14 | setDefaultSort |
10 | } from '../../middlewares' | 15 | } from '@server/middlewares' |
11 | import { availablePluginsSortValidator, pluginsSortValidator } from '../../middlewares/validators' | ||
12 | import { PluginModel } from '../../models/server/plugin' | ||
13 | import { UserRight } from '../../../shared/models/users' | ||
14 | import { | 16 | import { |
15 | existingPluginValidator, | 17 | existingPluginValidator, |
16 | installOrUpdatePluginValidator, | 18 | installOrUpdatePluginValidator, |
@@ -18,16 +20,17 @@ import { | |||
18 | listPluginsValidator, | 20 | listPluginsValidator, |
19 | uninstallPluginValidator, | 21 | uninstallPluginValidator, |
20 | updatePluginSettingsValidator | 22 | updatePluginSettingsValidator |
21 | } from '../../middlewares/validators/plugins' | 23 | } from '@server/middlewares/validators/plugins' |
22 | import { PluginManager } from '../../lib/plugins/plugin-manager' | 24 | import { PluginModel } from '@server/models/server/plugin' |
23 | import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/install-plugin.model' | 25 | import { HttpStatusCode } from '@shared/core-utils' |
24 | import { ManagePlugin } from '../../../shared/models/plugins/manage-plugin.model' | 26 | import { |
25 | import { logger } from '../../helpers/logger' | 27 | InstallOrUpdatePlugin, |
26 | import { listAvailablePluginsFromIndex } from '../../lib/plugins/plugin-index' | 28 | ManagePlugin, |
27 | import { PeertubePluginIndexList } from '../../../shared/models/plugins/peertube-plugin-index-list.model' | 29 | PeertubePluginIndexList, |
28 | import { RegisteredServerSettings } from '../../../shared/models/plugins/register-server-setting.model' | 30 | PublicServerSetting, |
29 | import { PublicServerSetting } from '../../../shared/models/plugins/public-server.setting' | 31 | RegisteredServerSettings, |
30 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 32 | UserRight |
33 | } from '@shared/models' | ||
31 | 34 | ||
32 | const pluginRouter = express.Router() | 35 | const pluginRouter = express.Router() |
33 | 36 | ||
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index 80025bc5b..daeef22de 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts | |||
@@ -1,9 +1,15 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getServerActor } from '@server/models/application/application' | ||
3 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
2 | import { UserRight } from '../../../../shared/models/users' | 4 | import { UserRight } from '../../../../shared/models/users' |
3 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
4 | import { getFormattedObjects } from '../../../helpers/utils' | 6 | import { getFormattedObjects } from '../../../helpers/utils' |
5 | import { SERVER_ACTOR_NAME } from '../../../initializers/constants' | 7 | import { SERVER_ACTOR_NAME } from '../../../initializers/constants' |
8 | import { sequelizeTypescript } from '../../../initializers/database' | ||
9 | import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow' | ||
6 | import { sendAccept, sendReject, sendUndoFollow } from '../../../lib/activitypub/send' | 10 | import { sendAccept, sendReject, sendUndoFollow } from '../../../lib/activitypub/send' |
11 | import { JobQueue } from '../../../lib/job-queue' | ||
12 | import { removeRedundanciesOfServer } from '../../../lib/redundancy' | ||
7 | import { | 13 | import { |
8 | asyncMiddleware, | 14 | asyncMiddleware, |
9 | authenticate, | 15 | authenticate, |
@@ -19,16 +25,10 @@ import { | |||
19 | followingSortValidator, | 25 | followingSortValidator, |
20 | followValidator, | 26 | followValidator, |
21 | getFollowerValidator, | 27 | getFollowerValidator, |
22 | removeFollowingValidator, | 28 | listFollowsValidator, |
23 | listFollowsValidator | 29 | removeFollowingValidator |
24 | } from '../../../middlewares/validators' | 30 | } from '../../../middlewares/validators' |
25 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 31 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
26 | import { JobQueue } from '../../../lib/job-queue' | ||
27 | import { removeRedundanciesOfServer } from '../../../lib/redundancy' | ||
28 | import { sequelizeTypescript } from '../../../initializers/database' | ||
29 | import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow' | ||
30 | import { getServerActor } from '@server/models/application/application' | ||
31 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
32 | 32 | ||
33 | const serverFollowsRouter = express.Router() | 33 | const serverFollowsRouter = express.Router() |
34 | serverFollowsRouter.get('/following', | 34 | serverFollowsRouter.get('/following', |
diff --git a/server/controllers/api/server/server-blocklist.ts b/server/controllers/api/server/server-blocklist.ts index 6e341c0fb..a86bc7d19 100644 --- a/server/controllers/api/server/server-blocklist.ts +++ b/server/controllers/api/server/server-blocklist.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import 'multer' | 1 | import 'multer' |
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { logger } from '@server/helpers/logger' | 3 | import { logger } from '@server/helpers/logger' |
4 | import { UserNotificationModel } from '@server/models/account/user-notification' | 4 | import { UserNotificationModel } from '@server/models/user/user-notification' |
5 | import { getServerActor } from '@server/models/application/application' | 5 | import { getServerActor } from '@server/models/application/application' |
6 | import { UserRight } from '../../../../shared/models/users' | 6 | import { UserRight } from '../../../../shared/models/users' |
7 | import { getFormattedObjects } from '../../../helpers/utils' | 7 | import { getFormattedObjects } from '../../../helpers/utils' |
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index e2b1ea7cd..f384f0f28 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts | |||
@@ -45,7 +45,7 @@ import { | |||
45 | usersResetPasswordValidator, | 45 | usersResetPasswordValidator, |
46 | usersVerifyEmailValidator | 46 | usersVerifyEmailValidator |
47 | } from '../../../middlewares/validators' | 47 | } from '../../../middlewares/validators' |
48 | import { UserModel } from '../../../models/account/user' | 48 | import { UserModel } from '../../../models/user/user' |
49 | import { meRouter } from './me' | 49 | import { meRouter } from './me' |
50 | import { myAbusesRouter } from './my-abuses' | 50 | import { myAbusesRouter } from './my-abuses' |
51 | import { myBlocklistRouter } from './my-blocklist' | 51 | import { myBlocklistRouter } from './my-blocklist' |
@@ -323,14 +323,20 @@ async function updateUser (req: express.Request, res: express.Response) { | |||
323 | const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON()) | 323 | const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON()) |
324 | const roleChanged = body.role !== undefined && body.role !== userToUpdate.role | 324 | const roleChanged = body.role !== undefined && body.role !== userToUpdate.role |
325 | 325 | ||
326 | if (body.password !== undefined) userToUpdate.password = body.password | 326 | const keysToUpdate: (keyof UserUpdate)[] = [ |
327 | if (body.email !== undefined) userToUpdate.email = body.email | 327 | 'password', |
328 | if (body.emailVerified !== undefined) userToUpdate.emailVerified = body.emailVerified | 328 | 'email', |
329 | if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota | 329 | 'emailVerified', |
330 | if (body.videoQuotaDaily !== undefined) userToUpdate.videoQuotaDaily = body.videoQuotaDaily | 330 | 'videoQuota', |
331 | if (body.role !== undefined) userToUpdate.role = body.role | 331 | 'videoQuotaDaily', |
332 | if (body.adminFlags !== undefined) userToUpdate.adminFlags = body.adminFlags | 332 | 'role', |
333 | if (body.pluginAuth !== undefined) userToUpdate.pluginAuth = body.pluginAuth | 333 | 'adminFlags', |
334 | 'pluginAuth' | ||
335 | ] | ||
336 | |||
337 | for (const key of keysToUpdate) { | ||
338 | if (body[key] !== undefined) userToUpdate.set(key, body[key]) | ||
339 | } | ||
334 | 340 | ||
335 | const user = await userToUpdate.save() | 341 | const user = await userToUpdate.save() |
336 | 342 | ||
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 0763d1900..a609abaa6 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -28,9 +28,10 @@ import { deleteMeValidator, videoImportsSortValidator, videosSortValidator } fro | |||
28 | import { updateAvatarValidator } from '../../../middlewares/validators/actor-image' | 28 | import { updateAvatarValidator } from '../../../middlewares/validators/actor-image' |
29 | import { AccountModel } from '../../../models/account/account' | 29 | import { AccountModel } from '../../../models/account/account' |
30 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 30 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
31 | import { UserModel } from '../../../models/account/user' | 31 | import { UserModel } from '../../../models/user/user' |
32 | import { VideoModel } from '../../../models/video/video' | 32 | import { VideoModel } from '../../../models/video/video' |
33 | import { VideoImportModel } from '../../../models/video/video-import' | 33 | import { VideoImportModel } from '../../../models/video/video-import' |
34 | import { AttributesOnly } from '@shared/core-utils' | ||
34 | 35 | ||
35 | const auditLogger = auditLoggerFactory('users') | 36 | const auditLogger = auditLoggerFactory('users') |
36 | 37 | ||
@@ -191,17 +192,23 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
191 | 192 | ||
192 | const user = res.locals.oauth.token.user | 193 | const user = res.locals.oauth.token.user |
193 | 194 | ||
194 | if (body.password !== undefined) user.password = body.password | 195 | const keysToUpdate: (keyof UserUpdateMe & keyof AttributesOnly<UserModel>)[] = [ |
195 | if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy | 196 | 'password', |
196 | if (body.webTorrentEnabled !== undefined) user.webTorrentEnabled = body.webTorrentEnabled | 197 | 'nsfwPolicy', |
197 | if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo | 198 | 'webTorrentEnabled', |
198 | if (body.autoPlayNextVideo !== undefined) user.autoPlayNextVideo = body.autoPlayNextVideo | 199 | 'autoPlayVideo', |
199 | if (body.autoPlayNextVideoPlaylist !== undefined) user.autoPlayNextVideoPlaylist = body.autoPlayNextVideoPlaylist | 200 | 'autoPlayNextVideo', |
200 | if (body.videosHistoryEnabled !== undefined) user.videosHistoryEnabled = body.videosHistoryEnabled | 201 | 'autoPlayNextVideoPlaylist', |
201 | if (body.videoLanguages !== undefined) user.videoLanguages = body.videoLanguages | 202 | 'videosHistoryEnabled', |
202 | if (body.theme !== undefined) user.theme = body.theme | 203 | 'videoLanguages', |
203 | if (body.noInstanceConfigWarningModal !== undefined) user.noInstanceConfigWarningModal = body.noInstanceConfigWarningModal | 204 | 'theme', |
204 | if (body.noWelcomeModal !== undefined) user.noWelcomeModal = body.noWelcomeModal | 205 | 'noInstanceConfigWarningModal', |
206 | 'noWelcomeModal' | ||
207 | ] | ||
208 | |||
209 | for (const key of keysToUpdate) { | ||
210 | if (body[key] !== undefined) user.set(key, body[key]) | ||
211 | } | ||
205 | 212 | ||
206 | if (body.email !== undefined) { | 213 | if (body.email !== undefined) { |
207 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { | 214 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { |
@@ -215,15 +222,15 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
215 | await sequelizeTypescript.transaction(async t => { | 222 | await sequelizeTypescript.transaction(async t => { |
216 | await user.save({ transaction: t }) | 223 | await user.save({ transaction: t }) |
217 | 224 | ||
218 | if (body.displayName !== undefined || body.description !== undefined) { | 225 | if (body.displayName === undefined && body.description === undefined) return |
219 | const userAccount = await AccountModel.load(user.Account.id, t) | ||
220 | 226 | ||
221 | if (body.displayName !== undefined) userAccount.name = body.displayName | 227 | const userAccount = await AccountModel.load(user.Account.id, t) |
222 | if (body.description !== undefined) userAccount.description = body.description | ||
223 | await userAccount.save({ transaction: t }) | ||
224 | 228 | ||
225 | await sendUpdateActor(userAccount, t) | 229 | if (body.displayName !== undefined) userAccount.name = body.displayName |
226 | } | 230 | if (body.description !== undefined) userAccount.description = body.description |
231 | await userAccount.save({ transaction: t }) | ||
232 | |||
233 | await sendUpdateActor(userAccount, t) | ||
227 | }) | 234 | }) |
228 | 235 | ||
229 | if (sendVerificationEmail === true) { | 236 | if (sendVerificationEmail === true) { |
diff --git a/server/controllers/api/users/my-blocklist.ts b/server/controllers/api/users/my-blocklist.ts index faaef3ac0..a1561b751 100644 --- a/server/controllers/api/users/my-blocklist.ts +++ b/server/controllers/api/users/my-blocklist.ts | |||
@@ -20,7 +20,7 @@ import { | |||
20 | import { AccountBlocklistModel } from '../../../models/account/account-blocklist' | 20 | import { AccountBlocklistModel } from '../../../models/account/account-blocklist' |
21 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' | 21 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' |
22 | import { ServerBlocklistModel } from '../../../models/server/server-blocklist' | 22 | import { ServerBlocklistModel } from '../../../models/server/server-blocklist' |
23 | import { UserNotificationModel } from '@server/models/account/user-notification' | 23 | import { UserNotificationModel } from '@server/models/user/user-notification' |
24 | import { logger } from '@server/helpers/logger' | 24 | import { logger } from '@server/helpers/logger' |
25 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 25 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
26 | 26 | ||
diff --git a/server/controllers/api/users/my-history.ts b/server/controllers/api/users/my-history.ts index 72c7da373..cff1697ab 100644 --- a/server/controllers/api/users/my-history.ts +++ b/server/controllers/api/users/my-history.ts | |||
@@ -9,7 +9,7 @@ import { | |||
9 | userHistoryRemoveValidator | 9 | userHistoryRemoveValidator |
10 | } from '../../../middlewares' | 10 | } from '../../../middlewares' |
11 | import { getFormattedObjects } from '../../../helpers/utils' | 11 | import { getFormattedObjects } from '../../../helpers/utils' |
12 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' | 12 | import { UserVideoHistoryModel } from '../../../models/user/user-video-history' |
13 | import { sequelizeTypescript } from '../../../initializers/database' | 13 | import { sequelizeTypescript } from '../../../initializers/database' |
14 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 14 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
15 | 15 | ||
diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts index 0a9101a46..2909770da 100644 --- a/server/controllers/api/users/my-notifications.ts +++ b/server/controllers/api/users/my-notifications.ts | |||
@@ -1,5 +1,9 @@ | |||
1 | import * as express from 'express' | ||
2 | import 'multer' | 1 | import 'multer' |
2 | import * as express from 'express' | ||
3 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
4 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
5 | import { UserNotificationSetting } from '../../../../shared/models/users' | ||
6 | import { getFormattedObjects } from '../../../helpers/utils' | ||
3 | import { | 7 | import { |
4 | asyncMiddleware, | 8 | asyncMiddleware, |
5 | asyncRetryTransactionMiddleware, | 9 | asyncRetryTransactionMiddleware, |
@@ -9,17 +13,13 @@ import { | |||
9 | setDefaultSort, | 13 | setDefaultSort, |
10 | userNotificationsSortValidator | 14 | userNotificationsSortValidator |
11 | } from '../../../middlewares' | 15 | } from '../../../middlewares' |
12 | import { getFormattedObjects } from '../../../helpers/utils' | ||
13 | import { UserNotificationModel } from '../../../models/account/user-notification' | ||
14 | import { meRouter } from './me' | ||
15 | import { | 16 | import { |
16 | listUserNotificationsValidator, | 17 | listUserNotificationsValidator, |
17 | markAsReadUserNotificationsValidator, | 18 | markAsReadUserNotificationsValidator, |
18 | updateNotificationSettingsValidator | 19 | updateNotificationSettingsValidator |
19 | } from '../../../middlewares/validators/user-notifications' | 20 | } from '../../../middlewares/validators/user-notifications' |
20 | import { UserNotificationSetting } from '../../../../shared/models/users' | 21 | import { UserNotificationSettingModel } from '../../../models/user/user-notification-setting' |
21 | import { UserNotificationSettingModel } from '../../../models/account/user-notification-setting' | 22 | import { meRouter } from './me' |
22 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
23 | 23 | ||
24 | const myNotificationsRouter = express.Router() | 24 | const myNotificationsRouter = express.Router() |
25 | 25 | ||
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts index 56b93276f..46a73d49e 100644 --- a/server/controllers/api/users/my-subscriptions.ts +++ b/server/controllers/api/users/my-subscriptions.ts | |||
@@ -27,7 +27,7 @@ import { | |||
27 | userSubscriptionsSortValidator, | 27 | userSubscriptionsSortValidator, |
28 | videosSortValidator | 28 | videosSortValidator |
29 | } from '../../../middlewares/validators' | 29 | } from '../../../middlewares/validators' |
30 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 30 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
31 | import { VideoModel } from '../../../models/video/video' | 31 | import { VideoModel } from '../../../models/video/video' |
32 | 32 | ||
33 | const mySubscriptionsRouter = express.Router() | 33 | const mySubscriptionsRouter = express.Router() |
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index a755d7e57..859d8b3c0 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -162,6 +162,7 @@ async function updateVideoChannelBanner (req: express.Request, res: express.Resp | |||
162 | 162 | ||
163 | return res.json({ banner: banner.toFormattedJSON() }) | 163 | return res.json({ banner: banner.toFormattedJSON() }) |
164 | } | 164 | } |
165 | |||
165 | async function updateVideoChannelAvatar (req: express.Request, res: express.Response) { | 166 | async function updateVideoChannelAvatar (req: express.Request, res: express.Response) { |
166 | const avatarPhysicalFile = req.files['avatarfile'][0] | 167 | const avatarPhysicalFile = req.files['avatarfile'][0] |
167 | const videoChannel = res.locals.videoChannel | 168 | const videoChannel = res.locals.videoChannel |
@@ -221,10 +222,6 @@ async function updateVideoChannel (req: express.Request, res: express.Response) | |||
221 | 222 | ||
222 | try { | 223 | try { |
223 | await sequelizeTypescript.transaction(async t => { | 224 | await sequelizeTypescript.transaction(async t => { |
224 | const sequelizeOptions = { | ||
225 | transaction: t | ||
226 | } | ||
227 | |||
228 | if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.name = videoChannelInfoToUpdate.displayName | 225 | if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.name = videoChannelInfoToUpdate.displayName |
229 | if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.description = videoChannelInfoToUpdate.description | 226 | if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.description = videoChannelInfoToUpdate.description |
230 | 227 | ||
@@ -238,7 +235,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response) | |||
238 | } | 235 | } |
239 | } | 236 | } |
240 | 237 | ||
241 | const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) as MChannelBannerAccountDefault | 238 | const videoChannelInstanceUpdated = await videoChannelInstance.save({ transaction: t }) as MChannelBannerAccountDefault |
242 | await sendUpdateActor(videoChannelInstanceUpdated, t) | 239 | await sendUpdateActor(videoChannelInstanceUpdated, t) |
243 | 240 | ||
244 | auditLogger.update( | 241 | auditLogger.update( |
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts index aab16533d..b8613699b 100644 --- a/server/controllers/api/video-playlist.ts +++ b/server/controllers/api/video-playlist.ts | |||
@@ -202,7 +202,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) { | |||
202 | id: videoPlaylistCreated.id, | 202 | id: videoPlaylistCreated.id, |
203 | uuid: videoPlaylistCreated.uuid | 203 | uuid: videoPlaylistCreated.uuid |
204 | } | 204 | } |
205 | }).end() | 205 | }) |
206 | } | 206 | } |
207 | 207 | ||
208 | async function updateVideoPlaylist (req: express.Request, res: express.Response) { | 208 | async function updateVideoPlaylist (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts index f1f53d354..cfdf2773f 100644 --- a/server/controllers/api/videos/comment.ts +++ b/server/controllers/api/videos/comment.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 2 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
3 | import { ResultList, ThreadsResultList, UserRight } from '../../../../shared/models' | 3 | import { ResultList, ThreadsResultList, UserRight } from '../../../../shared/models' |
4 | import { VideoCommentCreate } from '../../../../shared/models/videos/video-comment.model' | 4 | import { VideoCommentCreate } from '../../../../shared/models/videos/comment/video-comment.model' |
5 | import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger' | 5 | import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger' |
6 | import { getFormattedObjects } from '../../../helpers/utils' | 6 | import { getFormattedObjects } from '../../../helpers/utils' |
7 | import { sequelizeTypescript } from '../../../initializers/database' | 7 | import { sequelizeTypescript } from '../../../initializers/database' |
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index 3b9b887e2..ee63c7b77 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts | |||
@@ -3,7 +3,9 @@ import { move, readFile } from 'fs-extra' | |||
3 | import * as magnetUtil from 'magnet-uri' | 3 | import * as magnetUtil from 'magnet-uri' |
4 | import * as parseTorrent from 'parse-torrent' | 4 | import * as parseTorrent from 'parse-torrent' |
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import { getEnabledResolutions } from '@server/lib/config' | ||
6 | import { setVideoTags } from '@server/lib/video' | 7 | import { setVideoTags } from '@server/lib/video' |
8 | import { FilteredModelAttributes } from '@server/types' | ||
7 | import { | 9 | import { |
8 | MChannelAccountDefault, | 10 | MChannelAccountDefault, |
9 | MThumbnail, | 11 | MThumbnail, |
@@ -14,17 +16,17 @@ import { | |||
14 | MVideoThumbnail, | 16 | MVideoThumbnail, |
15 | MVideoWithBlacklistLight | 17 | MVideoWithBlacklistLight |
16 | } from '@server/types/models' | 18 | } from '@server/types/models' |
17 | import { MVideoImport, MVideoImportFormattable } from '@server/types/models/video/video-import' | 19 | import { MVideoImportFormattable } from '@server/types/models/video/video-import' |
18 | import { VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared' | 20 | import { ServerErrorCode, VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared' |
19 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 21 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
20 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | 22 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' |
21 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' | 23 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' |
22 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' | 24 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' |
23 | import { isArray } from '../../../helpers/custom-validators/misc' | 25 | import { isArray } from '../../../helpers/custom-validators/misc' |
24 | import { createReqFiles } from '../../../helpers/express-utils' | 26 | import { cleanUpReqFiles, createReqFiles } from '../../../helpers/express-utils' |
25 | import { logger } from '../../../helpers/logger' | 27 | import { logger } from '../../../helpers/logger' |
26 | import { getSecureTorrentName } from '../../../helpers/utils' | 28 | import { getSecureTorrentName } from '../../../helpers/utils' |
27 | import { getYoutubeDLInfo, getYoutubeDLSubs, YoutubeDLInfo } from '../../../helpers/youtube-dl' | 29 | import { YoutubeDL, YoutubeDLInfo } from '../../../helpers/youtube-dl' |
28 | import { CONFIG } from '../../../initializers/config' | 30 | import { CONFIG } from '../../../initializers/config' |
29 | import { MIMETYPES } from '../../../initializers/constants' | 31 | import { MIMETYPES } from '../../../initializers/constants' |
30 | import { sequelizeTypescript } from '../../../initializers/database' | 32 | import { sequelizeTypescript } from '../../../initializers/database' |
@@ -81,22 +83,15 @@ async function addTorrentImport (req: express.Request, res: express.Response, to | |||
81 | let magnetUri: string | 83 | let magnetUri: string |
82 | 84 | ||
83 | if (torrentfile) { | 85 | if (torrentfile) { |
84 | torrentName = torrentfile.originalname | 86 | const result = await processTorrentOrAbortRequest(req, res, torrentfile) |
87 | if (!result) return | ||
85 | 88 | ||
86 | // Rename the torrent to a secured name | 89 | videoName = result.name |
87 | const newTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, getSecureTorrentName(torrentName)) | 90 | torrentName = result.torrentName |
88 | await move(torrentfile.path, newTorrentPath) | ||
89 | torrentfile.path = newTorrentPath | ||
90 | |||
91 | const buf = await readFile(torrentfile.path) | ||
92 | const parsedTorrent = parseTorrent(buf) | ||
93 | |||
94 | videoName = isArray(parsedTorrent.name) ? parsedTorrent.name[0] : parsedTorrent.name as string | ||
95 | } else { | 91 | } else { |
96 | magnetUri = body.magnetUri | 92 | const result = processMagnetURI(body) |
97 | 93 | magnetUri = result.magnetUri | |
98 | const parsed = magnetUtil.decode(magnetUri) | 94 | videoName = result.name |
99 | videoName = isArray(parsed.name) ? parsed.name[0] : parsed.name as string | ||
100 | } | 95 | } |
101 | 96 | ||
102 | const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) | 97 | const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) |
@@ -104,26 +99,26 @@ async function addTorrentImport (req: express.Request, res: express.Response, to | |||
104 | const thumbnailModel = await processThumbnail(req, video) | 99 | const thumbnailModel = await processThumbnail(req, video) |
105 | const previewModel = await processPreview(req, video) | 100 | const previewModel = await processPreview(req, video) |
106 | 101 | ||
107 | const tags = body.tags || undefined | ||
108 | const videoImportAttributes = { | ||
109 | magnetUri, | ||
110 | torrentName, | ||
111 | state: VideoImportState.PENDING, | ||
112 | userId: user.id | ||
113 | } | ||
114 | const videoImport = await insertIntoDB({ | 102 | const videoImport = await insertIntoDB({ |
115 | video, | 103 | video, |
116 | thumbnailModel, | 104 | thumbnailModel, |
117 | previewModel, | 105 | previewModel, |
118 | videoChannel: res.locals.videoChannel, | 106 | videoChannel: res.locals.videoChannel, |
119 | tags, | 107 | tags: body.tags || undefined, |
120 | videoImportAttributes, | 108 | user, |
121 | user | 109 | videoImportAttributes: { |
110 | magnetUri, | ||
111 | torrentName, | ||
112 | state: VideoImportState.PENDING, | ||
113 | userId: user.id | ||
114 | } | ||
122 | }) | 115 | }) |
123 | 116 | ||
124 | // Create job to import the video | 117 | // Create job to import the video |
125 | const payload = { | 118 | const payload = { |
126 | type: torrentfile ? 'torrent-file' as 'torrent-file' : 'magnet-uri' as 'magnet-uri', | 119 | type: torrentfile |
120 | ? 'torrent-file' as 'torrent-file' | ||
121 | : 'magnet-uri' as 'magnet-uri', | ||
127 | videoImportId: videoImport.id, | 122 | videoImportId: videoImport.id, |
128 | magnetUri | 123 | magnetUri |
129 | } | 124 | } |
@@ -139,10 +134,12 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
139 | const targetUrl = body.targetUrl | 134 | const targetUrl = body.targetUrl |
140 | const user = res.locals.oauth.token.User | 135 | const user = res.locals.oauth.token.User |
141 | 136 | ||
137 | const youtubeDL = new YoutubeDL(targetUrl, getEnabledResolutions('vod')) | ||
138 | |||
142 | // Get video infos | 139 | // Get video infos |
143 | let youtubeDLInfo: YoutubeDLInfo | 140 | let youtubeDLInfo: YoutubeDLInfo |
144 | try { | 141 | try { |
145 | youtubeDLInfo = await getYoutubeDLInfo(targetUrl) | 142 | youtubeDLInfo = await youtubeDL.getYoutubeDLInfo() |
146 | } catch (err) { | 143 | } catch (err) { |
147 | logger.info('Cannot fetch information from import for URL %s.', targetUrl, { err }) | 144 | logger.info('Cannot fetch information from import for URL %s.', targetUrl, { err }) |
148 | 145 | ||
@@ -170,45 +167,22 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
170 | previewModel = await processPreviewFromUrl(youtubeDLInfo.thumbnailUrl, video) | 167 | previewModel = await processPreviewFromUrl(youtubeDLInfo.thumbnailUrl, video) |
171 | } | 168 | } |
172 | 169 | ||
173 | const tags = body.tags || youtubeDLInfo.tags | ||
174 | const videoImportAttributes = { | ||
175 | targetUrl, | ||
176 | state: VideoImportState.PENDING, | ||
177 | userId: user.id | ||
178 | } | ||
179 | const videoImport = await insertIntoDB({ | 170 | const videoImport = await insertIntoDB({ |
180 | video, | 171 | video, |
181 | thumbnailModel, | 172 | thumbnailModel, |
182 | previewModel, | 173 | previewModel, |
183 | videoChannel: res.locals.videoChannel, | 174 | videoChannel: res.locals.videoChannel, |
184 | tags, | 175 | tags: body.tags || youtubeDLInfo.tags, |
185 | videoImportAttributes, | 176 | user, |
186 | user | 177 | videoImportAttributes: { |
178 | targetUrl, | ||
179 | state: VideoImportState.PENDING, | ||
180 | userId: user.id | ||
181 | } | ||
187 | }) | 182 | }) |
188 | 183 | ||
189 | // Get video subtitles | 184 | // Get video subtitles |
190 | try { | 185 | await processYoutubeSubtitles(youtubeDL, targetUrl, video.id) |
191 | const subtitles = await getYoutubeDLSubs(targetUrl) | ||
192 | |||
193 | logger.info('Will create %s subtitles from youtube import %s.', subtitles.length, targetUrl) | ||
194 | |||
195 | for (const subtitle of subtitles) { | ||
196 | const videoCaption = new VideoCaptionModel({ | ||
197 | videoId: video.id, | ||
198 | language: subtitle.language, | ||
199 | filename: VideoCaptionModel.generateCaptionName(subtitle.language) | ||
200 | }) as MVideoCaption | ||
201 | |||
202 | // Move physical file | ||
203 | await moveAndProcessCaptionFile(subtitle, videoCaption) | ||
204 | |||
205 | await sequelizeTypescript.transaction(async t => { | ||
206 | await VideoCaptionModel.insertOrReplaceLanguage(videoCaption, t) | ||
207 | }) | ||
208 | } | ||
209 | } catch (err) { | ||
210 | logger.warn('Cannot get video subtitles.', { err }) | ||
211 | } | ||
212 | 186 | ||
213 | // Create job to import the video | 187 | // Create job to import the video |
214 | const payload = { | 188 | const payload = { |
@@ -240,7 +214,9 @@ function buildVideo (channelId: number, body: VideoImportCreate, importData: You | |||
240 | privacy: body.privacy || VideoPrivacy.PRIVATE, | 214 | privacy: body.privacy || VideoPrivacy.PRIVATE, |
241 | duration: 0, // duration will be set by the import job | 215 | duration: 0, // duration will be set by the import job |
242 | channelId: channelId, | 216 | channelId: channelId, |
243 | originallyPublishedAt: body.originallyPublishedAt || importData.originallyPublishedAt | 217 | originallyPublishedAt: body.originallyPublishedAt |
218 | ? new Date(body.originallyPublishedAt) | ||
219 | : importData.originallyPublishedAt | ||
244 | } | 220 | } |
245 | const video = new VideoModel(videoData) | 221 | const video = new VideoModel(videoData) |
246 | video.url = getLocalVideoActivityPubUrl(video) | 222 | video.url = getLocalVideoActivityPubUrl(video) |
@@ -304,7 +280,7 @@ async function insertIntoDB (parameters: { | |||
304 | previewModel: MThumbnail | 280 | previewModel: MThumbnail |
305 | videoChannel: MChannelAccountDefault | 281 | videoChannel: MChannelAccountDefault |
306 | tags: string[] | 282 | tags: string[] |
307 | videoImportAttributes: Partial<MVideoImport> | 283 | videoImportAttributes: FilteredModelAttributes<VideoImportModel> |
308 | user: MUser | 284 | user: MUser |
309 | }): Promise<MVideoImportFormattable> { | 285 | }): Promise<MVideoImportFormattable> { |
310 | const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters | 286 | const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters |
@@ -342,3 +318,71 @@ async function insertIntoDB (parameters: { | |||
342 | 318 | ||
343 | return videoImport | 319 | return videoImport |
344 | } | 320 | } |
321 | |||
322 | async function processTorrentOrAbortRequest (req: express.Request, res: express.Response, torrentfile: Express.Multer.File) { | ||
323 | const torrentName = torrentfile.originalname | ||
324 | |||
325 | // Rename the torrent to a secured name | ||
326 | const newTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, getSecureTorrentName(torrentName)) | ||
327 | await move(torrentfile.path, newTorrentPath, { overwrite: true }) | ||
328 | torrentfile.path = newTorrentPath | ||
329 | |||
330 | const buf = await readFile(torrentfile.path) | ||
331 | const parsedTorrent = parseTorrent(buf) as parseTorrent.Instance | ||
332 | |||
333 | if (parsedTorrent.files.length !== 1) { | ||
334 | cleanUpReqFiles(req) | ||
335 | |||
336 | res.status(HttpStatusCode.BAD_REQUEST_400) | ||
337 | .json({ | ||
338 | code: ServerErrorCode.INCORRECT_FILES_IN_TORRENT, | ||
339 | error: 'Torrents with only 1 file are supported.' | ||
340 | }) | ||
341 | |||
342 | return undefined | ||
343 | } | ||
344 | |||
345 | return { | ||
346 | name: extractNameFromArray(parsedTorrent.name), | ||
347 | torrentName | ||
348 | } | ||
349 | } | ||
350 | |||
351 | function processMagnetURI (body: VideoImportCreate) { | ||
352 | const magnetUri = body.magnetUri | ||
353 | const parsed = magnetUtil.decode(magnetUri) | ||
354 | |||
355 | return { | ||
356 | name: extractNameFromArray(parsed.name), | ||
357 | magnetUri | ||
358 | } | ||
359 | } | ||
360 | |||
361 | function extractNameFromArray (name: string | string[]) { | ||
362 | return isArray(name) ? name[0] : name | ||
363 | } | ||
364 | |||
365 | async function processYoutubeSubtitles (youtubeDL: YoutubeDL, targetUrl: string, videoId: number) { | ||
366 | try { | ||
367 | const subtitles = await youtubeDL.getYoutubeDLSubs() | ||
368 | |||
369 | logger.info('Will create %s subtitles from youtube import %s.', subtitles.length, targetUrl) | ||
370 | |||
371 | for (const subtitle of subtitles) { | ||
372 | const videoCaption = new VideoCaptionModel({ | ||
373 | videoId, | ||
374 | language: subtitle.language, | ||
375 | filename: VideoCaptionModel.generateCaptionName(subtitle.language) | ||
376 | }) as MVideoCaption | ||
377 | |||
378 | // Move physical file | ||
379 | await moveAndProcessCaptionFile(subtitle, videoCaption) | ||
380 | |||
381 | await sequelizeTypescript.transaction(async t => { | ||
382 | await VideoCaptionModel.insertOrReplaceLanguage(videoCaption, t) | ||
383 | }) | ||
384 | } | ||
385 | } catch (err) { | ||
386 | logger.warn('Cannot get video subtitles.', { err }) | ||
387 | } | ||
388 | } | ||
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index c32626d30..6483d2e8a 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -1,43 +1,20 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { move } from 'fs-extra' | ||
3 | import { extname } from 'path' | ||
4 | import toInt from 'validator/lib/toInt' | 2 | import toInt from 'validator/lib/toInt' |
5 | import { deleteResumableUploadMetaFile, getResumableUploadPath } from '@server/helpers/upload' | ||
6 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | ||
7 | import { changeVideoChannelShare } from '@server/lib/activitypub/share' | ||
8 | import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' | ||
9 | import { LiveManager } from '@server/lib/live-manager' | 3 | import { LiveManager } from '@server/lib/live-manager' |
10 | import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' | ||
11 | import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' | ||
12 | import { getServerActor } from '@server/models/application/application' | 4 | import { getServerActor } from '@server/models/application/application' |
13 | import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' | 5 | import { VideosCommonQuery } from '../../../../shared' |
14 | import { uploadx } from '@uploadx/core' | ||
15 | import { VideoCreate, VideosCommonQuery, VideoState, VideoUpdate } from '../../../../shared' | ||
16 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs' | 6 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs' |
17 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | 7 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' |
18 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils' | 8 | import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' |
19 | import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils' | 9 | import { logger } from '../../../helpers/logger' |
20 | import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' | ||
21 | import { logger, loggerTagsFactory } from '../../../helpers/logger' | ||
22 | import { getFormattedObjects } from '../../../helpers/utils' | 10 | import { getFormattedObjects } from '../../../helpers/utils' |
23 | import { CONFIG } from '../../../initializers/config' | 11 | import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers/constants' |
24 | import { | ||
25 | DEFAULT_AUDIO_RESOLUTION, | ||
26 | MIMETYPES, | ||
27 | VIDEO_CATEGORIES, | ||
28 | VIDEO_LANGUAGES, | ||
29 | VIDEO_LICENCES, | ||
30 | VIDEO_PRIVACIES | ||
31 | } from '../../../initializers/constants' | ||
32 | import { sequelizeTypescript } from '../../../initializers/database' | 12 | import { sequelizeTypescript } from '../../../initializers/database' |
33 | import { sendView } from '../../../lib/activitypub/send/send-view' | 13 | import { sendView } from '../../../lib/activitypub/send/send-view' |
34 | import { federateVideoIfNeeded, fetchRemoteVideoDescription } from '../../../lib/activitypub/videos' | 14 | import { fetchRemoteVideoDescription } from '../../../lib/activitypub/videos' |
35 | import { JobQueue } from '../../../lib/job-queue' | 15 | import { JobQueue } from '../../../lib/job-queue' |
36 | import { Notifier } from '../../../lib/notifier' | ||
37 | import { Hooks } from '../../../lib/plugins/hooks' | 16 | import { Hooks } from '../../../lib/plugins/hooks' |
38 | import { Redis } from '../../../lib/redis' | 17 | import { Redis } from '../../../lib/redis' |
39 | import { generateVideoMiniature } from '../../../lib/thumbnail' | ||
40 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | ||
41 | import { | 18 | import { |
42 | asyncMiddleware, | 19 | asyncMiddleware, |
43 | asyncRetryTransactionMiddleware, | 20 | asyncRetryTransactionMiddleware, |
@@ -49,16 +26,11 @@ import { | |||
49 | setDefaultPagination, | 26 | setDefaultPagination, |
50 | setDefaultVideosSort, | 27 | setDefaultVideosSort, |
51 | videoFileMetadataGetValidator, | 28 | videoFileMetadataGetValidator, |
52 | videosAddLegacyValidator, | ||
53 | videosAddResumableInitValidator, | ||
54 | videosAddResumableValidator, | ||
55 | videosCustomGetValidator, | 29 | videosCustomGetValidator, |
56 | videosGetValidator, | 30 | videosGetValidator, |
57 | videosRemoveValidator, | 31 | videosRemoveValidator, |
58 | videosSortValidator, | 32 | videosSortValidator |
59 | videosUpdateValidator | ||
60 | } from '../../../middlewares' | 33 | } from '../../../middlewares' |
61 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | ||
62 | import { VideoModel } from '../../../models/video/video' | 34 | import { VideoModel } from '../../../models/video/video' |
63 | import { VideoFileModel } from '../../../models/video/video-file' | 35 | import { VideoFileModel } from '../../../models/video/video-file' |
64 | import { blacklistRouter } from './blacklist' | 36 | import { blacklistRouter } from './blacklist' |
@@ -68,40 +40,12 @@ import { videoImportsRouter } from './import' | |||
68 | import { liveRouter } from './live' | 40 | import { liveRouter } from './live' |
69 | import { ownershipVideoRouter } from './ownership' | 41 | import { ownershipVideoRouter } from './ownership' |
70 | import { rateVideoRouter } from './rate' | 42 | import { rateVideoRouter } from './rate' |
43 | import { updateRouter } from './update' | ||
44 | import { uploadRouter } from './upload' | ||
71 | import { watchingRouter } from './watching' | 45 | import { watchingRouter } from './watching' |
72 | 46 | ||
73 | const lTags = loggerTagsFactory('api', 'video') | ||
74 | const auditLogger = auditLoggerFactory('videos') | 47 | const auditLogger = auditLoggerFactory('videos') |
75 | const videosRouter = express.Router() | 48 | const videosRouter = express.Router() |
76 | const uploadxMiddleware = uploadx.upload({ directory: getResumableUploadPath() }) | ||
77 | |||
78 | const reqVideoFileAdd = createReqFiles( | ||
79 | [ 'videofile', 'thumbnailfile', 'previewfile' ], | ||
80 | Object.assign({}, MIMETYPES.VIDEO.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT), | ||
81 | { | ||
82 | videofile: CONFIG.STORAGE.TMP_DIR, | ||
83 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, | ||
84 | previewfile: CONFIG.STORAGE.TMP_DIR | ||
85 | } | ||
86 | ) | ||
87 | |||
88 | const reqVideoFileAddResumable = createReqFiles( | ||
89 | [ 'thumbnailfile', 'previewfile' ], | ||
90 | MIMETYPES.IMAGE.MIMETYPE_EXT, | ||
91 | { | ||
92 | thumbnailfile: getResumableUploadPath(), | ||
93 | previewfile: getResumableUploadPath() | ||
94 | } | ||
95 | ) | ||
96 | |||
97 | const reqVideoFileUpdate = createReqFiles( | ||
98 | [ 'thumbnailfile', 'previewfile' ], | ||
99 | MIMETYPES.IMAGE.MIMETYPE_EXT, | ||
100 | { | ||
101 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, | ||
102 | previewfile: CONFIG.STORAGE.TMP_DIR | ||
103 | } | ||
104 | ) | ||
105 | 49 | ||
106 | videosRouter.use('/', blacklistRouter) | 50 | videosRouter.use('/', blacklistRouter) |
107 | videosRouter.use('/', rateVideoRouter) | 51 | videosRouter.use('/', rateVideoRouter) |
@@ -111,6 +55,8 @@ videosRouter.use('/', videoImportsRouter) | |||
111 | videosRouter.use('/', ownershipVideoRouter) | 55 | videosRouter.use('/', ownershipVideoRouter) |
112 | videosRouter.use('/', watchingRouter) | 56 | videosRouter.use('/', watchingRouter) |
113 | videosRouter.use('/', liveRouter) | 57 | videosRouter.use('/', liveRouter) |
58 | videosRouter.use('/', uploadRouter) | ||
59 | videosRouter.use('/', updateRouter) | ||
114 | 60 | ||
115 | videosRouter.get('/categories', listVideoCategories) | 61 | videosRouter.get('/categories', listVideoCategories) |
116 | videosRouter.get('/licences', listVideoLicences) | 62 | videosRouter.get('/licences', listVideoLicences) |
@@ -127,39 +73,6 @@ videosRouter.get('/', | |||
127 | asyncMiddleware(listVideos) | 73 | asyncMiddleware(listVideos) |
128 | ) | 74 | ) |
129 | 75 | ||
130 | videosRouter.post('/upload', | ||
131 | authenticate, | ||
132 | reqVideoFileAdd, | ||
133 | asyncMiddleware(videosAddLegacyValidator), | ||
134 | asyncRetryTransactionMiddleware(addVideoLegacy) | ||
135 | ) | ||
136 | |||
137 | videosRouter.post('/upload-resumable', | ||
138 | authenticate, | ||
139 | reqVideoFileAddResumable, | ||
140 | asyncMiddleware(videosAddResumableInitValidator), | ||
141 | uploadxMiddleware | ||
142 | ) | ||
143 | |||
144 | videosRouter.delete('/upload-resumable', | ||
145 | authenticate, | ||
146 | uploadxMiddleware | ||
147 | ) | ||
148 | |||
149 | videosRouter.put('/upload-resumable', | ||
150 | authenticate, | ||
151 | uploadxMiddleware, // uploadx doesn't use call next() before the file upload completes | ||
152 | asyncMiddleware(videosAddResumableValidator), | ||
153 | asyncMiddleware(addVideoResumable) | ||
154 | ) | ||
155 | |||
156 | videosRouter.put('/:id', | ||
157 | authenticate, | ||
158 | reqVideoFileUpdate, | ||
159 | asyncMiddleware(videosUpdateValidator), | ||
160 | asyncRetryTransactionMiddleware(updateVideo) | ||
161 | ) | ||
162 | |||
163 | videosRouter.get('/:id/description', | 76 | videosRouter.get('/:id/description', |
164 | asyncMiddleware(videosGetValidator), | 77 | asyncMiddleware(videosGetValidator), |
165 | asyncMiddleware(getVideoDescription) | 78 | asyncMiddleware(getVideoDescription) |
@@ -209,279 +122,7 @@ function listVideoPrivacies (_req: express.Request, res: express.Response) { | |||
209 | res.json(VIDEO_PRIVACIES) | 122 | res.json(VIDEO_PRIVACIES) |
210 | } | 123 | } |
211 | 124 | ||
212 | async function addVideoLegacy (req: express.Request, res: express.Response) { | 125 | async function getVideo (_req: express.Request, res: express.Response) { |
213 | // Uploading the video could be long | ||
214 | // Set timeout to 10 minutes, as Express's default is 2 minutes | ||
215 | req.setTimeout(1000 * 60 * 10, () => { | ||
216 | logger.error('Upload video has timed out.') | ||
217 | return res.sendStatus(HttpStatusCode.REQUEST_TIMEOUT_408) | ||
218 | }) | ||
219 | |||
220 | const videoPhysicalFile = req.files['videofile'][0] | ||
221 | const videoInfo: VideoCreate = req.body | ||
222 | const files = req.files | ||
223 | |||
224 | return addVideo({ res, videoPhysicalFile, videoInfo, files }) | ||
225 | } | ||
226 | |||
227 | async function addVideoResumable (_req: express.Request, res: express.Response) { | ||
228 | const videoPhysicalFile = res.locals.videoFileResumable | ||
229 | const videoInfo = videoPhysicalFile.metadata | ||
230 | const files = { previewfile: videoInfo.previewfile } | ||
231 | |||
232 | // Don't need the meta file anymore | ||
233 | await deleteResumableUploadMetaFile(videoPhysicalFile.path) | ||
234 | |||
235 | return addVideo({ res, videoPhysicalFile, videoInfo, files }) | ||
236 | } | ||
237 | |||
238 | async function addVideo (options: { | ||
239 | res: express.Response | ||
240 | videoPhysicalFile: express.VideoUploadFile | ||
241 | videoInfo: VideoCreate | ||
242 | files: express.UploadFiles | ||
243 | }) { | ||
244 | const { res, videoPhysicalFile, videoInfo, files } = options | ||
245 | const videoChannel = res.locals.videoChannel | ||
246 | const user = res.locals.oauth.token.User | ||
247 | |||
248 | const videoData = buildLocalVideoFromReq(videoInfo, videoChannel.id) | ||
249 | |||
250 | videoData.state = CONFIG.TRANSCODING.ENABLED | ||
251 | ? VideoState.TO_TRANSCODE | ||
252 | : VideoState.PUBLISHED | ||
253 | |||
254 | videoData.duration = videoPhysicalFile.duration // duration was added by a previous middleware | ||
255 | |||
256 | const video = new VideoModel(videoData) as MVideoFullLight | ||
257 | video.VideoChannel = videoChannel | ||
258 | video.url = getLocalVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object | ||
259 | |||
260 | const videoFile = new VideoFileModel({ | ||
261 | extname: extname(videoPhysicalFile.filename), | ||
262 | size: videoPhysicalFile.size, | ||
263 | videoStreamingPlaylistId: null, | ||
264 | metadata: await getMetadataFromFile(videoPhysicalFile.path) | ||
265 | }) | ||
266 | |||
267 | if (videoFile.isAudio()) { | ||
268 | videoFile.resolution = DEFAULT_AUDIO_RESOLUTION | ||
269 | } else { | ||
270 | videoFile.fps = await getVideoFileFPS(videoPhysicalFile.path) | ||
271 | videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path)).videoFileResolution | ||
272 | } | ||
273 | |||
274 | videoFile.filename = generateVideoFilename(video, false, videoFile.resolution, videoFile.extname) | ||
275 | |||
276 | // Move physical file | ||
277 | const destination = getVideoFilePath(video, videoFile) | ||
278 | await move(videoPhysicalFile.path, destination) | ||
279 | // This is important in case if there is another attempt in the retry process | ||
280 | videoPhysicalFile.filename = getVideoFilePath(video, videoFile) | ||
281 | videoPhysicalFile.path = destination | ||
282 | |||
283 | const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ | ||
284 | video, | ||
285 | files, | ||
286 | fallback: type => generateVideoMiniature({ video, videoFile, type }) | ||
287 | }) | ||
288 | |||
289 | const { videoCreated } = await sequelizeTypescript.transaction(async t => { | ||
290 | const sequelizeOptions = { transaction: t } | ||
291 | |||
292 | const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight | ||
293 | |||
294 | await videoCreated.addAndSaveThumbnail(thumbnailModel, t) | ||
295 | await videoCreated.addAndSaveThumbnail(previewModel, t) | ||
296 | |||
297 | // Do not forget to add video channel information to the created video | ||
298 | videoCreated.VideoChannel = res.locals.videoChannel | ||
299 | |||
300 | videoFile.videoId = video.id | ||
301 | await videoFile.save(sequelizeOptions) | ||
302 | |||
303 | video.VideoFiles = [ videoFile ] | ||
304 | |||
305 | await setVideoTags({ video, tags: videoInfo.tags, transaction: t }) | ||
306 | |||
307 | // Schedule an update in the future? | ||
308 | if (videoInfo.scheduleUpdate) { | ||
309 | await ScheduleVideoUpdateModel.create({ | ||
310 | videoId: video.id, | ||
311 | updateAt: videoInfo.scheduleUpdate.updateAt, | ||
312 | privacy: videoInfo.scheduleUpdate.privacy || null | ||
313 | }, { transaction: t }) | ||
314 | } | ||
315 | |||
316 | // Channel has a new content, set as updated | ||
317 | await videoCreated.VideoChannel.setAsUpdated(t) | ||
318 | |||
319 | await autoBlacklistVideoIfNeeded({ | ||
320 | video, | ||
321 | user, | ||
322 | isRemote: false, | ||
323 | isNew: true, | ||
324 | transaction: t | ||
325 | }) | ||
326 | |||
327 | auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) | ||
328 | logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid, lTags(videoCreated.uuid)) | ||
329 | |||
330 | return { videoCreated } | ||
331 | }) | ||
332 | |||
333 | // Create the torrent file in async way because it could be long | ||
334 | createTorrentAndSetInfoHashAsync(video, videoFile) | ||
335 | .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err, ...lTags(video.uuid) })) | ||
336 | .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id)) | ||
337 | .then(refreshedVideo => { | ||
338 | if (!refreshedVideo) return | ||
339 | |||
340 | // Only federate and notify after the torrent creation | ||
341 | Notifier.Instance.notifyOnNewVideoIfNeeded(refreshedVideo) | ||
342 | |||
343 | return retryTransactionWrapper(() => { | ||
344 | return sequelizeTypescript.transaction(t => federateVideoIfNeeded(refreshedVideo, true, t)) | ||
345 | }) | ||
346 | }) | ||
347 | .catch(err => logger.error('Cannot federate or notify video creation %s', video.url, { err, ...lTags(video.uuid) })) | ||
348 | |||
349 | if (video.state === VideoState.TO_TRANSCODE) { | ||
350 | await addOptimizeOrMergeAudioJob(videoCreated, videoFile, user) | ||
351 | } | ||
352 | |||
353 | Hooks.runAction('action:api.video.uploaded', { video: videoCreated }) | ||
354 | |||
355 | return res.json({ | ||
356 | video: { | ||
357 | id: videoCreated.id, | ||
358 | uuid: videoCreated.uuid | ||
359 | } | ||
360 | }) | ||
361 | } | ||
362 | |||
363 | async function updateVideo (req: express.Request, res: express.Response) { | ||
364 | const videoInstance = res.locals.videoAll | ||
365 | const videoFieldsSave = videoInstance.toJSON() | ||
366 | const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) | ||
367 | const videoInfoToUpdate: VideoUpdate = req.body | ||
368 | |||
369 | const wasConfidentialVideo = videoInstance.isConfidential() | ||
370 | const hadPrivacyForFederation = videoInstance.hasPrivacyForFederation() | ||
371 | |||
372 | const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ | ||
373 | video: videoInstance, | ||
374 | files: req.files, | ||
375 | fallback: () => Promise.resolve(undefined), | ||
376 | automaticallyGenerated: false | ||
377 | }) | ||
378 | |||
379 | try { | ||
380 | const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => { | ||
381 | const sequelizeOptions = { transaction: t } | ||
382 | const oldVideoChannel = videoInstance.VideoChannel | ||
383 | |||
384 | if (videoInfoToUpdate.name !== undefined) videoInstance.name = videoInfoToUpdate.name | ||
385 | if (videoInfoToUpdate.category !== undefined) videoInstance.category = videoInfoToUpdate.category | ||
386 | if (videoInfoToUpdate.licence !== undefined) videoInstance.licence = videoInfoToUpdate.licence | ||
387 | if (videoInfoToUpdate.language !== undefined) videoInstance.language = videoInfoToUpdate.language | ||
388 | if (videoInfoToUpdate.nsfw !== undefined) videoInstance.nsfw = videoInfoToUpdate.nsfw | ||
389 | if (videoInfoToUpdate.waitTranscoding !== undefined) videoInstance.waitTranscoding = videoInfoToUpdate.waitTranscoding | ||
390 | if (videoInfoToUpdate.support !== undefined) videoInstance.support = videoInfoToUpdate.support | ||
391 | if (videoInfoToUpdate.description !== undefined) videoInstance.description = videoInfoToUpdate.description | ||
392 | if (videoInfoToUpdate.commentsEnabled !== undefined) videoInstance.commentsEnabled = videoInfoToUpdate.commentsEnabled | ||
393 | if (videoInfoToUpdate.downloadEnabled !== undefined) videoInstance.downloadEnabled = videoInfoToUpdate.downloadEnabled | ||
394 | |||
395 | if (videoInfoToUpdate.originallyPublishedAt !== undefined && videoInfoToUpdate.originallyPublishedAt !== null) { | ||
396 | videoInstance.originallyPublishedAt = new Date(videoInfoToUpdate.originallyPublishedAt) | ||
397 | } | ||
398 | |||
399 | let isNewVideo = false | ||
400 | if (videoInfoToUpdate.privacy !== undefined) { | ||
401 | isNewVideo = videoInstance.isNewVideo(videoInfoToUpdate.privacy) | ||
402 | |||
403 | const newPrivacy = parseInt(videoInfoToUpdate.privacy.toString(), 10) | ||
404 | videoInstance.setPrivacy(newPrivacy) | ||
405 | |||
406 | // Unfederate the video if the new privacy is not compatible with federation | ||
407 | if (hadPrivacyForFederation && !videoInstance.hasPrivacyForFederation()) { | ||
408 | await VideoModel.sendDelete(videoInstance, { transaction: t }) | ||
409 | } | ||
410 | } | ||
411 | |||
412 | const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) as MVideoFullLight | ||
413 | |||
414 | if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t) | ||
415 | if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, t) | ||
416 | |||
417 | // Video tags update? | ||
418 | if (videoInfoToUpdate.tags !== undefined) { | ||
419 | await setVideoTags({ | ||
420 | video: videoInstanceUpdated, | ||
421 | tags: videoInfoToUpdate.tags, | ||
422 | transaction: t | ||
423 | }) | ||
424 | } | ||
425 | |||
426 | // Video channel update? | ||
427 | if (res.locals.videoChannel && videoInstanceUpdated.channelId !== res.locals.videoChannel.id) { | ||
428 | await videoInstanceUpdated.$set('VideoChannel', res.locals.videoChannel, { transaction: t }) | ||
429 | videoInstanceUpdated.VideoChannel = res.locals.videoChannel | ||
430 | |||
431 | if (hadPrivacyForFederation === true) await changeVideoChannelShare(videoInstanceUpdated, oldVideoChannel, t) | ||
432 | } | ||
433 | |||
434 | // Schedule an update in the future? | ||
435 | if (videoInfoToUpdate.scheduleUpdate) { | ||
436 | await ScheduleVideoUpdateModel.upsert({ | ||
437 | videoId: videoInstanceUpdated.id, | ||
438 | updateAt: videoInfoToUpdate.scheduleUpdate.updateAt, | ||
439 | privacy: videoInfoToUpdate.scheduleUpdate.privacy || null | ||
440 | }, { transaction: t }) | ||
441 | } else if (videoInfoToUpdate.scheduleUpdate === null) { | ||
442 | await ScheduleVideoUpdateModel.deleteByVideoId(videoInstanceUpdated.id, t) | ||
443 | } | ||
444 | |||
445 | await autoBlacklistVideoIfNeeded({ | ||
446 | video: videoInstanceUpdated, | ||
447 | user: res.locals.oauth.token.User, | ||
448 | isRemote: false, | ||
449 | isNew: false, | ||
450 | transaction: t | ||
451 | }) | ||
452 | |||
453 | await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) | ||
454 | |||
455 | auditLogger.update( | ||
456 | getAuditIdFromRes(res), | ||
457 | new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), | ||
458 | oldVideoAuditView | ||
459 | ) | ||
460 | logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid, lTags(videoInstance.uuid)) | ||
461 | |||
462 | return videoInstanceUpdated | ||
463 | }) | ||
464 | |||
465 | if (wasConfidentialVideo) { | ||
466 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoInstanceUpdated) | ||
467 | } | ||
468 | |||
469 | Hooks.runAction('action:api.video.updated', { video: videoInstanceUpdated, body: req.body }) | ||
470 | } catch (err) { | ||
471 | // Force fields we want to update | ||
472 | // If the transaction is retried, sequelize will think the object has not changed | ||
473 | // So it will skip the SQL request, even if the last one was ROLLBACKed! | ||
474 | resetSequelizeInstance(videoInstance, videoFieldsSave) | ||
475 | |||
476 | throw err | ||
477 | } | ||
478 | |||
479 | return res.type('json') | ||
480 | .status(HttpStatusCode.NO_CONTENT_204) | ||
481 | .end() | ||
482 | } | ||
483 | |||
484 | async function getVideo (req: express.Request, res: express.Response) { | ||
485 | // We need more attributes | 126 | // We need more attributes |
486 | const userId: number = res.locals.oauth ? res.locals.oauth.token.User.id : null | 127 | const userId: number = res.locals.oauth ? res.locals.oauth.token.User.id : null |
487 | 128 | ||
@@ -543,13 +184,10 @@ async function viewVideo (req: express.Request, res: express.Response) { | |||
543 | 184 | ||
544 | async function getVideoDescription (req: express.Request, res: express.Response) { | 185 | async function getVideoDescription (req: express.Request, res: express.Response) { |
545 | const videoInstance = res.locals.videoAll | 186 | const videoInstance = res.locals.videoAll |
546 | let description = '' | ||
547 | 187 | ||
548 | if (videoInstance.isOwned()) { | 188 | const description = videoInstance.isOwned() |
549 | description = videoInstance.description | 189 | ? videoInstance.description |
550 | } else { | 190 | : await fetchRemoteVideoDescription(videoInstance) |
551 | description = await fetchRemoteVideoDescription(videoInstance) | ||
552 | } | ||
553 | 191 | ||
554 | return res.json({ description }) | 192 | return res.json({ description }) |
555 | } | 193 | } |
@@ -591,7 +229,7 @@ async function listVideos (req: express.Request, res: express.Response) { | |||
591 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 229 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
592 | } | 230 | } |
593 | 231 | ||
594 | async function removeVideo (req: express.Request, res: express.Response) { | 232 | async function removeVideo (_req: express.Request, res: express.Response) { |
595 | const videoInstance = res.locals.videoAll | 233 | const videoInstance = res.locals.videoAll |
596 | 234 | ||
597 | await sequelizeTypescript.transaction(async t => { | 235 | await sequelizeTypescript.transaction(async t => { |
@@ -607,17 +245,3 @@ async function removeVideo (req: express.Request, res: express.Response) { | |||
607 | .status(HttpStatusCode.NO_CONTENT_204) | 245 | .status(HttpStatusCode.NO_CONTENT_204) |
608 | .end() | 246 | .end() |
609 | } | 247 | } |
610 | |||
611 | async function createTorrentAndSetInfoHashAsync (video: MVideo, fileArg: MVideoFile) { | ||
612 | await createTorrentAndSetInfoHash(video, fileArg) | ||
613 | |||
614 | // Refresh videoFile because the createTorrentAndSetInfoHash could be long | ||
615 | const refreshedFile = await VideoFileModel.loadWithVideo(fileArg.id) | ||
616 | // File does not exist anymore, remove the generated torrent | ||
617 | if (!refreshedFile) return fileArg.removeTorrent() | ||
618 | |||
619 | refreshedFile.infoHash = fileArg.infoHash | ||
620 | refreshedFile.torrentFilename = fileArg.torrentFilename | ||
621 | |||
622 | return refreshedFile.save() | ||
623 | } | ||
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts index a85d7c30b..6102f28dc 100644 --- a/server/controllers/api/videos/ownership.ts +++ b/server/controllers/api/videos/ownership.ts | |||
@@ -99,7 +99,7 @@ async function listVideoOwnership (req: express.Request, res: express.Response) | |||
99 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 99 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
100 | } | 100 | } |
101 | 101 | ||
102 | async function acceptOwnership (req: express.Request, res: express.Response) { | 102 | function acceptOwnership (req: express.Request, res: express.Response) { |
103 | return sequelizeTypescript.transaction(async t => { | 103 | return sequelizeTypescript.transaction(async t => { |
104 | const videoChangeOwnership = res.locals.videoChangeOwnership | 104 | const videoChangeOwnership = res.locals.videoChangeOwnership |
105 | const channel = res.locals.videoChannel | 105 | const channel = res.locals.videoChannel |
@@ -126,7 +126,7 @@ async function acceptOwnership (req: express.Request, res: express.Response) { | |||
126 | }) | 126 | }) |
127 | } | 127 | } |
128 | 128 | ||
129 | async function refuseOwnership (req: express.Request, res: express.Response) { | 129 | function refuseOwnership (req: express.Request, res: express.Response) { |
130 | return sequelizeTypescript.transaction(async t => { | 130 | return sequelizeTypescript.transaction(async t => { |
131 | const videoChangeOwnership = res.locals.videoChangeOwnership | 131 | const videoChangeOwnership = res.locals.videoChangeOwnership |
132 | 132 | ||
diff --git a/server/controllers/api/videos/update.ts b/server/controllers/api/videos/update.ts new file mode 100644 index 000000000..2450abd0e --- /dev/null +++ b/server/controllers/api/videos/update.ts | |||
@@ -0,0 +1,191 @@ | |||
1 | import * as express from 'express' | ||
2 | import { Transaction } from 'sequelize/types' | ||
3 | import { changeVideoChannelShare } from '@server/lib/activitypub/share' | ||
4 | import { buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' | ||
5 | import { FilteredModelAttributes } from '@server/types' | ||
6 | import { MVideoFullLight } from '@server/types/models' | ||
7 | import { VideoUpdate } from '../../../../shared' | ||
8 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs' | ||
9 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | ||
10 | import { resetSequelizeInstance } from '../../../helpers/database-utils' | ||
11 | import { createReqFiles } from '../../../helpers/express-utils' | ||
12 | import { logger, loggerTagsFactory } from '../../../helpers/logger' | ||
13 | import { CONFIG } from '../../../initializers/config' | ||
14 | import { MIMETYPES } from '../../../initializers/constants' | ||
15 | import { sequelizeTypescript } from '../../../initializers/database' | ||
16 | import { federateVideoIfNeeded } from '../../../lib/activitypub/videos' | ||
17 | import { Notifier } from '../../../lib/notifier' | ||
18 | import { Hooks } from '../../../lib/plugins/hooks' | ||
19 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | ||
20 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosUpdateValidator } from '../../../middlewares' | ||
21 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | ||
22 | import { VideoModel } from '../../../models/video/video' | ||
23 | |||
24 | const lTags = loggerTagsFactory('api', 'video') | ||
25 | const auditLogger = auditLoggerFactory('videos') | ||
26 | const updateRouter = express.Router() | ||
27 | |||
28 | const reqVideoFileUpdate = createReqFiles( | ||
29 | [ 'thumbnailfile', 'previewfile' ], | ||
30 | MIMETYPES.IMAGE.MIMETYPE_EXT, | ||
31 | { | ||
32 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, | ||
33 | previewfile: CONFIG.STORAGE.TMP_DIR | ||
34 | } | ||
35 | ) | ||
36 | |||
37 | updateRouter.put('/:id', | ||
38 | authenticate, | ||
39 | reqVideoFileUpdate, | ||
40 | asyncMiddleware(videosUpdateValidator), | ||
41 | asyncRetryTransactionMiddleware(updateVideo) | ||
42 | ) | ||
43 | |||
44 | // --------------------------------------------------------------------------- | ||
45 | |||
46 | export { | ||
47 | updateRouter | ||
48 | } | ||
49 | |||
50 | // --------------------------------------------------------------------------- | ||
51 | |||
52 | export async function updateVideo (req: express.Request, res: express.Response) { | ||
53 | const videoInstance = res.locals.videoAll | ||
54 | const videoFieldsSave = videoInstance.toJSON() | ||
55 | const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) | ||
56 | const videoInfoToUpdate: VideoUpdate = req.body | ||
57 | |||
58 | const wasConfidentialVideo = videoInstance.isConfidential() | ||
59 | const hadPrivacyForFederation = videoInstance.hasPrivacyForFederation() | ||
60 | |||
61 | const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ | ||
62 | video: videoInstance, | ||
63 | files: req.files, | ||
64 | fallback: () => Promise.resolve(undefined), | ||
65 | automaticallyGenerated: false | ||
66 | }) | ||
67 | |||
68 | try { | ||
69 | const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => { | ||
70 | const sequelizeOptions = { transaction: t } | ||
71 | const oldVideoChannel = videoInstance.VideoChannel | ||
72 | |||
73 | const keysToUpdate: (keyof VideoUpdate & FilteredModelAttributes<VideoModel>)[] = [ | ||
74 | 'name', | ||
75 | 'category', | ||
76 | 'licence', | ||
77 | 'language', | ||
78 | 'nsfw', | ||
79 | 'waitTranscoding', | ||
80 | 'support', | ||
81 | 'description', | ||
82 | 'commentsEnabled', | ||
83 | 'downloadEnabled' | ||
84 | ] | ||
85 | |||
86 | for (const key of keysToUpdate) { | ||
87 | if (videoInfoToUpdate[key] !== undefined) videoInstance.set(key, videoInfoToUpdate[key]) | ||
88 | } | ||
89 | |||
90 | if (videoInfoToUpdate.originallyPublishedAt !== undefined && videoInfoToUpdate.originallyPublishedAt !== null) { | ||
91 | videoInstance.originallyPublishedAt = new Date(videoInfoToUpdate.originallyPublishedAt) | ||
92 | } | ||
93 | |||
94 | // Privacy update? | ||
95 | let isNewVideo = false | ||
96 | if (videoInfoToUpdate.privacy !== undefined) { | ||
97 | isNewVideo = await updateVideoPrivacy({ videoInstance, videoInfoToUpdate, hadPrivacyForFederation, transaction: t }) | ||
98 | } | ||
99 | |||
100 | const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) as MVideoFullLight | ||
101 | |||
102 | // Thumbnail & preview updates? | ||
103 | if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t) | ||
104 | if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, t) | ||
105 | |||
106 | // Video tags update? | ||
107 | if (videoInfoToUpdate.tags !== undefined) { | ||
108 | await setVideoTags({ video: videoInstanceUpdated, tags: videoInfoToUpdate.tags, transaction: t }) | ||
109 | } | ||
110 | |||
111 | // Video channel update? | ||
112 | if (res.locals.videoChannel && videoInstanceUpdated.channelId !== res.locals.videoChannel.id) { | ||
113 | await videoInstanceUpdated.$set('VideoChannel', res.locals.videoChannel, { transaction: t }) | ||
114 | videoInstanceUpdated.VideoChannel = res.locals.videoChannel | ||
115 | |||
116 | if (hadPrivacyForFederation === true) await changeVideoChannelShare(videoInstanceUpdated, oldVideoChannel, t) | ||
117 | } | ||
118 | |||
119 | // Schedule an update in the future? | ||
120 | await updateSchedule(videoInstanceUpdated, videoInfoToUpdate, t) | ||
121 | |||
122 | await autoBlacklistVideoIfNeeded({ | ||
123 | video: videoInstanceUpdated, | ||
124 | user: res.locals.oauth.token.User, | ||
125 | isRemote: false, | ||
126 | isNew: false, | ||
127 | transaction: t | ||
128 | }) | ||
129 | |||
130 | await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) | ||
131 | |||
132 | auditLogger.update( | ||
133 | getAuditIdFromRes(res), | ||
134 | new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), | ||
135 | oldVideoAuditView | ||
136 | ) | ||
137 | logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid, lTags(videoInstance.uuid)) | ||
138 | |||
139 | return videoInstanceUpdated | ||
140 | }) | ||
141 | |||
142 | if (wasConfidentialVideo) { | ||
143 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoInstanceUpdated) | ||
144 | } | ||
145 | |||
146 | Hooks.runAction('action:api.video.updated', { video: videoInstanceUpdated, body: req.body }) | ||
147 | } catch (err) { | ||
148 | // Force fields we want to update | ||
149 | // If the transaction is retried, sequelize will think the object has not changed | ||
150 | // So it will skip the SQL request, even if the last one was ROLLBACKed! | ||
151 | resetSequelizeInstance(videoInstance, videoFieldsSave) | ||
152 | |||
153 | throw err | ||
154 | } | ||
155 | |||
156 | return res.type('json') | ||
157 | .status(HttpStatusCode.NO_CONTENT_204) | ||
158 | .end() | ||
159 | } | ||
160 | |||
161 | async function updateVideoPrivacy (options: { | ||
162 | videoInstance: MVideoFullLight | ||
163 | videoInfoToUpdate: VideoUpdate | ||
164 | hadPrivacyForFederation: boolean | ||
165 | transaction: Transaction | ||
166 | }) { | ||
167 | const { videoInstance, videoInfoToUpdate, hadPrivacyForFederation, transaction } = options | ||
168 | const isNewVideo = videoInstance.isNewVideo(videoInfoToUpdate.privacy) | ||
169 | |||
170 | const newPrivacy = parseInt(videoInfoToUpdate.privacy.toString(), 10) | ||
171 | videoInstance.setPrivacy(newPrivacy) | ||
172 | |||
173 | // Unfederate the video if the new privacy is not compatible with federation | ||
174 | if (hadPrivacyForFederation && !videoInstance.hasPrivacyForFederation()) { | ||
175 | await VideoModel.sendDelete(videoInstance, { transaction }) | ||
176 | } | ||
177 | |||
178 | return isNewVideo | ||
179 | } | ||
180 | |||
181 | function updateSchedule (videoInstance: MVideoFullLight, videoInfoToUpdate: VideoUpdate, transaction: Transaction) { | ||
182 | if (videoInfoToUpdate.scheduleUpdate) { | ||
183 | return ScheduleVideoUpdateModel.upsert({ | ||
184 | videoId: videoInstance.id, | ||
185 | updateAt: new Date(videoInfoToUpdate.scheduleUpdate.updateAt), | ||
186 | privacy: videoInfoToUpdate.scheduleUpdate.privacy || null | ||
187 | }, { transaction }) | ||
188 | } else if (videoInfoToUpdate.scheduleUpdate === null) { | ||
189 | return ScheduleVideoUpdateModel.deleteByVideoId(videoInstance.id, transaction) | ||
190 | } | ||
191 | } | ||
diff --git a/server/controllers/api/videos/upload.ts b/server/controllers/api/videos/upload.ts new file mode 100644 index 000000000..ebc17c760 --- /dev/null +++ b/server/controllers/api/videos/upload.ts | |||
@@ -0,0 +1,269 @@ | |||
1 | import * as express from 'express' | ||
2 | import { move } from 'fs-extra' | ||
3 | import { extname } from 'path' | ||
4 | import { deleteResumableUploadMetaFile, getResumableUploadPath } from '@server/helpers/upload' | ||
5 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | ||
6 | import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' | ||
7 | import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' | ||
8 | import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' | ||
9 | import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' | ||
10 | import { uploadx } from '@uploadx/core' | ||
11 | import { VideoCreate, VideoState } from '../../../../shared' | ||
12 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs' | ||
13 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | ||
14 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | ||
15 | import { createReqFiles } from '../../../helpers/express-utils' | ||
16 | import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' | ||
17 | import { logger, loggerTagsFactory } from '../../../helpers/logger' | ||
18 | import { CONFIG } from '../../../initializers/config' | ||
19 | import { DEFAULT_AUDIO_RESOLUTION, MIMETYPES } from '../../../initializers/constants' | ||
20 | import { sequelizeTypescript } from '../../../initializers/database' | ||
21 | import { federateVideoIfNeeded } from '../../../lib/activitypub/videos' | ||
22 | import { Notifier } from '../../../lib/notifier' | ||
23 | import { Hooks } from '../../../lib/plugins/hooks' | ||
24 | import { generateVideoMiniature } from '../../../lib/thumbnail' | ||
25 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | ||
26 | import { | ||
27 | asyncMiddleware, | ||
28 | asyncRetryTransactionMiddleware, | ||
29 | authenticate, | ||
30 | videosAddLegacyValidator, | ||
31 | videosAddResumableInitValidator, | ||
32 | videosAddResumableValidator | ||
33 | } from '../../../middlewares' | ||
34 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | ||
35 | import { VideoModel } from '../../../models/video/video' | ||
36 | import { VideoFileModel } from '../../../models/video/video-file' | ||
37 | |||
38 | const lTags = loggerTagsFactory('api', 'video') | ||
39 | const auditLogger = auditLoggerFactory('videos') | ||
40 | const uploadRouter = express.Router() | ||
41 | const uploadxMiddleware = uploadx.upload({ directory: getResumableUploadPath() }) | ||
42 | |||
43 | const reqVideoFileAdd = createReqFiles( | ||
44 | [ 'videofile', 'thumbnailfile', 'previewfile' ], | ||
45 | Object.assign({}, MIMETYPES.VIDEO.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT), | ||
46 | { | ||
47 | videofile: CONFIG.STORAGE.TMP_DIR, | ||
48 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, | ||
49 | previewfile: CONFIG.STORAGE.TMP_DIR | ||
50 | } | ||
51 | ) | ||
52 | |||
53 | const reqVideoFileAddResumable = createReqFiles( | ||
54 | [ 'thumbnailfile', 'previewfile' ], | ||
55 | MIMETYPES.IMAGE.MIMETYPE_EXT, | ||
56 | { | ||
57 | thumbnailfile: getResumableUploadPath(), | ||
58 | previewfile: getResumableUploadPath() | ||
59 | } | ||
60 | ) | ||
61 | |||
62 | uploadRouter.post('/upload', | ||
63 | authenticate, | ||
64 | reqVideoFileAdd, | ||
65 | asyncMiddleware(videosAddLegacyValidator), | ||
66 | asyncRetryTransactionMiddleware(addVideoLegacy) | ||
67 | ) | ||
68 | |||
69 | uploadRouter.post('/upload-resumable', | ||
70 | authenticate, | ||
71 | reqVideoFileAddResumable, | ||
72 | asyncMiddleware(videosAddResumableInitValidator), | ||
73 | uploadxMiddleware | ||
74 | ) | ||
75 | |||
76 | uploadRouter.delete('/upload-resumable', | ||
77 | authenticate, | ||
78 | uploadxMiddleware | ||
79 | ) | ||
80 | |||
81 | uploadRouter.put('/upload-resumable', | ||
82 | authenticate, | ||
83 | uploadxMiddleware, // uploadx doesn't use call next() before the file upload completes | ||
84 | asyncMiddleware(videosAddResumableValidator), | ||
85 | asyncMiddleware(addVideoResumable) | ||
86 | ) | ||
87 | |||
88 | // --------------------------------------------------------------------------- | ||
89 | |||
90 | export { | ||
91 | uploadRouter | ||
92 | } | ||
93 | |||
94 | // --------------------------------------------------------------------------- | ||
95 | |||
96 | export async function addVideoLegacy (req: express.Request, res: express.Response) { | ||
97 | // Uploading the video could be long | ||
98 | // Set timeout to 10 minutes, as Express's default is 2 minutes | ||
99 | req.setTimeout(1000 * 60 * 10, () => { | ||
100 | logger.error('Upload video has timed out.') | ||
101 | return res.sendStatus(HttpStatusCode.REQUEST_TIMEOUT_408) | ||
102 | }) | ||
103 | |||
104 | const videoPhysicalFile = req.files['videofile'][0] | ||
105 | const videoInfo: VideoCreate = req.body | ||
106 | const files = req.files | ||
107 | |||
108 | return addVideo({ res, videoPhysicalFile, videoInfo, files }) | ||
109 | } | ||
110 | |||
111 | export async function addVideoResumable (_req: express.Request, res: express.Response) { | ||
112 | const videoPhysicalFile = res.locals.videoFileResumable | ||
113 | const videoInfo = videoPhysicalFile.metadata | ||
114 | const files = { previewfile: videoInfo.previewfile } | ||
115 | |||
116 | // Don't need the meta file anymore | ||
117 | await deleteResumableUploadMetaFile(videoPhysicalFile.path) | ||
118 | |||
119 | return addVideo({ res, videoPhysicalFile, videoInfo, files }) | ||
120 | } | ||
121 | |||
122 | async function addVideo (options: { | ||
123 | res: express.Response | ||
124 | videoPhysicalFile: express.VideoUploadFile | ||
125 | videoInfo: VideoCreate | ||
126 | files: express.UploadFiles | ||
127 | }) { | ||
128 | const { res, videoPhysicalFile, videoInfo, files } = options | ||
129 | const videoChannel = res.locals.videoChannel | ||
130 | const user = res.locals.oauth.token.User | ||
131 | |||
132 | const videoData = buildLocalVideoFromReq(videoInfo, videoChannel.id) | ||
133 | |||
134 | videoData.state = CONFIG.TRANSCODING.ENABLED | ||
135 | ? VideoState.TO_TRANSCODE | ||
136 | : VideoState.PUBLISHED | ||
137 | |||
138 | videoData.duration = videoPhysicalFile.duration // duration was added by a previous middleware | ||
139 | |||
140 | const video = new VideoModel(videoData) as MVideoFullLight | ||
141 | video.VideoChannel = videoChannel | ||
142 | video.url = getLocalVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object | ||
143 | |||
144 | const videoFile = await buildNewFile(video, videoPhysicalFile) | ||
145 | |||
146 | // Move physical file | ||
147 | const destination = getVideoFilePath(video, videoFile) | ||
148 | await move(videoPhysicalFile.path, destination) | ||
149 | // This is important in case if there is another attempt in the retry process | ||
150 | videoPhysicalFile.filename = getVideoFilePath(video, videoFile) | ||
151 | videoPhysicalFile.path = destination | ||
152 | |||
153 | const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ | ||
154 | video, | ||
155 | files, | ||
156 | fallback: type => generateVideoMiniature({ video, videoFile, type }) | ||
157 | }) | ||
158 | |||
159 | const { videoCreated } = await sequelizeTypescript.transaction(async t => { | ||
160 | const sequelizeOptions = { transaction: t } | ||
161 | |||
162 | const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight | ||
163 | |||
164 | await videoCreated.addAndSaveThumbnail(thumbnailModel, t) | ||
165 | await videoCreated.addAndSaveThumbnail(previewModel, t) | ||
166 | |||
167 | // Do not forget to add video channel information to the created video | ||
168 | videoCreated.VideoChannel = res.locals.videoChannel | ||
169 | |||
170 | videoFile.videoId = video.id | ||
171 | await videoFile.save(sequelizeOptions) | ||
172 | |||
173 | video.VideoFiles = [ videoFile ] | ||
174 | |||
175 | await setVideoTags({ video, tags: videoInfo.tags, transaction: t }) | ||
176 | |||
177 | // Schedule an update in the future? | ||
178 | if (videoInfo.scheduleUpdate) { | ||
179 | await ScheduleVideoUpdateModel.create({ | ||
180 | videoId: video.id, | ||
181 | updateAt: new Date(videoInfo.scheduleUpdate.updateAt), | ||
182 | privacy: videoInfo.scheduleUpdate.privacy || null | ||
183 | }, sequelizeOptions) | ||
184 | } | ||
185 | |||
186 | // Channel has a new content, set as updated | ||
187 | await videoCreated.VideoChannel.setAsUpdated(t) | ||
188 | |||
189 | await autoBlacklistVideoIfNeeded({ | ||
190 | video, | ||
191 | user, | ||
192 | isRemote: false, | ||
193 | isNew: true, | ||
194 | transaction: t | ||
195 | }) | ||
196 | |||
197 | auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) | ||
198 | logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid, lTags(videoCreated.uuid)) | ||
199 | |||
200 | return { videoCreated } | ||
201 | }) | ||
202 | |||
203 | createTorrentFederate(video, videoFile) | ||
204 | |||
205 | if (video.state === VideoState.TO_TRANSCODE) { | ||
206 | await addOptimizeOrMergeAudioJob(videoCreated, videoFile, user) | ||
207 | } | ||
208 | |||
209 | Hooks.runAction('action:api.video.uploaded', { video: videoCreated }) | ||
210 | |||
211 | return res.json({ | ||
212 | video: { | ||
213 | id: videoCreated.id, | ||
214 | uuid: videoCreated.uuid | ||
215 | } | ||
216 | }) | ||
217 | } | ||
218 | |||
219 | async function buildNewFile (video: MVideo, videoPhysicalFile: express.VideoUploadFile) { | ||
220 | const videoFile = new VideoFileModel({ | ||
221 | extname: extname(videoPhysicalFile.filename), | ||
222 | size: videoPhysicalFile.size, | ||
223 | videoStreamingPlaylistId: null, | ||
224 | metadata: await getMetadataFromFile(videoPhysicalFile.path) | ||
225 | }) | ||
226 | |||
227 | if (videoFile.isAudio()) { | ||
228 | videoFile.resolution = DEFAULT_AUDIO_RESOLUTION | ||
229 | } else { | ||
230 | videoFile.fps = await getVideoFileFPS(videoPhysicalFile.path) | ||
231 | videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path)).videoFileResolution | ||
232 | } | ||
233 | |||
234 | videoFile.filename = generateVideoFilename(video, false, videoFile.resolution, videoFile.extname) | ||
235 | |||
236 | return videoFile | ||
237 | } | ||
238 | |||
239 | async function createTorrentAndSetInfoHashAsync (video: MVideo, fileArg: MVideoFile) { | ||
240 | await createTorrentAndSetInfoHash(video, fileArg) | ||
241 | |||
242 | // Refresh videoFile because the createTorrentAndSetInfoHash could be long | ||
243 | const refreshedFile = await VideoFileModel.loadWithVideo(fileArg.id) | ||
244 | // File does not exist anymore, remove the generated torrent | ||
245 | if (!refreshedFile) return fileArg.removeTorrent() | ||
246 | |||
247 | refreshedFile.infoHash = fileArg.infoHash | ||
248 | refreshedFile.torrentFilename = fileArg.torrentFilename | ||
249 | |||
250 | return refreshedFile.save() | ||
251 | } | ||
252 | |||
253 | function createTorrentFederate (video: MVideoFullLight, videoFile: MVideoFile): void { | ||
254 | // Create the torrent file in async way because it could be long | ||
255 | createTorrentAndSetInfoHashAsync(video, videoFile) | ||
256 | .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err, ...lTags(video.uuid) })) | ||
257 | .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id)) | ||
258 | .then(refreshedVideo => { | ||
259 | if (!refreshedVideo) return | ||
260 | |||
261 | // Only federate and notify after the torrent creation | ||
262 | Notifier.Instance.notifyOnNewVideoIfNeeded(refreshedVideo) | ||
263 | |||
264 | return retryTransactionWrapper(() => { | ||
265 | return sequelizeTypescript.transaction(t => federateVideoIfNeeded(refreshedVideo, true, t)) | ||
266 | }) | ||
267 | }) | ||
268 | .catch(err => logger.error('Cannot federate or notify video creation %s', video.url, { err, ...lTags(video.uuid) })) | ||
269 | } | ||
diff --git a/server/controllers/api/videos/watching.ts b/server/controllers/api/videos/watching.ts index 627f12aa9..08190e583 100644 --- a/server/controllers/api/videos/watching.ts +++ b/server/controllers/api/videos/watching.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { UserWatchingVideo } from '../../../../shared' | 2 | import { UserWatchingVideo } from '../../../../shared' |
3 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoWatchingValidator } from '../../../middlewares' | 3 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoWatchingValidator } from '../../../middlewares' |
4 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' | 4 | import { UserVideoHistoryModel } from '../../../models/user/user-video-history' |
5 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 5 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
6 | 6 | ||
7 | const watchingRouter = express.Router() | 7 | const watchingRouter = express.Router() |
diff --git a/server/controllers/lazy-static.ts b/server/controllers/lazy-static.ts index 6f71fdb16..25d3b49b4 100644 --- a/server/controllers/lazy-static.ts +++ b/server/controllers/lazy-static.ts | |||
@@ -7,7 +7,7 @@ import { LAZY_STATIC_PATHS, STATIC_MAX_AGE } from '../initializers/constants' | |||
7 | import { actorImagePathUnsafeCache, pushActorImageProcessInQueue } from '../lib/actor-image' | 7 | import { actorImagePathUnsafeCache, pushActorImageProcessInQueue } from '../lib/actor-image' |
8 | import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache' | 8 | import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache' |
9 | import { asyncMiddleware } from '../middlewares' | 9 | import { asyncMiddleware } from '../middlewares' |
10 | import { ActorImageModel } from '../models/account/actor-image' | 10 | import { ActorImageModel } from '../models/actor/actor-image' |
11 | 11 | ||
12 | const lazyStaticRouter = express.Router() | 12 | const lazyStaticRouter = express.Router() |
13 | 13 | ||
diff --git a/server/controllers/services.ts b/server/controllers/services.ts index 189e1651b..8c0af9ff7 100644 --- a/server/controllers/services.ts +++ b/server/controllers/services.ts | |||
@@ -78,17 +78,18 @@ function buildOEmbed (options: { | |||
78 | const maxWidth = parseInt(req.query.maxwidth, 10) | 78 | const maxWidth = parseInt(req.query.maxwidth, 10) |
79 | 79 | ||
80 | const embedUrl = webserverUrl + embedPath | 80 | const embedUrl = webserverUrl + embedPath |
81 | let embedWidth = EMBED_SIZE.width | ||
82 | let embedHeight = EMBED_SIZE.height | ||
83 | const embedTitle = escapeHTML(title) | 81 | const embedTitle = escapeHTML(title) |
84 | 82 | ||
85 | let thumbnailUrl = previewPath | 83 | let thumbnailUrl = previewPath |
86 | ? webserverUrl + previewPath | 84 | ? webserverUrl + previewPath |
87 | : undefined | 85 | : undefined |
88 | 86 | ||
89 | if (maxHeight < embedHeight) embedHeight = maxHeight | 87 | let embedWidth = EMBED_SIZE.width |
90 | if (maxWidth < embedWidth) embedWidth = maxWidth | 88 | if (maxWidth < embedWidth) embedWidth = maxWidth |
91 | 89 | ||
90 | let embedHeight = EMBED_SIZE.height | ||
91 | if (maxHeight < embedHeight) embedHeight = maxHeight | ||
92 | |||
92 | // Our thumbnail is too big for the consumer | 93 | // Our thumbnail is too big for the consumer |
93 | if ( | 94 | if ( |
94 | (maxHeight !== undefined && maxHeight < previewSize.height) || | 95 | (maxHeight !== undefined && maxHeight < previewSize.height) || |
diff --git a/server/controllers/static.ts b/server/controllers/static.ts index 8d9003a3e..8a747ec52 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts | |||
@@ -2,9 +2,9 @@ import * as cors from 'cors' | |||
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { join } from 'path' | 3 | import { join } from 'path' |
4 | import { serveIndexHTML } from '@server/lib/client-html' | 4 | import { serveIndexHTML } from '@server/lib/client-html' |
5 | import { getRegisteredPlugins, getRegisteredThemes } from '@server/lib/config' | 5 | import { getEnabledResolutions, getRegisteredPlugins, getRegisteredThemes } from '@server/lib/config' |
6 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | 6 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' |
7 | import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo' | 7 | import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model' |
8 | import { root } from '../helpers/core-utils' | 8 | import { root } from '../helpers/core-utils' |
9 | import { CONFIG, isEmailEnabled } from '../initializers/config' | 9 | import { CONFIG, isEmailEnabled } from '../initializers/config' |
10 | import { | 10 | import { |
@@ -18,10 +18,9 @@ import { | |||
18 | WEBSERVER | 18 | WEBSERVER |
19 | } from '../initializers/constants' | 19 | } from '../initializers/constants' |
20 | import { getThemeOrDefault } from '../lib/plugins/theme-utils' | 20 | import { getThemeOrDefault } from '../lib/plugins/theme-utils' |
21 | import { getEnabledResolutions } from '../lib/video-transcoding' | ||
22 | import { asyncMiddleware } from '../middlewares' | 21 | import { asyncMiddleware } from '../middlewares' |
23 | import { cacheRoute } from '../middlewares/cache' | 22 | import { cacheRoute } from '../middlewares/cache' |
24 | import { UserModel } from '../models/account/user' | 23 | import { UserModel } from '../models/user/user' |
25 | import { VideoModel } from '../models/video/video' | 24 | import { VideoModel } from '../models/video/video' |
26 | import { VideoCommentModel } from '../models/video/video-comment' | 25 | import { VideoCommentModel } from '../models/video/video-comment' |
27 | 26 | ||
diff --git a/server/helpers/actor.ts b/server/helpers/actor.ts index a60d3ed5d..5f742505b 100644 --- a/server/helpers/actor.ts +++ b/server/helpers/actor.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | 1 | ||
2 | import { ActorModel } from '../models/activitypub/actor' | 2 | import { ActorModel } from '../models/actor/actor' |
3 | import { MActorAccountChannelId, MActorFull } from '../types/models' | 3 | import { MActorAccountChannelId, MActorFull } from '../types/models' |
4 | 4 | ||
5 | type ActorFetchByUrlType = 'all' | 'association-ids' | 5 | type ActorFetchByUrlType = 'all' | 'association-ids' |
diff --git a/server/helpers/audit-logger.ts b/server/helpers/audit-logger.ts index 6aae5e821..884bd187d 100644 --- a/server/helpers/audit-logger.ts +++ b/server/helpers/audit-logger.ts | |||
@@ -7,7 +7,7 @@ import * as winston from 'winston' | |||
7 | import { AUDIT_LOG_FILENAME } from '@server/initializers/constants' | 7 | import { AUDIT_LOG_FILENAME } from '@server/initializers/constants' |
8 | import { AdminAbuse, User, VideoChannel, VideoDetails, VideoImport } from '../../shared' | 8 | import { AdminAbuse, User, VideoChannel, VideoDetails, VideoImport } from '../../shared' |
9 | import { CustomConfig } from '../../shared/models/server/custom-config.model' | 9 | import { CustomConfig } from '../../shared/models/server/custom-config.model' |
10 | import { VideoComment } from '../../shared/models/videos/video-comment.model' | 10 | import { VideoComment } from '../../shared/models/videos/comment/video-comment.model' |
11 | import { CONFIG } from '../initializers/config' | 11 | import { CONFIG } from '../initializers/config' |
12 | import { jsonLoggerFormat, labelFormatter } from './logger' | 12 | import { jsonLoggerFormat, labelFormatter } from './logger' |
13 | 13 | ||
diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts index fd3b45804..229e9f03c 100644 --- a/server/helpers/custom-validators/misc.ts +++ b/server/helpers/custom-validators/misc.ts | |||
@@ -14,7 +14,7 @@ function isSafePath (p: string) { | |||
14 | }) | 14 | }) |
15 | } | 15 | } |
16 | 16 | ||
17 | function isArray (value: any) { | 17 | function isArray (value: any): value is any[] { |
18 | return Array.isArray(value) | 18 | return Array.isArray(value) |
19 | } | 19 | } |
20 | 20 | ||
diff --git a/server/helpers/database-utils.ts b/server/helpers/database-utils.ts index f9cb33aca..7befa2c49 100644 --- a/server/helpers/database-utils.ts +++ b/server/helpers/database-utils.ts | |||
@@ -68,7 +68,7 @@ function transactionRetryer <T> (func: (err: any, data: T) => any) { | |||
68 | }) | 68 | }) |
69 | } | 69 | } |
70 | 70 | ||
71 | function updateInstanceWithAnother <T extends Model<T>> (instanceToUpdate: Model<T>, baseInstance: Model<T>) { | 71 | function updateInstanceWithAnother <M, T extends U, U extends Model<M>> (instanceToUpdate: T, baseInstance: U) { |
72 | const obj = baseInstance.toJSON() | 72 | const obj = baseInstance.toJSON() |
73 | 73 | ||
74 | for (const key of Object.keys(obj)) { | 74 | for (const key of Object.keys(obj)) { |
@@ -88,7 +88,7 @@ function afterCommitIfTransaction (t: Transaction, fn: Function) { | |||
88 | return fn() | 88 | return fn() |
89 | } | 89 | } |
90 | 90 | ||
91 | function deleteNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean } & Model<T>> ( | 91 | function deleteNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean } & Pick<Model, 'destroy'>> ( |
92 | fromDatabase: T[], | 92 | fromDatabase: T[], |
93 | newModels: T[], | 93 | newModels: T[], |
94 | t: Transaction | 94 | t: Transaction |
diff --git a/server/helpers/express-utils.ts b/server/helpers/express-utils.ts index ede22a3cc..010c6961a 100644 --- a/server/helpers/express-utils.ts +++ b/server/helpers/express-utils.ts | |||
@@ -1,13 +1,13 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import * as multer from 'multer' | 2 | import * as multer from 'multer' |
3 | import { extname } from 'path' | ||
4 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | ||
5 | import { CONFIG } from '../initializers/config' | ||
3 | import { REMOTE_SCHEME } from '../initializers/constants' | 6 | import { REMOTE_SCHEME } from '../initializers/constants' |
7 | import { isArray } from './custom-validators/misc' | ||
4 | import { logger } from './logger' | 8 | import { logger } from './logger' |
5 | import { deleteFileAndCatch, generateRandomString } from './utils' | 9 | import { deleteFileAndCatch, generateRandomString } from './utils' |
6 | import { extname } from 'path' | ||
7 | import { isArray } from './custom-validators/misc' | ||
8 | import { CONFIG } from '../initializers/config' | ||
9 | import { getExtFromMimetype } from './video' | 10 | import { getExtFromMimetype } from './video' |
10 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | ||
11 | 11 | ||
12 | function buildNSFWFilter (res?: express.Response, paramNSFW?: string) { | 12 | function buildNSFWFilter (res?: express.Response, paramNSFW?: string) { |
13 | if (paramNSFW === 'true') return true | 13 | if (paramNSFW === 'true') return true |
@@ -30,21 +30,21 @@ function buildNSFWFilter (res?: express.Response, paramNSFW?: string) { | |||
30 | return null | 30 | return null |
31 | } | 31 | } |
32 | 32 | ||
33 | function cleanUpReqFiles (req: { files: { [fieldname: string]: Express.Multer.File[] } | Express.Multer.File[] }) { | 33 | function cleanUpReqFiles ( |
34 | const files = req.files | 34 | req: { files: { [fieldname: string]: Express.Multer.File[] } | Express.Multer.File[] } |
35 | 35 | ) { | |
36 | if (!files) return | 36 | const filesObject = req.files |
37 | if (!filesObject) return | ||
37 | 38 | ||
38 | if (isArray(files)) { | 39 | if (isArray(filesObject)) { |
39 | (files as Express.Multer.File[]).forEach(f => deleteFileAndCatch(f.path)) | 40 | filesObject.forEach(f => deleteFileAndCatch(f.path)) |
40 | return | 41 | return |
41 | } | 42 | } |
42 | 43 | ||
43 | for (const key of Object.keys(files)) { | 44 | for (const key of Object.keys(filesObject)) { |
44 | const file = files[key] | 45 | const files = filesObject[key] |
45 | 46 | ||
46 | if (isArray(file)) file.forEach(f => deleteFileAndCatch(f.path)) | 47 | files.forEach(f => deleteFileAndCatch(f.path)) |
47 | else deleteFileAndCatch(file.path) | ||
48 | } | 48 | } |
49 | } | 49 | } |
50 | 50 | ||
diff --git a/server/helpers/ffprobe-utils.ts b/server/helpers/ffprobe-utils.ts index 40eaafd57..ef2aa3f89 100644 --- a/server/helpers/ffprobe-utils.ts +++ b/server/helpers/ffprobe-utils.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import * as ffmpeg from 'fluent-ffmpeg' | 1 | import * as ffmpeg from 'fluent-ffmpeg' |
2 | import { VideoFileMetadata } from '@shared/models/videos/video-file-metadata' | 2 | import { getMaxBitrate, VideoFileMetadata, VideoResolution } from '../../shared/models/videos' |
3 | import { getMaxBitrate, VideoResolution } from '../../shared/models/videos' | ||
4 | import { CONFIG } from '../initializers/config' | 3 | import { CONFIG } from '../initializers/config' |
5 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' | 4 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' |
6 | import { logger } from './logger' | 5 | import { logger } from './logger' |
diff --git a/server/helpers/middlewares/accounts.ts b/server/helpers/middlewares/accounts.ts index 13ae6cdf4..5addd3e1a 100644 --- a/server/helpers/middlewares/accounts.ts +++ b/server/helpers/middlewares/accounts.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Response } from 'express' | 1 | import { Response } from 'express' |
2 | import { UserModel } from '@server/models/account/user' | 2 | import { UserModel } from '@server/models/user/user' |
3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
4 | import { AccountModel } from '../../models/account/account' | 4 | import { AccountModel } from '../../models/account/account' |
5 | import { MAccountDefault } from '../../types/models' | 5 | import { MAccountDefault } from '../../types/models' |
diff --git a/server/helpers/signup.ts b/server/helpers/signup.ts index ed872539b..8fa81e601 100644 --- a/server/helpers/signup.ts +++ b/server/helpers/signup.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { UserModel } from '../models/account/user' | 1 | import { UserModel } from '../models/user/user' |
2 | import * as ipaddr from 'ipaddr.js' | 2 | import * as ipaddr from 'ipaddr.js' |
3 | import { CONFIG } from '../initializers/config' | 3 | import { CONFIG } from '../initializers/config' |
4 | 4 | ||
diff --git a/server/helpers/webfinger.ts b/server/helpers/webfinger.ts index da7e88077..33367f651 100644 --- a/server/helpers/webfinger.ts +++ b/server/helpers/webfinger.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import * as WebFinger from 'webfinger.js' | 1 | import * as WebFinger from 'webfinger.js' |
2 | import { WebFingerData } from '../../shared' | 2 | import { WebFingerData } from '../../shared' |
3 | import { ActorModel } from '../models/activitypub/actor' | ||
4 | import { isTestInstance } from './core-utils' | ||
5 | import { isActivityPubUrlValid } from './custom-validators/activitypub/misc' | ||
6 | import { WEBSERVER } from '../initializers/constants' | 3 | import { WEBSERVER } from '../initializers/constants' |
4 | import { ActorModel } from '../models/actor/actor' | ||
7 | import { MActorFull } from '../types/models' | 5 | import { MActorFull } from '../types/models' |
6 | import { isTestInstance } from './core-utils' | ||
7 | import { isActivityPubUrlValid } from './custom-validators/activitypub/misc' | ||
8 | 8 | ||
9 | const webfinger = new WebFinger({ | 9 | const webfinger = new WebFinger({ |
10 | webfist_fallback: false, | 10 | webfist_fallback: false, |
diff --git a/server/helpers/youtube-dl.ts b/server/helpers/youtube-dl.ts index fac3da6ba..d003ea3cf 100644 --- a/server/helpers/youtube-dl.ts +++ b/server/helpers/youtube-dl.ts | |||
@@ -6,7 +6,6 @@ import { CONFIG } from '@server/initializers/config' | |||
6 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | 6 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' |
7 | import { VideoResolution } from '../../shared/models/videos' | 7 | import { VideoResolution } from '../../shared/models/videos' |
8 | import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants' | 8 | import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants' |
9 | import { getEnabledResolutions } from '../lib/video-transcoding' | ||
10 | import { peertubeTruncate, pipelinePromise, root } from './core-utils' | 9 | import { peertubeTruncate, pipelinePromise, root } from './core-utils' |
11 | import { isVideoFileExtnameValid } from './custom-validators/videos' | 10 | import { isVideoFileExtnameValid } from './custom-validators/videos' |
12 | import { logger } from './logger' | 11 | import { logger } from './logger' |
@@ -35,361 +34,359 @@ const processOptions = { | |||
35 | maxBuffer: 1024 * 1024 * 10 // 10MB | 34 | maxBuffer: 1024 * 1024 * 10 // 10MB |
36 | } | 35 | } |
37 | 36 | ||
38 | function getYoutubeDLInfo (url: string, opts?: string[]): Promise<YoutubeDLInfo> { | 37 | class YoutubeDL { |
39 | return new Promise<YoutubeDLInfo>((res, rej) => { | ||
40 | let args = opts || [ '-j', '--flat-playlist' ] | ||
41 | 38 | ||
42 | if (CONFIG.IMPORT.VIDEOS.HTTP.FORCE_IPV4) { | 39 | constructor (private readonly url: string = '', private readonly enabledResolutions: number[] = []) { |
43 | args.push('--force-ipv4') | ||
44 | } | ||
45 | 40 | ||
46 | args = wrapWithProxyOptions(args) | 41 | } |
47 | args = [ '-f', getYoutubeDLVideoFormat() ].concat(args) | ||
48 | 42 | ||
49 | safeGetYoutubeDL() | 43 | getYoutubeDLInfo (opts?: string[]): Promise<YoutubeDLInfo> { |
50 | .then(youtubeDL => { | 44 | return new Promise<YoutubeDLInfo>((res, rej) => { |
51 | youtubeDL.getInfo(url, args, processOptions, (err, info) => { | 45 | let args = opts || [ '-j', '--flat-playlist' ] |
52 | if (err) return rej(err) | ||
53 | if (info.is_live === true) return rej(new Error('Cannot download a live streaming.')) | ||
54 | 46 | ||
55 | const obj = buildVideoInfo(normalizeObject(info)) | 47 | if (CONFIG.IMPORT.VIDEOS.HTTP.FORCE_IPV4) { |
56 | if (obj.name && obj.name.length < CONSTRAINTS_FIELDS.VIDEOS.NAME.min) obj.name += ' video' | 48 | args.push('--force-ipv4') |
49 | } | ||
57 | 50 | ||
58 | return res(obj) | 51 | args = this.wrapWithProxyOptions(args) |
59 | }) | 52 | args = [ '-f', this.getYoutubeDLVideoFormat() ].concat(args) |
60 | }) | ||
61 | .catch(err => rej(err)) | ||
62 | }) | ||
63 | } | ||
64 | 53 | ||
65 | function getYoutubeDLSubs (url: string, opts?: object): Promise<YoutubeDLSubs> { | 54 | YoutubeDL.safeGetYoutubeDL() |
66 | return new Promise<YoutubeDLSubs>((res, rej) => { | 55 | .then(youtubeDL => { |
67 | const cwd = CONFIG.STORAGE.TMP_DIR | 56 | youtubeDL.getInfo(this.url, args, processOptions, (err, info) => { |
68 | const options = opts || { all: true, format: 'vtt', cwd } | 57 | if (err) return rej(err) |
69 | 58 | if (info.is_live === true) return rej(new Error('Cannot download a live streaming.')) | |
70 | safeGetYoutubeDL() | ||
71 | .then(youtubeDL => { | ||
72 | youtubeDL.getSubs(url, options, (err, files) => { | ||
73 | if (err) return rej(err) | ||
74 | if (!files) return [] | ||
75 | |||
76 | logger.debug('Get subtitles from youtube dl.', { url, files }) | ||
77 | |||
78 | const subtitles = files.reduce((acc, filename) => { | ||
79 | const matched = filename.match(/\.([a-z]{2})(-[a-z]+)?\.(vtt|ttml)/i) | ||
80 | if (!matched || !matched[1]) return acc | ||
81 | |||
82 | return [ | ||
83 | ...acc, | ||
84 | { | ||
85 | language: matched[1], | ||
86 | path: join(cwd, filename), | ||
87 | filename | ||
88 | } | ||
89 | ] | ||
90 | }, []) | ||
91 | 59 | ||
92 | return res(subtitles) | 60 | const obj = this.buildVideoInfo(this.normalizeObject(info)) |
61 | if (obj.name && obj.name.length < CONSTRAINTS_FIELDS.VIDEOS.NAME.min) obj.name += ' video' | ||
62 | |||
63 | return res(obj) | ||
64 | }) | ||
93 | }) | 65 | }) |
94 | }) | 66 | .catch(err => rej(err)) |
95 | .catch(err => rej(err)) | 67 | }) |
96 | }) | 68 | } |
97 | } | ||
98 | 69 | ||
99 | function getYoutubeDLVideoFormat () { | 70 | getYoutubeDLSubs (opts?: object): Promise<YoutubeDLSubs> { |
100 | /** | 71 | return new Promise<YoutubeDLSubs>((res, rej) => { |
101 | * list of format selectors in order or preference | 72 | const cwd = CONFIG.STORAGE.TMP_DIR |
102 | * see https://github.com/ytdl-org/youtube-dl#format-selection | 73 | const options = opts || { all: true, format: 'vtt', cwd } |
103 | * | 74 | |
104 | * case #1 asks for a mp4 using h264 (avc1) and the exact resolution in the hope | 75 | YoutubeDL.safeGetYoutubeDL() |
105 | * of being able to do a "quick-transcode" | 76 | .then(youtubeDL => { |
106 | * case #2 is the first fallback. No "quick-transcode" means we can get anything else (like vp9) | 77 | youtubeDL.getSubs(this.url, options, (err, files) => { |
107 | * case #3 is the resolution-degraded equivalent of #1, and already a pretty safe fallback | 78 | if (err) return rej(err) |
108 | * | 79 | if (!files) return [] |
109 | * in any case we avoid AV1, see https://github.com/Chocobozzz/PeerTube/issues/3499 | 80 | |
110 | **/ | 81 | logger.debug('Get subtitles from youtube dl.', { url: this.url, files }) |
111 | const enabledResolutions = getEnabledResolutions('vod') | 82 | |
112 | const resolution = enabledResolutions.length === 0 | 83 | const subtitles = files.reduce((acc, filename) => { |
113 | ? VideoResolution.H_720P | 84 | const matched = filename.match(/\.([a-z]{2})(-[a-z]+)?\.(vtt|ttml)/i) |
114 | : Math.max(...enabledResolutions) | 85 | if (!matched || !matched[1]) return acc |
115 | 86 | ||
116 | return [ | 87 | return [ |
117 | `bestvideo[vcodec^=avc1][height=${resolution}]+bestaudio[ext=m4a]`, // case #1 | 88 | ...acc, |
118 | `bestvideo[vcodec!*=av01][vcodec!*=vp9.2][height=${resolution}]+bestaudio`, // case #2 | 89 | { |
119 | `bestvideo[vcodec^=avc1][height<=${resolution}]+bestaudio[ext=m4a]`, // case #3 | 90 | language: matched[1], |
120 | `bestvideo[vcodec!*=av01][vcodec!*=vp9.2]+bestaudio`, | 91 | path: join(cwd, filename), |
121 | 'best[vcodec!*=av01][vcodec!*=vp9.2]', // case fallback for known formats | 92 | filename |
122 | 'best' // Ultimate fallback | 93 | } |
123 | ].join('/') | 94 | ] |
124 | } | 95 | }, []) |
96 | |||
97 | return res(subtitles) | ||
98 | }) | ||
99 | }) | ||
100 | .catch(err => rej(err)) | ||
101 | }) | ||
102 | } | ||
125 | 103 | ||
126 | function downloadYoutubeDLVideo (url: string, fileExt: string, timeout: number) { | 104 | getYoutubeDLVideoFormat () { |
127 | // Leave empty the extension, youtube-dl will add it | 105 | /** |
128 | const pathWithoutExtension = generateVideoImportTmpPath(url, '') | 106 | * list of format selectors in order or preference |
107 | * see https://github.com/ytdl-org/youtube-dl#format-selection | ||
108 | * | ||
109 | * case #1 asks for a mp4 using h264 (avc1) and the exact resolution in the hope | ||
110 | * of being able to do a "quick-transcode" | ||
111 | * case #2 is the first fallback. No "quick-transcode" means we can get anything else (like vp9) | ||
112 | * case #3 is the resolution-degraded equivalent of #1, and already a pretty safe fallback | ||
113 | * | ||
114 | * in any case we avoid AV1, see https://github.com/Chocobozzz/PeerTube/issues/3499 | ||
115 | **/ | ||
116 | const resolution = this.enabledResolutions.length === 0 | ||
117 | ? VideoResolution.H_720P | ||
118 | : Math.max(...this.enabledResolutions) | ||
119 | |||
120 | return [ | ||
121 | `bestvideo[vcodec^=avc1][height=${resolution}]+bestaudio[ext=m4a]`, // case #1 | ||
122 | `bestvideo[vcodec!*=av01][vcodec!*=vp9.2][height=${resolution}]+bestaudio`, // case #2 | ||
123 | `bestvideo[vcodec^=avc1][height<=${resolution}]+bestaudio[ext=m4a]`, // case #3 | ||
124 | `bestvideo[vcodec!*=av01][vcodec!*=vp9.2]+bestaudio`, | ||
125 | 'best[vcodec!*=av01][vcodec!*=vp9.2]', // case fallback for known formats | ||
126 | 'best' // Ultimate fallback | ||
127 | ].join('/') | ||
128 | } | ||
129 | 129 | ||
130 | let timer | 130 | downloadYoutubeDLVideo (fileExt: string, timeout: number) { |
131 | // Leave empty the extension, youtube-dl will add it | ||
132 | const pathWithoutExtension = generateVideoImportTmpPath(this.url, '') | ||
131 | 133 | ||
132 | logger.info('Importing youtubeDL video %s to %s', url, pathWithoutExtension) | 134 | let timer |
133 | 135 | ||
134 | let options = [ '-f', getYoutubeDLVideoFormat(), '-o', pathWithoutExtension ] | 136 | logger.info('Importing youtubeDL video %s to %s', this.url, pathWithoutExtension) |
135 | options = wrapWithProxyOptions(options) | ||
136 | 137 | ||
137 | if (process.env.FFMPEG_PATH) { | 138 | let options = [ '-f', this.getYoutubeDLVideoFormat(), '-o', pathWithoutExtension ] |
138 | options = options.concat([ '--ffmpeg-location', process.env.FFMPEG_PATH ]) | 139 | options = this.wrapWithProxyOptions(options) |
139 | } | ||
140 | 140 | ||
141 | logger.debug('YoutubeDL options for %s.', url, { options }) | 141 | if (process.env.FFMPEG_PATH) { |
142 | options = options.concat([ '--ffmpeg-location', process.env.FFMPEG_PATH ]) | ||
143 | } | ||
142 | 144 | ||
143 | return new Promise<string>((res, rej) => { | 145 | logger.debug('YoutubeDL options for %s.', this.url, { options }) |
144 | safeGetYoutubeDL() | ||
145 | .then(youtubeDL => { | ||
146 | youtubeDL.exec(url, options, processOptions, async err => { | ||
147 | clearTimeout(timer) | ||
148 | 146 | ||
149 | try { | 147 | return new Promise<string>((res, rej) => { |
150 | // If youtube-dl did not guess an extension for our file, just use .mp4 as default | 148 | YoutubeDL.safeGetYoutubeDL() |
151 | if (await pathExists(pathWithoutExtension)) { | 149 | .then(youtubeDL => { |
152 | await move(pathWithoutExtension, pathWithoutExtension + '.mp4') | 150 | youtubeDL.exec(this.url, options, processOptions, async err => { |
153 | } | 151 | clearTimeout(timer) |
152 | |||
153 | try { | ||
154 | // If youtube-dl did not guess an extension for our file, just use .mp4 as default | ||
155 | if (await pathExists(pathWithoutExtension)) { | ||
156 | await move(pathWithoutExtension, pathWithoutExtension + '.mp4') | ||
157 | } | ||
154 | 158 | ||
155 | const path = await guessVideoPathWithExtension(pathWithoutExtension, fileExt) | 159 | const path = await this.guessVideoPathWithExtension(pathWithoutExtension, fileExt) |
156 | 160 | ||
157 | if (err) { | 161 | if (err) { |
158 | remove(path) | 162 | remove(path) |
159 | .catch(err => logger.error('Cannot delete path on YoutubeDL error.', { err })) | 163 | .catch(err => logger.error('Cannot delete path on YoutubeDL error.', { err })) |
160 | 164 | ||
165 | return rej(err) | ||
166 | } | ||
167 | |||
168 | return res(path) | ||
169 | } catch (err) { | ||
161 | return rej(err) | 170 | return rej(err) |
162 | } | 171 | } |
163 | 172 | }) | |
164 | return res(path) | 173 | |
165 | } catch (err) { | 174 | timer = setTimeout(() => { |
166 | return rej(err) | 175 | const err = new Error('YoutubeDL download timeout.') |
167 | } | 176 | |
177 | this.guessVideoPathWithExtension(pathWithoutExtension, fileExt) | ||
178 | .then(path => remove(path)) | ||
179 | .finally(() => rej(err)) | ||
180 | .catch(err => { | ||
181 | logger.error('Cannot remove file in youtubeDL timeout.', { err }) | ||
182 | return rej(err) | ||
183 | }) | ||
184 | }, timeout) | ||
168 | }) | 185 | }) |
186 | .catch(err => rej(err)) | ||
187 | }) | ||
188 | } | ||
169 | 189 | ||
170 | timer = setTimeout(() => { | 190 | buildOriginallyPublishedAt (obj: any) { |
171 | const err = new Error('YoutubeDL download timeout.') | 191 | let originallyPublishedAt: Date = null |
172 | 192 | ||
173 | guessVideoPathWithExtension(pathWithoutExtension, fileExt) | 193 | const uploadDateMatcher = /^(\d{4})(\d{2})(\d{2})$/.exec(obj.upload_date) |
174 | .then(path => remove(path)) | 194 | if (uploadDateMatcher) { |
175 | .finally(() => rej(err)) | 195 | originallyPublishedAt = new Date() |
176 | .catch(err => { | 196 | originallyPublishedAt.setHours(0, 0, 0, 0) |
177 | logger.error('Cannot remove file in youtubeDL timeout.', { err }) | ||
178 | return rej(err) | ||
179 | }) | ||
180 | }, timeout) | ||
181 | }) | ||
182 | .catch(err => rej(err)) | ||
183 | }) | ||
184 | } | ||
185 | |||
186 | // Thanks: https://github.com/przemyslawpluta/node-youtube-dl/blob/master/lib/downloader.js | ||
187 | // We rewrote it to avoid sync calls | ||
188 | async function updateYoutubeDLBinary () { | ||
189 | logger.info('Updating youtubeDL binary.') | ||
190 | 197 | ||
191 | const binDirectory = join(root(), 'node_modules', 'youtube-dl', 'bin') | 198 | const year = parseInt(uploadDateMatcher[1], 10) |
192 | const bin = join(binDirectory, 'youtube-dl') | 199 | // Month starts from 0 |
193 | const detailsPath = join(binDirectory, 'details') | 200 | const month = parseInt(uploadDateMatcher[2], 10) - 1 |
194 | const url = process.env.YOUTUBE_DL_DOWNLOAD_HOST || 'https://yt-dl.org/downloads/latest/youtube-dl' | 201 | const day = parseInt(uploadDateMatcher[3], 10) |
195 | 202 | ||
196 | await ensureDir(binDirectory) | 203 | originallyPublishedAt.setFullYear(year, month, day) |
204 | } | ||
197 | 205 | ||
198 | try { | 206 | return originallyPublishedAt |
199 | const result = await got(url, { followRedirect: false }) | 207 | } |
200 | 208 | ||
201 | if (result.statusCode !== HttpStatusCode.FOUND_302) { | 209 | private async guessVideoPathWithExtension (tmpPath: string, sourceExt: string) { |
202 | logger.error('youtube-dl update error: did not get redirect for the latest version link. Status %d', result.statusCode) | 210 | if (!isVideoFileExtnameValid(sourceExt)) { |
203 | return | 211 | throw new Error('Invalid video extension ' + sourceExt) |
204 | } | 212 | } |
205 | 213 | ||
206 | const newUrl = result.headers.location | 214 | const extensions = [ sourceExt, '.mp4', '.mkv', '.webm' ] |
207 | const newVersion = /yt-dl\.org\/downloads\/(\d{4}\.\d\d\.\d\d(\.\d)?)\/youtube-dl/.exec(newUrl)[1] | ||
208 | 215 | ||
209 | const downloadFileStream = got.stream(newUrl) | 216 | for (const extension of extensions) { |
210 | const writeStream = createWriteStream(bin, { mode: 493 }) | 217 | const path = tmpPath + extension |
211 | 218 | ||
212 | await pipelinePromise( | 219 | if (await pathExists(path)) return path |
213 | downloadFileStream, | 220 | } |
214 | writeStream | ||
215 | ) | ||
216 | |||
217 | const details = JSON.stringify({ version: newVersion, path: bin, exec: 'youtube-dl' }) | ||
218 | await writeFile(detailsPath, details, { encoding: 'utf8' }) | ||
219 | 221 | ||
220 | logger.info('youtube-dl updated to version %s.', newVersion) | 222 | throw new Error('Cannot guess path of ' + tmpPath) |
221 | } catch (err) { | ||
222 | logger.error('Cannot update youtube-dl.', { err }) | ||
223 | } | 223 | } |
224 | } | ||
225 | 224 | ||
226 | async function safeGetYoutubeDL () { | 225 | private normalizeObject (obj: any) { |
227 | let youtubeDL | 226 | const newObj: any = {} |
228 | 227 | ||
229 | try { | 228 | for (const key of Object.keys(obj)) { |
230 | youtubeDL = require('youtube-dl') | 229 | // Deprecated key |
231 | } catch (e) { | 230 | if (key === 'resolution') continue |
232 | // Download binary | ||
233 | await updateYoutubeDLBinary() | ||
234 | youtubeDL = require('youtube-dl') | ||
235 | } | ||
236 | 231 | ||
237 | return youtubeDL | 232 | const value = obj[key] |
238 | } | ||
239 | 233 | ||
240 | function buildOriginallyPublishedAt (obj: any) { | 234 | if (typeof value === 'string') { |
241 | let originallyPublishedAt: Date = null | 235 | newObj[key] = value.normalize() |
236 | } else { | ||
237 | newObj[key] = value | ||
238 | } | ||
239 | } | ||
242 | 240 | ||
243 | const uploadDateMatcher = /^(\d{4})(\d{2})(\d{2})$/.exec(obj.upload_date) | 241 | return newObj |
244 | if (uploadDateMatcher) { | 242 | } |
245 | originallyPublishedAt = new Date() | ||
246 | originallyPublishedAt.setHours(0, 0, 0, 0) | ||
247 | 243 | ||
248 | const year = parseInt(uploadDateMatcher[1], 10) | 244 | private buildVideoInfo (obj: any): YoutubeDLInfo { |
249 | // Month starts from 0 | 245 | return { |
250 | const month = parseInt(uploadDateMatcher[2], 10) - 1 | 246 | name: this.titleTruncation(obj.title), |
251 | const day = parseInt(uploadDateMatcher[3], 10) | 247 | description: this.descriptionTruncation(obj.description), |
248 | category: this.getCategory(obj.categories), | ||
249 | licence: this.getLicence(obj.license), | ||
250 | language: this.getLanguage(obj.language), | ||
251 | nsfw: this.isNSFW(obj), | ||
252 | tags: this.getTags(obj.tags), | ||
253 | thumbnailUrl: obj.thumbnail || undefined, | ||
254 | originallyPublishedAt: this.buildOriginallyPublishedAt(obj), | ||
255 | ext: obj.ext | ||
256 | } | ||
257 | } | ||
252 | 258 | ||
253 | originallyPublishedAt.setFullYear(year, month, day) | 259 | private titleTruncation (title: string) { |
260 | return peertubeTruncate(title, { | ||
261 | length: CONSTRAINTS_FIELDS.VIDEOS.NAME.max, | ||
262 | separator: /,? +/, | ||
263 | omission: ' […]' | ||
264 | }) | ||
254 | } | 265 | } |
255 | 266 | ||
256 | return originallyPublishedAt | 267 | private descriptionTruncation (description: string) { |
257 | } | 268 | if (!description || description.length < CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.min) return undefined |
258 | 269 | ||
259 | // --------------------------------------------------------------------------- | 270 | return peertubeTruncate(description, { |
271 | length: CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max, | ||
272 | separator: /,? +/, | ||
273 | omission: ' […]' | ||
274 | }) | ||
275 | } | ||
260 | 276 | ||
261 | export { | 277 | private isNSFW (info: any) { |
262 | updateYoutubeDLBinary, | 278 | return info.age_limit && info.age_limit >= 16 |
263 | getYoutubeDLVideoFormat, | 279 | } |
264 | downloadYoutubeDLVideo, | ||
265 | getYoutubeDLSubs, | ||
266 | getYoutubeDLInfo, | ||
267 | safeGetYoutubeDL, | ||
268 | buildOriginallyPublishedAt | ||
269 | } | ||
270 | 280 | ||
271 | // --------------------------------------------------------------------------- | 281 | private getTags (tags: any) { |
282 | if (Array.isArray(tags) === false) return [] | ||
272 | 283 | ||
273 | async function guessVideoPathWithExtension (tmpPath: string, sourceExt: string) { | 284 | return tags |
274 | if (!isVideoFileExtnameValid(sourceExt)) { | 285 | .filter(t => t.length < CONSTRAINTS_FIELDS.VIDEOS.TAG.max && t.length > CONSTRAINTS_FIELDS.VIDEOS.TAG.min) |
275 | throw new Error('Invalid video extension ' + sourceExt) | 286 | .map(t => t.normalize()) |
287 | .slice(0, 5) | ||
276 | } | 288 | } |
277 | 289 | ||
278 | const extensions = [ sourceExt, '.mp4', '.mkv', '.webm' ] | 290 | private getLicence (licence: string) { |
291 | if (!licence) return undefined | ||
279 | 292 | ||
280 | for (const extension of extensions) { | 293 | if (licence.includes('Creative Commons Attribution')) return 1 |
281 | const path = tmpPath + extension | ||
282 | 294 | ||
283 | if (await pathExists(path)) return path | 295 | for (const key of Object.keys(VIDEO_LICENCES)) { |
284 | } | 296 | const peertubeLicence = VIDEO_LICENCES[key] |
297 | if (peertubeLicence.toLowerCase() === licence.toLowerCase()) return parseInt(key, 10) | ||
298 | } | ||
285 | 299 | ||
286 | throw new Error('Cannot guess path of ' + tmpPath) | 300 | return undefined |
287 | } | 301 | } |
288 | 302 | ||
289 | function normalizeObject (obj: any) { | 303 | private getCategory (categories: string[]) { |
290 | const newObj: any = {} | 304 | if (!categories) return undefined |
291 | 305 | ||
292 | for (const key of Object.keys(obj)) { | 306 | const categoryString = categories[0] |
293 | // Deprecated key | 307 | if (!categoryString || typeof categoryString !== 'string') return undefined |
294 | if (key === 'resolution') continue | ||
295 | 308 | ||
296 | const value = obj[key] | 309 | if (categoryString === 'News & Politics') return 11 |
297 | 310 | ||
298 | if (typeof value === 'string') { | 311 | for (const key of Object.keys(VIDEO_CATEGORIES)) { |
299 | newObj[key] = value.normalize() | 312 | const category = VIDEO_CATEGORIES[key] |
300 | } else { | 313 | if (categoryString.toLowerCase() === category.toLowerCase()) return parseInt(key, 10) |
301 | newObj[key] = value | ||
302 | } | 314 | } |
303 | } | ||
304 | 315 | ||
305 | return newObj | 316 | return undefined |
306 | } | ||
307 | |||
308 | function buildVideoInfo (obj: any): YoutubeDLInfo { | ||
309 | return { | ||
310 | name: titleTruncation(obj.title), | ||
311 | description: descriptionTruncation(obj.description), | ||
312 | category: getCategory(obj.categories), | ||
313 | licence: getLicence(obj.license), | ||
314 | language: getLanguage(obj.language), | ||
315 | nsfw: isNSFW(obj), | ||
316 | tags: getTags(obj.tags), | ||
317 | thumbnailUrl: obj.thumbnail || undefined, | ||
318 | originallyPublishedAt: buildOriginallyPublishedAt(obj), | ||
319 | ext: obj.ext | ||
320 | } | 317 | } |
321 | } | ||
322 | 318 | ||
323 | function titleTruncation (title: string) { | 319 | private getLanguage (language: string) { |
324 | return peertubeTruncate(title, { | 320 | return VIDEO_LANGUAGES[language] ? language : undefined |
325 | length: CONSTRAINTS_FIELDS.VIDEOS.NAME.max, | 321 | } |
326 | separator: /,? +/, | ||
327 | omission: ' […]' | ||
328 | }) | ||
329 | } | ||
330 | 322 | ||
331 | function descriptionTruncation (description: string) { | 323 | private wrapWithProxyOptions (options: string[]) { |
332 | if (!description || description.length < CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.min) return undefined | 324 | if (CONFIG.IMPORT.VIDEOS.HTTP.PROXY.ENABLED) { |
325 | logger.debug('Using proxy for YoutubeDL') | ||
333 | 326 | ||
334 | return peertubeTruncate(description, { | 327 | return [ '--proxy', CONFIG.IMPORT.VIDEOS.HTTP.PROXY.URL ].concat(options) |
335 | length: CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max, | 328 | } |
336 | separator: /,? +/, | ||
337 | omission: ' […]' | ||
338 | }) | ||
339 | } | ||
340 | 329 | ||
341 | function isNSFW (info: any) { | 330 | return options |
342 | return info.age_limit && info.age_limit >= 16 | 331 | } |
343 | } | ||
344 | 332 | ||
345 | function getTags (tags: any) { | 333 | // Thanks: https://github.com/przemyslawpluta/node-youtube-dl/blob/master/lib/downloader.js |
346 | if (Array.isArray(tags) === false) return [] | 334 | // We rewrote it to avoid sync calls |
335 | static async updateYoutubeDLBinary () { | ||
336 | logger.info('Updating youtubeDL binary.') | ||
347 | 337 | ||
348 | return tags | 338 | const binDirectory = join(root(), 'node_modules', 'youtube-dl', 'bin') |
349 | .filter(t => t.length < CONSTRAINTS_FIELDS.VIDEOS.TAG.max && t.length > CONSTRAINTS_FIELDS.VIDEOS.TAG.min) | 339 | const bin = join(binDirectory, 'youtube-dl') |
350 | .map(t => t.normalize()) | 340 | const detailsPath = join(binDirectory, 'details') |
351 | .slice(0, 5) | 341 | const url = process.env.YOUTUBE_DL_DOWNLOAD_HOST || 'https://yt-dl.org/downloads/latest/youtube-dl' |
352 | } | ||
353 | 342 | ||
354 | function getLicence (licence: string) { | 343 | await ensureDir(binDirectory) |
355 | if (!licence) return undefined | ||
356 | 344 | ||
357 | if (licence.includes('Creative Commons Attribution')) return 1 | 345 | try { |
346 | const result = await got(url, { followRedirect: false }) | ||
358 | 347 | ||
359 | for (const key of Object.keys(VIDEO_LICENCES)) { | 348 | if (result.statusCode !== HttpStatusCode.FOUND_302) { |
360 | const peertubeLicence = VIDEO_LICENCES[key] | 349 | logger.error('youtube-dl update error: did not get redirect for the latest version link. Status %d', result.statusCode) |
361 | if (peertubeLicence.toLowerCase() === licence.toLowerCase()) return parseInt(key, 10) | 350 | return |
362 | } | 351 | } |
363 | 352 | ||
364 | return undefined | 353 | const newUrl = result.headers.location |
365 | } | 354 | const newVersion = /yt-dl\.org\/downloads\/(\d{4}\.\d\d\.\d\d(\.\d)?)\/youtube-dl/.exec(newUrl)[1] |
366 | 355 | ||
367 | function getCategory (categories: string[]) { | 356 | const downloadFileStream = got.stream(newUrl) |
368 | if (!categories) return undefined | 357 | const writeStream = createWriteStream(bin, { mode: 493 }) |
369 | 358 | ||
370 | const categoryString = categories[0] | 359 | await pipelinePromise( |
371 | if (!categoryString || typeof categoryString !== 'string') return undefined | 360 | downloadFileStream, |
361 | writeStream | ||
362 | ) | ||
372 | 363 | ||
373 | if (categoryString === 'News & Politics') return 11 | 364 | const details = JSON.stringify({ version: newVersion, path: bin, exec: 'youtube-dl' }) |
365 | await writeFile(detailsPath, details, { encoding: 'utf8' }) | ||
374 | 366 | ||
375 | for (const key of Object.keys(VIDEO_CATEGORIES)) { | 367 | logger.info('youtube-dl updated to version %s.', newVersion) |
376 | const category = VIDEO_CATEGORIES[key] | 368 | } catch (err) { |
377 | if (categoryString.toLowerCase() === category.toLowerCase()) return parseInt(key, 10) | 369 | logger.error('Cannot update youtube-dl.', { err }) |
370 | } | ||
378 | } | 371 | } |
379 | 372 | ||
380 | return undefined | 373 | static async safeGetYoutubeDL () { |
381 | } | 374 | let youtubeDL |
382 | |||
383 | function getLanguage (language: string) { | ||
384 | return VIDEO_LANGUAGES[language] ? language : undefined | ||
385 | } | ||
386 | 375 | ||
387 | function wrapWithProxyOptions (options: string[]) { | 376 | try { |
388 | if (CONFIG.IMPORT.VIDEOS.HTTP.PROXY.ENABLED) { | 377 | youtubeDL = require('youtube-dl') |
389 | logger.debug('Using proxy for YoutubeDL') | 378 | } catch (e) { |
379 | // Download binary | ||
380 | await this.updateYoutubeDLBinary() | ||
381 | youtubeDL = require('youtube-dl') | ||
382 | } | ||
390 | 383 | ||
391 | return [ '--proxy', CONFIG.IMPORT.VIDEOS.HTTP.PROXY.URL ].concat(options) | 384 | return youtubeDL |
392 | } | 385 | } |
386 | } | ||
387 | |||
388 | // --------------------------------------------------------------------------- | ||
393 | 389 | ||
394 | return options | 390 | export { |
391 | YoutubeDL | ||
395 | } | 392 | } |
diff --git a/server/initializers/checker-after-init.ts b/server/initializers/checker-after-init.ts index a93c8b7fd..911734fa0 100644 --- a/server/initializers/checker-after-init.ts +++ b/server/initializers/checker-after-init.ts | |||
@@ -7,7 +7,7 @@ import { RecentlyAddedStrategy } from '../../shared/models/redundancy' | |||
7 | import { isProdInstance, isTestInstance, parseSemVersion } from '../helpers/core-utils' | 7 | import { isProdInstance, isTestInstance, parseSemVersion } from '../helpers/core-utils' |
8 | import { isArray } from '../helpers/custom-validators/misc' | 8 | import { isArray } from '../helpers/custom-validators/misc' |
9 | import { logger } from '../helpers/logger' | 9 | import { logger } from '../helpers/logger' |
10 | import { UserModel } from '../models/account/user' | 10 | import { UserModel } from '../models/user/user' |
11 | import { ApplicationModel, getServerActor } from '../models/application/application' | 11 | import { ApplicationModel, getServerActor } from '../models/application/application' |
12 | import { OAuthClientModel } from '../models/oauth/oauth-client' | 12 | import { OAuthClientModel } from '../models/oauth/oauth-client' |
13 | import { CONFIG, isEmailEnabled } from './config' | 13 | import { CONFIG, isEmailEnabled } from './config' |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 6f388420e..4cf7dcf0a 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -702,7 +702,8 @@ const CUSTOM_HTML_TAG_COMMENTS = { | |||
702 | TITLE: '<!-- title tag -->', | 702 | TITLE: '<!-- title tag -->', |
703 | DESCRIPTION: '<!-- description tag -->', | 703 | DESCRIPTION: '<!-- description tag -->', |
704 | CUSTOM_CSS: '<!-- custom css tag -->', | 704 | CUSTOM_CSS: '<!-- custom css tag -->', |
705 | META_TAGS: '<!-- meta tags -->' | 705 | META_TAGS: '<!-- meta tags -->', |
706 | SERVER_CONFIG: '<!-- server config -->' | ||
706 | } | 707 | } |
707 | 708 | ||
708 | // --------------------------------------------------------------------------- | 709 | // --------------------------------------------------------------------------- |
diff --git a/server/initializers/database.ts b/server/initializers/database.ts index edf12bc41..75a13ec8b 100644 --- a/server/initializers/database.ts +++ b/server/initializers/database.ts | |||
@@ -2,6 +2,9 @@ import { QueryTypes, Transaction } from 'sequelize' | |||
2 | import { Sequelize as SequelizeTypescript } from 'sequelize-typescript' | 2 | import { Sequelize as SequelizeTypescript } from 'sequelize-typescript' |
3 | import { TrackerModel } from '@server/models/server/tracker' | 3 | import { TrackerModel } from '@server/models/server/tracker' |
4 | import { VideoTrackerModel } from '@server/models/server/video-tracker' | 4 | import { VideoTrackerModel } from '@server/models/server/video-tracker' |
5 | import { UserModel } from '@server/models/user/user' | ||
6 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
7 | import { UserVideoHistoryModel } from '@server/models/user/user-video-history' | ||
5 | import { isTestInstance } from '../helpers/core-utils' | 8 | import { isTestInstance } from '../helpers/core-utils' |
6 | import { logger } from '../helpers/logger' | 9 | import { logger } from '../helpers/logger' |
7 | import { AbuseModel } from '../models/abuse/abuse' | 10 | import { AbuseModel } from '../models/abuse/abuse' |
@@ -11,13 +14,9 @@ import { VideoCommentAbuseModel } from '../models/abuse/video-comment-abuse' | |||
11 | import { AccountModel } from '../models/account/account' | 14 | import { AccountModel } from '../models/account/account' |
12 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | 15 | import { AccountBlocklistModel } from '../models/account/account-blocklist' |
13 | import { AccountVideoRateModel } from '../models/account/account-video-rate' | 16 | import { AccountVideoRateModel } from '../models/account/account-video-rate' |
14 | import { ActorImageModel } from '../models/account/actor-image' | 17 | import { ActorModel } from '../models/actor/actor' |
15 | import { UserModel } from '../models/account/user' | 18 | import { ActorFollowModel } from '../models/actor/actor-follow' |
16 | import { UserNotificationModel } from '../models/account/user-notification' | 19 | import { ActorImageModel } from '../models/actor/actor-image' |
17 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' | ||
18 | import { UserVideoHistoryModel } from '../models/account/user-video-history' | ||
19 | import { ActorModel } from '../models/activitypub/actor' | ||
20 | import { ActorFollowModel } from '../models/activitypub/actor-follow' | ||
21 | import { ApplicationModel } from '../models/application/application' | 20 | import { ApplicationModel } from '../models/application/application' |
22 | import { OAuthClientModel } from '../models/oauth/oauth-client' | 21 | import { OAuthClientModel } from '../models/oauth/oauth-client' |
23 | import { OAuthTokenModel } from '../models/oauth/oauth-token' | 22 | import { OAuthTokenModel } from '../models/oauth/oauth-token' |
@@ -25,6 +24,7 @@ import { VideoRedundancyModel } from '../models/redundancy/video-redundancy' | |||
25 | import { PluginModel } from '../models/server/plugin' | 24 | import { PluginModel } from '../models/server/plugin' |
26 | import { ServerModel } from '../models/server/server' | 25 | import { ServerModel } from '../models/server/server' |
27 | import { ServerBlocklistModel } from '../models/server/server-blocklist' | 26 | import { ServerBlocklistModel } from '../models/server/server-blocklist' |
27 | import { UserNotificationSettingModel } from '../models/user/user-notification-setting' | ||
28 | import { ScheduleVideoUpdateModel } from '../models/video/schedule-video-update' | 28 | import { ScheduleVideoUpdateModel } from '../models/video/schedule-video-update' |
29 | import { TagModel } from '../models/video/tag' | 29 | import { TagModel } from '../models/video/tag' |
30 | import { ThumbnailModel } from '../models/video/thumbnail' | 30 | import { ThumbnailModel } from '../models/video/thumbnail' |
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts index 8dcff64e2..676f88653 100644 --- a/server/initializers/installer.ts +++ b/server/initializers/installer.ts | |||
@@ -2,7 +2,7 @@ import * as passwordGenerator from 'password-generator' | |||
2 | import { UserRole } from '../../shared' | 2 | import { UserRole } from '../../shared' |
3 | import { logger } from '../helpers/logger' | 3 | import { logger } from '../helpers/logger' |
4 | import { createApplicationActor, createUserAccountAndChannelAndPlaylist } from '../lib/user' | 4 | import { createApplicationActor, createUserAccountAndChannelAndPlaylist } from '../lib/user' |
5 | import { UserModel } from '../models/account/user' | 5 | import { UserModel } from '../models/user/user' |
6 | import { ApplicationModel } from '../models/application/application' | 6 | import { ApplicationModel } from '../models/application/application' |
7 | import { OAuthClientModel } from '../models/oauth/oauth-client' | 7 | import { OAuthClientModel } from '../models/oauth/oauth-client' |
8 | import { applicationExist, clientsExist, usersExist } from './checker-after-init' | 8 | import { applicationExist, clientsExist, usersExist } from './checker-after-init' |
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index 5fe7381c9..1bcee7ef9 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -20,8 +20,8 @@ import { getUrlFromWebfinger } from '../../helpers/webfinger' | |||
20 | import { MIMETYPES, WEBSERVER } from '../../initializers/constants' | 20 | import { MIMETYPES, WEBSERVER } from '../../initializers/constants' |
21 | import { sequelizeTypescript } from '../../initializers/database' | 21 | import { sequelizeTypescript } from '../../initializers/database' |
22 | import { AccountModel } from '../../models/account/account' | 22 | import { AccountModel } from '../../models/account/account' |
23 | import { ActorImageModel } from '../../models/account/actor-image' | 23 | import { ActorModel } from '../../models/actor/actor' |
24 | import { ActorModel } from '../../models/activitypub/actor' | 24 | import { ActorImageModel } from '../../models/actor/actor-image' |
25 | import { ServerModel } from '../../models/server/server' | 25 | import { ServerModel } from '../../models/server/server' |
26 | import { VideoChannelModel } from '../../models/video/video-channel' | 26 | import { VideoChannelModel } from '../../models/video/video-channel' |
27 | import { | 27 | import { |
@@ -132,12 +132,11 @@ async function getOrCreateActorAndServerAndModel ( | |||
132 | return actorRefreshed | 132 | return actorRefreshed |
133 | } | 133 | } |
134 | 134 | ||
135 | function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string, uuid?: string) { | 135 | function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string) { |
136 | return new ActorModel({ | 136 | return new ActorModel({ |
137 | type, | 137 | type, |
138 | url, | 138 | url, |
139 | preferredUsername, | 139 | preferredUsername, |
140 | uuid, | ||
141 | publicKey: null, | 140 | publicKey: null, |
142 | privateKey: null, | 141 | privateKey: null, |
143 | followersCount: 0, | 142 | followersCount: 0, |
diff --git a/server/lib/activitypub/audience.ts b/server/lib/activitypub/audience.ts index 2986714d3..d0558f191 100644 --- a/server/lib/activitypub/audience.ts +++ b/server/lib/activitypub/audience.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience } from '../../../shared/models/activitypub' | 2 | import { ActivityAudience } from '../../../shared/models/activitypub' |
3 | import { ACTIVITY_PUB } from '../../initializers/constants' | 3 | import { ACTIVITY_PUB } from '../../initializers/constants' |
4 | import { ActorModel } from '../../models/activitypub/actor' | 4 | import { ActorModel } from '../../models/actor/actor' |
5 | import { VideoModel } from '../../models/video/video' | 5 | import { VideoModel } from '../../models/video/video' |
6 | import { VideoShareModel } from '../../models/video/video-share' | 6 | import { VideoShareModel } from '../../models/video/video-share' |
7 | import { MActorFollowersUrl, MActorLight, MActorUrl, MCommentOwner, MCommentOwnerVideo, MVideoId } from '../../types/models' | 7 | import { MActorFollowersUrl, MActorLight, MActorUrl, MCommentOwner, MCommentOwnerVideo, MVideoId } from '../../types/models' |
diff --git a/server/lib/activitypub/process/process-accept.ts b/server/lib/activitypub/process/process-accept.ts index 1799829f8..8ad470cf4 100644 --- a/server/lib/activitypub/process/process-accept.ts +++ b/server/lib/activitypub/process/process-accept.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import { ActivityAccept } from '../../../../shared/models/activitypub' | 1 | import { ActivityAccept } from '../../../../shared/models/activitypub' |
2 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 2 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
3 | import { addFetchOutboxJob } from '../actor' | ||
4 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 3 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
5 | import { MActorDefault, MActorSignature } from '../../../types/models' | 4 | import { MActorDefault, MActorSignature } from '../../../types/models' |
5 | import { addFetchOutboxJob } from '../actor' | ||
6 | 6 | ||
7 | async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) { | 7 | async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) { |
8 | const { byActor: targetActor, inboxActor } = options | 8 | const { byActor: targetActor, inboxActor } = options |
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index 88a968318..20214246c 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts | |||
@@ -2,7 +2,7 @@ import { ActivityDelete } from '../../../../shared/models/activitypub' | |||
2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { sequelizeTypescript } from '../../../initializers/database' | 4 | import { sequelizeTypescript } from '../../../initializers/database' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | 5 | import { ActorModel } from '../../../models/actor/actor' |
6 | import { VideoModel } from '../../../models/video/video' | 6 | import { VideoModel } from '../../../models/video/video' |
7 | import { VideoCommentModel } from '../../../models/video/video-comment' | 7 | import { VideoCommentModel } from '../../../models/video/video-comment' |
8 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | 8 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' |
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index 38d684512..9009c6469 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts | |||
@@ -1,17 +1,17 @@ | |||
1 | import { getServerActor } from '@server/models/application/application' | ||
1 | import { ActivityFollow } from '../../../../shared/models/activitypub' | 2 | import { ActivityFollow } from '../../../../shared/models/activitypub' |
3 | import { getAPId } from '../../../helpers/activitypub' | ||
2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
3 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
4 | import { sequelizeTypescript } from '../../../initializers/database' | ||
5 | import { ActorModel } from '../../../models/activitypub/actor' | ||
6 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
7 | import { sendAccept, sendReject } from '../send' | ||
8 | import { Notifier } from '../../notifier' | ||
9 | import { getAPId } from '../../../helpers/activitypub' | ||
10 | import { CONFIG } from '../../../initializers/config' | 6 | import { CONFIG } from '../../../initializers/config' |
7 | import { sequelizeTypescript } from '../../../initializers/database' | ||
8 | import { ActorModel } from '../../../models/actor/actor' | ||
9 | import { ActorFollowModel } from '../../../models/actor/actor-follow' | ||
11 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 10 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
12 | import { MActorFollowActors, MActorSignature } from '../../../types/models' | 11 | import { MActorFollowActors, MActorSignature } from '../../../types/models' |
12 | import { Notifier } from '../../notifier' | ||
13 | import { autoFollowBackIfNeeded } from '../follow' | 13 | import { autoFollowBackIfNeeded } from '../follow' |
14 | import { getServerActor } from '@server/models/application/application' | 14 | import { sendAccept, sendReject } from '../send' |
15 | 15 | ||
16 | async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { | 16 | async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { |
17 | const { activity, byActor } = options | 17 | const { activity, byActor } = options |
diff --git a/server/lib/activitypub/process/process-reject.ts b/server/lib/activitypub/process/process-reject.ts index 03b669fd9..7f7ab305f 100644 --- a/server/lib/activitypub/process/process-reject.ts +++ b/server/lib/activitypub/process/process-reject.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { ActivityReject } from '../../../../shared/models/activitypub/activity' | 1 | import { ActivityReject } from '../../../../shared/models/activitypub/activity' |
2 | import { sequelizeTypescript } from '../../../initializers/database' | 2 | import { sequelizeTypescript } from '../../../initializers/database' |
3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 3 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
4 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 4 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
5 | import { MActor } from '../../../types/models' | 5 | import { MActor } from '../../../types/models' |
6 | 6 | ||
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts index e520c2f0d..9f031b528 100644 --- a/server/lib/activitypub/process/process-undo.ts +++ b/server/lib/activitypub/process/process-undo.ts | |||
@@ -4,8 +4,8 @@ import { retryTransactionWrapper } from '../../../helpers/database-utils' | |||
4 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
5 | import { sequelizeTypescript } from '../../../initializers/database' | 5 | import { sequelizeTypescript } from '../../../initializers/database' |
6 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 6 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
7 | import { ActorModel } from '../../../models/activitypub/actor' | 7 | import { ActorModel } from '../../../models/actor/actor' |
8 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 8 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
9 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | 9 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' |
10 | import { VideoShareModel } from '../../../models/video/video-share' | 10 | import { VideoShareModel } from '../../../models/video/video-share' |
11 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 11 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index 6df9b93b2..6cd9d0fba 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -1,23 +1,23 @@ | |||
1 | import { isRedundancyAccepted } from '@server/lib/redundancy' | ||
2 | import { ActorImageType } from '@shared/models' | ||
1 | import { ActivityUpdate, CacheFileObject, VideoObject } from '../../../../shared/models/activitypub' | 3 | import { ActivityUpdate, CacheFileObject, VideoObject } from '../../../../shared/models/activitypub' |
2 | import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor' | 4 | import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor' |
5 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' | ||
6 | import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' | ||
7 | import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos' | ||
3 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils' | 8 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils' |
4 | import { logger } from '../../../helpers/logger' | 9 | import { logger } from '../../../helpers/logger' |
5 | import { sequelizeTypescript } from '../../../initializers/database' | 10 | import { sequelizeTypescript } from '../../../initializers/database' |
6 | import { AccountModel } from '../../../models/account/account' | 11 | import { AccountModel } from '../../../models/account/account' |
7 | import { ActorModel } from '../../../models/activitypub/actor' | 12 | import { ActorModel } from '../../../models/actor/actor' |
8 | import { VideoChannelModel } from '../../../models/video/video-channel' | 13 | import { VideoChannelModel } from '../../../models/video/video-channel' |
14 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | ||
15 | import { MAccountIdActor, MActorSignature } from '../../../types/models' | ||
9 | import { getImageInfoIfExists, updateActorImageInstance, updateActorInstance } from '../actor' | 16 | import { getImageInfoIfExists, updateActorImageInstance, updateActorInstance } from '../actor' |
10 | import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos' | ||
11 | import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos' | ||
12 | import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' | ||
13 | import { createOrUpdateCacheFile } from '../cache-file' | 17 | import { createOrUpdateCacheFile } from '../cache-file' |
14 | import { forwardVideoRelatedActivity } from '../send/utils' | ||
15 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' | ||
16 | import { createOrUpdateVideoPlaylist } from '../playlist' | 18 | import { createOrUpdateVideoPlaylist } from '../playlist' |
17 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 19 | import { forwardVideoRelatedActivity } from '../send/utils' |
18 | import { MActorSignature, MAccountIdActor } from '../../../types/models' | 20 | import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos' |
19 | import { isRedundancyAccepted } from '@server/lib/redundancy' | ||
20 | import { ActorImageType } from '@shared/models' | ||
21 | 21 | ||
22 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { | 22 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { |
23 | const { activity, byActor } = options | 23 | const { activity, byActor } = options |
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts index e0acced18..d31f8c10b 100644 --- a/server/lib/activitypub/send/send-delete.ts +++ b/server/lib/activitypub/send/send-delete.ts | |||
@@ -2,7 +2,7 @@ import { Transaction } from 'sequelize' | |||
2 | import { getServerActor } from '@server/models/application/application' | 2 | import { getServerActor } from '@server/models/application/application' |
3 | import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' | 3 | import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' |
4 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | 5 | import { ActorModel } from '../../../models/actor/actor' |
6 | import { VideoCommentModel } from '../../../models/video/video-comment' | 6 | import { VideoCommentModel } from '../../../models/video/video-comment' |
7 | import { VideoShareModel } from '../../../models/video/video-share' | 7 | import { VideoShareModel } from '../../../models/video/video-share' |
8 | import { MActorUrl } from '../../../types/models' | 8 | import { MActorUrl } from '../../../types/models' |
diff --git a/server/lib/activitypub/send/send-view.ts b/server/lib/activitypub/send/send-view.ts index 9254dc7c5..153e94295 100644 --- a/server/lib/activitypub/send/send-view.ts +++ b/server/lib/activitypub/send/send-view.ts | |||
@@ -2,7 +2,7 @@ import { Transaction } from 'sequelize' | |||
2 | import { MActorAudience, MVideoImmutable, MVideoUrl } from '@server/types/models' | 2 | import { MActorAudience, MVideoImmutable, MVideoUrl } from '@server/types/models' |
3 | import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' | 3 | import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' |
4 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | 5 | import { ActorModel } from '../../../models/actor/actor' |
6 | import { audiencify, getAudience } from '../audience' | 6 | import { audiencify, getAudience } from '../audience' |
7 | import { getLocalVideoViewActivityPubUrl } from '../url' | 7 | import { getLocalVideoViewActivityPubUrl } from '../url' |
8 | import { sendVideoRelatedActivity } from './utils' | 8 | import { sendVideoRelatedActivity } from './utils' |
diff --git a/server/lib/activitypub/send/utils.ts b/server/lib/activitypub/send/utils.ts index 85a9f009d..db0e91b71 100644 --- a/server/lib/activitypub/send/utils.ts +++ b/server/lib/activitypub/send/utils.ts | |||
@@ -1,14 +1,14 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { getServerActor } from '@server/models/application/application' | ||
3 | import { ContextType } from '@shared/models/activitypub/context' | ||
2 | import { Activity, ActivityAudience } from '../../../../shared/models/activitypub' | 4 | import { Activity, ActivityAudience } from '../../../../shared/models/activitypub' |
5 | import { afterCommitIfTransaction } from '../../../helpers/database-utils' | ||
3 | import { logger } from '../../../helpers/logger' | 6 | import { logger } from '../../../helpers/logger' |
4 | import { ActorModel } from '../../../models/activitypub/actor' | 7 | import { ActorModel } from '../../../models/actor/actor' |
5 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 8 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
9 | import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../types/models' | ||
6 | import { JobQueue } from '../../job-queue' | 10 | import { JobQueue } from '../../job-queue' |
7 | import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' | 11 | import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' |
8 | import { afterCommitIfTransaction } from '../../../helpers/database-utils' | ||
9 | import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../types/models' | ||
10 | import { getServerActor } from '@server/models/application/application' | ||
11 | import { ContextType } from '@shared/models/activitypub/context' | ||
12 | 12 | ||
13 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { | 13 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { |
14 | byActor: MActorLight | 14 | byActor: MActorLight |
diff --git a/server/lib/auth/oauth-model.ts b/server/lib/auth/oauth-model.ts index b9c69eb2d..ae728d080 100644 --- a/server/lib/auth/oauth-model.ts +++ b/server/lib/auth/oauth-model.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { AccessDeniedError } from 'oauth2-server' | 2 | import { AccessDeniedError } from 'oauth2-server' |
3 | import { PluginManager } from '@server/lib/plugins/plugin-manager' | 3 | import { PluginManager } from '@server/lib/plugins/plugin-manager' |
4 | import { ActorModel } from '@server/models/activitypub/actor' | 4 | import { ActorModel } from '@server/models/actor/actor' |
5 | import { MOAuthClient } from '@server/types/models' | 5 | import { MOAuthClient } from '@server/types/models' |
6 | import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token' | 6 | import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token' |
7 | import { MUser } from '@server/types/models/user/user' | 7 | import { MUser } from '@server/types/models/user/user' |
@@ -9,7 +9,7 @@ import { UserAdminFlag } from '@shared/models/users/user-flag.model' | |||
9 | import { UserRole } from '@shared/models/users/user-role' | 9 | import { UserRole } from '@shared/models/users/user-role' |
10 | import { logger } from '../../helpers/logger' | 10 | import { logger } from '../../helpers/logger' |
11 | import { CONFIG } from '../../initializers/config' | 11 | import { CONFIG } from '../../initializers/config' |
12 | import { UserModel } from '../../models/account/user' | 12 | import { UserModel } from '../../models/user/user' |
13 | import { OAuthClientModel } from '../../models/oauth/oauth-client' | 13 | import { OAuthClientModel } from '../../models/oauth/oauth-client' |
14 | import { OAuthTokenModel } from '../../models/oauth/oauth-token' | 14 | import { OAuthTokenModel } from '../../models/oauth/oauth-token' |
15 | import { createUserAccountAndChannelAndPlaylist } from '../user' | 15 | import { createUserAccountAndChannelAndPlaylist } from '../user' |
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index 203bd3893..85fdc8754 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts | |||
@@ -2,12 +2,14 @@ import * as express from 'express' | |||
2 | import { readFile } from 'fs-extra' | 2 | import { readFile } from 'fs-extra' |
3 | import { join } from 'path' | 3 | import { join } from 'path' |
4 | import validator from 'validator' | 4 | import validator from 'validator' |
5 | import { escapeHTML } from '@shared/core-utils/renderer' | ||
6 | import { HTMLServerConfig } from '@shared/models' | ||
5 | import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n' | 7 | import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n' |
6 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | 8 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' |
7 | import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos' | 9 | import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos' |
8 | import { isTestInstance, sha256 } from '../helpers/core-utils' | 10 | import { isTestInstance, sha256 } from '../helpers/core-utils' |
9 | import { escapeHTML } from '@shared/core-utils/renderer' | ||
10 | import { logger } from '../helpers/logger' | 11 | import { logger } from '../helpers/logger' |
12 | import { mdToPlainText } from '../helpers/markdown' | ||
11 | import { CONFIG } from '../initializers/config' | 13 | import { CONFIG } from '../initializers/config' |
12 | import { | 14 | import { |
13 | ACCEPT_HEADERS, | 15 | ACCEPT_HEADERS, |
@@ -24,7 +26,7 @@ import { VideoChannelModel } from '../models/video/video-channel' | |||
24 | import { getActivityStreamDuration } from '../models/video/video-format-utils' | 26 | import { getActivityStreamDuration } from '../models/video/video-format-utils' |
25 | import { VideoPlaylistModel } from '../models/video/video-playlist' | 27 | import { VideoPlaylistModel } from '../models/video/video-playlist' |
26 | import { MAccountActor, MChannelActor } from '../types/models' | 28 | import { MAccountActor, MChannelActor } from '../types/models' |
27 | import { mdToPlainText } from '../helpers/markdown' | 29 | import { getHTMLServerConfig } from './config' |
28 | 30 | ||
29 | type Tags = { | 31 | type Tags = { |
30 | ogType: string | 32 | ogType: string |
@@ -209,11 +211,14 @@ class ClientHtml { | |||
209 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] | 211 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] |
210 | 212 | ||
211 | const buffer = await readFile(path) | 213 | const buffer = await readFile(path) |
214 | const serverConfig = await getHTMLServerConfig() | ||
212 | 215 | ||
213 | let html = buffer.toString() | 216 | let html = buffer.toString() |
214 | html = await ClientHtml.addAsyncPluginCSS(html) | 217 | html = await ClientHtml.addAsyncPluginCSS(html) |
215 | html = ClientHtml.addCustomCSS(html) | 218 | html = ClientHtml.addCustomCSS(html) |
216 | html = ClientHtml.addTitleTag(html) | 219 | html = ClientHtml.addTitleTag(html) |
220 | html = ClientHtml.addDescriptionTag(html) | ||
221 | html = ClientHtml.addServerConfig(html, serverConfig) | ||
217 | 222 | ||
218 | ClientHtml.htmlCache[path] = html | 223 | ClientHtml.htmlCache[path] = html |
219 | 224 | ||
@@ -275,6 +280,7 @@ class ClientHtml { | |||
275 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] | 280 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] |
276 | 281 | ||
277 | const buffer = await readFile(path) | 282 | const buffer = await readFile(path) |
283 | const serverConfig = await getHTMLServerConfig() | ||
278 | 284 | ||
279 | let html = buffer.toString() | 285 | let html = buffer.toString() |
280 | 286 | ||
@@ -283,6 +289,7 @@ class ClientHtml { | |||
283 | html = ClientHtml.addFaviconContentHash(html) | 289 | html = ClientHtml.addFaviconContentHash(html) |
284 | html = ClientHtml.addLogoContentHash(html) | 290 | html = ClientHtml.addLogoContentHash(html) |
285 | html = ClientHtml.addCustomCSS(html) | 291 | html = ClientHtml.addCustomCSS(html) |
292 | html = ClientHtml.addServerConfig(html, serverConfig) | ||
286 | html = await ClientHtml.addAsyncPluginCSS(html) | 293 | html = await ClientHtml.addAsyncPluginCSS(html) |
287 | 294 | ||
288 | ClientHtml.htmlCache[path] = html | 295 | ClientHtml.htmlCache[path] = html |
@@ -355,6 +362,13 @@ class ClientHtml { | |||
355 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) | 362 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) |
356 | } | 363 | } |
357 | 364 | ||
365 | private static addServerConfig (htmlStringPage: string, serverConfig: HTMLServerConfig) { | ||
366 | const serverConfigString = JSON.stringify(serverConfig) | ||
367 | const configScriptTag = `<script type="application/javascript">window.PeerTubeServerConfig = '${serverConfigString}'</script>` | ||
368 | |||
369 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.SERVER_CONFIG, configScriptTag) | ||
370 | } | ||
371 | |||
358 | private static async addAsyncPluginCSS (htmlStringPage: string) { | 372 | private static async addAsyncPluginCSS (htmlStringPage: string) { |
359 | const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH) | 373 | const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH) |
360 | if (globalCSSContent.byteLength === 0) return htmlStringPage | 374 | if (globalCSSContent.byteLength === 0) return htmlStringPage |
diff --git a/server/lib/config.ts b/server/lib/config.ts index b4c4c9299..18d49f05a 100644 --- a/server/lib/config.ts +++ b/server/lib/config.ts | |||
@@ -2,18 +2,13 @@ import { isSignupAllowed, isSignupAllowedForCurrentIP } from '@server/helpers/si | |||
2 | import { getServerCommit } from '@server/helpers/utils' | 2 | import { getServerCommit } from '@server/helpers/utils' |
3 | import { CONFIG, isEmailEnabled } from '@server/initializers/config' | 3 | import { CONFIG, isEmailEnabled } from '@server/initializers/config' |
4 | import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '@server/initializers/constants' | 4 | import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '@server/initializers/constants' |
5 | import { RegisteredExternalAuthConfig, RegisteredIdAndPassAuthConfig, ServerConfig } from '@shared/models' | 5 | import { HTMLServerConfig, RegisteredExternalAuthConfig, RegisteredIdAndPassAuthConfig, ServerConfig } from '@shared/models' |
6 | import { Hooks } from './plugins/hooks' | 6 | import { Hooks } from './plugins/hooks' |
7 | import { PluginManager } from './plugins/plugin-manager' | 7 | import { PluginManager } from './plugins/plugin-manager' |
8 | import { getThemeOrDefault } from './plugins/theme-utils' | 8 | import { getThemeOrDefault } from './plugins/theme-utils' |
9 | import { getEnabledResolutions } from './video-transcoding' | 9 | import { VideoTranscodingProfilesManager } from './transcoding/video-transcoding-profiles' |
10 | import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' | ||
11 | |||
12 | let serverCommit: string | ||
13 | 10 | ||
14 | async function getServerConfig (ip?: string): Promise<ServerConfig> { | 11 | async function getServerConfig (ip?: string): Promise<ServerConfig> { |
15 | if (serverCommit === undefined) serverCommit = await getServerCommit() | ||
16 | |||
17 | const { allowed } = await Hooks.wrapPromiseFun( | 12 | const { allowed } = await Hooks.wrapPromiseFun( |
18 | isSignupAllowed, | 13 | isSignupAllowed, |
19 | { | 14 | { |
@@ -23,6 +18,23 @@ async function getServerConfig (ip?: string): Promise<ServerConfig> { | |||
23 | ) | 18 | ) |
24 | 19 | ||
25 | const allowedForCurrentIP = isSignupAllowedForCurrentIP(ip) | 20 | const allowedForCurrentIP = isSignupAllowedForCurrentIP(ip) |
21 | |||
22 | const signup = { | ||
23 | allowed, | ||
24 | allowedForCurrentIP, | ||
25 | requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION | ||
26 | } | ||
27 | |||
28 | const htmlConfig = await getHTMLServerConfig() | ||
29 | |||
30 | return { ...htmlConfig, signup } | ||
31 | } | ||
32 | |||
33 | // Config injected in HTML | ||
34 | let serverCommit: string | ||
35 | async function getHTMLServerConfig (): Promise<HTMLServerConfig> { | ||
36 | if (serverCommit === undefined) serverCommit = await getServerCommit() | ||
37 | |||
26 | const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME) | 38 | const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME) |
27 | 39 | ||
28 | return { | 40 | return { |
@@ -66,11 +78,6 @@ async function getServerConfig (ip?: string): Promise<ServerConfig> { | |||
66 | }, | 78 | }, |
67 | serverVersion: PEERTUBE_VERSION, | 79 | serverVersion: PEERTUBE_VERSION, |
68 | serverCommit, | 80 | serverCommit, |
69 | signup: { | ||
70 | allowed, | ||
71 | allowedForCurrentIP, | ||
72 | requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION | ||
73 | }, | ||
74 | transcoding: { | 81 | transcoding: { |
75 | hls: { | 82 | hls: { |
76 | enabled: CONFIG.TRANSCODING.HLS.ENABLED | 83 | enabled: CONFIG.TRANSCODING.HLS.ENABLED |
@@ -208,12 +215,24 @@ function getRegisteredPlugins () { | |||
208 | })) | 215 | })) |
209 | } | 216 | } |
210 | 217 | ||
218 | function getEnabledResolutions (type: 'vod' | 'live') { | ||
219 | const transcoding = type === 'vod' | ||
220 | ? CONFIG.TRANSCODING | ||
221 | : CONFIG.LIVE.TRANSCODING | ||
222 | |||
223 | return Object.keys(transcoding.RESOLUTIONS) | ||
224 | .filter(key => transcoding.ENABLED && transcoding.RESOLUTIONS[key] === true) | ||
225 | .map(r => parseInt(r, 10)) | ||
226 | } | ||
227 | |||
211 | // --------------------------------------------------------------------------- | 228 | // --------------------------------------------------------------------------- |
212 | 229 | ||
213 | export { | 230 | export { |
214 | getServerConfig, | 231 | getServerConfig, |
215 | getRegisteredThemes, | 232 | getRegisteredThemes, |
216 | getRegisteredPlugins | 233 | getEnabledResolutions, |
234 | getRegisteredPlugins, | ||
235 | getHTMLServerConfig | ||
217 | } | 236 | } |
218 | 237 | ||
219 | // --------------------------------------------------------------------------- | 238 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/job-queue/handlers/activitypub-follow.ts b/server/lib/job-queue/handlers/activitypub-follow.ts index 82c95be80..ec8df8969 100644 --- a/server/lib/job-queue/handlers/activitypub-follow.ts +++ b/server/lib/job-queue/handlers/activitypub-follow.ts | |||
@@ -1,18 +1,18 @@ | |||
1 | import * as Bull from 'bull' | 1 | import * as Bull from 'bull' |
2 | import { logger } from '../../../helpers/logger' | 2 | import { getLocalActorFollowActivityPubUrl } from '@server/lib/activitypub/url' |
3 | import { REMOTE_SCHEME, WEBSERVER } from '../../../initializers/constants' | 3 | import { ActivitypubFollowPayload } from '@shared/models' |
4 | import { sendFollow } from '../../activitypub/send' | ||
5 | import { sanitizeHost } from '../../../helpers/core-utils' | 4 | import { sanitizeHost } from '../../../helpers/core-utils' |
6 | import { loadActorUrlOrGetFromWebfinger } from '../../../helpers/webfinger' | ||
7 | import { getOrCreateActorAndServerAndModel } from '../../activitypub/actor' | ||
8 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 5 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
9 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 6 | import { logger } from '../../../helpers/logger' |
10 | import { ActorModel } from '../../../models/activitypub/actor' | 7 | import { loadActorUrlOrGetFromWebfinger } from '../../../helpers/webfinger' |
11 | import { Notifier } from '../../notifier' | 8 | import { REMOTE_SCHEME, WEBSERVER } from '../../../initializers/constants' |
12 | import { sequelizeTypescript } from '../../../initializers/database' | 9 | import { sequelizeTypescript } from '../../../initializers/database' |
10 | import { ActorModel } from '../../../models/actor/actor' | ||
11 | import { ActorFollowModel } from '../../../models/actor/actor-follow' | ||
13 | import { MActor, MActorFollowActors, MActorFull } from '../../../types/models' | 12 | import { MActor, MActorFollowActors, MActorFull } from '../../../types/models' |
14 | import { ActivitypubFollowPayload } from '@shared/models' | 13 | import { getOrCreateActorAndServerAndModel } from '../../activitypub/actor' |
15 | import { getLocalActorFollowActivityPubUrl } from '@server/lib/activitypub/url' | 14 | import { sendFollow } from '../../activitypub/send' |
15 | import { Notifier } from '../../notifier' | ||
16 | 16 | ||
17 | async function processActivityPubFollow (job: Bull.Job) { | 17 | async function processActivityPubFollow (job: Bull.Job) { |
18 | const payload = job.data as ActivitypubFollowPayload | 18 | const payload = job.data as ActivitypubFollowPayload |
diff --git a/server/lib/job-queue/handlers/activitypub-refresher.ts b/server/lib/job-queue/handlers/activitypub-refresher.ts index 666e56868..c09b1bcc8 100644 --- a/server/lib/job-queue/handlers/activitypub-refresher.ts +++ b/server/lib/job-queue/handlers/activitypub-refresher.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import * as Bull from 'bull' | 1 | import * as Bull from 'bull' |
2 | import { refreshVideoPlaylistIfNeeded } from '@server/lib/activitypub/playlist' | ||
3 | import { RefreshPayload } from '@shared/models' | ||
2 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
3 | import { fetchVideoByUrl } from '../../../helpers/video' | 5 | import { fetchVideoByUrl } from '../../../helpers/video' |
6 | import { ActorModel } from '../../../models/actor/actor' | ||
7 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
4 | import { refreshActorIfNeeded } from '../../activitypub/actor' | 8 | import { refreshActorIfNeeded } from '../../activitypub/actor' |
5 | import { refreshVideoIfNeeded } from '../../activitypub/videos' | 9 | import { refreshVideoIfNeeded } from '../../activitypub/videos' |
6 | import { ActorModel } from '../../../models/activitypub/actor' | ||
7 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
8 | import { RefreshPayload } from '@shared/models' | ||
9 | import { refreshVideoPlaylistIfNeeded } from '@server/lib/activitypub/playlist' | ||
10 | 10 | ||
11 | async function refreshAPObject (job: Bull.Job) { | 11 | async function refreshAPObject (job: Bull.Job) { |
12 | const payload = job.data as RefreshPayload | 12 | const payload = job.data as RefreshPayload |
diff --git a/server/lib/job-queue/handlers/actor-keys.ts b/server/lib/job-queue/handlers/actor-keys.ts index 125307843..3eef565d0 100644 --- a/server/lib/job-queue/handlers/actor-keys.ts +++ b/server/lib/job-queue/handlers/actor-keys.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import * as Bull from 'bull' | 1 | import * as Bull from 'bull' |
2 | import { generateAndSaveActorKeys } from '@server/lib/activitypub/actor' | 2 | import { generateAndSaveActorKeys } from '@server/lib/activitypub/actor' |
3 | import { ActorModel } from '@server/models/activitypub/actor' | 3 | import { ActorModel } from '@server/models/actor/actor' |
4 | import { ActorKeysPayload } from '@shared/models' | 4 | import { ActorKeysPayload } from '@shared/models' |
5 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
6 | 6 | ||
diff --git a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts index e8a91450d..37e7c1fad 100644 --- a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts +++ b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { buildDigest } from '@server/helpers/peertube-crypto' | ||
2 | import { getServerActor } from '@server/models/application/application' | ||
3 | import { ContextType } from '@shared/models/activitypub/context' | ||
1 | import { buildSignedActivity } from '../../../../helpers/activitypub' | 4 | import { buildSignedActivity } from '../../../../helpers/activitypub' |
2 | import { ActorModel } from '../../../../models/activitypub/actor' | ||
3 | import { ACTIVITY_PUB, HTTP_SIGNATURE } from '../../../../initializers/constants' | 5 | import { ACTIVITY_PUB, HTTP_SIGNATURE } from '../../../../initializers/constants' |
6 | import { ActorModel } from '../../../../models/actor/actor' | ||
4 | import { MActor } from '../../../../types/models' | 7 | import { MActor } from '../../../../types/models' |
5 | import { getServerActor } from '@server/models/application/application' | ||
6 | import { buildDigest } from '@server/helpers/peertube-crypto' | ||
7 | import { ContextType } from '@shared/models/activitypub/context' | ||
8 | 8 | ||
9 | type Payload <T> = { body: T, contextType?: ContextType, signatureActorId?: number } | 9 | type Payload <T> = { body: T, contextType?: ContextType, signatureActorId?: number } |
10 | 10 | ||
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts index 71f2cafcd..8297a1571 100644 --- a/server/lib/job-queue/handlers/video-file-import.ts +++ b/server/lib/job-queue/handlers/video-file-import.ts | |||
@@ -3,7 +3,7 @@ import { copy, stat } from 'fs-extra' | |||
3 | import { extname } from 'path' | 3 | import { extname } from 'path' |
4 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | 4 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' |
5 | import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' | 5 | import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' |
6 | import { UserModel } from '@server/models/account/user' | 6 | import { UserModel } from '@server/models/user/user' |
7 | import { MVideoFullLight } from '@server/types/models' | 7 | import { MVideoFullLight } from '@server/types/models' |
8 | import { VideoFileImportPayload } from '@shared/models' | 8 | import { VideoFileImportPayload } from '@shared/models' |
9 | import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' | 9 | import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' |
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts index ed2c5eac0..3067ce214 100644 --- a/server/lib/job-queue/handlers/video-import.ts +++ b/server/lib/job-queue/handlers/video-import.ts | |||
@@ -23,7 +23,6 @@ import { getDurationFromVideoFile, getVideoFileFPS, getVideoFileResolution } fro | |||
23 | import { logger } from '../../../helpers/logger' | 23 | import { logger } from '../../../helpers/logger' |
24 | import { getSecureTorrentName } from '../../../helpers/utils' | 24 | import { getSecureTorrentName } from '../../../helpers/utils' |
25 | import { createTorrentAndSetInfoHash, downloadWebTorrentVideo } from '../../../helpers/webtorrent' | 25 | import { createTorrentAndSetInfoHash, downloadWebTorrentVideo } from '../../../helpers/webtorrent' |
26 | import { downloadYoutubeDLVideo } from '../../../helpers/youtube-dl' | ||
27 | import { CONFIG } from '../../../initializers/config' | 26 | import { CONFIG } from '../../../initializers/config' |
28 | import { VIDEO_IMPORT_TIMEOUT } from '../../../initializers/constants' | 27 | import { VIDEO_IMPORT_TIMEOUT } from '../../../initializers/constants' |
29 | import { sequelizeTypescript } from '../../../initializers/database' | 28 | import { sequelizeTypescript } from '../../../initializers/database' |
@@ -34,6 +33,8 @@ import { MThumbnail } from '../../../types/models/video/thumbnail' | |||
34 | import { federateVideoIfNeeded } from '../../activitypub/videos' | 33 | import { federateVideoIfNeeded } from '../../activitypub/videos' |
35 | import { Notifier } from '../../notifier' | 34 | import { Notifier } from '../../notifier' |
36 | import { generateVideoMiniature } from '../../thumbnail' | 35 | import { generateVideoMiniature } from '../../thumbnail' |
36 | import { YoutubeDL } from '@server/helpers/youtube-dl' | ||
37 | import { getEnabledResolutions } from '@server/lib/config' | ||
37 | 38 | ||
38 | async function processVideoImport (job: Bull.Job) { | 39 | async function processVideoImport (job: Bull.Job) { |
39 | const payload = job.data as VideoImportPayload | 40 | const payload = job.data as VideoImportPayload |
@@ -75,8 +76,10 @@ async function processYoutubeDLImport (job: Bull.Job, payload: VideoImportYoutub | |||
75 | videoImportId: videoImport.id | 76 | videoImportId: videoImport.id |
76 | } | 77 | } |
77 | 78 | ||
79 | const youtubeDL = new YoutubeDL(videoImport.targetUrl, getEnabledResolutions('vod')) | ||
80 | |||
78 | return processFile( | 81 | return processFile( |
79 | () => downloadYoutubeDLVideo(videoImport.targetUrl, payload.fileExt, VIDEO_IMPORT_TIMEOUT), | 82 | () => youtubeDL.downloadYoutubeDLVideo(payload.fileExt, VIDEO_IMPORT_TIMEOUT), |
80 | videoImport, | 83 | videoImport, |
81 | options | 84 | options |
82 | ) | 85 | ) |
diff --git a/server/lib/job-queue/handlers/video-live-ending.ts b/server/lib/job-queue/handlers/video-live-ending.ts index d57202ca5..517b90abc 100644 --- a/server/lib/job-queue/handlers/video-live-ending.ts +++ b/server/lib/job-queue/handlers/video-live-ending.ts | |||
@@ -5,9 +5,9 @@ import { ffprobePromise, getAudioStream, getDurationFromVideoFile, getVideoFileR | |||
5 | import { VIDEO_LIVE } from '@server/initializers/constants' | 5 | import { VIDEO_LIVE } from '@server/initializers/constants' |
6 | import { LiveManager } from '@server/lib/live-manager' | 6 | import { LiveManager } from '@server/lib/live-manager' |
7 | import { generateVideoMiniature } from '@server/lib/thumbnail' | 7 | import { generateVideoMiniature } from '@server/lib/thumbnail' |
8 | import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/video-transcoding' | ||
8 | import { publishAndFederateIfNeeded } from '@server/lib/video' | 9 | import { publishAndFederateIfNeeded } from '@server/lib/video' |
9 | import { getHLSDirectory } from '@server/lib/video-paths' | 10 | import { getHLSDirectory } from '@server/lib/video-paths' |
10 | import { generateHlsPlaylistResolutionFromTS } from '@server/lib/video-transcoding' | ||
11 | import { VideoModel } from '@server/models/video/video' | 11 | import { VideoModel } from '@server/models/video/video' |
12 | import { VideoFileModel } from '@server/models/video/video-file' | 12 | import { VideoFileModel } from '@server/models/video/video-file' |
13 | import { VideoLiveModel } from '@server/models/video/video-live' | 13 | import { VideoLiveModel } from '@server/models/video/video-live' |
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts index 010b95b05..8d659daa6 100644 --- a/server/lib/job-queue/handlers/video-transcoding.ts +++ b/server/lib/job-queue/handlers/video-transcoding.ts | |||
@@ -2,7 +2,7 @@ import * as Bull from 'bull' | |||
2 | import { TranscodeOptionsType } from '@server/helpers/ffmpeg-utils' | 2 | import { TranscodeOptionsType } from '@server/helpers/ffmpeg-utils' |
3 | import { getTranscodingJobPriority, publishAndFederateIfNeeded } from '@server/lib/video' | 3 | import { getTranscodingJobPriority, publishAndFederateIfNeeded } from '@server/lib/video' |
4 | import { getVideoFilePath } from '@server/lib/video-paths' | 4 | import { getVideoFilePath } from '@server/lib/video-paths' |
5 | import { UserModel } from '@server/models/account/user' | 5 | import { UserModel } from '@server/models/user/user' |
6 | import { MUser, MUserId, MVideoFullLight, MVideoUUID, MVideoWithFile } from '@server/types/models' | 6 | import { MUser, MUserId, MVideoFullLight, MVideoUUID, MVideoWithFile } from '@server/types/models' |
7 | import { | 7 | import { |
8 | HLSTranscodingPayload, | 8 | HLSTranscodingPayload, |
@@ -24,7 +24,7 @@ import { | |||
24 | mergeAudioVideofile, | 24 | mergeAudioVideofile, |
25 | optimizeOriginalVideofile, | 25 | optimizeOriginalVideofile, |
26 | transcodeNewWebTorrentResolution | 26 | transcodeNewWebTorrentResolution |
27 | } from '../../video-transcoding' | 27 | } from '../../transcoding/video-transcoding' |
28 | import { JobQueue } from '../job-queue' | 28 | import { JobQueue } from '../job-queue' |
29 | 29 | ||
30 | type HandlerFunction = (job: Bull.Job, payload: VideoTranscodingPayload, video: MVideoFullLight, user: MUser) => Promise<any> | 30 | type HandlerFunction = (job: Bull.Job, payload: VideoTranscodingPayload, video: MVideoFullLight, user: MUser) => Promise<any> |
diff --git a/server/lib/job-queue/handlers/video-views.ts b/server/lib/job-queue/handlers/video-views.ts index 897235ec0..86d0a271f 100644 --- a/server/lib/job-queue/handlers/video-views.ts +++ b/server/lib/job-queue/handlers/video-views.ts | |||
@@ -36,8 +36,8 @@ async function processVideosViews () { | |||
36 | } | 36 | } |
37 | 37 | ||
38 | await VideoViewModel.create({ | 38 | await VideoViewModel.create({ |
39 | startDate, | 39 | startDate: new Date(startDate), |
40 | endDate, | 40 | endDate: new Date(endDate), |
41 | views, | 41 | views, |
42 | videoId | 42 | videoId |
43 | }) | 43 | }) |
diff --git a/server/lib/live-manager.ts b/server/lib/live-manager.ts index 66b5d119b..8e7fd5511 100644 --- a/server/lib/live-manager.ts +++ b/server/lib/live-manager.ts | |||
@@ -11,7 +11,7 @@ import { computeResolutionsToTranscode, getVideoFileFPS, getVideoFileResolution | |||
11 | import { logger } from '@server/helpers/logger' | 11 | import { logger } from '@server/helpers/logger' |
12 | import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' | 12 | import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' |
13 | import { MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE, VIEW_LIFETIME, WEBSERVER } from '@server/initializers/constants' | 13 | import { MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE, VIEW_LIFETIME, WEBSERVER } from '@server/initializers/constants' |
14 | import { UserModel } from '@server/models/account/user' | 14 | import { UserModel } from '@server/models/user/user' |
15 | import { VideoModel } from '@server/models/video/video' | 15 | import { VideoModel } from '@server/models/video/video' |
16 | import { VideoFileModel } from '@server/models/video/video-file' | 16 | import { VideoFileModel } from '@server/models/video/video-file' |
17 | import { VideoLiveModel } from '@server/models/video/video-live' | 17 | import { VideoLiveModel } from '@server/models/video/video-live' |
@@ -23,9 +23,9 @@ import { buildSha256Segment } from './hls' | |||
23 | import { JobQueue } from './job-queue' | 23 | import { JobQueue } from './job-queue' |
24 | import { cleanupLive } from './job-queue/handlers/video-live-ending' | 24 | import { cleanupLive } from './job-queue/handlers/video-live-ending' |
25 | import { PeerTubeSocket } from './peertube-socket' | 25 | import { PeerTubeSocket } from './peertube-socket' |
26 | import { VideoTranscodingProfilesManager } from './transcoding/video-transcoding-profiles' | ||
26 | import { isAbleToUploadVideo } from './user' | 27 | import { isAbleToUploadVideo } from './user' |
27 | import { getHLSDirectory } from './video-paths' | 28 | import { getHLSDirectory } from './video-paths' |
28 | import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' | ||
29 | 29 | ||
30 | import memoizee = require('memoizee') | 30 | import memoizee = require('memoizee') |
31 | const NodeRtmpSession = require('node-media-server/node_rtmp_session') | 31 | const NodeRtmpSession = require('node-media-server/node_rtmp_session') |
diff --git a/server/lib/moderation.ts b/server/lib/moderation.ts index 925d64902..0cefe1648 100644 --- a/server/lib/moderation.ts +++ b/server/lib/moderation.ts | |||
@@ -23,9 +23,9 @@ import { ActivityCreate } from '../../shared/models/activitypub' | |||
23 | import { VideoObject } from '../../shared/models/activitypub/objects' | 23 | import { VideoObject } from '../../shared/models/activitypub/objects' |
24 | import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object' | 24 | import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object' |
25 | import { LiveVideoCreate, VideoCreate, VideoImportCreate } from '../../shared/models/videos' | 25 | import { LiveVideoCreate, VideoCreate, VideoImportCreate } from '../../shared/models/videos' |
26 | import { VideoCommentCreate } from '../../shared/models/videos/video-comment.model' | 26 | import { VideoCommentCreate } from '../../shared/models/videos/comment/video-comment.model' |
27 | import { UserModel } from '../models/account/user' | 27 | import { ActorModel } from '../models/actor/actor' |
28 | import { ActorModel } from '../models/activitypub/actor' | 28 | import { UserModel } from '../models/user/user' |
29 | import { VideoModel } from '../models/video/video' | 29 | import { VideoModel } from '../models/video/video' |
30 | import { VideoCommentModel } from '../models/video/video-comment' | 30 | import { VideoCommentModel } from '../models/video/video-comment' |
31 | import { sendAbuse } from './activitypub/send/send-flag' | 31 | import { sendAbuse } from './activitypub/send/send-flag' |
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts index da7f7cc05..1f9ff16df 100644 --- a/server/lib/notifier.ts +++ b/server/lib/notifier.ts | |||
@@ -17,8 +17,8 @@ import { VideoPrivacy, VideoState } from '../../shared/models/videos' | |||
17 | import { logger } from '../helpers/logger' | 17 | import { logger } from '../helpers/logger' |
18 | import { CONFIG } from '../initializers/config' | 18 | import { CONFIG } from '../initializers/config' |
19 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | 19 | import { AccountBlocklistModel } from '../models/account/account-blocklist' |
20 | import { UserModel } from '../models/account/user' | 20 | import { UserModel } from '../models/user/user' |
21 | import { UserNotificationModel } from '../models/account/user-notification' | 21 | import { UserNotificationModel } from '../models/user/user-notification' |
22 | import { MAbuseFull, MAbuseMessage, MAccountServer, MActorFollowFull, MApplication, MPlugin } from '../types/models' | 22 | import { MAbuseFull, MAbuseMessage, MAccountServer, MActorFollowFull, MApplication, MPlugin } from '../types/models' |
23 | import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../types/models/video' | 23 | import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../types/models/video' |
24 | import { isBlockedByServerOrAccount } from './blocklist' | 24 | import { isBlockedByServerOrAccount } from './blocklist' |
diff --git a/server/lib/plugins/hooks.ts b/server/lib/plugins/hooks.ts index aa92f03cc..5e97b52a0 100644 --- a/server/lib/plugins/hooks.ts +++ b/server/lib/plugins/hooks.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { ServerActionHookName, ServerFilterHookName } from '../../../shared/models/plugins/server-hook.model' | ||
2 | import { PluginManager } from './plugin-manager' | ||
3 | import { logger } from '../../helpers/logger' | ||
4 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { ServerActionHookName, ServerFilterHookName } from '../../../shared/models' | ||
3 | import { logger } from '../../helpers/logger' | ||
4 | import { PluginManager } from './plugin-manager' | ||
5 | 5 | ||
6 | type PromiseFunction <U, T> = (params: U) => Promise<T> | Bluebird<T> | 6 | type PromiseFunction <U, T> = (params: U) => Promise<T> | Bluebird<T> |
7 | type RawFunction <U, T> = (params: U) => T | 7 | type RawFunction <U, T> = (params: U) => T |
diff --git a/server/lib/plugins/plugin-helpers-builder.ts b/server/lib/plugins/plugin-helpers-builder.ts index f1bc24d8b..cb1cd4d9a 100644 --- a/server/lib/plugins/plugin-helpers-builder.ts +++ b/server/lib/plugins/plugin-helpers-builder.ts | |||
@@ -17,7 +17,7 @@ import { VideoBlacklistCreate } from '@shared/models' | |||
17 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../blocklist' | 17 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../blocklist' |
18 | import { getServerConfig } from '../config' | 18 | import { getServerConfig } from '../config' |
19 | import { blacklistVideo, unblacklistVideo } from '../video-blacklist' | 19 | import { blacklistVideo, unblacklistVideo } from '../video-blacklist' |
20 | import { UserModel } from '@server/models/account/user' | 20 | import { UserModel } from '@server/models/user/user' |
21 | 21 | ||
22 | function buildPluginHelpers (pluginModel: MPlugin, npmName: string): PeerTubeHelpers { | 22 | function buildPluginHelpers (pluginModel: MPlugin, npmName: string): PeerTubeHelpers { |
23 | const logger = buildPluginLogger(npmName) | 23 | const logger = buildPluginLogger(npmName) |
diff --git a/server/lib/plugins/plugin-index.ts b/server/lib/plugins/plugin-index.ts index 165bc91b3..119cee8e0 100644 --- a/server/lib/plugins/plugin-index.ts +++ b/server/lib/plugins/plugin-index.ts | |||
@@ -1,16 +1,16 @@ | |||
1 | import { sanitizeUrl } from '@server/helpers/core-utils' | 1 | import { sanitizeUrl } from '@server/helpers/core-utils' |
2 | import { ResultList } from '../../../shared/models' | 2 | import { logger } from '@server/helpers/logger' |
3 | import { PeertubePluginIndexList } from '../../../shared/models/plugins/peertube-plugin-index-list.model' | 3 | import { doJSONRequest } from '@server/helpers/requests' |
4 | import { PeerTubePluginIndex } from '../../../shared/models/plugins/peertube-plugin-index.model' | 4 | import { CONFIG } from '@server/initializers/config' |
5 | import { PEERTUBE_VERSION } from '@server/initializers/constants' | ||
6 | import { PluginModel } from '@server/models/server/plugin' | ||
5 | import { | 7 | import { |
8 | PeerTubePluginIndex, | ||
9 | PeertubePluginIndexList, | ||
6 | PeertubePluginLatestVersionRequest, | 10 | PeertubePluginLatestVersionRequest, |
7 | PeertubePluginLatestVersionResponse | 11 | PeertubePluginLatestVersionResponse, |
8 | } from '../../../shared/models/plugins/peertube-plugin-latest-version.model' | 12 | ResultList |
9 | import { logger } from '../../helpers/logger' | 13 | } from '@shared/models' |
10 | import { doJSONRequest } from '../../helpers/requests' | ||
11 | import { CONFIG } from '../../initializers/config' | ||
12 | import { PEERTUBE_VERSION } from '../../initializers/constants' | ||
13 | import { PluginModel } from '../../models/server/plugin' | ||
14 | import { PluginManager } from './plugin-manager' | 14 | import { PluginManager } from './plugin-manager' |
15 | 15 | ||
16 | async function listAvailablePluginsFromIndex (options: PeertubePluginIndexList) { | 16 | async function listAvailablePluginsFromIndex (options: PeertubePluginIndexList) { |
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts index ba9814383..6b9a255a4 100644 --- a/server/lib/plugins/plugin-manager.ts +++ b/server/lib/plugins/plugin-manager.ts | |||
@@ -4,16 +4,11 @@ import { createReadStream, createWriteStream } from 'fs' | |||
4 | import { ensureDir, outputFile, readJSON } from 'fs-extra' | 4 | import { ensureDir, outputFile, readJSON } from 'fs-extra' |
5 | import { basename, join } from 'path' | 5 | import { basename, join } from 'path' |
6 | import { MOAuthTokenUser, MUser } from '@server/types/models' | 6 | import { MOAuthTokenUser, MUser } from '@server/types/models' |
7 | import { RegisterServerHookOptions } from '@shared/models/plugins/register-server-hook.model' | 7 | import { getCompleteLocale } from '@shared/core-utils' |
8 | import { ClientScript, PluginPackageJson, PluginTranslation, PluginTranslationPaths, RegisterServerHookOptions } from '@shared/models' | ||
8 | import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks' | 9 | import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks' |
9 | import { | ||
10 | ClientScript, | ||
11 | PluginPackageJson, | ||
12 | PluginTranslationPaths as PackagePluginTranslations | ||
13 | } from '../../../shared/models/plugins/plugin-package-json.model' | ||
14 | import { PluginTranslation } from '../../../shared/models/plugins/plugin-translation.model' | ||
15 | import { PluginType } from '../../../shared/models/plugins/plugin.type' | 10 | import { PluginType } from '../../../shared/models/plugins/plugin.type' |
16 | import { ServerHook, ServerHookName } from '../../../shared/models/plugins/server-hook.model' | 11 | import { ServerHook, ServerHookName } from '../../../shared/models/plugins/server/server-hook.model' |
17 | import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins' | 12 | import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins' |
18 | import { logger } from '../../helpers/logger' | 13 | import { logger } from '../../helpers/logger' |
19 | import { CONFIG } from '../../initializers/config' | 14 | import { CONFIG } from '../../initializers/config' |
@@ -23,7 +18,6 @@ import { PluginLibrary, RegisterServerAuthExternalOptions, RegisterServerAuthPas | |||
23 | import { ClientHtml } from '../client-html' | 18 | import { ClientHtml } from '../client-html' |
24 | import { RegisterHelpers } from './register-helpers' | 19 | import { RegisterHelpers } from './register-helpers' |
25 | import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' | 20 | import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' |
26 | import { getCompleteLocale } from '@shared/core-utils' | ||
27 | 21 | ||
28 | export interface RegisteredPlugin { | 22 | export interface RegisteredPlugin { |
29 | npmName: string | 23 | npmName: string |
@@ -443,7 +437,7 @@ export class PluginManager implements ServerHook { | |||
443 | 437 | ||
444 | // ###################### Translations ###################### | 438 | // ###################### Translations ###################### |
445 | 439 | ||
446 | private async addTranslations (plugin: PluginModel, npmName: string, translationPaths: PackagePluginTranslations) { | 440 | private async addTranslations (plugin: PluginModel, npmName: string, translationPaths: PluginTranslationPaths) { |
447 | for (const locale of Object.keys(translationPaths)) { | 441 | for (const locale of Object.keys(translationPaths)) { |
448 | const path = translationPaths[locale] | 442 | const path = translationPaths[locale] |
449 | const json = await readJSON(join(this.getPluginPath(plugin.name, plugin.type), path)) | 443 | const json = await readJSON(join(this.getPluginPath(plugin.name, plugin.type), path)) |
diff --git a/server/lib/plugins/register-helpers.ts b/server/lib/plugins/register-helpers.ts index aa69ca2a2..f5b573370 100644 --- a/server/lib/plugins/register-helpers.ts +++ b/server/lib/plugins/register-helpers.ts | |||
@@ -26,10 +26,10 @@ import { | |||
26 | PluginVideoLicenceManager, | 26 | PluginVideoLicenceManager, |
27 | PluginVideoPrivacyManager, | 27 | PluginVideoPrivacyManager, |
28 | RegisterServerHookOptions, | 28 | RegisterServerHookOptions, |
29 | RegisterServerSettingOptions | 29 | RegisterServerSettingOptions, |
30 | serverHookObject | ||
30 | } from '@shared/models' | 31 | } from '@shared/models' |
31 | import { serverHookObject } from '@shared/models/plugins/server-hook.model' | 32 | import { VideoTranscodingProfilesManager } from '../transcoding/video-transcoding-profiles' |
32 | import { VideoTranscodingProfilesManager } from '../video-transcoding-profiles' | ||
33 | import { buildPluginHelpers } from './plugin-helpers-builder' | 33 | import { buildPluginHelpers } from './plugin-helpers-builder' |
34 | 34 | ||
35 | type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy' | 35 | type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy' |
diff --git a/server/lib/redundancy.ts b/server/lib/redundancy.ts index da620b607..2a9241249 100644 --- a/server/lib/redundancy.ts +++ b/server/lib/redundancy.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import { VideoRedundancyModel } from '../models/redundancy/video-redundancy' | ||
2 | import { sendUndoCacheFile } from './activitypub/send' | ||
3 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
4 | import { MActorSignature, MVideoRedundancyVideo } from '@server/types/models' | ||
5 | import { CONFIG } from '@server/initializers/config' | ||
6 | import { logger } from '@server/helpers/logger' | 2 | import { logger } from '@server/helpers/logger' |
7 | import { ActorFollowModel } from '@server/models/activitypub/actor-follow' | 3 | import { CONFIG } from '@server/initializers/config' |
8 | import { Activity } from '@shared/models' | 4 | import { ActorFollowModel } from '@server/models/actor/actor-follow' |
9 | import { getServerActor } from '@server/models/application/application' | 5 | import { getServerActor } from '@server/models/application/application' |
6 | import { MActorSignature, MVideoRedundancyVideo } from '@server/types/models' | ||
7 | import { Activity } from '@shared/models' | ||
8 | import { VideoRedundancyModel } from '../models/redundancy/video-redundancy' | ||
9 | import { sendUndoCacheFile } from './activitypub/send' | ||
10 | 10 | ||
11 | async function removeVideoRedundancy (videoRedundancy: MVideoRedundancyVideo, t?: Transaction) { | 11 | async function removeVideoRedundancy (videoRedundancy: MVideoRedundancyVideo, t?: Transaction) { |
12 | const serverActor = await getServerActor() | 12 | const serverActor = await getServerActor() |
diff --git a/server/lib/schedulers/actor-follow-scheduler.ts b/server/lib/schedulers/actor-follow-scheduler.ts index 598c0211f..1b80316e9 100644 --- a/server/lib/schedulers/actor-follow-scheduler.ts +++ b/server/lib/schedulers/actor-follow-scheduler.ts | |||
@@ -1,9 +1,9 @@ | |||
1 | import { isTestInstance } from '../../helpers/core-utils' | 1 | import { isTestInstance } from '../../helpers/core-utils' |
2 | import { logger } from '../../helpers/logger' | 2 | import { logger } from '../../helpers/logger' |
3 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | ||
4 | import { AbstractScheduler } from './abstract-scheduler' | ||
5 | import { ACTOR_FOLLOW_SCORE, SCHEDULER_INTERVALS_MS } from '../../initializers/constants' | 3 | import { ACTOR_FOLLOW_SCORE, SCHEDULER_INTERVALS_MS } from '../../initializers/constants' |
4 | import { ActorFollowModel } from '../../models/actor/actor-follow' | ||
6 | import { ActorFollowScoreCache } from '../files-cache' | 5 | import { ActorFollowScoreCache } from '../files-cache' |
6 | import { AbstractScheduler } from './abstract-scheduler' | ||
7 | 7 | ||
8 | export class ActorFollowScheduler extends AbstractScheduler { | 8 | export class ActorFollowScheduler extends AbstractScheduler { |
9 | 9 | ||
diff --git a/server/lib/schedulers/auto-follow-index-instances.ts b/server/lib/schedulers/auto-follow-index-instances.ts index 0b8cd1389..aaa5feed5 100644 --- a/server/lib/schedulers/auto-follow-index-instances.ts +++ b/server/lib/schedulers/auto-follow-index-instances.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { chunk } from 'lodash' | 1 | import { chunk } from 'lodash' |
2 | import { doJSONRequest } from '@server/helpers/requests' | 2 | import { doJSONRequest } from '@server/helpers/requests' |
3 | import { JobQueue } from '@server/lib/job-queue' | 3 | import { JobQueue } from '@server/lib/job-queue' |
4 | import { ActorFollowModel } from '@server/models/activitypub/actor-follow' | 4 | import { ActorFollowModel } from '@server/models/actor/actor-follow' |
5 | import { getServerActor } from '@server/models/application/application' | 5 | import { getServerActor } from '@server/models/application/application' |
6 | import { logger } from '../../helpers/logger' | 6 | import { logger } from '../../helpers/logger' |
7 | import { CONFIG } from '../../initializers/config' | 7 | import { CONFIG } from '../../initializers/config' |
diff --git a/server/lib/schedulers/remove-old-history-scheduler.ts b/server/lib/schedulers/remove-old-history-scheduler.ts index 17a42b2c4..225669ea2 100644 --- a/server/lib/schedulers/remove-old-history-scheduler.ts +++ b/server/lib/schedulers/remove-old-history-scheduler.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { logger } from '../../helpers/logger' | 1 | import { logger } from '../../helpers/logger' |
2 | import { AbstractScheduler } from './abstract-scheduler' | 2 | import { AbstractScheduler } from './abstract-scheduler' |
3 | import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants' | 3 | import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants' |
4 | import { UserVideoHistoryModel } from '../../models/account/user-video-history' | 4 | import { UserVideoHistoryModel } from '../../models/user/user-video-history' |
5 | import { CONFIG } from '../../initializers/config' | 5 | import { CONFIG } from '../../initializers/config' |
6 | 6 | ||
7 | export class RemoveOldHistoryScheduler extends AbstractScheduler { | 7 | export class RemoveOldHistoryScheduler extends AbstractScheduler { |
diff --git a/server/lib/schedulers/youtube-dl-update-scheduler.ts b/server/lib/schedulers/youtube-dl-update-scheduler.ts index aefe6aba4..898691c13 100644 --- a/server/lib/schedulers/youtube-dl-update-scheduler.ts +++ b/server/lib/schedulers/youtube-dl-update-scheduler.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { AbstractScheduler } from './abstract-scheduler' | 1 | import { YoutubeDL } from '@server/helpers/youtube-dl' |
2 | import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants' | 2 | import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants' |
3 | import { updateYoutubeDLBinary } from '../../helpers/youtube-dl' | 3 | import { AbstractScheduler } from './abstract-scheduler' |
4 | 4 | ||
5 | export class YoutubeDlUpdateScheduler extends AbstractScheduler { | 5 | export class YoutubeDlUpdateScheduler extends AbstractScheduler { |
6 | 6 | ||
@@ -13,7 +13,7 @@ export class YoutubeDlUpdateScheduler extends AbstractScheduler { | |||
13 | } | 13 | } |
14 | 14 | ||
15 | protected internalExecute () { | 15 | protected internalExecute () { |
16 | return updateYoutubeDLBinary() | 16 | return YoutubeDL.updateYoutubeDLBinary() |
17 | } | 17 | } |
18 | 18 | ||
19 | static get Instance () { | 19 | static get Instance () { |
diff --git a/server/lib/stat-manager.ts b/server/lib/stat-manager.ts index 09ba208bd..25ed21927 100644 --- a/server/lib/stat-manager.ts +++ b/server/lib/stat-manager.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { CONFIG } from '@server/initializers/config' | 1 | import { CONFIG } from '@server/initializers/config' |
2 | import { UserModel } from '@server/models/account/user' | 2 | import { UserModel } from '@server/models/user/user' |
3 | import { ActorFollowModel } from '@server/models/activitypub/actor-follow' | 3 | import { ActorFollowModel } from '@server/models/actor/actor-follow' |
4 | import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy' | 4 | import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy' |
5 | import { VideoModel } from '@server/models/video/video' | 5 | import { VideoModel } from '@server/models/video/video' |
6 | import { VideoChannelModel } from '@server/models/video/video-channel' | 6 | import { VideoChannelModel } from '@server/models/video/video-channel' |
diff --git a/server/lib/video-transcoding-profiles.ts b/server/lib/transcoding/video-transcoding-profiles.ts index 81f5e1962..c5ea72a5f 100644 --- a/server/lib/video-transcoding-profiles.ts +++ b/server/lib/transcoding/video-transcoding-profiles.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { logger } from '@server/helpers/logger' | 1 | import { logger } from '@server/helpers/logger' |
2 | import { AvailableEncoders, EncoderOptionsBuilder, getTargetBitrate, VideoResolution } from '../../shared/models/videos' | 2 | import { AvailableEncoders, EncoderOptionsBuilder, getTargetBitrate, VideoResolution } from '../../../shared/models/videos' |
3 | import { buildStreamSuffix, resetSupportedEncoders } from '../helpers/ffmpeg-utils' | 3 | import { buildStreamSuffix, resetSupportedEncoders } from '../../helpers/ffmpeg-utils' |
4 | import { | 4 | import { |
5 | canDoQuickAudioTranscode, | 5 | canDoQuickAudioTranscode, |
6 | ffprobePromise, | 6 | ffprobePromise, |
@@ -8,8 +8,8 @@ import { | |||
8 | getMaxAudioBitrate, | 8 | getMaxAudioBitrate, |
9 | getVideoFileBitrate, | 9 | getVideoFileBitrate, |
10 | getVideoStreamFromFile | 10 | getVideoStreamFromFile |
11 | } from '../helpers/ffprobe-utils' | 11 | } from '../../helpers/ffprobe-utils' |
12 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' | 12 | import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants' |
13 | 13 | ||
14 | /** | 14 | /** |
15 | * | 15 | * |
diff --git a/server/lib/video-transcoding.ts b/server/lib/transcoding/video-transcoding.ts index c949dca2e..5df192575 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/transcoding/video-transcoding.ts | |||
@@ -3,17 +3,17 @@ import { copyFile, ensureDir, move, remove, stat } from 'fs-extra' | |||
3 | import { basename, extname as extnameUtil, join } from 'path' | 3 | import { basename, extname as extnameUtil, join } from 'path' |
4 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | 4 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' |
5 | import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoFullLight } from '@server/types/models' | 5 | import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoFullLight } from '@server/types/models' |
6 | import { VideoResolution } from '../../shared/models/videos' | 6 | import { VideoResolution } from '../../../shared/models/videos' |
7 | import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' | 7 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' |
8 | import { transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils' | 8 | import { transcode, TranscodeOptions, TranscodeOptionsType } from '../../helpers/ffmpeg-utils' |
9 | import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../helpers/ffprobe-utils' | 9 | import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../../helpers/ffprobe-utils' |
10 | import { logger } from '../helpers/logger' | 10 | import { logger } from '../../helpers/logger' |
11 | import { CONFIG } from '../initializers/config' | 11 | import { CONFIG } from '../../initializers/config' |
12 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../initializers/constants' | 12 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../../initializers/constants' |
13 | import { VideoFileModel } from '../models/video/video-file' | 13 | import { VideoFileModel } from '../../models/video/video-file' |
14 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | 14 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' |
15 | import { updateMasterHLSPlaylist, updateSha256VODSegments } from './hls' | 15 | import { updateMasterHLSPlaylist, updateSha256VODSegments } from '../hls' |
16 | import { generateVideoFilename, generateVideoStreamingPlaylistName, getVideoFilePath } from './video-paths' | 16 | import { generateVideoFilename, generateVideoStreamingPlaylistName, getVideoFilePath } from '../video-paths' |
17 | import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' | 17 | import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' |
18 | 18 | ||
19 | /** | 19 | /** |
@@ -215,16 +215,6 @@ function generateHlsPlaylistResolution (options: { | |||
215 | }) | 215 | }) |
216 | } | 216 | } |
217 | 217 | ||
218 | function getEnabledResolutions (type: 'vod' | 'live') { | ||
219 | const transcoding = type === 'vod' | ||
220 | ? CONFIG.TRANSCODING | ||
221 | : CONFIG.LIVE.TRANSCODING | ||
222 | |||
223 | return Object.keys(transcoding.RESOLUTIONS) | ||
224 | .filter(key => transcoding.ENABLED && transcoding.RESOLUTIONS[key] === true) | ||
225 | .map(r => parseInt(r, 10)) | ||
226 | } | ||
227 | |||
228 | // --------------------------------------------------------------------------- | 218 | // --------------------------------------------------------------------------- |
229 | 219 | ||
230 | export { | 220 | export { |
@@ -232,8 +222,7 @@ export { | |||
232 | generateHlsPlaylistResolutionFromTS, | 222 | generateHlsPlaylistResolutionFromTS, |
233 | optimizeOriginalVideofile, | 223 | optimizeOriginalVideofile, |
234 | transcodeNewWebTorrentResolution, | 224 | transcodeNewWebTorrentResolution, |
235 | mergeAudioVideofile, | 225 | mergeAudioVideofile |
236 | getEnabledResolutions | ||
237 | } | 226 | } |
238 | 227 | ||
239 | // --------------------------------------------------------------------------- | 228 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/user.ts b/server/lib/user.ts index 9b0a0a2f1..8a6fcebc7 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts | |||
@@ -1,14 +1,15 @@ | |||
1 | import { Transaction } from 'sequelize/types' | 1 | import { Transaction } from 'sequelize/types' |
2 | import { v4 as uuidv4 } from 'uuid' | 2 | import { v4 as uuidv4 } from 'uuid' |
3 | import { UserModel } from '@server/models/account/user' | 3 | import { UserModel } from '@server/models/user/user' |
4 | import { MActorDefault } from '@server/types/models/actor' | ||
4 | import { ActivityPubActorType } from '../../shared/models/activitypub' | 5 | import { ActivityPubActorType } from '../../shared/models/activitypub' |
5 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' | 6 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' |
6 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants' | 7 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants' |
7 | import { sequelizeTypescript } from '../initializers/database' | 8 | import { sequelizeTypescript } from '../initializers/database' |
8 | import { AccountModel } from '../models/account/account' | 9 | import { AccountModel } from '../models/account/account' |
9 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' | 10 | import { ActorModel } from '../models/actor/actor' |
10 | import { ActorModel } from '../models/activitypub/actor' | 11 | import { UserNotificationSettingModel } from '../models/user/user-notification-setting' |
11 | import { MAccountDefault, MActorDefault, MChannelActor } from '../types/models' | 12 | import { MAccountDefault, MChannelActor } from '../types/models' |
12 | import { MUser, MUserDefault, MUserId } from '../types/models/user' | 13 | import { MUser, MUserDefault, MUserId } from '../types/models/user' |
13 | import { buildActorInstance, generateAndSaveActorKeys } from './activitypub/actor' | 14 | import { buildActorInstance, generateAndSaveActorKeys } from './activitypub/actor' |
14 | import { getLocalAccountActivityPubUrl } from './activitypub/url' | 15 | import { getLocalAccountActivityPubUrl } from './activitypub/url' |
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index 0476cb2d5..d57e832fe 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { v4 as uuidv4 } from 'uuid' | ||
3 | import { VideoChannelCreate } from '../../shared/models' | 2 | import { VideoChannelCreate } from '../../shared/models' |
4 | import { VideoModel } from '../models/video/video' | 3 | import { VideoModel } from '../models/video/video' |
5 | import { VideoChannelModel } from '../models/video/video-channel' | 4 | import { VideoChannelModel } from '../models/video/video-channel' |
@@ -9,9 +8,8 @@ import { getLocalVideoChannelActivityPubUrl } from './activitypub/url' | |||
9 | import { federateVideoIfNeeded } from './activitypub/videos' | 8 | import { federateVideoIfNeeded } from './activitypub/videos' |
10 | 9 | ||
11 | async function createLocalVideoChannel (videoChannelInfo: VideoChannelCreate, account: MAccountId, t: Sequelize.Transaction) { | 10 | async function createLocalVideoChannel (videoChannelInfo: VideoChannelCreate, account: MAccountId, t: Sequelize.Transaction) { |
12 | const uuid = uuidv4() | ||
13 | const url = getLocalVideoChannelActivityPubUrl(videoChannelInfo.name) | 11 | const url = getLocalVideoChannelActivityPubUrl(videoChannelInfo.name) |
14 | const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid) | 12 | const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name) |
15 | 13 | ||
16 | const actorInstanceCreated = await actorInstance.save({ transaction: t }) | 14 | const actorInstanceCreated = await actorInstance.save({ transaction: t }) |
17 | 15 | ||
diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts index 736ebb2f8..51a9c747e 100644 --- a/server/lib/video-comment.ts +++ b/server/lib/video-comment.ts | |||
@@ -3,7 +3,7 @@ import * as Sequelize from 'sequelize' | |||
3 | import { logger } from '@server/helpers/logger' | 3 | import { logger } from '@server/helpers/logger' |
4 | import { sequelizeTypescript } from '@server/initializers/database' | 4 | import { sequelizeTypescript } from '@server/initializers/database' |
5 | import { ResultList } from '../../shared/models' | 5 | import { ResultList } from '../../shared/models' |
6 | import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model' | 6 | import { VideoCommentThreadTree } from '../../shared/models/videos/comment/video-comment.model' |
7 | import { VideoCommentModel } from '../models/video/video-comment' | 7 | import { VideoCommentModel } from '../models/video/video-comment' |
8 | import { MAccountDefault, MComment, MCommentOwnerVideo, MCommentOwnerVideoReply, MVideoFullLight } from '../types/models' | 8 | import { MAccountDefault, MComment, MCommentOwnerVideo, MCommentOwnerVideoReply, MVideoFullLight } from '../types/models' |
9 | import { sendCreateVideoComment, sendDeleteVideoComment } from './activitypub/send' | 9 | import { sendCreateVideoComment, sendDeleteVideoComment } from './activitypub/send' |
diff --git a/server/lib/video.ts b/server/lib/video.ts index 21e4b7ff2..d26cf85cd 100644 --- a/server/lib/video.ts +++ b/server/lib/video.ts | |||
@@ -28,6 +28,8 @@ function buildLocalVideoFromReq (videoInfo: VideoCreate, channelId: number): Fil | |||
28 | privacy: videoInfo.privacy || VideoPrivacy.PRIVATE, | 28 | privacy: videoInfo.privacy || VideoPrivacy.PRIVATE, |
29 | channelId: channelId, | 29 | channelId: channelId, |
30 | originallyPublishedAt: videoInfo.originallyPublishedAt | 30 | originallyPublishedAt: videoInfo.originallyPublishedAt |
31 | ? new Date(videoInfo.originallyPublishedAt) | ||
32 | : null | ||
31 | } | 33 | } |
32 | } | 34 | } |
33 | 35 | ||
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts index bb849dc72..1d18de8cd 100644 --- a/server/middlewares/validators/follows.ts +++ b/server/middlewares/validators/follows.ts | |||
@@ -1,18 +1,18 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { isFollowStateValid } from '@server/helpers/custom-validators/follows' | ||
4 | import { getServerActor } from '@server/models/application/application' | ||
5 | import { MActorFollowActorsDefault } from '@server/types/models' | ||
6 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
3 | import { isTestInstance } from '../../helpers/core-utils' | 7 | import { isTestInstance } from '../../helpers/core-utils' |
8 | import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' | ||
4 | import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' | 9 | import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' |
5 | import { logger } from '../../helpers/logger' | 10 | import { logger } from '../../helpers/logger' |
11 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' | ||
6 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' | 12 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' |
7 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | 13 | import { ActorModel } from '../../models/actor/actor' |
14 | import { ActorFollowModel } from '../../models/actor/actor-follow' | ||
8 | import { areValidationErrors } from './utils' | 15 | import { areValidationErrors } from './utils' |
9 | import { ActorModel } from '../../models/activitypub/actor' | ||
10 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' | ||
11 | import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' | ||
12 | import { MActorFollowActorsDefault } from '@server/types/models' | ||
13 | import { isFollowStateValid } from '@server/helpers/custom-validators/follows' | ||
14 | import { getServerActor } from '@server/models/application/application' | ||
15 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
16 | 16 | ||
17 | const listFollowsValidator = [ | 17 | const listFollowsValidator = [ |
18 | query('state') | 18 | query('state') |
diff --git a/server/middlewares/validators/plugins.ts b/server/middlewares/validators/plugins.ts index ab87fe720..2c47ec5bb 100644 --- a/server/middlewares/validators/plugins.ts +++ b/server/middlewares/validators/plugins.ts | |||
@@ -1,15 +1,15 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query, ValidationChain } from 'express-validator' | 2 | import { body, param, query, ValidationChain } from 'express-validator' |
3 | import { logger } from '../../helpers/logger' | 3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
4 | import { areValidationErrors } from './utils' | 4 | import { PluginType } from '../../../shared/models/plugins/plugin.type' |
5 | import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/server/api/install-plugin.model' | ||
6 | import { exists, isBooleanValid, isSafePath, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' | ||
5 | import { isNpmPluginNameValid, isPluginNameValid, isPluginTypeValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins' | 7 | import { isNpmPluginNameValid, isPluginNameValid, isPluginTypeValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins' |
8 | import { logger } from '../../helpers/logger' | ||
9 | import { CONFIG } from '../../initializers/config' | ||
6 | import { PluginManager } from '../../lib/plugins/plugin-manager' | 10 | import { PluginManager } from '../../lib/plugins/plugin-manager' |
7 | import { isBooleanValid, isSafePath, toBooleanOrNull, exists, toIntOrNull } from '../../helpers/custom-validators/misc' | ||
8 | import { PluginModel } from '../../models/server/plugin' | 11 | import { PluginModel } from '../../models/server/plugin' |
9 | import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/install-plugin.model' | 12 | import { areValidationErrors } from './utils' |
10 | import { PluginType } from '../../../shared/models/plugins/plugin.type' | ||
11 | import { CONFIG } from '../../initializers/config' | ||
12 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
13 | 13 | ||
14 | const getPluginValidator = (pluginType: PluginType, withVersion = true) => { | 14 | const getPluginValidator = (pluginType: PluginType, withVersion = true) => { |
15 | const validators: (ValidationChain | express.Handler)[] = [ | 15 | const validators: (ValidationChain | express.Handler)[] = [ |
diff --git a/server/middlewares/validators/user-subscriptions.ts b/server/middlewares/validators/user-subscriptions.ts index 0d0c8ccbf..1823892b6 100644 --- a/server/middlewares/validators/user-subscriptions.ts +++ b/server/middlewares/validators/user-subscriptions.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { logger } from '../../helpers/logger' | 3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
4 | import { areValidationErrors } from './utils' | ||
5 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | ||
6 | import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' | 4 | import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' |
7 | import { toArray } from '../../helpers/custom-validators/misc' | 5 | import { toArray } from '../../helpers/custom-validators/misc' |
6 | import { logger } from '../../helpers/logger' | ||
8 | import { WEBSERVER } from '../../initializers/constants' | 7 | import { WEBSERVER } from '../../initializers/constants' |
9 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 8 | import { ActorFollowModel } from '../../models/actor/actor-follow' |
9 | import { areValidationErrors } from './utils' | ||
10 | 10 | ||
11 | const userSubscriptionListValidator = [ | 11 | const userSubscriptionListValidator = [ |
12 | query('search').optional().not().isEmpty().withMessage('Should have a valid search'), | 12 | query('search').optional().not().isEmpty().withMessage('Should have a valid search'), |
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 37119e279..548d5df4d 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts | |||
@@ -34,8 +34,8 @@ import { doesVideoExist } from '../../helpers/middlewares' | |||
34 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup' | 34 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup' |
35 | import { isThemeRegistered } from '../../lib/plugins/theme-utils' | 35 | import { isThemeRegistered } from '../../lib/plugins/theme-utils' |
36 | import { Redis } from '../../lib/redis' | 36 | import { Redis } from '../../lib/redis' |
37 | import { UserModel } from '../../models/account/user' | 37 | import { UserModel } from '../../models/user/user' |
38 | import { ActorModel } from '../../models/activitypub/actor' | 38 | import { ActorModel } from '../../models/actor/actor' |
39 | import { areValidationErrors } from './utils' | 39 | import { areValidationErrors } from './utils' |
40 | 40 | ||
41 | const usersListValidator = [ | 41 | const usersListValidator = [ |
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts index 2463d281c..e881f0d3e 100644 --- a/server/middlewares/validators/videos/video-channels.ts +++ b/server/middlewares/validators/videos/video-channels.ts | |||
@@ -3,6 +3,7 @@ import { body, param, query } from 'express-validator' | |||
3 | import { VIDEO_CHANNELS } from '@server/initializers/constants' | 3 | import { VIDEO_CHANNELS } from '@server/initializers/constants' |
4 | import { MChannelAccountDefault, MUser } from '@server/types/models' | 4 | import { MChannelAccountDefault, MUser } from '@server/types/models' |
5 | import { UserRight } from '../../../../shared' | 5 | import { UserRight } from '../../../../shared' |
6 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
6 | import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' | 7 | import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' |
7 | import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' | 8 | import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' |
8 | import { | 9 | import { |
@@ -12,10 +13,9 @@ import { | |||
12 | } from '../../../helpers/custom-validators/video-channels' | 13 | } from '../../../helpers/custom-validators/video-channels' |
13 | import { logger } from '../../../helpers/logger' | 14 | import { logger } from '../../../helpers/logger' |
14 | import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares' | 15 | import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares' |
15 | import { ActorModel } from '../../../models/activitypub/actor' | 16 | import { ActorModel } from '../../../models/actor/actor' |
16 | import { VideoChannelModel } from '../../../models/video/video-channel' | 17 | import { VideoChannelModel } from '../../../models/video/video-channel' |
17 | import { areValidationErrors } from '../utils' | 18 | import { areValidationErrors } from '../utils' |
18 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
19 | 19 | ||
20 | const videoChannelsAddValidator = [ | 20 | const videoChannelsAddValidator = [ |
21 | body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), | 21 | body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), |
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts index c53af3861..d0643ff26 100644 --- a/server/middlewares/validators/videos/video-imports.ts +++ b/server/middlewares/validators/videos/video-imports.ts | |||
@@ -47,14 +47,12 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | |||
47 | cleanUpReqFiles(req) | 47 | cleanUpReqFiles(req) |
48 | return res.status(HttpStatusCode.CONFLICT_409) | 48 | return res.status(HttpStatusCode.CONFLICT_409) |
49 | .json({ error: 'HTTP import is not enabled on this instance.' }) | 49 | .json({ error: 'HTTP import is not enabled on this instance.' }) |
50 | .end() | ||
51 | } | 50 | } |
52 | 51 | ||
53 | if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) { | 52 | if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) { |
54 | cleanUpReqFiles(req) | 53 | cleanUpReqFiles(req) |
55 | return res.status(HttpStatusCode.CONFLICT_409) | 54 | return res.status(HttpStatusCode.CONFLICT_409) |
56 | .json({ error: 'Torrent/magnet URI import is not enabled on this instance.' }) | 55 | .json({ error: 'Torrent/magnet URI import is not enabled on this instance.' }) |
57 | .end() | ||
58 | } | 56 | } |
59 | 57 | ||
60 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) | 58 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) |
@@ -65,7 +63,6 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | |||
65 | 63 | ||
66 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 64 | return res.status(HttpStatusCode.BAD_REQUEST_400) |
67 | .json({ error: 'Should have a magnetUri or a targetUrl or a torrent file.' }) | 65 | .json({ error: 'Should have a magnetUri or a targetUrl or a torrent file.' }) |
68 | .end() | ||
69 | } | 66 | } |
70 | 67 | ||
71 | if (!await isImportAccepted(req, res)) return cleanUpReqFiles(req) | 68 | if (!await isImportAccepted(req, res)) return cleanUpReqFiles(req) |
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts index d26bcd4a6..3219e10d4 100644 --- a/server/middlewares/validators/videos/videos.ts +++ b/server/middlewares/validators/videos/videos.ts | |||
@@ -7,7 +7,7 @@ import { ExpressPromiseHandler } from '@server/types/express' | |||
7 | import { MUserAccountId, MVideoWithRights } from '@server/types/models' | 7 | import { MUserAccountId, MVideoWithRights } from '@server/types/models' |
8 | import { ServerErrorCode, UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared' | 8 | import { ServerErrorCode, UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared' |
9 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 9 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
10 | import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model' | 10 | import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/change-ownership/video-change-ownership-accept.model' |
11 | import { | 11 | import { |
12 | exists, | 12 | exists, |
13 | isBooleanValid, | 13 | isBooleanValid, |
diff --git a/server/middlewares/validators/webfinger.ts b/server/middlewares/validators/webfinger.ts index a71422ed8..c2dfccc96 100644 --- a/server/middlewares/validators/webfinger.ts +++ b/server/middlewares/validators/webfinger.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { query } from 'express-validator' | 2 | import { query } from 'express-validator' |
3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
3 | import { isWebfingerLocalResourceValid } from '../../helpers/custom-validators/webfinger' | 4 | import { isWebfingerLocalResourceValid } from '../../helpers/custom-validators/webfinger' |
5 | import { getHostWithPort } from '../../helpers/express-utils' | ||
4 | import { logger } from '../../helpers/logger' | 6 | import { logger } from '../../helpers/logger' |
5 | import { ActorModel } from '../../models/activitypub/actor' | 7 | import { ActorModel } from '../../models/actor/actor' |
6 | import { areValidationErrors } from './utils' | 8 | import { areValidationErrors } from './utils' |
7 | import { getHostWithPort } from '../../helpers/express-utils' | ||
8 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
9 | 9 | ||
10 | const webfingerValidator = [ | 10 | const webfingerValidator = [ |
11 | query('resource').custom(isWebfingerLocalResourceValid).withMessage('Should have a valid webfinger resource'), | 11 | query('resource').custom(isWebfingerLocalResourceValid).withMessage('Should have a valid webfinger resource'), |
diff --git a/server/models/abuse/abuse-message.ts b/server/models/abuse/abuse-message.ts index 7e51b3e07..2c5987e96 100644 --- a/server/models/abuse/abuse-message.ts +++ b/server/models/abuse/abuse-message.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses' | 2 | import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses' |
3 | import { MAbuseMessage, MAbuseMessageFormattable } from '@server/types/models' | 3 | import { MAbuseMessage, MAbuseMessageFormattable } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { AbuseMessage } from '@shared/models' | 5 | import { AbuseMessage } from '@shared/models' |
5 | import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account' | 6 | import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account' |
6 | import { getSort, throwIfNotValid } from '../utils' | 7 | import { getSort, throwIfNotValid } from '../utils' |
@@ -17,7 +18,7 @@ import { AbuseModel } from './abuse' | |||
17 | } | 18 | } |
18 | ] | 19 | ] |
19 | }) | 20 | }) |
20 | export class AbuseMessageModel extends Model { | 21 | export class AbuseMessageModel extends Model<Partial<AttributesOnly<AbuseMessageModel>>> { |
21 | 22 | ||
22 | @AllowNull(false) | 23 | @AllowNull(false) |
23 | @Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message')) | 24 | @Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message')) |
diff --git a/server/models/abuse/abuse.ts b/server/models/abuse/abuse.ts index 262f364f1..3518f5c02 100644 --- a/server/models/abuse/abuse.ts +++ b/server/models/abuse/abuse.ts | |||
@@ -16,7 +16,7 @@ import { | |||
16 | UpdatedAt | 16 | UpdatedAt |
17 | } from 'sequelize-typescript' | 17 | } from 'sequelize-typescript' |
18 | import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses' | 18 | import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses' |
19 | import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' | 19 | import { abusePredefinedReasonsMap, AttributesOnly } from '@shared/core-utils' |
20 | import { | 20 | import { |
21 | AbuseFilter, | 21 | AbuseFilter, |
22 | AbuseObject, | 22 | AbuseObject, |
@@ -187,7 +187,7 @@ export enum ScopeNames { | |||
187 | } | 187 | } |
188 | ] | 188 | ] |
189 | }) | 189 | }) |
190 | export class AbuseModel extends Model { | 190 | export class AbuseModel extends Model<Partial<AttributesOnly<AbuseModel>>> { |
191 | 191 | ||
192 | @AllowNull(false) | 192 | @AllowNull(false) |
193 | @Default(null) | 193 | @Default(null) |
diff --git a/server/models/abuse/video-abuse.ts b/server/models/abuse/video-abuse.ts index 90aa0695e..95bff50d0 100644 --- a/server/models/abuse/video-abuse.ts +++ b/server/models/abuse/video-abuse.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { VideoDetails } from '@shared/models' | 3 | import { VideoDetails } from '@shared/models' |
3 | import { VideoModel } from '../video/video' | 4 | import { VideoModel } from '../video/video' |
4 | import { AbuseModel } from './abuse' | 5 | import { AbuseModel } from './abuse' |
@@ -14,7 +15,7 @@ import { AbuseModel } from './abuse' | |||
14 | } | 15 | } |
15 | ] | 16 | ] |
16 | }) | 17 | }) |
17 | export class VideoAbuseModel extends Model { | 18 | export class VideoAbuseModel extends Model<Partial<AttributesOnly<VideoAbuseModel>>> { |
18 | 19 | ||
19 | @CreatedAt | 20 | @CreatedAt |
20 | createdAt: Date | 21 | createdAt: Date |
diff --git a/server/models/abuse/video-comment-abuse.ts b/server/models/abuse/video-comment-abuse.ts index d3fce76a5..32cb2ca64 100644 --- a/server/models/abuse/video-comment-abuse.ts +++ b/server/models/abuse/video-comment-abuse.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { VideoCommentModel } from '../video/video-comment' | 3 | import { VideoCommentModel } from '../video/video-comment' |
3 | import { AbuseModel } from './abuse' | 4 | import { AbuseModel } from './abuse' |
4 | 5 | ||
@@ -13,7 +14,7 @@ import { AbuseModel } from './abuse' | |||
13 | } | 14 | } |
14 | ] | 15 | ] |
15 | }) | 16 | }) |
16 | export class VideoCommentAbuseModel extends Model { | 17 | export class VideoCommentAbuseModel extends Model<Partial<AttributesOnly<VideoCommentAbuseModel>>> { |
17 | 18 | ||
18 | @CreatedAt | 19 | @CreatedAt |
19 | createdAt: Date | 20 | createdAt: Date |
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts index fe9168ab8..b2375b006 100644 --- a/server/models/account/account-blocklist.ts +++ b/server/models/account/account-blocklist.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import { Op } from 'sequelize' | 1 | import { Op } from 'sequelize' |
2 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/types/models' | 3 | import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { AccountBlock } from '../../../shared/models' | 5 | import { AccountBlock } from '../../../shared/models' |
5 | import { ActorModel } from '../activitypub/actor' | 6 | import { ActorModel } from '../actor/actor' |
6 | import { ServerModel } from '../server/server' | 7 | import { ServerModel } from '../server/server' |
7 | import { getSort, searchAttribute } from '../utils' | 8 | import { getSort, searchAttribute } from '../utils' |
8 | import { AccountModel } from './account' | 9 | import { AccountModel } from './account' |
@@ -40,7 +41,7 @@ enum ScopeNames { | |||
40 | } | 41 | } |
41 | ] | 42 | ] |
42 | }) | 43 | }) |
43 | export class AccountBlocklistModel extends Model { | 44 | export class AccountBlocklistModel extends Model<Partial<AttributesOnly<AccountBlocklistModel>>> { |
44 | 45 | ||
45 | @CreatedAt | 46 | @CreatedAt |
46 | createdAt: Date | 47 | createdAt: Date |
diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts index 801f76bba..ee6dbc6da 100644 --- a/server/models/account/account-video-rate.ts +++ b/server/models/account/account-video-rate.ts | |||
@@ -7,11 +7,12 @@ import { | |||
7 | MAccountVideoRateAccountVideo, | 7 | MAccountVideoRateAccountVideo, |
8 | MAccountVideoRateFormattable | 8 | MAccountVideoRateFormattable |
9 | } from '@server/types/models/video/video-rate' | 9 | } from '@server/types/models/video/video-rate' |
10 | import { AttributesOnly } from '@shared/core-utils' | ||
10 | import { AccountVideoRate } from '../../../shared' | 11 | import { AccountVideoRate } from '../../../shared' |
11 | import { VideoRateType } from '../../../shared/models/videos' | 12 | import { VideoRateType } from '../../../shared/models/videos' |
12 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 13 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
13 | import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants' | 14 | import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants' |
14 | import { ActorModel } from '../activitypub/actor' | 15 | import { ActorModel } from '../actor/actor' |
15 | import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' | 16 | import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' |
16 | import { VideoModel } from '../video/video' | 17 | import { VideoModel } from '../video/video' |
17 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' | 18 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' |
@@ -42,7 +43,7 @@ import { AccountModel } from './account' | |||
42 | } | 43 | } |
43 | ] | 44 | ] |
44 | }) | 45 | }) |
45 | export class AccountVideoRateModel extends Model { | 46 | export class AccountVideoRateModel extends Model<Partial<AttributesOnly<AccountVideoRateModel>>> { |
46 | 47 | ||
47 | @AllowNull(false) | 48 | @AllowNull(false) |
48 | @Column(DataType.ENUM(...values(VIDEO_RATE_TYPES))) | 49 | @Column(DataType.ENUM(...values(VIDEO_RATE_TYPES))) |
diff --git a/server/models/account/account.ts b/server/models/account/account.ts index d33353af7..665ecd595 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts | |||
@@ -17,10 +17,11 @@ import { | |||
17 | UpdatedAt | 17 | UpdatedAt |
18 | } from 'sequelize-typescript' | 18 | } from 'sequelize-typescript' |
19 | import { ModelCache } from '@server/models/model-cache' | 19 | import { ModelCache } from '@server/models/model-cache' |
20 | import { AttributesOnly } from '@shared/core-utils' | ||
20 | import { Account, AccountSummary } from '../../../shared/models/actors' | 21 | import { Account, AccountSummary } from '../../../shared/models/actors' |
21 | import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts' | 22 | import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts' |
22 | import { CONSTRAINTS_FIELDS, SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' | 23 | import { CONSTRAINTS_FIELDS, SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' |
23 | import { sendDeleteActor } from '../../lib/activitypub/send' | 24 | import { sendDeleteActor } from '../../lib/activitypub/send/send-delete' |
24 | import { | 25 | import { |
25 | MAccount, | 26 | MAccount, |
26 | MAccountActor, | 27 | MAccountActor, |
@@ -30,19 +31,19 @@ import { | |||
30 | MAccountSummaryFormattable, | 31 | MAccountSummaryFormattable, |
31 | MChannelActor | 32 | MChannelActor |
32 | } from '../../types/models' | 33 | } from '../../types/models' |
33 | import { ActorModel } from '../activitypub/actor' | 34 | import { ActorModel } from '../actor/actor' |
34 | import { ActorFollowModel } from '../activitypub/actor-follow' | 35 | import { ActorFollowModel } from '../actor/actor-follow' |
36 | import { ActorImageModel } from '../actor/actor-image' | ||
35 | import { ApplicationModel } from '../application/application' | 37 | import { ApplicationModel } from '../application/application' |
36 | import { ActorImageModel } from './actor-image' | ||
37 | import { ServerModel } from '../server/server' | 38 | import { ServerModel } from '../server/server' |
38 | import { ServerBlocklistModel } from '../server/server-blocklist' | 39 | import { ServerBlocklistModel } from '../server/server-blocklist' |
40 | import { UserModel } from '../user/user' | ||
39 | import { getSort, throwIfNotValid } from '../utils' | 41 | import { getSort, throwIfNotValid } from '../utils' |
40 | import { VideoModel } from '../video/video' | 42 | import { VideoModel } from '../video/video' |
41 | import { VideoChannelModel } from '../video/video-channel' | 43 | import { VideoChannelModel } from '../video/video-channel' |
42 | import { VideoCommentModel } from '../video/video-comment' | 44 | import { VideoCommentModel } from '../video/video-comment' |
43 | import { VideoPlaylistModel } from '../video/video-playlist' | 45 | import { VideoPlaylistModel } from '../video/video-playlist' |
44 | import { AccountBlocklistModel } from './account-blocklist' | 46 | import { AccountBlocklistModel } from './account-blocklist' |
45 | import { UserModel } from './user' | ||
46 | 47 | ||
47 | export enum ScopeNames { | 48 | export enum ScopeNames { |
48 | SUMMARY = 'SUMMARY' | 49 | SUMMARY = 'SUMMARY' |
@@ -141,7 +142,7 @@ export type SummaryOptions = { | |||
141 | } | 142 | } |
142 | ] | 143 | ] |
143 | }) | 144 | }) |
144 | export class AccountModel extends Model { | 145 | export class AccountModel extends Model<Partial<AttributesOnly<AccountModel>>> { |
145 | 146 | ||
146 | @AllowNull(false) | 147 | @AllowNull(false) |
147 | @Column | 148 | @Column |
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/actor/actor-follow.ts index 4c5f37620..3a09e51d6 100644 --- a/server/models/activitypub/actor-follow.ts +++ b/server/models/actor/actor-follow.ts | |||
@@ -28,6 +28,7 @@ import { | |||
28 | MActorFollowFormattable, | 28 | MActorFollowFormattable, |
29 | MActorFollowSubscriptions | 29 | MActorFollowSubscriptions |
30 | } from '@server/types/models' | 30 | } from '@server/types/models' |
31 | import { AttributesOnly } from '@shared/core-utils' | ||
31 | import { ActivityPubActorType } from '@shared/models' | 32 | import { ActivityPubActorType } from '@shared/models' |
32 | import { FollowState } from '../../../shared/models/actors' | 33 | import { FollowState } from '../../../shared/models/actors' |
33 | import { ActorFollow } from '../../../shared/models/actors/follow.model' | 34 | import { ActorFollow } from '../../../shared/models/actors/follow.model' |
@@ -61,7 +62,7 @@ import { ActorModel, unusedActorAttributesForAPI } from './actor' | |||
61 | } | 62 | } |
62 | ] | 63 | ] |
63 | }) | 64 | }) |
64 | export class ActorFollowModel extends Model { | 65 | export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowModel>>> { |
65 | 66 | ||
66 | @AllowNull(false) | 67 | @AllowNull(false) |
67 | @Column(DataType.ENUM(...values(FOLLOW_STATES))) | 68 | @Column(DataType.ENUM(...values(FOLLOW_STATES))) |
@@ -619,7 +620,7 @@ export class ActorFollowModel extends Model { | |||
619 | if (serverIds.length === 0) return | 620 | if (serverIds.length === 0) return |
620 | 621 | ||
621 | const me = await getServerActor() | 622 | const me = await getServerActor() |
622 | const serverIdsString = createSafeIn(ActorFollowModel, serverIds) | 623 | const serverIdsString = createSafeIn(ActorFollowModel.sequelize, serverIds) |
623 | 624 | ||
624 | const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` + | 625 | const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` + |
625 | 'WHERE id IN (' + | 626 | 'WHERE id IN (' + |
diff --git a/server/models/account/actor-image.ts b/server/models/actor/actor-image.ts index ae05b4969..a35f9edb0 100644 --- a/server/models/account/actor-image.ts +++ b/server/models/actor/actor-image.ts | |||
@@ -2,6 +2,7 @@ import { remove } from 'fs-extra' | |||
2 | import { join } from 'path' | 2 | import { join } from 'path' |
3 | import { AfterDestroy, AllowNull, Column, CreatedAt, Default, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 3 | import { AfterDestroy, AllowNull, Column, CreatedAt, Default, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
4 | import { MActorImageFormattable } from '@server/types/models' | 4 | import { MActorImageFormattable } from '@server/types/models' |
5 | import { AttributesOnly } from '@shared/core-utils' | ||
5 | import { ActorImageType } from '@shared/models' | 6 | import { ActorImageType } from '@shared/models' |
6 | import { ActorImage } from '../../../shared/models/actors/actor-image.model' | 7 | import { ActorImage } from '../../../shared/models/actors/actor-image.model' |
7 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 8 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
@@ -19,7 +20,7 @@ import { throwIfNotValid } from '../utils' | |||
19 | } | 20 | } |
20 | ] | 21 | ] |
21 | }) | 22 | }) |
22 | export class ActorImageModel extends Model { | 23 | export class ActorImageModel extends Model<Partial<AttributesOnly<ActorImageModel>>> { |
23 | 24 | ||
24 | @AllowNull(false) | 25 | @AllowNull(false) |
25 | @Column | 26 | @Column |
diff --git a/server/models/activitypub/actor.ts b/server/models/actor/actor.ts index 1af9efac2..65c53f8f8 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/actor/actor.ts | |||
@@ -18,6 +18,7 @@ import { | |||
18 | UpdatedAt | 18 | UpdatedAt |
19 | } from 'sequelize-typescript' | 19 | } from 'sequelize-typescript' |
20 | import { ModelCache } from '@server/models/model-cache' | 20 | import { ModelCache } from '@server/models/model-cache' |
21 | import { AttributesOnly } from '@shared/core-utils' | ||
21 | import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub' | 22 | import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub' |
22 | import { ActorImage } from '../../../shared/models/actors/actor-image.model' | 23 | import { ActorImage } from '../../../shared/models/actors/actor-image.model' |
23 | import { activityPubContextify } from '../../helpers/activitypub' | 24 | import { activityPubContextify } from '../../helpers/activitypub' |
@@ -51,12 +52,12 @@ import { | |||
51 | MActorWithInboxes | 52 | MActorWithInboxes |
52 | } from '../../types/models' | 53 | } from '../../types/models' |
53 | import { AccountModel } from '../account/account' | 54 | import { AccountModel } from '../account/account' |
54 | import { ActorImageModel } from '../account/actor-image' | ||
55 | import { ServerModel } from '../server/server' | 55 | import { ServerModel } from '../server/server' |
56 | import { isOutdated, throwIfNotValid } from '../utils' | 56 | import { isOutdated, throwIfNotValid } from '../utils' |
57 | import { VideoModel } from '../video/video' | 57 | import { VideoModel } from '../video/video' |
58 | import { VideoChannelModel } from '../video/video-channel' | 58 | import { VideoChannelModel } from '../video/video-channel' |
59 | import { ActorFollowModel } from './actor-follow' | 59 | import { ActorFollowModel } from './actor-follow' |
60 | import { ActorImageModel } from './actor-image' | ||
60 | 61 | ||
61 | enum ScopeNames { | 62 | enum ScopeNames { |
62 | FULL = 'FULL' | 63 | FULL = 'FULL' |
@@ -159,7 +160,7 @@ export const unusedActorAttributesForAPI = [ | |||
159 | } | 160 | } |
160 | ] | 161 | ] |
161 | }) | 162 | }) |
162 | export class ActorModel extends Model { | 163 | export class ActorModel extends Model<Partial<AttributesOnly<ActorModel>>> { |
163 | 164 | ||
164 | @AllowNull(false) | 165 | @AllowNull(false) |
165 | @Column(DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES))) | 166 | @Column(DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES))) |
diff --git a/server/models/application/application.ts b/server/models/application/application.ts index 21f8b1cbc..5531d134a 100644 --- a/server/models/application/application.ts +++ b/server/models/application/application.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import * as memoizee from 'memoizee' | ||
1 | import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript' | 2 | import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript' |
3 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { AccountModel } from '../account/account' | 4 | import { AccountModel } from '../account/account' |
3 | import * as memoizee from 'memoizee' | ||
4 | 5 | ||
5 | export const getServerActor = memoizee(async function () { | 6 | export const getServerActor = memoizee(async function () { |
6 | const application = await ApplicationModel.load() | 7 | const application = await ApplicationModel.load() |
@@ -24,7 +25,7 @@ export const getServerActor = memoizee(async function () { | |||
24 | tableName: 'application', | 25 | tableName: 'application', |
25 | timestamps: false | 26 | timestamps: false |
26 | }) | 27 | }) |
27 | export class ApplicationModel extends Model { | 28 | export class ApplicationModel extends Model<Partial<AttributesOnly<ApplicationModel>>> { |
28 | 29 | ||
29 | @AllowNull(false) | 30 | @AllowNull(false) |
30 | @Default(0) | 31 | @Default(0) |
diff --git a/server/models/oauth/oauth-client.ts b/server/models/oauth/oauth-client.ts index 8dbc1c2f5..890954bdb 100644 --- a/server/models/oauth/oauth-client.ts +++ b/server/models/oauth/oauth-client.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { AllowNull, Column, CreatedAt, DataType, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, Column, CreatedAt, DataType, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { OAuthTokenModel } from './oauth-token' | 3 | import { OAuthTokenModel } from './oauth-token' |
3 | 4 | ||
4 | @Table({ | 5 | @Table({ |
@@ -14,7 +15,7 @@ import { OAuthTokenModel } from './oauth-token' | |||
14 | } | 15 | } |
15 | ] | 16 | ] |
16 | }) | 17 | }) |
17 | export class OAuthClientModel extends Model { | 18 | export class OAuthClientModel extends Model<Partial<AttributesOnly<OAuthClientModel>>> { |
18 | 19 | ||
19 | @AllowNull(false) | 20 | @AllowNull(false) |
20 | @Column | 21 | @Column |
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts index 27e643aa7..af4b0ec42 100644 --- a/server/models/oauth/oauth-token.ts +++ b/server/models/oauth/oauth-token.ts | |||
@@ -15,10 +15,11 @@ import { | |||
15 | import { TokensCache } from '@server/lib/auth/tokens-cache' | 15 | import { TokensCache } from '@server/lib/auth/tokens-cache' |
16 | import { MUserAccountId } from '@server/types/models' | 16 | import { MUserAccountId } from '@server/types/models' |
17 | import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token' | 17 | import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token' |
18 | import { AttributesOnly } from '@shared/core-utils' | ||
18 | import { logger } from '../../helpers/logger' | 19 | import { logger } from '../../helpers/logger' |
19 | import { AccountModel } from '../account/account' | 20 | import { AccountModel } from '../account/account' |
20 | import { UserModel } from '../account/user' | 21 | import { ActorModel } from '../actor/actor' |
21 | import { ActorModel } from '../activitypub/actor' | 22 | import { UserModel } from '../user/user' |
22 | import { OAuthClientModel } from './oauth-client' | 23 | import { OAuthClientModel } from './oauth-client' |
23 | 24 | ||
24 | export type OAuthTokenInfo = { | 25 | export type OAuthTokenInfo = { |
@@ -78,7 +79,7 @@ enum ScopeNames { | |||
78 | } | 79 | } |
79 | ] | 80 | ] |
80 | }) | 81 | }) |
81 | export class OAuthTokenModel extends Model { | 82 | export class OAuthTokenModel extends Model<Partial<AttributesOnly<OAuthTokenModel>>> { |
82 | 83 | ||
83 | @AllowNull(false) | 84 | @AllowNull(false) |
84 | @Column | 85 | @Column |
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts index 349dba513..ca56a57dc 100644 --- a/server/models/redundancy/video-redundancy.ts +++ b/server/models/redundancy/video-redundancy.ts | |||
@@ -16,6 +16,7 @@ import { | |||
16 | } from 'sequelize-typescript' | 16 | } from 'sequelize-typescript' |
17 | import { getServerActor } from '@server/models/application/application' | 17 | import { getServerActor } from '@server/models/application/application' |
18 | import { MActor, MVideoForRedundancyAPI, MVideoRedundancy, MVideoRedundancyAP, MVideoRedundancyVideo } from '@server/types/models' | 18 | import { MActor, MVideoForRedundancyAPI, MVideoRedundancy, MVideoRedundancyAP, MVideoRedundancyVideo } from '@server/types/models' |
19 | import { AttributesOnly } from '@shared/core-utils' | ||
19 | import { VideoRedundanciesTarget } from '@shared/models/redundancy/video-redundancies-filters.model' | 20 | import { VideoRedundanciesTarget } from '@shared/models/redundancy/video-redundancies-filters.model' |
20 | import { | 21 | import { |
21 | FileRedundancyInformation, | 22 | FileRedundancyInformation, |
@@ -29,7 +30,7 @@ import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validato | |||
29 | import { logger } from '../../helpers/logger' | 30 | import { logger } from '../../helpers/logger' |
30 | import { CONFIG } from '../../initializers/config' | 31 | import { CONFIG } from '../../initializers/config' |
31 | import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants' | 32 | import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants' |
32 | import { ActorModel } from '../activitypub/actor' | 33 | import { ActorModel } from '../actor/actor' |
33 | import { ServerModel } from '../server/server' | 34 | import { ServerModel } from '../server/server' |
34 | import { getSort, getVideoSort, parseAggregateResult, throwIfNotValid } from '../utils' | 35 | import { getSort, getVideoSort, parseAggregateResult, throwIfNotValid } from '../utils' |
35 | import { ScheduleVideoUpdateModel } from '../video/schedule-video-update' | 36 | import { ScheduleVideoUpdateModel } from '../video/schedule-video-update' |
@@ -84,7 +85,7 @@ export enum ScopeNames { | |||
84 | } | 85 | } |
85 | ] | 86 | ] |
86 | }) | 87 | }) |
87 | export class VideoRedundancyModel extends Model { | 88 | export class VideoRedundancyModel extends Model<Partial<AttributesOnly<VideoRedundancyModel>>> { |
88 | 89 | ||
89 | @CreatedAt | 90 | @CreatedAt |
90 | createdAt: Date | 91 | createdAt: Date |
diff --git a/server/models/server/plugin.ts b/server/models/server/plugin.ts index 80c8a6be5..a8de64dd4 100644 --- a/server/models/server/plugin.ts +++ b/server/models/server/plugin.ts | |||
@@ -1,9 +1,8 @@ | |||
1 | import { FindAndCountOptions, json, QueryTypes } from 'sequelize' | 1 | import { FindAndCountOptions, json, QueryTypes } from 'sequelize' |
2 | import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MPlugin, MPluginFormattable } from '@server/types/models' | 3 | import { MPlugin, MPluginFormattable } from '@server/types/models' |
4 | import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model' | 4 | import { AttributesOnly } from '@shared/core-utils' |
5 | import { PluginType } from '../../../shared/models/plugins/plugin.type' | 5 | import { PeerTubePlugin, PluginType, RegisterServerSettingOptions } from '../../../shared/models' |
6 | import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model' | ||
7 | import { | 6 | import { |
8 | isPluginDescriptionValid, | 7 | isPluginDescriptionValid, |
9 | isPluginHomepage, | 8 | isPluginHomepage, |
@@ -28,7 +27,7 @@ import { getSort, throwIfNotValid } from '../utils' | |||
28 | } | 27 | } |
29 | ] | 28 | ] |
30 | }) | 29 | }) |
31 | export class PluginModel extends Model { | 30 | export class PluginModel extends Model<Partial<AttributesOnly<PluginModel>>> { |
32 | 31 | ||
33 | @AllowNull(false) | 32 | @AllowNull(false) |
34 | @Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name')) | 33 | @Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name')) |
diff --git a/server/models/server/server-blocklist.ts b/server/models/server/server-blocklist.ts index 4dc236537..b3579d589 100644 --- a/server/models/server/server-blocklist.ts +++ b/server/models/server/server-blocklist.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { Op } from 'sequelize' | 1 | import { Op } from 'sequelize' |
2 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MServerBlocklist, MServerBlocklistAccountServer, MServerBlocklistFormattable } from '@server/types/models' | 3 | import { MServerBlocklist, MServerBlocklistAccountServer, MServerBlocklistFormattable } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { ServerBlock } from '@shared/models' | 5 | import { ServerBlock } from '@shared/models' |
5 | import { AccountModel } from '../account/account' | 6 | import { AccountModel } from '../account/account' |
6 | import { getSort, searchAttribute } from '../utils' | 7 | import { getSort, searchAttribute } from '../utils' |
@@ -42,7 +43,7 @@ enum ScopeNames { | |||
42 | } | 43 | } |
43 | ] | 44 | ] |
44 | }) | 45 | }) |
45 | export class ServerBlocklistModel extends Model { | 46 | export class ServerBlocklistModel extends Model<Partial<AttributesOnly<ServerBlocklistModel>>> { |
46 | 47 | ||
47 | @CreatedAt | 48 | @CreatedAt |
48 | createdAt: Date | 49 | createdAt: Date |
diff --git a/server/models/server/server.ts b/server/models/server/server.ts index 0e58beeaf..25d9924fb 100644 --- a/server/models/server/server.ts +++ b/server/models/server/server.ts | |||
@@ -1,7 +1,8 @@ | |||
1 | import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { MServer, MServerFormattable } from '@server/types/models/server' | 2 | import { MServer, MServerFormattable } from '@server/types/models/server' |
3 | import { AttributesOnly } from '@shared/core-utils' | ||
3 | import { isHostValid } from '../../helpers/custom-validators/servers' | 4 | import { isHostValid } from '../../helpers/custom-validators/servers' |
4 | import { ActorModel } from '../activitypub/actor' | 5 | import { ActorModel } from '../actor/actor' |
5 | import { throwIfNotValid } from '../utils' | 6 | import { throwIfNotValid } from '../utils' |
6 | import { ServerBlocklistModel } from './server-blocklist' | 7 | import { ServerBlocklistModel } from './server-blocklist' |
7 | 8 | ||
@@ -14,7 +15,7 @@ import { ServerBlocklistModel } from './server-blocklist' | |||
14 | } | 15 | } |
15 | ] | 16 | ] |
16 | }) | 17 | }) |
17 | export class ServerModel extends Model { | 18 | export class ServerModel extends Model<Partial<AttributesOnly<ServerModel>>> { |
18 | 19 | ||
19 | @AllowNull(false) | 20 | @AllowNull(false) |
20 | @Is('Host', value => throwIfNotValid(value, isHostValid, 'valid host')) | 21 | @Is('Host', value => throwIfNotValid(value, isHostValid, 'valid host')) |
diff --git a/server/models/server/tracker.ts b/server/models/server/tracker.ts index 97520f92d..c09fdd64b 100644 --- a/server/models/server/tracker.ts +++ b/server/models/server/tracker.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { AllowNull, BelongsToMany, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsToMany, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { Transaction } from 'sequelize/types' | 2 | import { Transaction } from 'sequelize/types' |
3 | import { MTracker } from '@server/types/models/server/tracker' | 3 | import { MTracker } from '@server/types/models/server/tracker' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { VideoModel } from '../video/video' | 5 | import { VideoModel } from '../video/video' |
5 | import { VideoTrackerModel } from './video-tracker' | 6 | import { VideoTrackerModel } from './video-tracker' |
6 | 7 | ||
@@ -13,7 +14,7 @@ import { VideoTrackerModel } from './video-tracker' | |||
13 | } | 14 | } |
14 | ] | 15 | ] |
15 | }) | 16 | }) |
16 | export class TrackerModel extends Model { | 17 | export class TrackerModel extends Model<Partial<AttributesOnly<TrackerModel>>> { |
17 | 18 | ||
18 | @AllowNull(false) | 19 | @AllowNull(false) |
19 | @Column | 20 | @Column |
diff --git a/server/models/server/video-tracker.ts b/server/models/server/video-tracker.ts index 367bf0117..c49fbd1c6 100644 --- a/server/models/server/video-tracker.ts +++ b/server/models/server/video-tracker.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { VideoModel } from '../video/video' | 3 | import { VideoModel } from '../video/video' |
3 | import { TrackerModel } from './tracker' | 4 | import { TrackerModel } from './tracker' |
4 | 5 | ||
@@ -13,7 +14,7 @@ import { TrackerModel } from './tracker' | |||
13 | } | 14 | } |
14 | ] | 15 | ] |
15 | }) | 16 | }) |
16 | export class VideoTrackerModel extends Model { | 17 | export class VideoTrackerModel extends Model<Partial<AttributesOnly<VideoTrackerModel>>> { |
17 | @CreatedAt | 18 | @CreatedAt |
18 | createdAt: Date | 19 | createdAt: Date |
19 | 20 | ||
diff --git a/server/models/account/user-notification-setting.ts b/server/models/user/user-notification-setting.ts index 138051528..bee7d7851 100644 --- a/server/models/account/user-notification-setting.ts +++ b/server/models/user/user-notification-setting.ts | |||
@@ -14,6 +14,7 @@ import { | |||
14 | } from 'sequelize-typescript' | 14 | } from 'sequelize-typescript' |
15 | import { TokensCache } from '@server/lib/auth/tokens-cache' | 15 | import { TokensCache } from '@server/lib/auth/tokens-cache' |
16 | import { MNotificationSettingFormattable } from '@server/types/models' | 16 | import { MNotificationSettingFormattable } from '@server/types/models' |
17 | import { AttributesOnly } from '@shared/core-utils' | ||
17 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model' | 18 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model' |
18 | import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' | 19 | import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' |
19 | import { throwIfNotValid } from '../utils' | 20 | import { throwIfNotValid } from '../utils' |
@@ -28,7 +29,7 @@ import { UserModel } from './user' | |||
28 | } | 29 | } |
29 | ] | 30 | ] |
30 | }) | 31 | }) |
31 | export class UserNotificationSettingModel extends Model { | 32 | export class UserNotificationSettingModel extends Model<Partial<AttributesOnly<UserNotificationSettingModel>>> { |
32 | 33 | ||
33 | @AllowNull(false) | 34 | @AllowNull(false) |
34 | @Default(null) | 35 | @Default(null) |
diff --git a/server/models/account/user-notification.ts b/server/models/user/user-notification.ts index 805095002..a7f84e9ca 100644 --- a/server/models/account/user-notification.ts +++ b/server/models/user/user-notification.ts | |||
@@ -1,14 +1,17 @@ | |||
1 | import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize' | 1 | import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize' |
2 | import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user' | 3 | import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { UserNotification, UserNotificationType } from '../../../shared' | 5 | import { UserNotification, UserNotificationType } from '../../../shared' |
5 | import { isBooleanValid } from '../../helpers/custom-validators/misc' | 6 | import { isBooleanValid } from '../../helpers/custom-validators/misc' |
6 | import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications' | 7 | import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications' |
7 | import { AbuseModel } from '../abuse/abuse' | 8 | import { AbuseModel } from '../abuse/abuse' |
8 | import { VideoAbuseModel } from '../abuse/video-abuse' | 9 | import { VideoAbuseModel } from '../abuse/video-abuse' |
9 | import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' | 10 | import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' |
10 | import { ActorModel } from '../activitypub/actor' | 11 | import { AccountModel } from '../account/account' |
11 | import { ActorFollowModel } from '../activitypub/actor-follow' | 12 | import { ActorModel } from '../actor/actor' |
13 | import { ActorFollowModel } from '../actor/actor-follow' | ||
14 | import { ActorImageModel } from '../actor/actor-image' | ||
12 | import { ApplicationModel } from '../application/application' | 15 | import { ApplicationModel } from '../application/application' |
13 | import { PluginModel } from '../server/plugin' | 16 | import { PluginModel } from '../server/plugin' |
14 | import { ServerModel } from '../server/server' | 17 | import { ServerModel } from '../server/server' |
@@ -18,8 +21,6 @@ import { VideoBlacklistModel } from '../video/video-blacklist' | |||
18 | import { VideoChannelModel } from '../video/video-channel' | 21 | import { VideoChannelModel } from '../video/video-channel' |
19 | import { VideoCommentModel } from '../video/video-comment' | 22 | import { VideoCommentModel } from '../video/video-comment' |
20 | import { VideoImportModel } from '../video/video-import' | 23 | import { VideoImportModel } from '../video/video-import' |
21 | import { AccountModel } from './account' | ||
22 | import { ActorImageModel } from './actor-image' | ||
23 | import { UserModel } from './user' | 24 | import { UserModel } from './user' |
24 | 25 | ||
25 | enum ScopeNames { | 26 | enum ScopeNames { |
@@ -286,7 +287,7 @@ function buildAccountInclude (required: boolean, withActor = false) { | |||
286 | } | 287 | } |
287 | ] as (ModelIndexesOptions & { where?: WhereOptions })[] | 288 | ] as (ModelIndexesOptions & { where?: WhereOptions })[] |
288 | }) | 289 | }) |
289 | export class UserNotificationModel extends Model { | 290 | export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNotificationModel>>> { |
290 | 291 | ||
291 | @AllowNull(false) | 292 | @AllowNull(false) |
292 | @Default(null) | 293 | @Default(null) |
diff --git a/server/models/account/user-video-history.ts b/server/models/user/user-video-history.ts index 6be1d65ea..e3dc4a062 100644 --- a/server/models/account/user-video-history.ts +++ b/server/models/user/user-video-history.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import { DestroyOptions, Op, Transaction } from 'sequelize' | ||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MUserAccountId, MUserId } from '@server/types/models' | ||
4 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { VideoModel } from '../video/video' | 5 | import { VideoModel } from '../video/video' |
3 | import { UserModel } from './user' | 6 | import { UserModel } from './user' |
4 | import { DestroyOptions, Op, Transaction } from 'sequelize' | ||
5 | import { MUserAccountId, MUserId } from '@server/types/models' | ||
6 | 7 | ||
7 | @Table({ | 8 | @Table({ |
8 | tableName: 'userVideoHistory', | 9 | tableName: 'userVideoHistory', |
@@ -19,7 +20,7 @@ import { MUserAccountId, MUserId } from '@server/types/models' | |||
19 | } | 20 | } |
20 | ] | 21 | ] |
21 | }) | 22 | }) |
22 | export class UserVideoHistoryModel extends Model { | 23 | export class UserVideoHistoryModel extends Model<Partial<AttributesOnly<UserVideoHistoryModel>>> { |
23 | @CreatedAt | 24 | @CreatedAt |
24 | createdAt: Date | 25 | createdAt: Date |
25 | 26 | ||
diff --git a/server/models/account/user.ts b/server/models/user/user.ts index 513455773..20696b1f4 100644 --- a/server/models/account/user.ts +++ b/server/models/user/user.ts | |||
@@ -31,6 +31,7 @@ import { | |||
31 | MUserWithNotificationSetting, | 31 | MUserWithNotificationSetting, |
32 | MVideoWithRights | 32 | MVideoWithRights |
33 | } from '@server/types/models' | 33 | } from '@server/types/models' |
34 | import { AttributesOnly } from '@shared/core-utils' | ||
34 | import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users' | 35 | import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users' |
35 | import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models' | 36 | import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models' |
36 | import { User, UserRole } from '../../../shared/models/users' | 37 | import { User, UserRole } from '../../../shared/models/users' |
@@ -60,8 +61,10 @@ import { | |||
60 | import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' | 61 | import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' |
61 | import { DEFAULT_USER_THEME_NAME, NSFW_POLICY_TYPES } from '../../initializers/constants' | 62 | import { DEFAULT_USER_THEME_NAME, NSFW_POLICY_TYPES } from '../../initializers/constants' |
62 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' | 63 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' |
63 | import { ActorModel } from '../activitypub/actor' | 64 | import { AccountModel } from '../account/account' |
64 | import { ActorFollowModel } from '../activitypub/actor-follow' | 65 | import { ActorModel } from '../actor/actor' |
66 | import { ActorFollowModel } from '../actor/actor-follow' | ||
67 | import { ActorImageModel } from '../actor/actor-image' | ||
65 | import { OAuthTokenModel } from '../oauth/oauth-token' | 68 | import { OAuthTokenModel } from '../oauth/oauth-token' |
66 | import { getSort, throwIfNotValid } from '../utils' | 69 | import { getSort, throwIfNotValid } from '../utils' |
67 | import { VideoModel } from '../video/video' | 70 | import { VideoModel } from '../video/video' |
@@ -69,9 +72,7 @@ import { VideoChannelModel } from '../video/video-channel' | |||
69 | import { VideoImportModel } from '../video/video-import' | 72 | import { VideoImportModel } from '../video/video-import' |
70 | import { VideoLiveModel } from '../video/video-live' | 73 | import { VideoLiveModel } from '../video/video-live' |
71 | import { VideoPlaylistModel } from '../video/video-playlist' | 74 | import { VideoPlaylistModel } from '../video/video-playlist' |
72 | import { AccountModel } from './account' | ||
73 | import { UserNotificationSettingModel } from './user-notification-setting' | 75 | import { UserNotificationSettingModel } from './user-notification-setting' |
74 | import { ActorImageModel } from './actor-image' | ||
75 | 76 | ||
76 | enum ScopeNames { | 77 | enum ScopeNames { |
77 | FOR_ME_API = 'FOR_ME_API', | 78 | FOR_ME_API = 'FOR_ME_API', |
@@ -233,7 +234,7 @@ enum ScopeNames { | |||
233 | } | 234 | } |
234 | ] | 235 | ] |
235 | }) | 236 | }) |
236 | export class UserModel extends Model { | 237 | export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> { |
237 | 238 | ||
238 | @AllowNull(true) | 239 | @AllowNull(true) |
239 | @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true)) | 240 | @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true)) |
diff --git a/server/models/utils.ts b/server/models/utils.ts index ec51c66bf..e27625bc8 100644 --- a/server/models/utils.ts +++ b/server/models/utils.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import { literal, Op, OrderItem } from 'sequelize' | 1 | import { literal, Op, OrderItem, Sequelize } from 'sequelize' |
2 | import { Model, Sequelize } from 'sequelize-typescript' | ||
3 | import { Col } from 'sequelize/types/lib/utils' | 2 | import { Col } from 'sequelize/types/lib/utils' |
4 | import validator from 'validator' | 3 | import validator from 'validator' |
5 | 4 | ||
@@ -195,11 +194,11 @@ function parseAggregateResult (result: any) { | |||
195 | return total | 194 | return total |
196 | } | 195 | } |
197 | 196 | ||
198 | const createSafeIn = (model: typeof Model, stringArr: (string | number)[]) => { | 197 | function createSafeIn (sequelize: Sequelize, stringArr: (string | number)[]) { |
199 | return stringArr.map(t => { | 198 | return stringArr.map(t => { |
200 | return t === null | 199 | return t === null |
201 | ? null | 200 | ? null |
202 | : model.sequelize.escape('' + t) | 201 | : sequelize.escape('' + t) |
203 | }).join(', ') | 202 | }).join(', ') |
204 | } | 203 | } |
205 | 204 | ||
diff --git a/server/models/video/schedule-video-update.ts b/server/models/video/schedule-video-update.ts index 22b08e91a..b0952c431 100644 --- a/server/models/video/schedule-video-update.ts +++ b/server/models/video/schedule-video-update.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | ||
2 | import { ScopeNames as VideoScopeNames, VideoModel } from './video' | ||
3 | import { VideoPrivacy } from '../../../shared/models/videos' | ||
4 | import { Op, Transaction } from 'sequelize' | 1 | import { Op, Transaction } from 'sequelize' |
2 | import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | ||
5 | import { MScheduleVideoUpdateFormattable, MScheduleVideoUpdateVideoAll } from '@server/types/models' | 3 | import { MScheduleVideoUpdateFormattable, MScheduleVideoUpdateVideoAll } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
5 | import { VideoPrivacy } from '../../../shared/models/videos' | ||
6 | import { ScopeNames as VideoScopeNames, VideoModel } from './video' | ||
6 | 7 | ||
7 | @Table({ | 8 | @Table({ |
8 | tableName: 'scheduleVideoUpdate', | 9 | tableName: 'scheduleVideoUpdate', |
@@ -16,7 +17,7 @@ import { MScheduleVideoUpdateFormattable, MScheduleVideoUpdateVideoAll } from '@ | |||
16 | } | 17 | } |
17 | ] | 18 | ] |
18 | }) | 19 | }) |
19 | export class ScheduleVideoUpdateModel extends Model { | 20 | export class ScheduleVideoUpdateModel extends Model<Partial<AttributesOnly<ScheduleVideoUpdateModel>>> { |
20 | 21 | ||
21 | @AllowNull(false) | 22 | @AllowNull(false) |
22 | @Default(null) | 23 | @Default(null) |
diff --git a/server/models/video/tag.ts b/server/models/video/tag.ts index d04205703..c1eebe27f 100644 --- a/server/models/video/tag.ts +++ b/server/models/video/tag.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { col, fn, QueryTypes, Transaction } from 'sequelize' | 1 | import { col, fn, QueryTypes, Transaction } from 'sequelize' |
2 | import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MTag } from '@server/types/models' | 3 | import { MTag } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { VideoPrivacy, VideoState } from '../../../shared/models/videos' | 5 | import { VideoPrivacy, VideoState } from '../../../shared/models/videos' |
5 | import { isVideoTagValid } from '../../helpers/custom-validators/videos' | 6 | import { isVideoTagValid } from '../../helpers/custom-validators/videos' |
6 | import { throwIfNotValid } from '../utils' | 7 | import { throwIfNotValid } from '../utils' |
@@ -21,7 +22,7 @@ import { VideoTagModel } from './video-tag' | |||
21 | } | 22 | } |
22 | ] | 23 | ] |
23 | }) | 24 | }) |
24 | export class TagModel extends Model { | 25 | export class TagModel extends Model<Partial<AttributesOnly<TagModel>>> { |
25 | 26 | ||
26 | @AllowNull(false) | 27 | @AllowNull(false) |
27 | @Is('VideoTag', value => throwIfNotValid(value, isVideoTagValid, 'tag')) | 28 | @Is('VideoTag', value => throwIfNotValid(value, isVideoTagValid, 'tag')) |
diff --git a/server/models/video/thumbnail.ts b/server/models/video/thumbnail.ts index f1187c8d6..3388478d9 100644 --- a/server/models/video/thumbnail.ts +++ b/server/models/video/thumbnail.ts | |||
@@ -17,6 +17,7 @@ import { | |||
17 | } from 'sequelize-typescript' | 17 | } from 'sequelize-typescript' |
18 | import { afterCommitIfTransaction } from '@server/helpers/database-utils' | 18 | import { afterCommitIfTransaction } from '@server/helpers/database-utils' |
19 | import { MThumbnail, MThumbnailVideo, MVideo } from '@server/types/models' | 19 | import { MThumbnail, MThumbnailVideo, MVideo } from '@server/types/models' |
20 | import { AttributesOnly } from '@shared/core-utils' | ||
20 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' | 21 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' |
21 | import { logger } from '../../helpers/logger' | 22 | import { logger } from '../../helpers/logger' |
22 | import { CONFIG } from '../../initializers/config' | 23 | import { CONFIG } from '../../initializers/config' |
@@ -40,7 +41,7 @@ import { VideoPlaylistModel } from './video-playlist' | |||
40 | } | 41 | } |
41 | ] | 42 | ] |
42 | }) | 43 | }) |
43 | export class ThumbnailModel extends Model { | 44 | export class ThumbnailModel extends Model<Partial<AttributesOnly<ThumbnailModel>>> { |
44 | 45 | ||
45 | @AllowNull(false) | 46 | @AllowNull(false) |
46 | @Column | 47 | @Column |
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts index aa18896da..98f4ec9c5 100644 --- a/server/models/video/video-blacklist.ts +++ b/server/models/video/video-blacklist.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { FindOptions } from 'sequelize' | 1 | import { FindOptions } from 'sequelize' |
2 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/types/models' | 3 | import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' | 5 | import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' |
5 | import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' | 6 | import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' |
6 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' | 7 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' |
@@ -18,7 +19,7 @@ import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel | |||
18 | } | 19 | } |
19 | ] | 20 | ] |
20 | }) | 21 | }) |
21 | export class VideoBlacklistModel extends Model { | 22 | export class VideoBlacklistModel extends Model<Partial<AttributesOnly<VideoBlacklistModel>>> { |
22 | 23 | ||
23 | @AllowNull(true) | 24 | @AllowNull(true) |
24 | @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true)) | 25 | @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true)) |
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts index bfdec73e9..d2c742b66 100644 --- a/server/models/video/video-caption.ts +++ b/server/models/video/video-caption.ts | |||
@@ -17,6 +17,7 @@ import { | |||
17 | } from 'sequelize-typescript' | 17 | } from 'sequelize-typescript' |
18 | import { v4 as uuidv4 } from 'uuid' | 18 | import { v4 as uuidv4 } from 'uuid' |
19 | import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models' | 19 | import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models' |
20 | import { AttributesOnly } from '@shared/core-utils' | ||
20 | import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' | 21 | import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' |
21 | import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' | 22 | import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' |
22 | import { logger } from '../../helpers/logger' | 23 | import { logger } from '../../helpers/logger' |
@@ -57,7 +58,7 @@ export enum ScopeNames { | |||
57 | } | 58 | } |
58 | ] | 59 | ] |
59 | }) | 60 | }) |
60 | export class VideoCaptionModel extends Model { | 61 | export class VideoCaptionModel extends Model<Partial<AttributesOnly<VideoCaptionModel>>> { |
61 | @CreatedAt | 62 | @CreatedAt |
62 | createdAt: Date | 63 | createdAt: Date |
63 | 64 | ||
diff --git a/server/models/video/video-change-ownership.ts b/server/models/video/video-change-ownership.ts index 298e8bfe2..7d20a954d 100644 --- a/server/models/video/video-change-ownership.ts +++ b/server/models/video/video-change-ownership.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { MVideoChangeOwnershipFormattable, MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership' | 2 | import { MVideoChangeOwnershipFormattable, MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership' |
3 | import { AttributesOnly } from '@shared/core-utils' | ||
3 | import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos' | 4 | import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos' |
4 | import { AccountModel } from '../account/account' | 5 | import { AccountModel } from '../account/account' |
5 | import { getSort } from '../utils' | 6 | import { getSort } from '../utils' |
@@ -53,7 +54,7 @@ enum ScopeNames { | |||
53 | ] | 54 | ] |
54 | } | 55 | } |
55 | })) | 56 | })) |
56 | export class VideoChangeOwnershipModel extends Model { | 57 | export class VideoChangeOwnershipModel extends Model<Partial<AttributesOnly<VideoChangeOwnershipModel>>> { |
57 | @CreatedAt | 58 | @CreatedAt |
58 | createdAt: Date | 59 | createdAt: Date |
59 | 60 | ||
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 081b21f2d..8c4357009 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -19,6 +19,7 @@ import { | |||
19 | } from 'sequelize-typescript' | 19 | } from 'sequelize-typescript' |
20 | import { setAsUpdated } from '@server/helpers/database-utils' | 20 | import { setAsUpdated } from '@server/helpers/database-utils' |
21 | import { MAccountActor } from '@server/types/models' | 21 | import { MAccountActor } from '@server/types/models' |
22 | import { AttributesOnly } from '@shared/core-utils' | ||
22 | import { ActivityPubActor } from '../../../shared/models/activitypub' | 23 | import { ActivityPubActor } from '../../../shared/models/activitypub' |
23 | import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' | 24 | import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' |
24 | import { | 25 | import { |
@@ -36,9 +37,9 @@ import { | |||
36 | MChannelSummaryFormattable | 37 | MChannelSummaryFormattable |
37 | } from '../../types/models/video' | 38 | } from '../../types/models/video' |
38 | import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' | 39 | import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' |
39 | import { ActorImageModel } from '../account/actor-image' | 40 | import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor' |
40 | import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' | 41 | import { ActorFollowModel } from '../actor/actor-follow' |
41 | import { ActorFollowModel } from '../activitypub/actor-follow' | 42 | import { ActorImageModel } from '../actor/actor-image' |
42 | import { ServerModel } from '../server/server' | 43 | import { ServerModel } from '../server/server' |
43 | import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' | 44 | import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' |
44 | import { VideoModel } from './video' | 45 | import { VideoModel } from './video' |
@@ -246,7 +247,7 @@ export type SummaryOptions = { | |||
246 | } | 247 | } |
247 | ] | 248 | ] |
248 | }) | 249 | }) |
249 | export class VideoChannelModel extends Model { | 250 | export class VideoChannelModel extends Model<Partial<AttributesOnly<VideoChannelModel>>> { |
250 | 251 | ||
251 | @AllowNull(false) | 252 | @AllowNull(false) |
252 | @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name')) | 253 | @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name')) |
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index 151c2bc81..bdf5d86bc 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts | |||
@@ -16,10 +16,11 @@ import { | |||
16 | } from 'sequelize-typescript' | 16 | } from 'sequelize-typescript' |
17 | import { getServerActor } from '@server/models/application/application' | 17 | import { getServerActor } from '@server/models/application/application' |
18 | import { MAccount, MAccountId, MUserAccountId } from '@server/types/models' | 18 | import { MAccount, MAccountId, MUserAccountId } from '@server/types/models' |
19 | import { AttributesOnly } from '@shared/core-utils' | ||
19 | import { VideoPrivacy } from '@shared/models' | 20 | import { VideoPrivacy } from '@shared/models' |
20 | import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects' | 21 | import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects' |
21 | import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' | 22 | import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' |
22 | import { VideoComment, VideoCommentAdmin } from '../../../shared/models/videos/video-comment.model' | 23 | import { VideoComment, VideoCommentAdmin } from '../../../shared/models/videos/comment/video-comment.model' |
23 | import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' | 24 | import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' |
24 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 25 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
25 | import { regexpCapture } from '../../helpers/regexp' | 26 | import { regexpCapture } from '../../helpers/regexp' |
@@ -39,7 +40,7 @@ import { | |||
39 | } from '../../types/models/video' | 40 | } from '../../types/models/video' |
40 | import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' | 41 | import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' |
41 | import { AccountModel } from '../account/account' | 42 | import { AccountModel } from '../account/account' |
42 | import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' | 43 | import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor' |
43 | import { | 44 | import { |
44 | buildBlockedAccountSQL, | 45 | buildBlockedAccountSQL, |
45 | buildBlockedAccountSQLOptimized, | 46 | buildBlockedAccountSQLOptimized, |
@@ -173,7 +174,7 @@ export enum ScopeNames { | |||
173 | } | 174 | } |
174 | ] | 175 | ] |
175 | }) | 176 | }) |
176 | export class VideoCommentModel extends Model { | 177 | export class VideoCommentModel extends Model<Partial<AttributesOnly<VideoCommentModel>>> { |
177 | @CreatedAt | 178 | @CreatedAt |
178 | createdAt: Date | 179 | createdAt: Date |
179 | 180 | ||
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts index 0b5946149..22cf63804 100644 --- a/server/models/video/video-file.ts +++ b/server/models/video/video-file.ts | |||
@@ -25,6 +25,7 @@ import { logger } from '@server/helpers/logger' | |||
25 | import { extractVideo } from '@server/helpers/video' | 25 | import { extractVideo } from '@server/helpers/video' |
26 | import { getTorrentFilePath } from '@server/lib/video-paths' | 26 | import { getTorrentFilePath } from '@server/lib/video-paths' |
27 | import { MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models' | 27 | import { MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models' |
28 | import { AttributesOnly } from '@shared/core-utils' | ||
28 | import { | 29 | import { |
29 | isVideoFileExtnameValid, | 30 | isVideoFileExtnameValid, |
30 | isVideoFileInfoHashValid, | 31 | isVideoFileInfoHashValid, |
@@ -149,7 +150,7 @@ export enum ScopeNames { | |||
149 | } | 150 | } |
150 | ] | 151 | ] |
151 | }) | 152 | }) |
152 | export class VideoFileModel extends Model { | 153 | export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>>> { |
153 | @CreatedAt | 154 | @CreatedAt |
154 | createdAt: Date | 155 | createdAt: Date |
155 | 156 | ||
diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts index 8324166cc..5c73fb07c 100644 --- a/server/models/video/video-import.ts +++ b/server/models/video/video-import.ts | |||
@@ -13,15 +13,16 @@ import { | |||
13 | Table, | 13 | Table, |
14 | UpdatedAt | 14 | UpdatedAt |
15 | } from 'sequelize-typescript' | 15 | } from 'sequelize-typescript' |
16 | import { afterCommitIfTransaction } from '@server/helpers/database-utils' | ||
16 | import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import' | 17 | import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import' |
18 | import { AttributesOnly } from '@shared/core-utils' | ||
17 | import { VideoImport, VideoImportState } from '../../../shared' | 19 | import { VideoImport, VideoImportState } from '../../../shared' |
18 | import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports' | 20 | import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports' |
19 | import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' | 21 | import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' |
20 | import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers/constants' | 22 | import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers/constants' |
21 | import { UserModel } from '../account/user' | 23 | import { UserModel } from '../user/user' |
22 | import { getSort, throwIfNotValid } from '../utils' | 24 | import { getSort, throwIfNotValid } from '../utils' |
23 | import { ScopeNames as VideoModelScopeNames, VideoModel } from './video' | 25 | import { ScopeNames as VideoModelScopeNames, VideoModel } from './video' |
24 | import { afterCommitIfTransaction } from '@server/helpers/database-utils' | ||
25 | 26 | ||
26 | @DefaultScope(() => ({ | 27 | @DefaultScope(() => ({ |
27 | include: [ | 28 | include: [ |
@@ -52,7 +53,7 @@ import { afterCommitIfTransaction } from '@server/helpers/database-utils' | |||
52 | } | 53 | } |
53 | ] | 54 | ] |
54 | }) | 55 | }) |
55 | export class VideoImportModel extends Model { | 56 | export class VideoImportModel extends Model<Partial<AttributesOnly<VideoImportModel>>> { |
56 | @CreatedAt | 57 | @CreatedAt |
57 | createdAt: Date | 58 | createdAt: Date |
58 | 59 | ||
diff --git a/server/models/video/video-live.ts b/server/models/video/video-live.ts index cb4a9b896..014491d50 100644 --- a/server/models/video/video-live.ts +++ b/server/models/video/video-live.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, DefaultScope, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, DefaultScope, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { WEBSERVER } from '@server/initializers/constants' | 2 | import { WEBSERVER } from '@server/initializers/constants' |
3 | import { MVideoLive, MVideoLiveVideo } from '@server/types/models' | 3 | import { MVideoLive, MVideoLiveVideo } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { LiveVideo, VideoState } from '@shared/models' | 5 | import { LiveVideo, VideoState } from '@shared/models' |
5 | import { VideoModel } from './video' | 6 | import { VideoModel } from './video' |
6 | import { VideoBlacklistModel } from './video-blacklist' | 7 | import { VideoBlacklistModel } from './video-blacklist' |
@@ -28,7 +29,7 @@ import { VideoBlacklistModel } from './video-blacklist' | |||
28 | } | 29 | } |
29 | ] | 30 | ] |
30 | }) | 31 | }) |
31 | export class VideoLiveModel extends Model { | 32 | export class VideoLiveModel extends Model<Partial<AttributesOnly<VideoLiveModel>>> { |
32 | 33 | ||
33 | @AllowNull(true) | 34 | @AllowNull(true) |
34 | @Column(DataType.STRING) | 35 | @Column(DataType.STRING) |
diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts index d2d7e2740..e6906cb19 100644 --- a/server/models/video/video-playlist-element.ts +++ b/server/models/video/video-playlist-element.ts | |||
@@ -32,6 +32,7 @@ import { AccountModel } from '../account/account' | |||
32 | import { getSort, throwIfNotValid } from '../utils' | 32 | import { getSort, throwIfNotValid } from '../utils' |
33 | import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video' | 33 | import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video' |
34 | import { VideoPlaylistModel } from './video-playlist' | 34 | import { VideoPlaylistModel } from './video-playlist' |
35 | import { AttributesOnly } from '@shared/core-utils' | ||
35 | 36 | ||
36 | @Table({ | 37 | @Table({ |
37 | tableName: 'videoPlaylistElement', | 38 | tableName: 'videoPlaylistElement', |
@@ -48,7 +49,7 @@ import { VideoPlaylistModel } from './video-playlist' | |||
48 | } | 49 | } |
49 | ] | 50 | ] |
50 | }) | 51 | }) |
51 | export class VideoPlaylistElementModel extends Model { | 52 | export class VideoPlaylistElementModel extends Model<Partial<AttributesOnly<VideoPlaylistElementModel>>> { |
52 | @CreatedAt | 53 | @CreatedAt |
53 | createdAt: Date | 54 | createdAt: Date |
54 | 55 | ||
@@ -274,7 +275,8 @@ export class VideoPlaylistElementModel extends Model { | |||
274 | validate: false // We use a literal to update the position | 275 | validate: false // We use a literal to update the position |
275 | } | 276 | } |
276 | 277 | ||
277 | return VideoPlaylistElementModel.update({ position: Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`) }, query) | 278 | const positionQuery = Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`) |
279 | return VideoPlaylistElementModel.update({ position: positionQuery as any }, query) | ||
278 | } | 280 | } |
279 | 281 | ||
280 | static increasePositionOf ( | 282 | static increasePositionOf ( |
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index efe5be36d..c293287d3 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts | |||
@@ -19,6 +19,7 @@ import { | |||
19 | } from 'sequelize-typescript' | 19 | } from 'sequelize-typescript' |
20 | import { v4 as uuidv4 } from 'uuid' | 20 | import { v4 as uuidv4 } from 'uuid' |
21 | import { MAccountId, MChannelId } from '@server/types/models' | 21 | import { MAccountId, MChannelId } from '@server/types/models' |
22 | import { AttributesOnly } from '@shared/core-utils' | ||
22 | import { ActivityIconObject } from '../../../shared/models/activitypub/objects' | 23 | import { ActivityIconObject } from '../../../shared/models/activitypub/objects' |
23 | import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' | 24 | import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' |
24 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' | 25 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' |
@@ -50,11 +51,11 @@ import { | |||
50 | MVideoPlaylistIdWithElements | 51 | MVideoPlaylistIdWithElements |
51 | } from '../../types/models/video/video-playlist' | 52 | } from '../../types/models/video/video-playlist' |
52 | import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account' | 53 | import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account' |
54 | import { ActorModel } from '../actor/actor' | ||
53 | import { buildServerIdsFollowedBy, buildWhereIdOrUUID, getPlaylistSort, isOutdated, throwIfNotValid } from '../utils' | 55 | import { buildServerIdsFollowedBy, buildWhereIdOrUUID, getPlaylistSort, isOutdated, throwIfNotValid } from '../utils' |
54 | import { ThumbnailModel } from './thumbnail' | 56 | import { ThumbnailModel } from './thumbnail' |
55 | import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel' | 57 | import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel' |
56 | import { VideoPlaylistElementModel } from './video-playlist-element' | 58 | import { VideoPlaylistElementModel } from './video-playlist-element' |
57 | import { ActorModel } from '../activitypub/actor' | ||
58 | 59 | ||
59 | enum ScopeNames { | 60 | enum ScopeNames { |
60 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', | 61 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', |
@@ -221,7 +222,7 @@ type AvailableForListOptions = { | |||
221 | } | 222 | } |
222 | ] | 223 | ] |
223 | }) | 224 | }) |
224 | export class VideoPlaylistModel extends Model { | 225 | export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlaylistModel>>> { |
225 | @CreatedAt | 226 | @CreatedAt |
226 | createdAt: Date | 227 | createdAt: Date |
227 | 228 | ||
diff --git a/server/models/video/video-query-builder.ts b/server/models/video/video-query-builder.ts index 155afe64b..2aa5e65c8 100644 --- a/server/models/video/video-query-builder.ts +++ b/server/models/video/video-query-builder.ts | |||
@@ -1,9 +1,9 @@ | |||
1 | import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models' | 1 | import { Sequelize } from 'sequelize/types' |
2 | import { buildDirectionAndField, createSafeIn } from '@server/models/utils' | ||
3 | import { Model } from 'sequelize-typescript' | ||
4 | import { MUserAccountId, MUserId } from '@server/types/models' | ||
5 | import validator from 'validator' | 2 | import validator from 'validator' |
6 | import { exists } from '@server/helpers/custom-validators/misc' | 3 | import { exists } from '@server/helpers/custom-validators/misc' |
4 | import { buildDirectionAndField, createSafeIn } from '@server/models/utils' | ||
5 | import { MUserAccountId, MUserId } from '@server/types/models' | ||
6 | import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models' | ||
7 | 7 | ||
8 | export type BuildVideosQueryOptions = { | 8 | export type BuildVideosQueryOptions = { |
9 | attributes?: string[] | 9 | attributes?: string[] |
@@ -55,7 +55,7 @@ export type BuildVideosQueryOptions = { | |||
55 | having?: string | 55 | having?: string |
56 | } | 56 | } |
57 | 57 | ||
58 | function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) { | 58 | function buildListQuery (sequelize: Sequelize, options: BuildVideosQueryOptions) { |
59 | const and: string[] = [] | 59 | const and: string[] = [] |
60 | const joins: string[] = [] | 60 | const joins: string[] = [] |
61 | const replacements: any = {} | 61 | const replacements: any = {} |
@@ -77,7 +77,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) | |||
77 | const blockerIds = [ options.serverAccountId ] | 77 | const blockerIds = [ options.serverAccountId ] |
78 | if (options.user) blockerIds.push(options.user.Account.id) | 78 | if (options.user) blockerIds.push(options.user.Account.id) |
79 | 79 | ||
80 | const inClause = createSafeIn(model, blockerIds) | 80 | const inClause = createSafeIn(sequelize, blockerIds) |
81 | 81 | ||
82 | and.push( | 82 | and.push( |
83 | 'NOT EXISTS (' + | 83 | 'NOT EXISTS (' + |
@@ -179,7 +179,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) | |||
179 | 'EXISTS (' + | 179 | 'EXISTS (' + |
180 | ' SELECT 1 FROM "videoTag" ' + | 180 | ' SELECT 1 FROM "videoTag" ' + |
181 | ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + | 181 | ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + |
182 | ' WHERE lower("tag"."name") IN (' + createSafeIn(model, tagsOneOfLower) + ') ' + | 182 | ' WHERE lower("tag"."name") IN (' + createSafeIn(sequelize, tagsOneOfLower) + ') ' + |
183 | ' AND "video"."id" = "videoTag"."videoId"' + | 183 | ' AND "video"."id" = "videoTag"."videoId"' + |
184 | ')' | 184 | ')' |
185 | ) | 185 | ) |
@@ -192,7 +192,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) | |||
192 | 'EXISTS (' + | 192 | 'EXISTS (' + |
193 | ' SELECT 1 FROM "videoTag" ' + | 193 | ' SELECT 1 FROM "videoTag" ' + |
194 | ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + | 194 | ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + |
195 | ' WHERE lower("tag"."name") IN (' + createSafeIn(model, tagsAllOfLower) + ') ' + | 195 | ' WHERE lower("tag"."name") IN (' + createSafeIn(sequelize, tagsAllOfLower) + ') ' + |
196 | ' AND "video"."id" = "videoTag"."videoId" ' + | 196 | ' AND "video"."id" = "videoTag"."videoId" ' + |
197 | ' GROUP BY "videoTag"."videoId" HAVING COUNT(*) = ' + tagsAllOfLower.length + | 197 | ' GROUP BY "videoTag"."videoId" HAVING COUNT(*) = ' + tagsAllOfLower.length + |
198 | ')' | 198 | ')' |
@@ -232,7 +232,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) | |||
232 | languagesQueryParts.push( | 232 | languagesQueryParts.push( |
233 | 'EXISTS (' + | 233 | 'EXISTS (' + |
234 | ' SELECT 1 FROM "videoCaption" WHERE "videoCaption"."language" ' + | 234 | ' SELECT 1 FROM "videoCaption" WHERE "videoCaption"."language" ' + |
235 | ' IN (' + createSafeIn(model, languages) + ') AND ' + | 235 | ' IN (' + createSafeIn(sequelize, languages) + ') AND ' + |
236 | ' "videoCaption"."videoId" = "video"."id"' + | 236 | ' "videoCaption"."videoId" = "video"."id"' + |
237 | ')' | 237 | ')' |
238 | ) | 238 | ) |
@@ -345,8 +345,8 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) | |||
345 | } | 345 | } |
346 | 346 | ||
347 | if (options.search) { | 347 | if (options.search) { |
348 | const escapedSearch = model.sequelize.escape(options.search) | 348 | const escapedSearch = sequelize.escape(options.search) |
349 | const escapedLikeSearch = model.sequelize.escape('%' + options.search + '%') | 349 | const escapedLikeSearch = sequelize.escape('%' + options.search + '%') |
350 | 350 | ||
351 | cte.push( | 351 | cte.push( |
352 | '"trigramSearch" AS (' + | 352 | '"trigramSearch" AS (' + |
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts index 5059c1fa6..505c305e2 100644 --- a/server/models/video/video-share.ts +++ b/server/models/video/video-share.ts | |||
@@ -1,10 +1,11 @@ | |||
1 | import { literal, Op, QueryTypes, Transaction } from 'sequelize' | 1 | import { literal, Op, QueryTypes, Transaction } from 'sequelize' |
2 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { AttributesOnly } from '@shared/core-utils' | ||
3 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 4 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
4 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' | 5 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' |
5 | import { MActorDefault } from '../../types/models' | 6 | import { MActorDefault } from '../../types/models' |
6 | import { MVideoShareActor, MVideoShareFull } from '../../types/models/video' | 7 | import { MVideoShareActor, MVideoShareFull } from '../../types/models/video' |
7 | import { ActorModel } from '../activitypub/actor' | 8 | import { ActorModel } from '../actor/actor' |
8 | import { buildLocalActorIdsIn, throwIfNotValid } from '../utils' | 9 | import { buildLocalActorIdsIn, throwIfNotValid } from '../utils' |
9 | import { VideoModel } from './video' | 10 | import { VideoModel } from './video' |
10 | 11 | ||
@@ -50,7 +51,7 @@ enum ScopeNames { | |||
50 | } | 51 | } |
51 | ] | 52 | ] |
52 | }) | 53 | }) |
53 | export class VideoShareModel extends Model { | 54 | export class VideoShareModel extends Model<Partial<AttributesOnly<VideoShareModel>>> { |
54 | 55 | ||
55 | @AllowNull(false) | 56 | @AllowNull(false) |
56 | @Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) | 57 | @Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) |
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts index c9375b433..d627e8c9d 100644 --- a/server/models/video/video-streaming-playlist.ts +++ b/server/models/video/video-streaming-playlist.ts | |||
@@ -13,6 +13,7 @@ import { CONSTRAINTS_FIELDS, MEMOIZE_LENGTH, MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_ | |||
13 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | 13 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' |
14 | import { throwIfNotValid } from '../utils' | 14 | import { throwIfNotValid } from '../utils' |
15 | import { VideoModel } from './video' | 15 | import { VideoModel } from './video' |
16 | import { AttributesOnly } from '@shared/core-utils' | ||
16 | 17 | ||
17 | @Table({ | 18 | @Table({ |
18 | tableName: 'videoStreamingPlaylist', | 19 | tableName: 'videoStreamingPlaylist', |
@@ -30,7 +31,7 @@ import { VideoModel } from './video' | |||
30 | } | 31 | } |
31 | ] | 32 | ] |
32 | }) | 33 | }) |
33 | export class VideoStreamingPlaylistModel extends Model { | 34 | export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<VideoStreamingPlaylistModel>>> { |
34 | @CreatedAt | 35 | @CreatedAt |
35 | createdAt: Date | 36 | createdAt: Date |
36 | 37 | ||
diff --git a/server/models/video/video-tag.ts b/server/models/video/video-tag.ts index 5052b8c4d..1285d375b 100644 --- a/server/models/video/video-tag.ts +++ b/server/models/video/video-tag.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { TagModel } from './tag' | 3 | import { TagModel } from './tag' |
3 | import { VideoModel } from './video' | 4 | import { VideoModel } from './video' |
4 | 5 | ||
@@ -13,7 +14,7 @@ import { VideoModel } from './video' | |||
13 | } | 14 | } |
14 | ] | 15 | ] |
15 | }) | 16 | }) |
16 | export class VideoTagModel extends Model { | 17 | export class VideoTagModel extends Model<Partial<AttributesOnly<VideoTagModel>>> { |
17 | @CreatedAt | 18 | @CreatedAt |
18 | createdAt: Date | 19 | createdAt: Date |
19 | 20 | ||
diff --git a/server/models/video/video-view.ts b/server/models/video/video-view.ts index 992cf258a..dfc6296ce 100644 --- a/server/models/video/video-view.ts +++ b/server/models/video/video-view.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table } from 'sequelize-typescript' |
3 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { VideoModel } from './video' | 4 | import { VideoModel } from './video' |
3 | import * as Sequelize from 'sequelize' | ||
4 | 5 | ||
5 | @Table({ | 6 | @Table({ |
6 | tableName: 'videoView', | 7 | tableName: 'videoView', |
@@ -14,7 +15,7 @@ import * as Sequelize from 'sequelize' | |||
14 | } | 15 | } |
15 | ] | 16 | ] |
16 | }) | 17 | }) |
17 | export class VideoViewModel extends Model { | 18 | export class VideoViewModel extends Model<Partial<AttributesOnly<VideoViewModel>>> { |
18 | @CreatedAt | 19 | @CreatedAt |
19 | createdAt: Date | 20 | createdAt: Date |
20 | 21 | ||
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 18afba1ba..d4a258187 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -31,6 +31,7 @@ import { LiveManager } from '@server/lib/live-manager' | |||
31 | import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths' | 31 | import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths' |
32 | import { getServerActor } from '@server/models/application/application' | 32 | import { getServerActor } from '@server/models/application/application' |
33 | import { ModelCache } from '@server/models/model-cache' | 33 | import { ModelCache } from '@server/models/model-cache' |
34 | import { AttributesOnly } from '@shared/core-utils' | ||
34 | import { VideoFile } from '@shared/models/videos/video-file.model' | 35 | import { VideoFile } from '@shared/models/videos/video-file.model' |
35 | import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared' | 36 | import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared' |
36 | import { VideoObject } from '../../../shared/models/activitypub/objects' | 37 | import { VideoObject } from '../../../shared/models/activitypub/objects' |
@@ -100,14 +101,14 @@ import { MVideoFile, MVideoFileStreamingPlaylistVideo } from '../../types/models | |||
100 | import { VideoAbuseModel } from '../abuse/video-abuse' | 101 | import { VideoAbuseModel } from '../abuse/video-abuse' |
101 | import { AccountModel } from '../account/account' | 102 | import { AccountModel } from '../account/account' |
102 | import { AccountVideoRateModel } from '../account/account-video-rate' | 103 | import { AccountVideoRateModel } from '../account/account-video-rate' |
103 | import { ActorImageModel } from '../account/actor-image' | 104 | import { ActorModel } from '../actor/actor' |
104 | import { UserModel } from '../account/user' | 105 | import { ActorImageModel } from '../actor/actor-image' |
105 | import { UserVideoHistoryModel } from '../account/user-video-history' | ||
106 | import { ActorModel } from '../activitypub/actor' | ||
107 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | 106 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' |
108 | import { ServerModel } from '../server/server' | 107 | import { ServerModel } from '../server/server' |
109 | import { TrackerModel } from '../server/tracker' | 108 | import { TrackerModel } from '../server/tracker' |
110 | import { VideoTrackerModel } from '../server/video-tracker' | 109 | import { VideoTrackerModel } from '../server/video-tracker' |
110 | import { UserModel } from '../user/user' | ||
111 | import { UserVideoHistoryModel } from '../user/user-video-history' | ||
111 | import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils' | 112 | import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils' |
112 | import { ScheduleVideoUpdateModel } from './schedule-video-update' | 113 | import { ScheduleVideoUpdateModel } from './schedule-video-update' |
113 | import { TagModel } from './tag' | 114 | import { TagModel } from './tag' |
@@ -489,7 +490,7 @@ export type AvailableForListIDsOptions = { | |||
489 | } | 490 | } |
490 | ] | 491 | ] |
491 | }) | 492 | }) |
492 | export class VideoModel extends Model { | 493 | export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> { |
493 | 494 | ||
494 | @AllowNull(false) | 495 | @AllowNull(false) |
495 | @Default(DataType.UUIDV4) | 496 | @Default(DataType.UUIDV4) |
@@ -1618,7 +1619,7 @@ export class VideoModel extends Model { | |||
1618 | includeLocalVideos: true | 1619 | includeLocalVideos: true |
1619 | } | 1620 | } |
1620 | 1621 | ||
1621 | const { query, replacements } = buildListQuery(VideoModel, queryOptions) | 1622 | const { query, replacements } = buildListQuery(VideoModel.sequelize, queryOptions) |
1622 | 1623 | ||
1623 | return this.sequelize.query<any>(query, { replacements, type: QueryTypes.SELECT }) | 1624 | return this.sequelize.query<any>(query, { replacements, type: QueryTypes.SELECT }) |
1624 | .then(rows => rows.map(r => r[field])) | 1625 | .then(rows => rows.map(r => r[field])) |
@@ -1646,7 +1647,7 @@ export class VideoModel extends Model { | |||
1646 | if (countVideos !== true) return Promise.resolve(undefined) | 1647 | if (countVideos !== true) return Promise.resolve(undefined) |
1647 | 1648 | ||
1648 | const countOptions = Object.assign({}, options, { isCount: true }) | 1649 | const countOptions = Object.assign({}, options, { isCount: true }) |
1649 | const { query: queryCount, replacements: replacementsCount } = buildListQuery(VideoModel, countOptions) | 1650 | const { query: queryCount, replacements: replacementsCount } = buildListQuery(VideoModel.sequelize, countOptions) |
1650 | 1651 | ||
1651 | return VideoModel.sequelize.query<any>(queryCount, { replacements: replacementsCount, type: QueryTypes.SELECT }) | 1652 | return VideoModel.sequelize.query<any>(queryCount, { replacements: replacementsCount, type: QueryTypes.SELECT }) |
1652 | .then(rows => rows.length !== 0 ? rows[0].total : 0) | 1653 | .then(rows => rows.length !== 0 ? rows[0].total : 0) |
@@ -1655,7 +1656,7 @@ export class VideoModel extends Model { | |||
1655 | function getModels () { | 1656 | function getModels () { |
1656 | if (options.count === 0) return Promise.resolve([]) | 1657 | if (options.count === 0) return Promise.resolve([]) |
1657 | 1658 | ||
1658 | const { query, replacements, order } = buildListQuery(VideoModel, options) | 1659 | const { query, replacements, order } = buildListQuery(VideoModel.sequelize, options) |
1659 | const queryModels = wrapForAPIResults(query, replacements, options, order) | 1660 | const queryModels = wrapForAPIResults(query, replacements, options, order) |
1660 | 1661 | ||
1661 | return VideoModel.sequelize.query<any>(queryModels, { replacements, type: QueryTypes.SELECT, nest: true }) | 1662 | return VideoModel.sequelize.query<any>(queryModels, { replacements, type: QueryTypes.SELECT, nest: true }) |
diff --git a/server/tests/api/check-params/plugins.ts b/server/tests/api/check-params/plugins.ts index 6e540bcbb..a833fe6ff 100644 --- a/server/tests/api/check-params/plugins.ts +++ b/server/tests/api/check-params/plugins.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | 4 | import { HttpStatusCode } from '@shared/core-utils' | |
5 | import { | 5 | import { |
6 | checkBadCountPagination, | 6 | checkBadCountPagination, |
7 | checkBadSortPagination, | 7 | checkBadSortPagination, |
@@ -11,14 +11,14 @@ import { | |||
11 | flushAndRunServer, | 11 | flushAndRunServer, |
12 | immutableAssign, | 12 | immutableAssign, |
13 | installPlugin, | 13 | installPlugin, |
14 | makeGetRequest, makePostBodyRequest, makePutBodyRequest, | 14 | makeGetRequest, |
15 | makePostBodyRequest, | ||
16 | makePutBodyRequest, | ||
15 | ServerInfo, | 17 | ServerInfo, |
16 | setAccessTokensToServers, | 18 | setAccessTokensToServers, |
17 | userLogin | 19 | userLogin |
18 | } from '../../../../shared/extra-utils' | 20 | } from '@shared/extra-utils' |
19 | import { PluginType } from '../../../../shared/models/plugins/plugin.type' | 21 | import { PeerTubePlugin, PluginType } from '@shared/models' |
20 | import { PeerTubePlugin } from '../../../../shared/models/plugins/peertube-plugin.model' | ||
21 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
22 | 22 | ||
23 | describe('Test server plugins API validators', function () { | 23 | describe('Test server plugins API validators', function () { |
24 | let server: ServerInfo | 24 | let server: ServerInfo |
diff --git a/server/tests/api/moderation/blocklist.ts b/server/tests/api/moderation/blocklist.ts index e8202aff1..b767d38c7 100644 --- a/server/tests/api/moderation/blocklist.ts +++ b/server/tests/api/moderation/blocklist.ts | |||
@@ -1,46 +1,50 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | 3 | import 'mocha' |
5 | import { AccountBlock, ServerBlock, Video, UserNotification, UserNotificationType } from '../../../../shared/index' | 4 | import * as chai from 'chai' |
6 | import { | 5 | import { |
6 | addAccountToAccountBlocklist, | ||
7 | addAccountToServerBlocklist, | ||
8 | addServerToAccountBlocklist, | ||
9 | addServerToServerBlocklist, | ||
10 | addVideoCommentReply, | ||
11 | addVideoCommentThread, | ||
7 | cleanupTests, | 12 | cleanupTests, |
8 | createUser, | 13 | createUser, |
9 | deleteVideoComment, | 14 | deleteVideoComment, |
10 | doubleFollow, | 15 | doubleFollow, |
16 | findCommentId, | ||
11 | flushAndRunMultipleServers, | 17 | flushAndRunMultipleServers, |
12 | ServerInfo, | ||
13 | uploadVideo, | ||
14 | userLogin, | ||
15 | follow, | 18 | follow, |
16 | unfollow | ||
17 | } from '../../../../shared/extra-utils/index' | ||
18 | import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login' | ||
19 | import { getVideosList, getVideosListWithToken } from '../../../../shared/extra-utils/videos/videos' | ||
20 | import { | ||
21 | addVideoCommentReply, | ||
22 | addVideoCommentThread, | ||
23 | getVideoCommentThreads, | ||
24 | getVideoThreadComments, | ||
25 | findCommentId | ||
26 | } from '../../../../shared/extra-utils/videos/video-comments' | ||
27 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | ||
28 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | ||
29 | import { | ||
30 | addAccountToAccountBlocklist, | ||
31 | addAccountToServerBlocklist, | ||
32 | addServerToAccountBlocklist, | ||
33 | addServerToServerBlocklist, | ||
34 | getAccountBlocklistByAccount, | 19 | getAccountBlocklistByAccount, |
35 | getAccountBlocklistByServer, | 20 | getAccountBlocklistByServer, |
36 | getServerBlocklistByAccount, | 21 | getServerBlocklistByAccount, |
37 | getServerBlocklistByServer, | 22 | getServerBlocklistByServer, |
23 | getUserNotifications, | ||
24 | getVideoCommentThreads, | ||
25 | getVideosList, | ||
26 | getVideosListWithToken, | ||
27 | getVideoThreadComments, | ||
38 | removeAccountFromAccountBlocklist, | 28 | removeAccountFromAccountBlocklist, |
39 | removeAccountFromServerBlocklist, | 29 | removeAccountFromServerBlocklist, |
40 | removeServerFromAccountBlocklist, | 30 | removeServerFromAccountBlocklist, |
41 | removeServerFromServerBlocklist | 31 | removeServerFromServerBlocklist, |
42 | } from '../../../../shared/extra-utils/users/blocklist' | 32 | ServerInfo, |
43 | import { getUserNotifications } from '../../../../shared/extra-utils/users/user-notifications' | 33 | setAccessTokensToServers, |
34 | unfollow, | ||
35 | uploadVideo, | ||
36 | userLogin, | ||
37 | waitJobs | ||
38 | } from '@shared/extra-utils' | ||
39 | import { | ||
40 | AccountBlock, | ||
41 | ServerBlock, | ||
42 | UserNotification, | ||
43 | UserNotificationType, | ||
44 | Video, | ||
45 | VideoComment, | ||
46 | VideoCommentThreadTree | ||
47 | } from '@shared/models' | ||
44 | 48 | ||
45 | const expect = chai.expect | 49 | const expect = chai.expect |
46 | 50 | ||
diff --git a/server/tests/api/notifications/comments-notifications.ts b/server/tests/api/notifications/comments-notifications.ts index 5e4ab0d6c..d2badf237 100644 --- a/server/tests/api/notifications/comments-notifications.ts +++ b/server/tests/api/notifications/comments-notifications.ts | |||
@@ -2,20 +2,25 @@ | |||
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { cleanupTests, getVideoCommentThreads, getVideoThreadComments, updateMyUser } from '../../../../shared/extra-utils' | ||
6 | import { ServerInfo, uploadVideo } from '../../../../shared/extra-utils/index' | ||
7 | import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' | ||
8 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | ||
9 | import { addAccountToAccountBlocklist, removeAccountFromAccountBlocklist } from '../../../../shared/extra-utils/users/blocklist' | ||
10 | import { | 5 | import { |
6 | addAccountToAccountBlocklist, | ||
7 | addVideoCommentReply, | ||
8 | addVideoCommentThread, | ||
11 | checkCommentMention, | 9 | checkCommentMention, |
12 | CheckerBaseParams, | 10 | CheckerBaseParams, |
13 | checkNewCommentOnMyVideo, | 11 | checkNewCommentOnMyVideo, |
14 | prepareNotificationsTest | 12 | cleanupTests, |
15 | } from '../../../../shared/extra-utils/users/user-notifications' | 13 | getVideoCommentThreads, |
16 | import { addVideoCommentReply, addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments' | 14 | getVideoThreadComments, |
17 | import { UserNotification } from '../../../../shared/models/users' | 15 | MockSmtpServer, |
18 | import { VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | 16 | prepareNotificationsTest, |
17 | removeAccountFromAccountBlocklist, | ||
18 | ServerInfo, | ||
19 | updateMyUser, | ||
20 | uploadVideo, | ||
21 | waitJobs | ||
22 | } from '@shared/extra-utils' | ||
23 | import { UserNotification, VideoCommentThreadTree } from '@shared/models' | ||
19 | 24 | ||
20 | const expect = chai.expect | 25 | const expect = chai.expect |
21 | 26 | ||
diff --git a/server/tests/api/server/bulk.ts b/server/tests/api/server/bulk.ts index 51ba0e7af..80fa7fce6 100644 --- a/server/tests/api/server/bulk.ts +++ b/server/tests/api/server/bulk.ts | |||
@@ -2,12 +2,14 @@ | |||
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { VideoComment } from '@shared/models/videos/video-comment.model' | 5 | import { Video, VideoComment } from '@shared/models' |
6 | import { | 6 | import { |
7 | addVideoCommentReply, | ||
7 | addVideoCommentThread, | 8 | addVideoCommentThread, |
8 | bulkRemoveCommentsOf, | 9 | bulkRemoveCommentsOf, |
9 | cleanupTests, | 10 | cleanupTests, |
10 | createUser, | 11 | createUser, |
12 | doubleFollow, | ||
11 | flushAndRunMultipleServers, | 13 | flushAndRunMultipleServers, |
12 | getVideoCommentThreads, | 14 | getVideoCommentThreads, |
13 | getVideosList, | 15 | getVideosList, |
@@ -15,11 +17,8 @@ import { | |||
15 | setAccessTokensToServers, | 17 | setAccessTokensToServers, |
16 | uploadVideo, | 18 | uploadVideo, |
17 | userLogin, | 19 | userLogin, |
18 | waitJobs, | 20 | waitJobs |
19 | addVideoCommentReply | ||
20 | } from '../../../../shared/extra-utils/index' | 21 | } from '../../../../shared/extra-utils/index' |
21 | import { doubleFollow } from '../../../../shared/extra-utils/server/follows' | ||
22 | import { Video } from '@shared/models' | ||
23 | 22 | ||
24 | const expect = chai.expect | 23 | const expect = chai.expect |
25 | 24 | ||
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts index eb9ab10eb..e1c062020 100644 --- a/server/tests/api/server/follows.ts +++ b/server/tests/api/server/follows.ts | |||
@@ -1,37 +1,35 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | 3 | import 'mocha' |
5 | import { Video, VideoPrivacy } from '../../../../shared/models/videos' | 4 | import * as chai from 'chai' |
6 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | ||
7 | import { cleanupTests, completeVideoCheck, deleteVideoComment } from '../../../../shared/extra-utils' | ||
8 | import { | 5 | import { |
6 | addVideoCommentReply, | ||
7 | addVideoCommentThread, | ||
8 | cleanupTests, | ||
9 | completeVideoCheck, | ||
10 | createUser, | ||
11 | createVideoCaption, | ||
12 | dateIsValid, | ||
13 | deleteVideoComment, | ||
14 | expectAccountFollows, | ||
9 | flushAndRunMultipleServers, | 15 | flushAndRunMultipleServers, |
10 | getVideosList, | ||
11 | ServerInfo, | ||
12 | setAccessTokensToServers, | ||
13 | uploadVideo | ||
14 | } from '../../../../shared/extra-utils/index' | ||
15 | import { dateIsValid } from '../../../../shared/extra-utils/miscs/miscs' | ||
16 | import { | ||
17 | follow, | 16 | follow, |
18 | getFollowersListPaginationAndSort, | 17 | getFollowersListPaginationAndSort, |
19 | getFollowingListPaginationAndSort, | 18 | getFollowingListPaginationAndSort, |
20 | unfollow | ||
21 | } from '../../../../shared/extra-utils/server/follows' | ||
22 | import { expectAccountFollows } from '../../../../shared/extra-utils/users/accounts' | ||
23 | import { userLogin } from '../../../../shared/extra-utils/users/login' | ||
24 | import { createUser } from '../../../../shared/extra-utils/users/users' | ||
25 | import { | ||
26 | addVideoCommentReply, | ||
27 | addVideoCommentThread, | ||
28 | getVideoCommentThreads, | 19 | getVideoCommentThreads, |
29 | getVideoThreadComments | 20 | getVideosList, |
30 | } from '../../../../shared/extra-utils/videos/video-comments' | 21 | getVideoThreadComments, |
31 | import { rateVideo } from '../../../../shared/extra-utils/videos/videos' | 22 | listVideoCaptions, |
32 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 23 | rateVideo, |
33 | import { createVideoCaption, listVideoCaptions, testCaptionFile } from '../../../../shared/extra-utils/videos/video-captions' | 24 | ServerInfo, |
34 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' | 25 | setAccessTokensToServers, |
26 | testCaptionFile, | ||
27 | unfollow, | ||
28 | uploadVideo, | ||
29 | userLogin, | ||
30 | waitJobs | ||
31 | } from '@shared/extra-utils' | ||
32 | import { Video, VideoCaption, VideoComment, VideoCommentThreadTree, VideoPrivacy } from '@shared/models' | ||
35 | 33 | ||
36 | const expect = chai.expect | 34 | const expect = chai.expect |
37 | 35 | ||
diff --git a/server/tests/api/server/handle-down.ts b/server/tests/api/server/handle-down.ts index 817c79f6e..fe4a0e100 100644 --- a/server/tests/api/server/handle-down.ts +++ b/server/tests/api/server/handle-down.ts | |||
@@ -4,7 +4,7 @@ import * as chai from 'chai' | |||
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { JobState, Video } from '../../../../shared/models' | 5 | import { JobState, Video } from '../../../../shared/models' |
6 | import { VideoPrivacy } from '../../../../shared/models/videos' | 6 | import { VideoPrivacy } from '../../../../shared/models/videos' |
7 | import { VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | 7 | import { VideoCommentThreadTree } from '../../../../shared/models/videos/comment/video-comment.model' |
8 | 8 | ||
9 | import { | 9 | import { |
10 | cleanupTests, | 10 | cleanupTests, |
@@ -346,10 +346,12 @@ describe('Test handle downs', function () { | |||
346 | // Wait video expiration | 346 | // Wait video expiration |
347 | await wait(11000) | 347 | await wait(11000) |
348 | 348 | ||
349 | for (let i = 0; i < 3; i++) { | 349 | for (let i = 0; i < 5; i++) { |
350 | await getVideo(servers[1].url, videoIdsServer1[i]) | 350 | try { |
351 | await waitJobs([ servers[1] ]) | 351 | await getVideo(servers[1].url, videoIdsServer1[i]) |
352 | await wait(1500) | 352 | await waitJobs([ servers[1] ]) |
353 | await wait(1500) | ||
354 | } catch {} | ||
353 | } | 355 | } |
354 | 356 | ||
355 | for (const id of videoIdsServer1) { | 357 | for (const id of videoIdsServer1) { |
diff --git a/server/tests/api/server/plugins.ts b/server/tests/api/server/plugins.ts index f4190c352..6046ab97e 100644 --- a/server/tests/api/server/plugins.ts +++ b/server/tests/api/server/plugins.ts | |||
@@ -28,14 +28,8 @@ import { | |||
28 | updatePluginSettings, | 28 | updatePluginSettings, |
29 | wait, | 29 | wait, |
30 | waitUntilLog | 30 | waitUntilLog |
31 | } from '../../../../shared/extra-utils' | 31 | } from '@shared/extra-utils' |
32 | import { PeerTubePluginIndex } from '../../../../shared/models/plugins/peertube-plugin-index.model' | 32 | import { PeerTubePlugin, PeerTubePluginIndex, PluginPackageJson, PluginType, PublicServerSetting, ServerConfig, User } from '@shared/models' |
33 | import { PeerTubePlugin } from '../../../../shared/models/plugins/peertube-plugin.model' | ||
34 | import { PluginPackageJson } from '../../../../shared/models/plugins/plugin-package-json.model' | ||
35 | import { PluginType } from '../../../../shared/models/plugins/plugin.type' | ||
36 | import { PublicServerSetting } from '../../../../shared/models/plugins/public-server.setting' | ||
37 | import { ServerConfig } from '../../../../shared/models/server' | ||
38 | import { User } from '../../../../shared/models/users' | ||
39 | 33 | ||
40 | const expect = chai.expect | 34 | const expect = chai.expect |
41 | 35 | ||
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts index 41cd814e0..6aa996038 100644 --- a/server/tests/api/videos/multiple-servers.ts +++ b/server/tests/api/videos/multiple-servers.ts | |||
@@ -1,11 +1,10 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | ||
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import * as request from 'supertest' | 6 | import * as request from 'supertest' |
7 | import { VideoPrivacy } from '../../../../shared/models/videos' | 7 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
8 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | ||
9 | import { | 8 | import { |
10 | addVideoChannel, | 9 | addVideoChannel, |
11 | checkTmpIsEmpty, | 10 | checkTmpIsEmpty, |
@@ -32,16 +31,16 @@ import { | |||
32 | wait, | 31 | wait, |
33 | webtorrentAdd | 32 | webtorrentAdd |
34 | } from '../../../../shared/extra-utils' | 33 | } from '../../../../shared/extra-utils' |
34 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | ||
35 | import { | 35 | import { |
36 | addVideoCommentReply, | 36 | addVideoCommentReply, |
37 | addVideoCommentThread, | 37 | addVideoCommentThread, |
38 | deleteVideoComment, | 38 | deleteVideoComment, |
39 | findCommentId, | ||
39 | getVideoCommentThreads, | 40 | getVideoCommentThreads, |
40 | getVideoThreadComments, | 41 | getVideoThreadComments |
41 | findCommentId | ||
42 | } from '../../../../shared/extra-utils/videos/video-comments' | 42 | } from '../../../../shared/extra-utils/videos/video-comments' |
43 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 43 | import { VideoComment, VideoCommentThreadTree, VideoPrivacy } from '../../../../shared/models/videos' |
44 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
45 | 44 | ||
46 | const expect = chai.expect | 45 | const expect = chai.expect |
47 | 46 | ||
diff --git a/server/tests/api/videos/video-comments.ts b/server/tests/api/videos/video-comments.ts index 615e0ea45..a5ff3a39d 100644 --- a/server/tests/api/videos/video-comments.ts +++ b/server/tests/api/videos/video-comments.ts | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | 5 | import { VideoComment, VideoCommentAdmin, VideoCommentThreadTree } from '@shared/models' | |
6 | import { cleanupTests, testImage } from '../../../../shared/extra-utils' | 6 | import { cleanupTests, testImage } from '../../../../shared/extra-utils' |
7 | import { | 7 | import { |
8 | createUser, | 8 | createUser, |
@@ -22,7 +22,6 @@ import { | |||
22 | getVideoCommentThreads, | 22 | getVideoCommentThreads, |
23 | getVideoThreadComments | 23 | getVideoThreadComments |
24 | } from '../../../../shared/extra-utils/videos/video-comments' | 24 | } from '../../../../shared/extra-utils/videos/video-comments' |
25 | import { VideoComment, VideoCommentAdmin, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | ||
26 | 25 | ||
27 | const expect = chai.expect | 26 | const expect = chai.expect |
28 | 27 | ||
diff --git a/server/tests/client.ts b/server/tests/client.ts index 3c99bcd1f..a385edd26 100644 --- a/server/tests/client.ts +++ b/server/tests/client.ts | |||
@@ -3,7 +3,7 @@ | |||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import * as request from 'supertest' | 5 | import * as request from 'supertest' |
6 | import { Account, VideoPlaylistPrivacy } from '@shared/models' | 6 | import { Account, HTMLServerConfig, ServerConfig, VideoPlaylistPrivacy } from '@shared/models' |
7 | import { | 7 | import { |
8 | addVideoInPlaylist, | 8 | addVideoInPlaylist, |
9 | cleanupTests, | 9 | cleanupTests, |
@@ -11,6 +11,7 @@ import { | |||
11 | doubleFollow, | 11 | doubleFollow, |
12 | flushAndRunMultipleServers, | 12 | flushAndRunMultipleServers, |
13 | getAccount, | 13 | getAccount, |
14 | getConfig, | ||
14 | getCustomConfig, | 15 | getCustomConfig, |
15 | getVideosList, | 16 | getVideosList, |
16 | makeHTMLRequest, | 17 | makeHTMLRequest, |
@@ -25,13 +26,17 @@ import { | |||
25 | waitJobs | 26 | waitJobs |
26 | } from '../../shared/extra-utils' | 27 | } from '../../shared/extra-utils' |
27 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | 28 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' |
29 | import { omit } from 'lodash' | ||
28 | 30 | ||
29 | const expect = chai.expect | 31 | const expect = chai.expect |
30 | 32 | ||
31 | function checkIndexTags (html: string, title: string, description: string, css: string) { | 33 | function checkIndexTags (html: string, title: string, description: string, css: string, config: ServerConfig) { |
32 | expect(html).to.contain('<title>' + title + '</title>') | 34 | expect(html).to.contain('<title>' + title + '</title>') |
33 | expect(html).to.contain('<meta name="description" content="' + description + '" />') | 35 | expect(html).to.contain('<meta name="description" content="' + description + '" />') |
34 | expect(html).to.contain('<style class="custom-css-style">' + css + '</style>') | 36 | expect(html).to.contain('<style class="custom-css-style">' + css + '</style>') |
37 | |||
38 | const htmlConfig: HTMLServerConfig = omit(config, 'signup') | ||
39 | expect(html).to.contain(`<script type="application/javascript">window.PeerTubeServerConfig = '${JSON.stringify(htmlConfig)}'</script>`) | ||
35 | } | 40 | } |
36 | 41 | ||
37 | describe('Test a client controllers', function () { | 42 | describe('Test a client controllers', function () { |
@@ -296,10 +301,11 @@ describe('Test a client controllers', function () { | |||
296 | describe('Index HTML', function () { | 301 | describe('Index HTML', function () { |
297 | 302 | ||
298 | it('Should have valid index html tags (title, description...)', async function () { | 303 | it('Should have valid index html tags (title, description...)', async function () { |
304 | const resConfig = await getConfig(servers[0].url) | ||
299 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') | 305 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') |
300 | 306 | ||
301 | const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.' | 307 | const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.' |
302 | checkIndexTags(res.text, 'PeerTube', description, '') | 308 | checkIndexTags(res.text, 'PeerTube', description, '', resConfig.body) |
303 | }) | 309 | }) |
304 | 310 | ||
305 | it('Should update the customized configuration and have the correct index html tags', async function () { | 311 | it('Should update the customized configuration and have the correct index html tags', async function () { |
@@ -318,15 +324,17 @@ describe('Test a client controllers', function () { | |||
318 | } | 324 | } |
319 | }) | 325 | }) |
320 | 326 | ||
327 | const resConfig = await getConfig(servers[0].url) | ||
321 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') | 328 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') |
322 | 329 | ||
323 | checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }') | 330 | checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) |
324 | }) | 331 | }) |
325 | 332 | ||
326 | it('Should have valid index html updated tags (title, description...)', async function () { | 333 | it('Should have valid index html updated tags (title, description...)', async function () { |
334 | const resConfig = await getConfig(servers[0].url) | ||
327 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') | 335 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') |
328 | 336 | ||
329 | checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }') | 337 | checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) |
330 | }) | 338 | }) |
331 | 339 | ||
332 | it('Should use the original video URL for the canonical tag', async function () { | 340 | it('Should use the original video URL for the canonical tag', async function () { |
@@ -350,6 +358,16 @@ describe('Test a client controllers', function () { | |||
350 | }) | 358 | }) |
351 | }) | 359 | }) |
352 | 360 | ||
361 | describe('Embed HTML', function () { | ||
362 | |||
363 | it('Should have the correct embed html tags', async function () { | ||
364 | const resConfig = await getConfig(servers[0].url) | ||
365 | const res = await makeHTMLRequest(servers[0].url, servers[0].video.embedPath) | ||
366 | |||
367 | checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) | ||
368 | }) | ||
369 | }) | ||
370 | |||
353 | after(async function () { | 371 | after(async function () { |
354 | await cleanupTests(servers) | 372 | await cleanupTests(servers) |
355 | }) | 373 | }) |
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts index 7d4f7abb4..1d6bb6cf4 100644 --- a/server/tests/plugins/filter-hooks.ts +++ b/server/tests/plugins/filter-hooks.ts | |||
@@ -38,6 +38,7 @@ import { | |||
38 | import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers' | 38 | import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers' |
39 | import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports' | 39 | import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports' |
40 | import { | 40 | import { |
41 | VideoCommentThreadTree, | ||
41 | VideoDetails, | 42 | VideoDetails, |
42 | VideoImport, | 43 | VideoImport, |
43 | VideoImportState, | 44 | VideoImportState, |
@@ -45,7 +46,6 @@ import { | |||
45 | VideoPlaylistPrivacy, | 46 | VideoPlaylistPrivacy, |
46 | VideoPrivacy | 47 | VideoPrivacy |
47 | } from '../../../shared/models/videos' | 48 | } from '../../../shared/models/videos' |
48 | import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model' | ||
49 | 49 | ||
50 | const expect = chai.expect | 50 | const expect = chai.expect |
51 | 51 | ||
diff --git a/server/tools/peertube-import-videos.ts b/server/tools/peertube-import-videos.ts index 915995031..b3f57a8f9 100644 --- a/server/tools/peertube-import-videos.ts +++ b/server/tools/peertube-import-videos.ts | |||
@@ -11,9 +11,9 @@ import { promisify } from 'util' | |||
11 | import { advancedVideosSearch, getClient, getVideoCategories, login, uploadVideo } from '../../shared/extra-utils/index' | 11 | import { advancedVideosSearch, getClient, getVideoCategories, login, uploadVideo } from '../../shared/extra-utils/index' |
12 | import { sha256 } from '../helpers/core-utils' | 12 | import { sha256 } from '../helpers/core-utils' |
13 | import { doRequestAndSaveToFile } from '../helpers/requests' | 13 | import { doRequestAndSaveToFile } from '../helpers/requests' |
14 | import { buildOriginallyPublishedAt, getYoutubeDLVideoFormat, safeGetYoutubeDL } from '../helpers/youtube-dl' | ||
15 | import { CONSTRAINTS_FIELDS } from '../initializers/constants' | 14 | import { CONSTRAINTS_FIELDS } from '../initializers/constants' |
16 | import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getLogger, getServerCredentials } from './cli' | 15 | import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getLogger, getServerCredentials } from './cli' |
16 | import { YoutubeDL } from '@server/helpers/youtube-dl' | ||
17 | 17 | ||
18 | type UserInfo = { | 18 | type UserInfo = { |
19 | username: string | 19 | username: string |
@@ -74,9 +74,9 @@ async function run (url: string, user: UserInfo) { | |||
74 | user.password = await promptPassword() | 74 | user.password = await promptPassword() |
75 | } | 75 | } |
76 | 76 | ||
77 | const youtubeDL = await safeGetYoutubeDL() | 77 | const youtubeDLBinary = await YoutubeDL.safeGetYoutubeDL() |
78 | 78 | ||
79 | let info = await getYoutubeDLInfo(youtubeDL, options.targetUrl, command.args) | 79 | let info = await getYoutubeDLInfo(youtubeDLBinary, options.targetUrl, command.args) |
80 | 80 | ||
81 | if (!Array.isArray(info)) info = [ info ] | 81 | if (!Array.isArray(info)) info = [ info ] |
82 | 82 | ||
@@ -86,7 +86,7 @@ async function run (url: string, user: UserInfo) { | |||
86 | if (uploadsObject) { | 86 | if (uploadsObject) { |
87 | console.log('Fixing URL to %s.', uploadsObject.url) | 87 | console.log('Fixing URL to %s.', uploadsObject.url) |
88 | 88 | ||
89 | info = await getYoutubeDLInfo(youtubeDL, uploadsObject.url, command.args) | 89 | info = await getYoutubeDLInfo(youtubeDLBinary, uploadsObject.url, command.args) |
90 | } | 90 | } |
91 | 91 | ||
92 | let infoArray: any[] | 92 | let infoArray: any[] |
@@ -130,13 +130,14 @@ async function processVideo (parameters: { | |||
130 | youtubeInfo: any | 130 | youtubeInfo: any |
131 | }) { | 131 | }) { |
132 | const { youtubeInfo, cwd, url, user } = parameters | 132 | const { youtubeInfo, cwd, url, user } = parameters |
133 | const youtubeDL = new YoutubeDL('', []) | ||
133 | 134 | ||
134 | log.debug('Fetching object.', youtubeInfo) | 135 | log.debug('Fetching object.', youtubeInfo) |
135 | 136 | ||
136 | const videoInfo = await fetchObject(youtubeInfo) | 137 | const videoInfo = await fetchObject(youtubeInfo) |
137 | log.debug('Fetched object.', videoInfo) | 138 | log.debug('Fetched object.', videoInfo) |
138 | 139 | ||
139 | const originallyPublishedAt = buildOriginallyPublishedAt(videoInfo) | 140 | const originallyPublishedAt = youtubeDL.buildOriginallyPublishedAt(videoInfo) |
140 | if (options.since && originallyPublishedAt && originallyPublishedAt.getTime() < options.since.getTime()) { | 141 | if (options.since && originallyPublishedAt && originallyPublishedAt.getTime() < options.since.getTime()) { |
141 | log.info('Video "%s" has been published before "%s", don\'t upload it.\n', | 142 | log.info('Video "%s" has been published before "%s", don\'t upload it.\n', |
142 | videoInfo.title, formatDate(options.since)) | 143 | videoInfo.title, formatDate(options.since)) |
@@ -161,13 +162,14 @@ async function processVideo (parameters: { | |||
161 | 162 | ||
162 | log.info('Downloading video "%s"...', videoInfo.title) | 163 | log.info('Downloading video "%s"...', videoInfo.title) |
163 | 164 | ||
164 | const youtubeDLOptions = [ '-f', getYoutubeDLVideoFormat(), ...command.args, '-o', path ] | 165 | const youtubeDLOptions = [ '-f', youtubeDL.getYoutubeDLVideoFormat(), ...command.args, '-o', path ] |
165 | try { | 166 | try { |
166 | const youtubeDL = await safeGetYoutubeDL() | 167 | const youtubeDLBinary = await YoutubeDL.safeGetYoutubeDL() |
167 | const youtubeDLExec = promisify(youtubeDL.exec).bind(youtubeDL) | 168 | const youtubeDLExec = promisify(youtubeDLBinary.exec).bind(youtubeDLBinary) |
168 | const output = await youtubeDLExec(videoInfo.url, youtubeDLOptions, processOptions) | 169 | const output = await youtubeDLExec(videoInfo.url, youtubeDLOptions, processOptions) |
169 | log.info(output.join('\n')) | 170 | log.info(output.join('\n')) |
170 | await uploadVideoOnPeerTube({ | 171 | await uploadVideoOnPeerTube({ |
172 | youtubeDL, | ||
171 | cwd, | 173 | cwd, |
172 | url, | 174 | url, |
173 | user, | 175 | user, |
@@ -180,13 +182,14 @@ async function processVideo (parameters: { | |||
180 | } | 182 | } |
181 | 183 | ||
182 | async function uploadVideoOnPeerTube (parameters: { | 184 | async function uploadVideoOnPeerTube (parameters: { |
185 | youtubeDL: YoutubeDL | ||
183 | videoInfo: any | 186 | videoInfo: any |
184 | videoPath: string | 187 | videoPath: string |
185 | cwd: string | 188 | cwd: string |
186 | url: string | 189 | url: string |
187 | user: { username: string, password: string } | 190 | user: { username: string, password: string } |
188 | }) { | 191 | }) { |
189 | const { videoInfo, videoPath, cwd, url, user } = parameters | 192 | const { youtubeDL, videoInfo, videoPath, cwd, url, user } = parameters |
190 | 193 | ||
191 | const category = await getCategory(videoInfo.categories, url) | 194 | const category = await getCategory(videoInfo.categories, url) |
192 | const licence = getLicence(videoInfo.license) | 195 | const licence = getLicence(videoInfo.license) |
@@ -205,7 +208,7 @@ async function uploadVideoOnPeerTube (parameters: { | |||
205 | await doRequestAndSaveToFile(videoInfo.thumbnail, thumbnailfile) | 208 | await doRequestAndSaveToFile(videoInfo.thumbnail, thumbnailfile) |
206 | } | 209 | } |
207 | 210 | ||
208 | const originallyPublishedAt = buildOriginallyPublishedAt(videoInfo) | 211 | const originallyPublishedAt = youtubeDL.buildOriginallyPublishedAt(videoInfo) |
209 | 212 | ||
210 | const defaultAttributes = { | 213 | const defaultAttributes = { |
211 | name: truncate(videoInfo.title, { | 214 | name: truncate(videoInfo.title, { |
@@ -304,7 +307,7 @@ function fetchObject (info: any) { | |||
304 | const url = buildUrl(info) | 307 | const url = buildUrl(info) |
305 | 308 | ||
306 | return new Promise<any>(async (res, rej) => { | 309 | return new Promise<any>(async (res, rej) => { |
307 | const youtubeDL = await safeGetYoutubeDL() | 310 | const youtubeDL = await YoutubeDL.safeGetYoutubeDL() |
308 | youtubeDL.getInfo(url, undefined, processOptions, (err, videoInfo) => { | 311 | youtubeDL.getInfo(url, undefined, processOptions, (err, videoInfo) => { |
309 | if (err) return rej(err) | 312 | if (err) return rej(err) |
310 | 313 | ||
diff --git a/server/tools/peertube-plugins.ts b/server/tools/peertube-plugins.ts index c8a576844..cb591377b 100644 --- a/server/tools/peertube-plugins.ts +++ b/server/tools/peertube-plugins.ts | |||
@@ -4,10 +4,9 @@ import { registerTSPaths } from '../helpers/register-ts-paths' | |||
4 | registerTSPaths() | 4 | registerTSPaths() |
5 | 5 | ||
6 | import * as program from 'commander' | 6 | import * as program from 'commander' |
7 | import { PluginType } from '../../shared/models/plugins/plugin.type' | ||
8 | import { installPlugin, listPlugins, uninstallPlugin, updatePlugin } from '../../shared/extra-utils/server/plugins' | 7 | import { installPlugin, listPlugins, uninstallPlugin, updatePlugin } from '../../shared/extra-utils/server/plugins' |
9 | import { getAdminTokenOrDie, getServerCredentials } from './cli' | 8 | import { getAdminTokenOrDie, getServerCredentials } from './cli' |
10 | import { PeerTubePlugin } from '../../shared/models/plugins/peertube-plugin.model' | 9 | import { PeerTubePlugin, PluginType } from '../../shared/models' |
11 | import { isAbsolute } from 'path' | 10 | import { isAbsolute } from 'path' |
12 | import * as CliTable3 from 'cli-table3' | 11 | import * as CliTable3 from 'cli-table3' |
13 | import commander = require('commander') | 12 | import commander = require('commander') |
diff --git a/server/types/models/moderation/abuse-message.ts b/server/types/models/abuse/abuse-message.ts index 565eca706..565eca706 100644 --- a/server/types/models/moderation/abuse-message.ts +++ b/server/types/models/abuse/abuse-message.ts | |||
diff --git a/server/types/models/moderation/abuse.ts b/server/types/models/abuse/abuse.ts index 6fd83684c..6fd83684c 100644 --- a/server/types/models/moderation/abuse.ts +++ b/server/types/models/abuse/abuse.ts | |||
diff --git a/server/types/models/moderation/index.ts b/server/types/models/abuse/index.ts index 1ed91b249..1ed91b249 100644 --- a/server/types/models/moderation/index.ts +++ b/server/types/models/abuse/index.ts | |||
diff --git a/server/types/models/account/account.ts b/server/types/models/account/account.ts index 9513acad8..984841291 100644 --- a/server/types/models/account/account.ts +++ b/server/types/models/account/account.ts | |||
@@ -1,7 +1,5 @@ | |||
1 | import { FunctionProperties, PickWith } from '@shared/core-utils' | 1 | import { FunctionProperties, PickWith } from '@shared/core-utils' |
2 | import { AccountModel } from '../../../models/account/account' | 2 | import { AccountModel } from '../../../models/account/account' |
3 | import { MChannelDefault } from '../video/video-channels' | ||
4 | import { MAccountBlocklistId } from './account-blocklist' | ||
5 | import { | 3 | import { |
6 | MActor, | 4 | MActor, |
7 | MActorAPAccount, | 5 | MActorAPAccount, |
@@ -15,7 +13,9 @@ import { | |||
15 | MActorSummary, | 13 | MActorSummary, |
16 | MActorSummaryFormattable, | 14 | MActorSummaryFormattable, |
17 | MActorUrl | 15 | MActorUrl |
18 | } from './actor' | 16 | } from '../actor' |
17 | import { MChannelDefault } from '../video/video-channels' | ||
18 | import { MAccountBlocklistId } from './account-blocklist' | ||
19 | 19 | ||
20 | type Use<K extends keyof AccountModel, M> = PickWith<AccountModel, K, M> | 20 | type Use<K extends keyof AccountModel, M> = PickWith<AccountModel, K, M> |
21 | 21 | ||
diff --git a/server/types/models/account/index.ts b/server/types/models/account/index.ts index e3fc00f94..dab2eea7e 100644 --- a/server/types/models/account/index.ts +++ b/server/types/models/account/index.ts | |||
@@ -1,5 +1,2 @@ | |||
1 | export * from './account' | 1 | export * from './account' |
2 | export * from './account-blocklist' | 2 | export * from './account-blocklist' |
3 | export * from './actor-follow' | ||
4 | export * from './actor-image' | ||
5 | export * from './actor' | ||
diff --git a/server/types/models/account/actor-follow.ts b/server/types/models/actor/actor-follow.ts index 8e19c6140..98a6ca8a5 100644 --- a/server/types/models/account/actor-follow.ts +++ b/server/types/models/actor/actor-follow.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { PickWith } from '@shared/core-utils' | 1 | import { PickWith } from '@shared/core-utils' |
2 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 2 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
3 | import { | 3 | import { |
4 | MActor, | 4 | MActor, |
5 | MActorChannelAccountActor, | 5 | MActorChannelAccountActor, |
diff --git a/server/types/models/account/actor-image.ts b/server/types/models/actor/actor-image.ts index e59f8b141..89adb01ae 100644 --- a/server/types/models/account/actor-image.ts +++ b/server/types/models/actor/actor-image.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { ActorImageModel } from '../../../models/account/actor-image' | ||
2 | import { FunctionProperties } from '@shared/core-utils' | 1 | import { FunctionProperties } from '@shared/core-utils' |
2 | import { ActorImageModel } from '../../../models/actor/actor-image' | ||
3 | 3 | ||
4 | export type MActorImage = ActorImageModel | 4 | export type MActorImage = ActorImageModel |
5 | 5 | ||
diff --git a/server/types/models/account/actor.ts b/server/types/models/actor/actor.ts index 0b620872e..b3a70cbce 100644 --- a/server/types/models/account/actor.ts +++ b/server/types/models/actor/actor.ts | |||
@@ -1,9 +1,8 @@ | |||
1 | |||
2 | import { FunctionProperties, PickWith, PickWithOpt } from '@shared/core-utils' | 1 | import { FunctionProperties, PickWith, PickWithOpt } from '@shared/core-utils' |
3 | import { ActorModel } from '../../../models/activitypub/actor' | 2 | import { ActorModel } from '../../../models/actor/actor' |
3 | import { MAccount, MAccountDefault, MAccountId, MAccountIdActor } from '../account' | ||
4 | import { MServer, MServerHost, MServerHostBlocks, MServerRedundancyAllowed } from '../server' | 4 | import { MServer, MServerHost, MServerHostBlocks, MServerRedundancyAllowed } from '../server' |
5 | import { MChannel, MChannelAccountActor, MChannelAccountDefault, MChannelId, MChannelIdActor } from '../video' | 5 | import { MChannel, MChannelAccountActor, MChannelAccountDefault, MChannelId, MChannelIdActor } from '../video' |
6 | import { MAccount, MAccountDefault, MAccountId, MAccountIdActor } from './account' | ||
7 | import { MActorImage, MActorImageFormattable } from './actor-image' | 6 | import { MActorImage, MActorImageFormattable } from './actor-image' |
8 | 7 | ||
9 | type Use<K extends keyof ActorModel, M> = PickWith<ActorModel, K, M> | 8 | type Use<K extends keyof ActorModel, M> = PickWith<ActorModel, K, M> |
diff --git a/server/types/models/actor/index.ts b/server/types/models/actor/index.ts new file mode 100644 index 000000000..b27815255 --- /dev/null +++ b/server/types/models/actor/index.ts | |||
@@ -0,0 +1,3 @@ | |||
1 | export * from './actor-follow' | ||
2 | export * from './actor-image' | ||
3 | export * from './actor' | ||
diff --git a/server/types/models/index.ts b/server/types/models/index.ts index b4fdb1ff3..704cb9844 100644 --- a/server/types/models/index.ts +++ b/server/types/models/index.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | export * from './abuse' | ||
1 | export * from './account' | 2 | export * from './account' |
3 | export * from './actor' | ||
2 | export * from './application' | 4 | export * from './application' |
3 | export * from './moderation' | ||
4 | export * from './oauth' | 5 | export * from './oauth' |
5 | export * from './server' | 6 | export * from './server' |
6 | export * from './user' | 7 | export * from './user' |
diff --git a/server/types/models/user/user-notification-setting.ts b/server/types/models/user/user-notification-setting.ts index c674add1b..d1db645e7 100644 --- a/server/types/models/user/user-notification-setting.ts +++ b/server/types/models/user/user-notification-setting.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { UserNotificationSettingModel } from '@server/models/account/user-notification-setting' | 1 | import { UserNotificationSettingModel } from '@server/models/user/user-notification-setting' |
2 | 2 | ||
3 | export type MNotificationSetting = Omit<UserNotificationSettingModel, 'User'> | 3 | export type MNotificationSetting = Omit<UserNotificationSettingModel, 'User'> |
4 | 4 | ||
diff --git a/server/types/models/user/user-notification.ts b/server/types/models/user/user-notification.ts index 7ebb0485d..918614dd1 100644 --- a/server/types/models/user/user-notification.ts +++ b/server/types/models/user/user-notification.ts | |||
@@ -2,13 +2,13 @@ import { VideoAbuseModel } from '@server/models/abuse/video-abuse' | |||
2 | import { VideoCommentAbuseModel } from '@server/models/abuse/video-comment-abuse' | 2 | import { VideoCommentAbuseModel } from '@server/models/abuse/video-comment-abuse' |
3 | import { ApplicationModel } from '@server/models/application/application' | 3 | import { ApplicationModel } from '@server/models/application/application' |
4 | import { PluginModel } from '@server/models/server/plugin' | 4 | import { PluginModel } from '@server/models/server/plugin' |
5 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
5 | import { PickWith, PickWithOpt } from '@shared/core-utils' | 6 | import { PickWith, PickWithOpt } from '@shared/core-utils' |
6 | import { AbuseModel } from '../../../models/abuse/abuse' | 7 | import { AbuseModel } from '../../../models/abuse/abuse' |
7 | import { AccountModel } from '../../../models/account/account' | 8 | import { AccountModel } from '../../../models/account/account' |
8 | import { ActorImageModel } from '../../../models/account/actor-image' | 9 | import { ActorModel } from '../../../models/actor/actor' |
9 | import { UserNotificationModel } from '../../../models/account/user-notification' | 10 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
10 | import { ActorModel } from '../../../models/activitypub/actor' | 11 | import { ActorImageModel } from '../../../models/actor/actor-image' |
11 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
12 | import { ServerModel } from '../../../models/server/server' | 12 | import { ServerModel } from '../../../models/server/server' |
13 | import { VideoModel } from '../../../models/video/video' | 13 | import { VideoModel } from '../../../models/video/video' |
14 | import { VideoBlacklistModel } from '../../../models/video/video-blacklist' | 14 | import { VideoBlacklistModel } from '../../../models/video/video-blacklist' |
diff --git a/server/types/models/user/user-video-history.ts b/server/types/models/user/user-video-history.ts index 62673ab1b..34e2930e7 100644 --- a/server/types/models/user/user-video-history.ts +++ b/server/types/models/user/user-video-history.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' | 1 | import { UserVideoHistoryModel } from '../../../models/user/user-video-history' |
2 | 2 | ||
3 | export type MUserVideoHistory = Omit<UserVideoHistoryModel, 'Video' | 'User'> | 3 | export type MUserVideoHistory = Omit<UserVideoHistoryModel, 'Video' | 'User'> |
4 | 4 | ||
diff --git a/server/types/models/user/user.ts b/server/types/models/user/user.ts index fa7de9c52..f79220e11 100644 --- a/server/types/models/user/user.ts +++ b/server/types/models/user/user.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { AccountModel } from '@server/models/account/account' | 1 | import { AccountModel } from '@server/models/account/account' |
2 | import { UserModel } from '@server/models/user/user' | ||
2 | import { MVideoPlaylist } from '@server/types/models' | 3 | import { MVideoPlaylist } from '@server/types/models' |
3 | import { PickWith, PickWithOpt } from '@shared/core-utils' | 4 | import { PickWith, PickWithOpt } from '@shared/core-utils' |
4 | import { UserModel } from '../../../models/account/user' | ||
5 | import { | 5 | import { |
6 | MAccount, | 6 | MAccount, |
7 | MAccountDefault, | 7 | MAccountDefault, |
diff --git a/server/types/models/video/video-channels.ts b/server/types/models/video/video-channels.ts index f577807ca..c147567d9 100644 --- a/server/types/models/video/video-channels.ts +++ b/server/types/models/video/video-channels.ts | |||
@@ -9,7 +9,9 @@ import { | |||
9 | MAccountSummaryBlocks, | 9 | MAccountSummaryBlocks, |
10 | MAccountSummaryFormattable, | 10 | MAccountSummaryFormattable, |
11 | MAccountUrl, | 11 | MAccountUrl, |
12 | MAccountUserId, | 12 | MAccountUserId |
13 | } from '../account' | ||
14 | import { | ||
13 | MActor, | 15 | MActor, |
14 | MActorAccountChannelId, | 16 | MActorAccountChannelId, |
15 | MActorAPChannel, | 17 | MActorAPChannel, |
@@ -23,7 +25,7 @@ import { | |||
23 | MActorSummary, | 25 | MActorSummary, |
24 | MActorSummaryFormattable, | 26 | MActorSummaryFormattable, |
25 | MActorUrl | 27 | MActorUrl |
26 | } from '../account' | 28 | } from '../actor' |
27 | import { MVideo } from './video' | 29 | import { MVideo } from './video' |
28 | 30 | ||
29 | type Use<K extends keyof VideoChannelModel, M> = PickWith<VideoChannelModel, K, M> | 31 | type Use<K extends keyof VideoChannelModel, M> = PickWith<VideoChannelModel, K, M> |
diff --git a/server/types/models/video/video-share.ts b/server/types/models/video/video-share.ts index b7a783bb6..78f44e58c 100644 --- a/server/types/models/video/video-share.ts +++ b/server/types/models/video/video-share.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { VideoShareModel } from '../../../models/video/video-share' | ||
2 | import { PickWith } from '@shared/core-utils' | 1 | import { PickWith } from '@shared/core-utils' |
3 | import { MActorDefault } from '../account' | 2 | import { VideoShareModel } from '../../../models/video/video-share' |
3 | import { MActorDefault } from '../actor' | ||
4 | import { MVideo } from './video' | 4 | import { MVideo } from './video' |
5 | 5 | ||
6 | type Use<K extends keyof VideoShareModel, M> = PickWith<VideoShareModel, K, M> | 6 | type Use<K extends keyof VideoShareModel, M> = PickWith<VideoShareModel, K, M> |
diff --git a/server/types/plugins/register-server-option.model.ts b/server/types/plugins/register-server-option.model.ts index 2432b7ac4..8774bcd8c 100644 --- a/server/types/plugins/register-server-option.model.ts +++ b/server/types/plugins/register-server-option.model.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Router, Response } from 'express' | 1 | import { Response, Router } from 'express' |
2 | import { Logger } from 'winston' | 2 | import { Logger } from 'winston' |
3 | import { ActorModel } from '@server/models/activitypub/actor' | 3 | import { ActorModel } from '@server/models/actor/actor' |
4 | import { | 4 | import { |
5 | PluginPlaylistPrivacyManager, | 5 | PluginPlaylistPrivacyManager, |
6 | PluginSettingsManager, | 6 | PluginSettingsManager, |
diff --git a/server/types/sequelize.ts b/server/types/sequelize.ts index 9cd83612d..535113d01 100644 --- a/server/types/sequelize.ts +++ b/server/types/sequelize.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Model } from 'sequelize-typescript' | 1 | import { AttributesOnly } from '@shared/core-utils' |
2 | import { Model } from 'sequelize' | ||
2 | 3 | ||
3 | // Thanks to sequelize-typescript: https://github.com/RobinBuschmann/sequelize-typescript | 4 | // Thanks to sequelize-typescript: https://github.com/RobinBuschmann/sequelize-typescript |
4 | 5 | ||
@@ -9,7 +10,7 @@ export type Omit<T, K extends keyof T> = { [P in Diff<keyof T, K>]: T[P] } | |||
9 | 10 | ||
10 | export type RecursivePartial<T> = { [P in keyof T]?: RecursivePartial<T[P]> } | 11 | export type RecursivePartial<T> = { [P in keyof T]?: RecursivePartial<T[P]> } |
11 | 12 | ||
12 | export type FilteredModelAttributes<T extends Model<T>> = RecursivePartial<Omit<T, keyof Model<any>>> & { | 13 | export type FilteredModelAttributes<T extends Model<any>> = Partial<AttributesOnly<T>> & { |
13 | id?: number | any | 14 | id?: number | any |
14 | createdAt?: Date | any | 15 | createdAt?: Date | any |
15 | updatedAt?: Date | any | 16 | updatedAt?: Date | any |