diff options
Diffstat (limited to 'server/controllers/api')
22 files changed, 260 insertions, 97 deletions
diff --git a/server/controllers/api/abuse.ts b/server/controllers/api/abuse.ts index 72c418e74..d6211cc83 100644 --- a/server/controllers/api/abuse.ts +++ b/server/controllers/api/abuse.ts | |||
@@ -6,8 +6,7 @@ import { AbuseModel } from '@server/models/abuse/abuse' | |||
6 | import { AbuseMessageModel } from '@server/models/abuse/abuse-message' | 6 | import { AbuseMessageModel } from '@server/models/abuse/abuse-message' |
7 | import { getServerActor } from '@server/models/application/application' | 7 | import { getServerActor } from '@server/models/application/application' |
8 | import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' | 8 | import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' |
9 | import { HttpStatusCode } from '@shared/models' | 9 | import { AbuseCreate, AbuseState, HttpStatusCode, UserRight } from '@shared/models' |
10 | import { AbuseCreate, AbuseState, UserRight } from '../../../shared' | ||
11 | import { getFormattedObjects } from '../../helpers/utils' | 10 | import { getFormattedObjects } from '../../helpers/utils' |
12 | import { sequelizeTypescript } from '../../initializers/database' | 11 | import { sequelizeTypescript } from '../../initializers/database' |
13 | import { | 12 | import { |
@@ -167,7 +166,11 @@ async function reportAbuse (req: express.Request, res: express.Response) { | |||
167 | const body: AbuseCreate = req.body | 166 | const body: AbuseCreate = req.body |
168 | 167 | ||
169 | const { id } = await sequelizeTypescript.transaction(async t => { | 168 | const { id } = await sequelizeTypescript.transaction(async t => { |
170 | const reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) | 169 | const user = res.locals.oauth.token.User |
170 | // Don't send abuse notification if reporter is an admin/moderator | ||
171 | const skipNotification = user.hasRight(UserRight.MANAGE_ABUSES) | ||
172 | |||
173 | const reporterAccount = await AccountModel.load(user.Account.id, t) | ||
171 | const predefinedReasons = body.predefinedReasons?.map(r => abusePredefinedReasonsMap[r]) | 174 | const predefinedReasons = body.predefinedReasons?.map(r => abusePredefinedReasonsMap[r]) |
172 | 175 | ||
173 | const baseAbuse = { | 176 | const baseAbuse = { |
@@ -184,7 +187,8 @@ async function reportAbuse (req: express.Request, res: express.Response) { | |||
184 | reporterAccount, | 187 | reporterAccount, |
185 | transaction: t, | 188 | transaction: t, |
186 | startAt: body.video.startAt, | 189 | startAt: body.video.startAt, |
187 | endAt: body.video.endAt | 190 | endAt: body.video.endAt, |
191 | skipNotification | ||
188 | }) | 192 | }) |
189 | } | 193 | } |
190 | 194 | ||
@@ -193,7 +197,8 @@ async function reportAbuse (req: express.Request, res: express.Response) { | |||
193 | baseAbuse, | 197 | baseAbuse, |
194 | commentInstance, | 198 | commentInstance, |
195 | reporterAccount, | 199 | reporterAccount, |
196 | transaction: t | 200 | transaction: t, |
201 | skipNotification | ||
197 | }) | 202 | }) |
198 | } | 203 | } |
199 | 204 | ||
@@ -202,7 +207,8 @@ async function reportAbuse (req: express.Request, res: express.Response) { | |||
202 | baseAbuse, | 207 | baseAbuse, |
203 | accountInstance, | 208 | accountInstance, |
204 | reporterAccount, | 209 | reporterAccount, |
205 | transaction: t | 210 | transaction: t, |
211 | skipNotification | ||
206 | }) | 212 | }) |
207 | }) | 213 | }) |
208 | 214 | ||
diff --git a/server/controllers/api/blocklist.ts b/server/controllers/api/blocklist.ts new file mode 100644 index 000000000..1e936ad10 --- /dev/null +++ b/server/controllers/api/blocklist.ts | |||
@@ -0,0 +1,108 @@ | |||
1 | import express from 'express' | ||
2 | import { handleToNameAndHost } from '@server/helpers/actors' | ||
3 | import { AccountBlocklistModel } from '@server/models/account/account-blocklist' | ||
4 | import { getServerActor } from '@server/models/application/application' | ||
5 | import { ServerBlocklistModel } from '@server/models/server/server-blocklist' | ||
6 | import { MActorAccountId, MUserAccountId } from '@server/types/models' | ||
7 | import { BlockStatus } from '@shared/models' | ||
8 | import { asyncMiddleware, blocklistStatusValidator, optionalAuthenticate } from '../../middlewares' | ||
9 | import { logger } from '@server/helpers/logger' | ||
10 | |||
11 | const blocklistRouter = express.Router() | ||
12 | |||
13 | blocklistRouter.get('/status', | ||
14 | optionalAuthenticate, | ||
15 | blocklistStatusValidator, | ||
16 | asyncMiddleware(getBlocklistStatus) | ||
17 | ) | ||
18 | |||
19 | // --------------------------------------------------------------------------- | ||
20 | |||
21 | export { | ||
22 | blocklistRouter | ||
23 | } | ||
24 | |||
25 | // --------------------------------------------------------------------------- | ||
26 | |||
27 | async function getBlocklistStatus (req: express.Request, res: express.Response) { | ||
28 | const hosts = req.query.hosts as string[] | ||
29 | const accounts = req.query.accounts as string[] | ||
30 | const user = res.locals.oauth?.token.User | ||
31 | |||
32 | const serverActor = await getServerActor() | ||
33 | |||
34 | const byAccountIds = [ serverActor.Account.id ] | ||
35 | if (user) byAccountIds.push(user.Account.id) | ||
36 | |||
37 | const status: BlockStatus = { | ||
38 | accounts: {}, | ||
39 | hosts: {} | ||
40 | } | ||
41 | |||
42 | const baseOptions = { | ||
43 | byAccountIds, | ||
44 | user, | ||
45 | serverActor, | ||
46 | status | ||
47 | } | ||
48 | |||
49 | await Promise.all([ | ||
50 | populateServerBlocklistStatus({ ...baseOptions, hosts }), | ||
51 | populateAccountBlocklistStatus({ ...baseOptions, accounts }) | ||
52 | ]) | ||
53 | |||
54 | return res.json(status) | ||
55 | } | ||
56 | |||
57 | async function populateServerBlocklistStatus (options: { | ||
58 | byAccountIds: number[] | ||
59 | user?: MUserAccountId | ||
60 | serverActor: MActorAccountId | ||
61 | hosts: string[] | ||
62 | status: BlockStatus | ||
63 | }) { | ||
64 | const { byAccountIds, user, serverActor, hosts, status } = options | ||
65 | |||
66 | if (!hosts || hosts.length === 0) return | ||
67 | |||
68 | const serverBlocklistStatus = await ServerBlocklistModel.getBlockStatus(byAccountIds, hosts) | ||
69 | |||
70 | logger.debug('Got server blocklist status.', { serverBlocklistStatus, byAccountIds, hosts }) | ||
71 | |||
72 | for (const host of hosts) { | ||
73 | const block = serverBlocklistStatus.find(b => b.host === host) | ||
74 | |||
75 | status.hosts[host] = getStatus(block, serverActor, user) | ||
76 | } | ||
77 | } | ||
78 | |||
79 | async function populateAccountBlocklistStatus (options: { | ||
80 | byAccountIds: number[] | ||
81 | user?: MUserAccountId | ||
82 | serverActor: MActorAccountId | ||
83 | accounts: string[] | ||
84 | status: BlockStatus | ||
85 | }) { | ||
86 | const { byAccountIds, user, serverActor, accounts, status } = options | ||
87 | |||
88 | if (!accounts || accounts.length === 0) return | ||
89 | |||
90 | const accountBlocklistStatus = await AccountBlocklistModel.getBlockStatus(byAccountIds, accounts) | ||
91 | |||
92 | logger.debug('Got account blocklist status.', { accountBlocklistStatus, byAccountIds, accounts }) | ||
93 | |||
94 | for (const account of accounts) { | ||
95 | const sanitizedHandle = handleToNameAndHost(account) | ||
96 | |||
97 | const block = accountBlocklistStatus.find(b => b.name === sanitizedHandle.name && b.host === sanitizedHandle.host) | ||
98 | |||
99 | status.accounts[sanitizedHandle.handle] = getStatus(block, serverActor, user) | ||
100 | } | ||
101 | } | ||
102 | |||
103 | function getStatus (block: { accountId: number }, serverActor: MActorAccountId, user?: MUserAccountId) { | ||
104 | return { | ||
105 | blockedByServer: !!(block && block.accountId === serverActor.Account.id), | ||
106 | blockedByUser: !!(block && user && block.accountId === user.Account.id) | ||
107 | } | ||
108 | } | ||
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 805ad99c7..4e3dd4d80 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -3,9 +3,7 @@ import { remove, writeJSON } from 'fs-extra' | |||
3 | import { snakeCase } from 'lodash' | 3 | import { snakeCase } from 'lodash' |
4 | import validator from 'validator' | 4 | import validator from 'validator' |
5 | import { ServerConfigManager } from '@server/lib/server-config-manager' | 5 | import { ServerConfigManager } from '@server/lib/server-config-manager' |
6 | import { UserRight } from '../../../shared' | 6 | import { About, CustomConfig, UserRight } from '@shared/models' |
7 | import { About } from '../../../shared/models/server/about.model' | ||
8 | import { CustomConfig } from '../../../shared/models/server/custom-config.model' | ||
9 | import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '../../helpers/audit-logger' | 7 | import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '../../helpers/audit-logger' |
10 | import { objectConverter } from '../../helpers/core-utils' | 8 | import { objectConverter } from '../../helpers/core-utils' |
11 | import { CONFIG, reloadConfig } from '../../initializers/config' | 9 | import { CONFIG, reloadConfig } from '../../initializers/config' |
@@ -169,6 +167,18 @@ function customConfig (): CustomConfig { | |||
169 | whitelisted: CONFIG.SERVICES.TWITTER.WHITELISTED | 167 | whitelisted: CONFIG.SERVICES.TWITTER.WHITELISTED |
170 | } | 168 | } |
171 | }, | 169 | }, |
170 | client: { | ||
171 | videos: { | ||
172 | miniature: { | ||
173 | preferAuthorDisplayName: CONFIG.CLIENT.VIDEOS.MINIATURE.PREFER_AUTHOR_DISPLAY_NAME | ||
174 | } | ||
175 | }, | ||
176 | menu: { | ||
177 | login: { | ||
178 | redirectOnSingleExternalAuth: CONFIG.CLIENT.MENU.LOGIN.REDIRECT_ON_SINGLE_EXTERNAL_AUTH | ||
179 | } | ||
180 | } | ||
181 | }, | ||
172 | cache: { | 182 | cache: { |
173 | previews: { | 183 | previews: { |
174 | size: CONFIG.CACHE.PREVIEWS.SIZE | 184 | size: CONFIG.CACHE.PREVIEWS.SIZE |
diff --git a/server/controllers/api/index.ts b/server/controllers/api/index.ts index 9949b378a..5f49336b1 100644 --- a/server/controllers/api/index.ts +++ b/server/controllers/api/index.ts | |||
@@ -6,6 +6,7 @@ import { badRequest } from '../../helpers/express-utils' | |||
6 | import { CONFIG } from '../../initializers/config' | 6 | import { CONFIG } from '../../initializers/config' |
7 | import { abuseRouter } from './abuse' | 7 | import { abuseRouter } from './abuse' |
8 | import { accountsRouter } from './accounts' | 8 | import { accountsRouter } from './accounts' |
9 | import { blocklistRouter } from './blocklist' | ||
9 | import { bulkRouter } from './bulk' | 10 | import { bulkRouter } from './bulk' |
10 | import { configRouter } from './config' | 11 | import { configRouter } from './config' |
11 | import { customPageRouter } from './custom-page' | 12 | import { customPageRouter } from './custom-page' |
@@ -49,6 +50,7 @@ apiRouter.use('/search', searchRouter) | |||
49 | apiRouter.use('/overviews', overviewsRouter) | 50 | apiRouter.use('/overviews', overviewsRouter) |
50 | apiRouter.use('/plugins', pluginRouter) | 51 | apiRouter.use('/plugins', pluginRouter) |
51 | apiRouter.use('/custom-pages', customPageRouter) | 52 | apiRouter.use('/custom-pages', customPageRouter) |
53 | apiRouter.use('/blocklist', blocklistRouter) | ||
52 | apiRouter.use('/ping', pong) | 54 | apiRouter.use('/ping', pong) |
53 | apiRouter.use('/*', badRequest) | 55 | apiRouter.use('/*', badRequest) |
54 | 56 | ||
diff --git a/server/controllers/api/jobs.ts b/server/controllers/api/jobs.ts index 7001674bb..eebd195b0 100644 --- a/server/controllers/api/jobs.ts +++ b/server/controllers/api/jobs.ts | |||
@@ -1,7 +1,5 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { ResultList } from '../../../shared' | 2 | import { Job, JobState, JobType, ResultList, UserRight } from '@shared/models' |
3 | import { Job, JobState, JobType } from '../../../shared/models' | ||
4 | import { UserRight } from '../../../shared/models/users' | ||
5 | import { isArray } from '../../helpers/custom-validators/misc' | 3 | import { isArray } from '../../helpers/custom-validators/misc' |
6 | import { JobQueue } from '../../lib/job-queue' | 4 | import { JobQueue } from '../../lib/job-queue' |
7 | import { | 5 | import { |
diff --git a/server/controllers/api/oauth-clients.ts b/server/controllers/api/oauth-clients.ts index 4990fb0df..2d847bdc1 100644 --- a/server/controllers/api/oauth-clients.ts +++ b/server/controllers/api/oauth-clients.ts | |||
@@ -1,10 +1,9 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { OAuthClientLocal } from '../../../shared' | 2 | import { OAuthClientModel } from '@server/models/oauth/oauth-client' |
3 | import { HttpStatusCode } from '../../../shared/models/http/http-error-codes' | 3 | import { HttpStatusCode, OAuthClientLocal } from '@shared/models' |
4 | import { logger } from '../../helpers/logger' | 4 | import { logger } from '../../helpers/logger' |
5 | import { CONFIG } from '../../initializers/config' | 5 | import { CONFIG } from '../../initializers/config' |
6 | import { asyncMiddleware, openapiOperationDoc } from '../../middlewares' | 6 | import { asyncMiddleware, openapiOperationDoc } from '../../middlewares' |
7 | import { OAuthClientModel } from '../../models/oauth/oauth-client' | ||
8 | 7 | ||
9 | const oauthClientsRouter = express.Router() | 8 | const oauthClientsRouter = express.Router() |
10 | 9 | ||
diff --git a/server/controllers/api/plugins.ts b/server/controllers/api/plugins.ts index 2de7fe41f..de9e055dc 100644 --- a/server/controllers/api/plugins.ts +++ b/server/controllers/api/plugins.ts | |||
@@ -144,8 +144,13 @@ async function installPlugin (req: express.Request, res: express.Response) { | |||
144 | 144 | ||
145 | const fromDisk = !!body.path | 145 | const fromDisk = !!body.path |
146 | const toInstall = body.npmName || body.path | 146 | const toInstall = body.npmName || body.path |
147 | |||
148 | const pluginVersion = body.pluginVersion && body.npmName | ||
149 | ? body.pluginVersion | ||
150 | : undefined | ||
151 | |||
147 | try { | 152 | try { |
148 | const plugin = await PluginManager.Instance.install(toInstall, undefined, fromDisk) | 153 | const plugin = await PluginManager.Instance.install(toInstall, pluginVersion, fromDisk) |
149 | 154 | ||
150 | return res.json(plugin.toFormattedJSON()) | 155 | return res.json(plugin.toFormattedJSON()) |
151 | } catch (err) { | 156 | } catch (err) { |
diff --git a/server/controllers/api/server/stats.ts b/server/controllers/api/server/stats.ts index d661144ca..2ab398f4d 100644 --- a/server/controllers/api/server/stats.ts +++ b/server/controllers/api/server/stats.ts | |||
@@ -3,6 +3,7 @@ import { StatsManager } from '@server/lib/stat-manager' | |||
3 | import { ROUTE_CACHE_LIFETIME } from '../../../initializers/constants' | 3 | import { ROUTE_CACHE_LIFETIME } from '../../../initializers/constants' |
4 | import { asyncMiddleware } from '../../../middlewares' | 4 | import { asyncMiddleware } from '../../../middlewares' |
5 | import { cacheRoute } from '../../../middlewares/cache/cache' | 5 | import { cacheRoute } from '../../../middlewares/cache/cache' |
6 | import { Hooks } from '@server/lib/plugins/hooks' | ||
6 | 7 | ||
7 | const statsRouter = express.Router() | 8 | const statsRouter = express.Router() |
8 | 9 | ||
@@ -12,7 +13,8 @@ statsRouter.get('/stats', | |||
12 | ) | 13 | ) |
13 | 14 | ||
14 | async function getStats (_req: express.Request, res: express.Response) { | 15 | async function getStats (_req: express.Request, res: express.Response) { |
15 | const data = await StatsManager.Instance.getStats() | 16 | let data = await StatsManager.Instance.getStats() |
17 | data = await Hooks.wrapObject(data, 'filter:api.server.stats.get.result') | ||
16 | 18 | ||
17 | return res.json(data) | 19 | return res.json(data) |
18 | } | 20 | } |
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index 11d3525e4..7efc3a137 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts | |||
@@ -4,10 +4,7 @@ import { tokensRouter } from '@server/controllers/api/users/token' | |||
4 | import { Hooks } from '@server/lib/plugins/hooks' | 4 | import { Hooks } from '@server/lib/plugins/hooks' |
5 | import { OAuthTokenModel } from '@server/models/oauth/oauth-token' | 5 | import { OAuthTokenModel } from '@server/models/oauth/oauth-token' |
6 | import { MUser, MUserAccountDefault } from '@server/types/models' | 6 | import { MUser, MUserAccountDefault } from '@server/types/models' |
7 | import { UserCreate, UserCreateResult, UserRight, UserRole, UserUpdate } from '../../../../shared' | 7 | import { HttpStatusCode, UserAdminFlag, UserCreate, UserCreateResult, UserRegister, UserRight, UserRole, UserUpdate } from '@shared/models' |
8 | import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' | ||
9 | import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' | ||
10 | import { UserRegister } from '../../../../shared/models/users/user-register.model' | ||
11 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' | 8 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' |
12 | import { logger } from '../../../helpers/logger' | 9 | import { logger } from '../../../helpers/logger' |
13 | import { generateRandomString, getFormattedObjects } from '../../../helpers/utils' | 10 | import { generateRandomString, getFormattedObjects } from '../../../helpers/utils' |
@@ -183,6 +180,7 @@ async function createUser (req: express.Request, res: express.Response) { | |||
183 | password: body.password, | 180 | password: body.password, |
184 | email: body.email, | 181 | email: body.email, |
185 | nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY, | 182 | nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY, |
183 | p2pEnabled: CONFIG.DEFAULTS.P2P.WEBAPP.ENABLED, | ||
186 | autoPlayVideo: true, | 184 | autoPlayVideo: true, |
187 | role: body.role, | 185 | role: body.role, |
188 | videoQuota: body.videoQuota, | 186 | videoQuota: body.videoQuota, |
@@ -209,7 +207,7 @@ async function createUser (req: express.Request, res: express.Response) { | |||
209 | logger.info('Sending to user %s a create password email', body.username) | 207 | logger.info('Sending to user %s a create password email', body.username) |
210 | const verificationString = await Redis.Instance.setCreatePasswordVerificationString(user.id) | 208 | const verificationString = await Redis.Instance.setCreatePasswordVerificationString(user.id) |
211 | const url = WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString | 209 | const url = WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString |
212 | await Emailer.Instance.addPasswordCreateEmailJob(userToCreate.username, user.email, url) | 210 | Emailer.Instance.addPasswordCreateEmailJob(userToCreate.username, user.email, url) |
213 | } | 211 | } |
214 | 212 | ||
215 | Hooks.runAction('action:api.user.created', { body, user, account, videoChannel, req, res }) | 213 | Hooks.runAction('action:api.user.created', { body, user, account, videoChannel, req, res }) |
@@ -232,6 +230,7 @@ async function registerUser (req: express.Request, res: express.Response) { | |||
232 | password: body.password, | 230 | password: body.password, |
233 | email: body.email, | 231 | email: body.email, |
234 | nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY, | 232 | nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY, |
233 | p2pEnabled: CONFIG.DEFAULTS.P2P.WEBAPP.ENABLED, | ||
235 | autoPlayVideo: true, | 234 | autoPlayVideo: true, |
236 | role: UserRole.USER, | 235 | role: UserRole.USER, |
237 | videoQuota: CONFIG.USER.VIDEO_QUOTA, | 236 | videoQuota: CONFIG.USER.VIDEO_QUOTA, |
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 6bacdbbb6..878dd5a84 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -2,10 +2,8 @@ import 'multer' | |||
2 | import express from 'express' | 2 | import express from 'express' |
3 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '@server/helpers/audit-logger' | 3 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '@server/helpers/audit-logger' |
4 | import { Hooks } from '@server/lib/plugins/hooks' | 4 | import { Hooks } from '@server/lib/plugins/hooks' |
5 | import { AttributesOnly } from '@shared/core-utils' | 5 | import { ActorImageType, HttpStatusCode, UserUpdateMe, UserVideoQuota, UserVideoRate as FormattedUserVideoRate } from '@shared/models' |
6 | import { ActorImageType, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared' | 6 | import { AttributesOnly } from '@shared/typescript-utils' |
7 | import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' | ||
8 | import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model' | ||
9 | import { createReqFiles } from '../../../helpers/express-utils' | 7 | import { createReqFiles } from '../../../helpers/express-utils' |
10 | import { getFormattedObjects } from '../../../helpers/utils' | 8 | import { getFormattedObjects } from '../../../helpers/utils' |
11 | import { CONFIG } from '../../../initializers/config' | 9 | import { CONFIG } from '../../../initializers/config' |
@@ -197,7 +195,7 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
197 | const keysToUpdate: (keyof UserUpdateMe & keyof AttributesOnly<UserModel>)[] = [ | 195 | const keysToUpdate: (keyof UserUpdateMe & keyof AttributesOnly<UserModel>)[] = [ |
198 | 'password', | 196 | 'password', |
199 | 'nsfwPolicy', | 197 | 'nsfwPolicy', |
200 | 'webTorrentEnabled', | 198 | 'p2pEnabled', |
201 | 'autoPlayVideo', | 199 | 'autoPlayVideo', |
202 | 'autoPlayNextVideo', | 200 | 'autoPlayNextVideo', |
203 | 'autoPlayNextVideoPlaylist', | 201 | 'autoPlayNextVideoPlaylist', |
@@ -213,6 +211,12 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
213 | if (body[key] !== undefined) user.set(key, body[key]) | 211 | if (body[key] !== undefined) user.set(key, body[key]) |
214 | } | 212 | } |
215 | 213 | ||
214 | if (body.p2pEnabled !== undefined) { | ||
215 | user.set('p2pEnabled', body.p2pEnabled) | ||
216 | } else if (body.webTorrentEnabled !== undefined) { // FIXME: deprecated in 4.1 | ||
217 | user.set('p2pEnabled', body.webTorrentEnabled) | ||
218 | } | ||
219 | |||
216 | if (body.email !== undefined) { | 220 | if (body.email !== undefined) { |
217 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { | 221 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { |
218 | user.pendingEmail = body.email | 222 | user.pendingEmail = body.email |
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts index 6799ca8c5..fb1f68635 100644 --- a/server/controllers/api/users/my-subscriptions.ts +++ b/server/controllers/api/users/my-subscriptions.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import 'multer' | 1 | import 'multer' |
2 | import express from 'express' | 2 | import express from 'express' |
3 | import { handlesToNameAndHost } from '@server/helpers/actors' | ||
3 | import { pickCommonVideoQuery } from '@server/helpers/query' | 4 | import { pickCommonVideoQuery } from '@server/helpers/query' |
4 | import { sendUndoFollow } from '@server/lib/activitypub/send' | 5 | import { sendUndoFollow } from '@server/lib/activitypub/send' |
5 | import { guessAdditionalAttributesFromQuery } from '@server/models/video/formatter/video-format-utils' | 6 | import { guessAdditionalAttributesFromQuery } from '@server/models/video/formatter/video-format-utils' |
@@ -7,7 +8,6 @@ import { VideoChannelModel } from '@server/models/video/video-channel' | |||
7 | import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' | 8 | import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' |
8 | import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' | 9 | import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' |
9 | import { getFormattedObjects } from '../../../helpers/utils' | 10 | import { getFormattedObjects } from '../../../helpers/utils' |
10 | import { WEBSERVER } from '../../../initializers/constants' | ||
11 | import { sequelizeTypescript } from '../../../initializers/database' | 11 | import { sequelizeTypescript } from '../../../initializers/database' |
12 | import { JobQueue } from '../../../lib/job-queue' | 12 | import { JobQueue } from '../../../lib/job-queue' |
13 | import { | 13 | import { |
@@ -89,28 +89,23 @@ async function areSubscriptionsExist (req: express.Request, res: express.Respons | |||
89 | const uris = req.query.uris as string[] | 89 | const uris = req.query.uris as string[] |
90 | const user = res.locals.oauth.token.User | 90 | const user = res.locals.oauth.token.User |
91 | 91 | ||
92 | const handles = uris.map(u => { | 92 | const sanitizedHandles = handlesToNameAndHost(uris) |
93 | let [ name, host ] = u.split('@') | ||
94 | if (host === WEBSERVER.HOST) host = null | ||
95 | 93 | ||
96 | return { name, host, uri: u } | 94 | const results = await ActorFollowModel.listSubscriptionsOf(user.Account.Actor.id, sanitizedHandles) |
97 | }) | ||
98 | |||
99 | const results = await ActorFollowModel.listSubscriptionsOf(user.Account.Actor.id, handles) | ||
100 | 95 | ||
101 | const existObject: { [id: string ]: boolean } = {} | 96 | const existObject: { [id: string ]: boolean } = {} |
102 | for (const handle of handles) { | 97 | for (const sanitizedHandle of sanitizedHandles) { |
103 | const obj = results.find(r => { | 98 | const obj = results.find(r => { |
104 | const server = r.ActorFollowing.Server | 99 | const server = r.ActorFollowing.Server |
105 | 100 | ||
106 | return r.ActorFollowing.preferredUsername === handle.name && | 101 | return r.ActorFollowing.preferredUsername === sanitizedHandle.name && |
107 | ( | 102 | ( |
108 | (!server && !handle.host) || | 103 | (!server && !sanitizedHandle.host) || |
109 | (server.host === handle.host) | 104 | (server.host === sanitizedHandle.host) |
110 | ) | 105 | ) |
111 | }) | 106 | }) |
112 | 107 | ||
113 | existObject[handle.uri] = obj !== undefined | 108 | existObject[sanitizedHandle.handle] = obj !== undefined |
114 | } | 109 | } |
115 | 110 | ||
116 | return res.json(existObject) | 111 | return res.json(existObject) |
diff --git a/server/controllers/api/users/token.ts b/server/controllers/api/users/token.ts index 1d4004ce0..258b50fe9 100644 --- a/server/controllers/api/users/token.ts +++ b/server/controllers/api/users/token.ts | |||
@@ -1,13 +1,13 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import RateLimit from 'express-rate-limit' | 2 | import RateLimit from 'express-rate-limit' |
3 | import { logger } from '@server/helpers/logger' | 3 | import { logger } from '@server/helpers/logger' |
4 | import { buildUUID } from '@server/helpers/uuid' | ||
5 | import { CONFIG } from '@server/initializers/config' | 4 | import { CONFIG } from '@server/initializers/config' |
6 | import { getAuthNameFromRefreshGrant, getBypassFromExternalAuth, getBypassFromPasswordGrant } from '@server/lib/auth/external-auth' | 5 | import { getAuthNameFromRefreshGrant, getBypassFromExternalAuth, getBypassFromPasswordGrant } from '@server/lib/auth/external-auth' |
7 | import { handleOAuthToken } from '@server/lib/auth/oauth' | 6 | import { handleOAuthToken } from '@server/lib/auth/oauth' |
8 | import { BypassLogin, revokeToken } from '@server/lib/auth/oauth-model' | 7 | import { BypassLogin, revokeToken } from '@server/lib/auth/oauth-model' |
9 | import { Hooks } from '@server/lib/plugins/hooks' | 8 | import { Hooks } from '@server/lib/plugins/hooks' |
10 | import { asyncMiddleware, authenticate, openapiOperationDoc } from '@server/middlewares' | 9 | import { asyncMiddleware, authenticate, openapiOperationDoc } from '@server/middlewares' |
10 | import { buildUUID } from '@shared/extra-utils' | ||
11 | import { ScopedToken } from '@shared/models/users/user-scoped-token' | 11 | import { ScopedToken } from '@shared/models/users/user-scoped-token' |
12 | 12 | ||
13 | const tokensRouter = express.Router() | 13 | const tokensRouter = express.Router() |
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index d1a1e6473..e65550a22 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -5,8 +5,7 @@ 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 { guessAdditionalAttributesFromQuery } from '@server/models/video/formatter/video-format-utils' | 6 | import { guessAdditionalAttributesFromQuery } from '@server/models/video/formatter/video-format-utils' |
7 | import { MChannelBannerAccountDefault } from '@server/types/models' | 7 | import { MChannelBannerAccountDefault } from '@server/types/models' |
8 | import { ActorImageType, VideoChannelCreate, VideoChannelUpdate } from '../../../shared' | 8 | import { ActorImageType, HttpStatusCode, VideoChannelCreate, VideoChannelUpdate } from '@shared/models' |
9 | import { HttpStatusCode } from '../../../shared/models/http/http-error-codes' | ||
10 | import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' | 9 | import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' |
11 | import { resetSequelizeInstance } from '../../helpers/database-utils' | 10 | import { resetSequelizeInstance } from '../../helpers/database-utils' |
12 | import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | 11 | import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' |
@@ -24,6 +23,7 @@ import { | |||
24 | asyncRetryTransactionMiddleware, | 23 | asyncRetryTransactionMiddleware, |
25 | authenticate, | 24 | authenticate, |
26 | commonVideosFiltersValidator, | 25 | commonVideosFiltersValidator, |
26 | ensureCanManageChannel, | ||
27 | optionalAuthenticate, | 27 | optionalAuthenticate, |
28 | paginationValidator, | 28 | paginationValidator, |
29 | setDefaultPagination, | 29 | setDefaultPagination, |
@@ -36,7 +36,7 @@ import { | |||
36 | videoPlaylistsSortValidator | 36 | videoPlaylistsSortValidator |
37 | } from '../../middlewares' | 37 | } from '../../middlewares' |
38 | import { | 38 | import { |
39 | ensureAuthUserOwnsChannelValidator, | 39 | ensureIsLocalChannel, |
40 | videoChannelsFollowersSortValidator, | 40 | videoChannelsFollowersSortValidator, |
41 | videoChannelsListValidator, | 41 | videoChannelsListValidator, |
42 | videoChannelsNameWithHostValidator, | 42 | videoChannelsNameWithHostValidator, |
@@ -74,7 +74,8 @@ videoChannelRouter.post('/:nameWithHost/avatar/pick', | |||
74 | authenticate, | 74 | authenticate, |
75 | reqAvatarFile, | 75 | reqAvatarFile, |
76 | asyncMiddleware(videoChannelsNameWithHostValidator), | 76 | asyncMiddleware(videoChannelsNameWithHostValidator), |
77 | ensureAuthUserOwnsChannelValidator, | 77 | ensureIsLocalChannel, |
78 | ensureCanManageChannel, | ||
78 | updateAvatarValidator, | 79 | updateAvatarValidator, |
79 | asyncMiddleware(updateVideoChannelAvatar) | 80 | asyncMiddleware(updateVideoChannelAvatar) |
80 | ) | 81 | ) |
@@ -83,7 +84,8 @@ videoChannelRouter.post('/:nameWithHost/banner/pick', | |||
83 | authenticate, | 84 | authenticate, |
84 | reqBannerFile, | 85 | reqBannerFile, |
85 | asyncMiddleware(videoChannelsNameWithHostValidator), | 86 | asyncMiddleware(videoChannelsNameWithHostValidator), |
86 | ensureAuthUserOwnsChannelValidator, | 87 | ensureIsLocalChannel, |
88 | ensureCanManageChannel, | ||
87 | updateBannerValidator, | 89 | updateBannerValidator, |
88 | asyncMiddleware(updateVideoChannelBanner) | 90 | asyncMiddleware(updateVideoChannelBanner) |
89 | ) | 91 | ) |
@@ -91,27 +93,33 @@ videoChannelRouter.post('/:nameWithHost/banner/pick', | |||
91 | videoChannelRouter.delete('/:nameWithHost/avatar', | 93 | videoChannelRouter.delete('/:nameWithHost/avatar', |
92 | authenticate, | 94 | authenticate, |
93 | asyncMiddleware(videoChannelsNameWithHostValidator), | 95 | asyncMiddleware(videoChannelsNameWithHostValidator), |
94 | ensureAuthUserOwnsChannelValidator, | 96 | ensureIsLocalChannel, |
97 | ensureCanManageChannel, | ||
95 | asyncMiddleware(deleteVideoChannelAvatar) | 98 | asyncMiddleware(deleteVideoChannelAvatar) |
96 | ) | 99 | ) |
97 | 100 | ||
98 | videoChannelRouter.delete('/:nameWithHost/banner', | 101 | videoChannelRouter.delete('/:nameWithHost/banner', |
99 | authenticate, | 102 | authenticate, |
100 | asyncMiddleware(videoChannelsNameWithHostValidator), | 103 | asyncMiddleware(videoChannelsNameWithHostValidator), |
101 | ensureAuthUserOwnsChannelValidator, | 104 | ensureIsLocalChannel, |
105 | ensureCanManageChannel, | ||
102 | asyncMiddleware(deleteVideoChannelBanner) | 106 | asyncMiddleware(deleteVideoChannelBanner) |
103 | ) | 107 | ) |
104 | 108 | ||
105 | videoChannelRouter.put('/:nameWithHost', | 109 | videoChannelRouter.put('/:nameWithHost', |
106 | authenticate, | 110 | authenticate, |
107 | asyncMiddleware(videoChannelsNameWithHostValidator), | 111 | asyncMiddleware(videoChannelsNameWithHostValidator), |
108 | ensureAuthUserOwnsChannelValidator, | 112 | ensureIsLocalChannel, |
113 | ensureCanManageChannel, | ||
109 | videoChannelsUpdateValidator, | 114 | videoChannelsUpdateValidator, |
110 | asyncRetryTransactionMiddleware(updateVideoChannel) | 115 | asyncRetryTransactionMiddleware(updateVideoChannel) |
111 | ) | 116 | ) |
112 | 117 | ||
113 | videoChannelRouter.delete('/:nameWithHost', | 118 | videoChannelRouter.delete('/:nameWithHost', |
114 | authenticate, | 119 | authenticate, |
120 | asyncMiddleware(videoChannelsNameWithHostValidator), | ||
121 | ensureIsLocalChannel, | ||
122 | ensureCanManageChannel, | ||
115 | asyncMiddleware(videoChannelsRemoveValidator), | 123 | asyncMiddleware(videoChannelsRemoveValidator), |
116 | asyncRetryTransactionMiddleware(removeVideoChannel) | 124 | asyncRetryTransactionMiddleware(removeVideoChannel) |
117 | ) | 125 | ) |
@@ -145,7 +153,7 @@ videoChannelRouter.get('/:nameWithHost/videos', | |||
145 | videoChannelRouter.get('/:nameWithHost/followers', | 153 | videoChannelRouter.get('/:nameWithHost/followers', |
146 | authenticate, | 154 | authenticate, |
147 | asyncMiddleware(videoChannelsNameWithHostValidator), | 155 | asyncMiddleware(videoChannelsNameWithHostValidator), |
148 | ensureAuthUserOwnsChannelValidator, | 156 | ensureCanManageChannel, |
149 | paginationValidator, | 157 | paginationValidator, |
150 | videoChannelsFollowersSortValidator, | 158 | videoChannelsFollowersSortValidator, |
151 | setDefaultSort, | 159 | setDefaultSort, |
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts index 8b7a76718..795e14e73 100644 --- a/server/controllers/api/video-playlist.ts +++ b/server/controllers/api/video-playlist.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { join } from 'path' | 2 | import { join } from 'path' |
3 | import { uuidToShort } from '@server/helpers/uuid' | ||
4 | import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists' | 3 | import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists' |
5 | import { Hooks } from '@server/lib/plugins/hooks' | 4 | import { Hooks } from '@server/lib/plugins/hooks' |
6 | import { getServerActor } from '@server/models/application/application' | 5 | import { getServerActor } from '@server/models/application/application' |
7 | import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models' | 6 | import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models' |
7 | import { uuidToShort } from '@shared/extra-utils' | ||
8 | import { VideoPlaylistCreateResult, VideoPlaylistElementCreateResult } from '@shared/models' | 8 | import { VideoPlaylistCreateResult, VideoPlaylistElementCreateResult } from '@shared/models' |
9 | import { HttpStatusCode } from '../../../shared/models/http/http-error-codes' | 9 | import { HttpStatusCode } from '../../../shared/models/http/http-error-codes' |
10 | import { VideoPlaylistCreate } from '../../../shared/models/videos/playlist/video-playlist-create.model' | 10 | import { VideoPlaylistCreate } from '../../../shared/models/videos/playlist/video-playlist-create.model' |
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts index de65c74f1..4103bb063 100644 --- a/server/controllers/api/videos/blacklist.ts +++ b/server/controllers/api/videos/blacklist.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { blacklistVideo, unblacklistVideo } from '@server/lib/video-blacklist' | 2 | import { blacklistVideo, unblacklistVideo } from '@server/lib/video-blacklist' |
3 | import { UserRight, VideoBlacklistCreate } from '../../../../shared' | 3 | import { HttpStatusCode, UserRight, VideoBlacklistCreate } from '@shared/models' |
4 | import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' | ||
5 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
6 | import { getFormattedObjects } from '../../../helpers/utils' | 5 | import { getFormattedObjects } from '../../../helpers/utils' |
7 | import { sequelizeTypescript } from '../../../initializers/database' | 6 | import { sequelizeTypescript } from '../../../initializers/database' |
diff --git a/server/controllers/api/videos/captions.ts b/server/controllers/api/videos/captions.ts index aa7259ee9..2a9a9d233 100644 --- a/server/controllers/api/videos/captions.ts +++ b/server/controllers/api/videos/captions.ts | |||
@@ -12,6 +12,7 @@ import { federateVideoIfNeeded } from '../../../lib/activitypub/videos' | |||
12 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares' | 12 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares' |
13 | import { addVideoCaptionValidator, deleteVideoCaptionValidator, listVideoCaptionsValidator } from '../../../middlewares/validators' | 13 | import { addVideoCaptionValidator, deleteVideoCaptionValidator, listVideoCaptionsValidator } from '../../../middlewares/validators' |
14 | import { VideoCaptionModel } from '../../../models/video/video-caption' | 14 | import { VideoCaptionModel } from '../../../models/video/video-caption' |
15 | import { Hooks } from '@server/lib/plugins/hooks' | ||
15 | 16 | ||
16 | const reqVideoCaptionAdd = createReqFiles( | 17 | const reqVideoCaptionAdd = createReqFiles( |
17 | [ 'captionfile' ], | 18 | [ 'captionfile' ], |
@@ -75,6 +76,8 @@ async function addVideoCaption (req: express.Request, res: express.Response) { | |||
75 | await federateVideoIfNeeded(video, false, t) | 76 | await federateVideoIfNeeded(video, false, t) |
76 | }) | 77 | }) |
77 | 78 | ||
79 | Hooks.runAction('action:api.video-caption.created', { caption: videoCaption, req, res }) | ||
80 | |||
78 | return res.status(HttpStatusCode.NO_CONTENT_204).end() | 81 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
79 | } | 82 | } |
80 | 83 | ||
@@ -91,5 +94,7 @@ async function deleteVideoCaption (req: express.Request, res: express.Response) | |||
91 | 94 | ||
92 | logger.info('Video caption %s of video %s deleted.', videoCaption.language, video.uuid) | 95 | logger.info('Video caption %s of video %s deleted.', videoCaption.language, video.uuid) |
93 | 96 | ||
97 | Hooks.runAction('action:api.video-caption.deleted', { caption: videoCaption, req, res }) | ||
98 | |||
94 | return res.type('json').status(HttpStatusCode.NO_CONTENT_204).end() | 99 | return res.type('json').status(HttpStatusCode.NO_CONTENT_204).end() |
95 | } | 100 | } |
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index eddb9b32d..08d69827b 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts | |||
@@ -4,6 +4,7 @@ import { decode } from 'magnet-uri' | |||
4 | import parseTorrent, { Instance } from 'parse-torrent' | 4 | import parseTorrent, { Instance } from 'parse-torrent' |
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import { isVideoFileExtnameValid } from '@server/helpers/custom-validators/videos' | 6 | import { isVideoFileExtnameValid } from '@server/helpers/custom-validators/videos' |
7 | import { Hooks } from '@server/lib/plugins/hooks' | ||
7 | import { ServerConfigManager } from '@server/lib/server-config-manager' | 8 | import { ServerConfigManager } from '@server/lib/server-config-manager' |
8 | import { setVideoTags } from '@server/lib/video' | 9 | import { setVideoTags } from '@server/lib/video' |
9 | import { FilteredModelAttributes } from '@server/types' | 10 | import { FilteredModelAttributes } from '@server/types' |
@@ -18,15 +19,14 @@ import { | |||
18 | MVideoWithBlacklistLight | 19 | MVideoWithBlacklistLight |
19 | } from '@server/types/models' | 20 | } from '@server/types/models' |
20 | import { MVideoImportFormattable } from '@server/types/models/video/video-import' | 21 | import { MVideoImportFormattable } from '@server/types/models/video/video-import' |
21 | import { ServerErrorCode, VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared' | 22 | import { ServerErrorCode, ThumbnailType, VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '@shared/models' |
22 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | ||
23 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' | 23 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' |
24 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' | 24 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' |
25 | import { isArray } from '../../../helpers/custom-validators/misc' | 25 | import { isArray } from '../../../helpers/custom-validators/misc' |
26 | import { cleanUpReqFiles, createReqFiles } from '../../../helpers/express-utils' | 26 | import { cleanUpReqFiles, createReqFiles } from '../../../helpers/express-utils' |
27 | import { logger } from '../../../helpers/logger' | 27 | import { logger } from '../../../helpers/logger' |
28 | import { getSecureTorrentName } from '../../../helpers/utils' | 28 | import { getSecureTorrentName } from '../../../helpers/utils' |
29 | import { YoutubeDLWrapper, YoutubeDLInfo } from '../../../helpers/youtube-dl' | 29 | import { YoutubeDLInfo, YoutubeDLWrapper } from '../../../helpers/youtube-dl' |
30 | import { CONFIG } from '../../../initializers/config' | 30 | import { CONFIG } from '../../../initializers/config' |
31 | import { MIMETYPES } from '../../../initializers/constants' | 31 | import { MIMETYPES } from '../../../initializers/constants' |
32 | import { sequelizeTypescript } from '../../../initializers/database' | 32 | import { sequelizeTypescript } from '../../../initializers/database' |
@@ -94,7 +94,7 @@ async function addTorrentImport (req: express.Request, res: express.Response, to | |||
94 | videoName = result.name | 94 | videoName = result.name |
95 | } | 95 | } |
96 | 96 | ||
97 | const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) | 97 | const video = await buildVideo(res.locals.videoChannel.id, body, { name: videoName }) |
98 | 98 | ||
99 | const thumbnailModel = await processThumbnail(req, video) | 99 | const thumbnailModel = await processThumbnail(req, video) |
100 | const previewModel = await processPreview(req, video) | 100 | const previewModel = await processPreview(req, video) |
@@ -151,7 +151,7 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
151 | }) | 151 | }) |
152 | } | 152 | } |
153 | 153 | ||
154 | const video = buildVideo(res.locals.videoChannel.id, body, youtubeDLInfo) | 154 | const video = await buildVideo(res.locals.videoChannel.id, body, youtubeDLInfo) |
155 | 155 | ||
156 | // Process video thumbnail from request.files | 156 | // Process video thumbnail from request.files |
157 | let thumbnailModel = await processThumbnail(req, video) | 157 | let thumbnailModel = await processThumbnail(req, video) |
@@ -210,15 +210,15 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
210 | return res.json(videoImport.toFormattedJSON()).end() | 210 | return res.json(videoImport.toFormattedJSON()).end() |
211 | } | 211 | } |
212 | 212 | ||
213 | function buildVideo (channelId: number, body: VideoImportCreate, importData: YoutubeDLInfo): MVideoThumbnail { | 213 | async function buildVideo (channelId: number, body: VideoImportCreate, importData: YoutubeDLInfo): Promise<MVideoThumbnail> { |
214 | const videoData = { | 214 | let videoData = { |
215 | name: body.name || importData.name || 'Unknown name', | 215 | name: body.name || importData.name || 'Unknown name', |
216 | remote: false, | 216 | remote: false, |
217 | category: body.category || importData.category, | 217 | category: body.category || importData.category, |
218 | licence: body.licence || importData.licence, | 218 | licence: body.licence ?? importData.licence ?? CONFIG.DEFAULTS.PUBLISH.LICENCE, |
219 | language: body.language || importData.language, | 219 | language: body.language || importData.language, |
220 | commentsEnabled: body.commentsEnabled !== false, // If the value is not "false", the default is "true" | 220 | commentsEnabled: body.commentsEnabled ?? CONFIG.DEFAULTS.PUBLISH.COMMENTS_ENABLED, |
221 | downloadEnabled: body.downloadEnabled !== false, | 221 | downloadEnabled: body.downloadEnabled ?? CONFIG.DEFAULTS.PUBLISH.DOWNLOAD_ENABLED, |
222 | waitTranscoding: body.waitTranscoding || false, | 222 | waitTranscoding: body.waitTranscoding || false, |
223 | state: VideoState.TO_IMPORT, | 223 | state: VideoState.TO_IMPORT, |
224 | nsfw: body.nsfw || importData.nsfw || false, | 224 | nsfw: body.nsfw || importData.nsfw || false, |
@@ -231,6 +231,14 @@ function buildVideo (channelId: number, body: VideoImportCreate, importData: You | |||
231 | ? new Date(body.originallyPublishedAt) | 231 | ? new Date(body.originallyPublishedAt) |
232 | : importData.originallyPublishedAt | 232 | : importData.originallyPublishedAt |
233 | } | 233 | } |
234 | |||
235 | videoData = await Hooks.wrapObject( | ||
236 | videoData, | ||
237 | body.targetUrl | ||
238 | ? 'filter:api.video.import-url.video-attribute.result' | ||
239 | : 'filter:api.video.import-torrent.video-attribute.result' | ||
240 | ) | ||
241 | |||
234 | const video = new VideoModel(videoData) | 242 | const video = new VideoModel(videoData) |
235 | video.url = getLocalVideoActivityPubUrl(video) | 243 | video.url = getLocalVideoActivityPubUrl(video) |
236 | 244 | ||
diff --git a/server/controllers/api/videos/live.ts b/server/controllers/api/videos/live.ts index e29615ff5..8b8cacff9 100644 --- a/server/controllers/api/videos/live.ts +++ b/server/controllers/api/videos/live.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { createReqFiles } from '@server/helpers/express-utils' | 2 | import { createReqFiles } from '@server/helpers/express-utils' |
3 | import { buildUUID, uuidToShort } from '@server/helpers/uuid' | ||
4 | import { CONFIG } from '@server/initializers/config' | 3 | import { CONFIG } from '@server/initializers/config' |
5 | import { ASSETS_PATH, MIMETYPES } from '@server/initializers/constants' | 4 | import { ASSETS_PATH, MIMETYPES } from '@server/initializers/constants' |
6 | import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' | 5 | import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' |
@@ -10,8 +9,8 @@ import { buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } fro | |||
10 | import { videoLiveAddValidator, videoLiveGetValidator, videoLiveUpdateValidator } from '@server/middlewares/validators/videos/video-live' | 9 | import { videoLiveAddValidator, videoLiveGetValidator, videoLiveUpdateValidator } from '@server/middlewares/validators/videos/video-live' |
11 | import { VideoLiveModel } from '@server/models/video/video-live' | 10 | import { VideoLiveModel } from '@server/models/video/video-live' |
12 | import { MVideoDetails, MVideoFullLight } from '@server/types/models' | 11 | import { MVideoDetails, MVideoFullLight } from '@server/types/models' |
13 | import { LiveVideoCreate, LiveVideoUpdate, VideoState } from '../../../../shared' | 12 | import { buildUUID, uuidToShort } from '@shared/extra-utils' |
14 | import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' | 13 | import { HttpStatusCode, LiveVideoCreate, LiveVideoUpdate, VideoState } from '@shared/models' |
15 | import { logger } from '../../../helpers/logger' | 14 | import { logger } from '../../../helpers/logger' |
16 | import { sequelizeTypescript } from '../../../initializers/database' | 15 | import { sequelizeTypescript } from '../../../initializers/database' |
17 | import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail' | 16 | import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail' |
@@ -83,7 +82,9 @@ async function addLiveVideo (req: express.Request, res: express.Response) { | |||
83 | const videoInfo: LiveVideoCreate = req.body | 82 | const videoInfo: LiveVideoCreate = req.body |
84 | 83 | ||
85 | // Prepare data so we don't block the transaction | 84 | // Prepare data so we don't block the transaction |
86 | const videoData = buildLocalVideoFromReq(videoInfo, res.locals.videoChannel.id) | 85 | let videoData = buildLocalVideoFromReq(videoInfo, res.locals.videoChannel.id) |
86 | videoData = await Hooks.wrapObject(videoData, 'filter:api.video.live.video-attribute.result') | ||
87 | |||
87 | videoData.isLive = true | 88 | videoData.isLive = true |
88 | videoData.state = VideoState.WAITING_FOR_LIVE | 89 | videoData.state = VideoState.WAITING_FOR_LIVE |
89 | videoData.duration = 0 | 90 | videoData.duration = 0 |
diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts index c9cc16644..6b26a8eee 100644 --- a/server/controllers/api/videos/rate.ts +++ b/server/controllers/api/videos/rate.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { UserVideoRateUpdate } from '../../../../shared' | 2 | import { HttpStatusCode, UserVideoRateUpdate } from '@shared/models' |
3 | import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' | ||
4 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
5 | import { VIDEO_RATE_TYPES } from '../../../initializers/constants' | 4 | import { VIDEO_RATE_TYPES } from '../../../initializers/constants' |
6 | import { sequelizeTypescript } from '../../../initializers/database' | 5 | import { sequelizeTypescript } from '../../../initializers/database' |
diff --git a/server/controllers/api/videos/update.ts b/server/controllers/api/videos/update.ts index 3fcff3e86..f600847d4 100644 --- a/server/controllers/api/videos/update.ts +++ b/server/controllers/api/videos/update.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { Transaction } from 'sequelize/types' | 2 | import { Transaction } from 'sequelize/types' |
3 | import { updateTorrentMetadata } from '@server/helpers/webtorrent' | ||
3 | import { changeVideoChannelShare } from '@server/lib/activitypub/share' | 4 | import { changeVideoChannelShare } from '@server/lib/activitypub/share' |
4 | import { buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' | 5 | import { buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' |
5 | import { openapiOperationDoc } from '@server/middlewares/doc' | 6 | import { openapiOperationDoc } from '@server/middlewares/doc' |
6 | import { FilteredModelAttributes } from '@server/types' | 7 | import { FilteredModelAttributes } from '@server/types' |
7 | import { MVideoFullLight } from '@server/types/models' | 8 | import { MVideoFullLight } from '@server/types/models' |
8 | import { VideoUpdate } from '../../../../shared' | 9 | import { HttpStatusCode, VideoUpdate } from '@shared/models' |
9 | import { HttpStatusCode } from '../../../../shared/models' | ||
10 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | 10 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' |
11 | import { resetSequelizeInstance } from '../../../helpers/database-utils' | 11 | import { resetSequelizeInstance } from '../../../helpers/database-utils' |
12 | import { createReqFiles } from '../../../helpers/express-utils' | 12 | import { createReqFiles } from '../../../helpers/express-utils' |
@@ -68,7 +68,7 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
68 | }) | 68 | }) |
69 | 69 | ||
70 | try { | 70 | try { |
71 | const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => { | 71 | const { videoInstanceUpdated, isNewVideo } = await sequelizeTypescript.transaction(async t => { |
72 | // Refresh video since thumbnails to prevent concurrent updates | 72 | // Refresh video since thumbnails to prevent concurrent updates |
73 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoFromReq.id, t) | 73 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoFromReq.id, t) |
74 | 74 | ||
@@ -137,8 +137,6 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
137 | transaction: t | 137 | transaction: t |
138 | }) | 138 | }) |
139 | 139 | ||
140 | await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) | ||
141 | |||
142 | auditLogger.update( | 140 | auditLogger.update( |
143 | getAuditIdFromRes(res), | 141 | getAuditIdFromRes(res), |
144 | new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), | 142 | new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), |
@@ -146,12 +144,14 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
146 | ) | 144 | ) |
147 | logger.info('Video with name %s and uuid %s updated.', video.name, video.uuid, lTags(video.uuid)) | 145 | logger.info('Video with name %s and uuid %s updated.', video.name, video.uuid, lTags(video.uuid)) |
148 | 146 | ||
149 | return videoInstanceUpdated | 147 | return { videoInstanceUpdated, isNewVideo } |
150 | }) | 148 | }) |
151 | 149 | ||
152 | if (wasConfidentialVideo) { | 150 | if (videoInfoToUpdate.name) await updateTorrentsMetadata(videoInstanceUpdated) |
153 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoInstanceUpdated) | 151 | |
154 | } | 152 | await sequelizeTypescript.transaction(t => federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t)) |
153 | |||
154 | if (wasConfidentialVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoInstanceUpdated) | ||
155 | 155 | ||
156 | Hooks.runAction('action:api.video.updated', { video: videoInstanceUpdated, body: req.body, req, res }) | 156 | Hooks.runAction('action:api.video.updated', { video: videoInstanceUpdated, body: req.body, req, res }) |
157 | } catch (err) { | 157 | } catch (err) { |
@@ -199,3 +199,20 @@ function updateSchedule (videoInstance: MVideoFullLight, videoInfoToUpdate: Vide | |||
199 | return ScheduleVideoUpdateModel.deleteByVideoId(videoInstance.id, transaction) | 199 | return ScheduleVideoUpdateModel.deleteByVideoId(videoInstance.id, transaction) |
200 | } | 200 | } |
201 | } | 201 | } |
202 | |||
203 | async function updateTorrentsMetadata (video: MVideoFullLight) { | ||
204 | for (const file of (video.VideoFiles || [])) { | ||
205 | await updateTorrentMetadata(video, file) | ||
206 | |||
207 | await file.save() | ||
208 | } | ||
209 | |||
210 | const hls = video.getHLSPlaylist() | ||
211 | if (!hls) return | ||
212 | |||
213 | for (const file of (hls.VideoFiles || [])) { | ||
214 | await updateTorrentMetadata(hls, file) | ||
215 | |||
216 | await file.save() | ||
217 | } | ||
218 | } | ||
diff --git a/server/controllers/api/videos/upload.ts b/server/controllers/api/videos/upload.ts index 6773b500f..89787f20b 100644 --- a/server/controllers/api/videos/upload.ts +++ b/server/controllers/api/videos/upload.ts | |||
@@ -1,13 +1,12 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { move } from 'fs-extra' | 2 | import { move } from 'fs-extra' |
3 | import { basename } from 'path' | 3 | import { basename } from 'path' |
4 | import { getLowercaseExtension } from '@server/helpers/core-utils' | ||
5 | import { getResumableUploadPath } from '@server/helpers/upload' | 4 | import { getResumableUploadPath } from '@server/helpers/upload' |
6 | import { uuidToShort } from '@server/helpers/uuid' | ||
7 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | 5 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' |
8 | import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' | 6 | import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' |
9 | import { generateWebTorrentVideoFilename } from '@server/lib/paths' | 7 | import { generateWebTorrentVideoFilename } from '@server/lib/paths' |
10 | import { Redis } from '@server/lib/redis' | 8 | import { Redis } from '@server/lib/redis' |
9 | import { uploadx } from '@server/lib/uploadx' | ||
11 | import { | 10 | import { |
12 | addMoveToObjectStorageJob, | 11 | addMoveToObjectStorageJob, |
13 | addOptimizeOrMergeAudioJob, | 12 | addOptimizeOrMergeAudioJob, |
@@ -19,16 +18,16 @@ import { VideoPathManager } from '@server/lib/video-path-manager' | |||
19 | import { buildNextVideoState } from '@server/lib/video-state' | 18 | import { buildNextVideoState } from '@server/lib/video-state' |
20 | import { openapiOperationDoc } from '@server/middlewares/doc' | 19 | import { openapiOperationDoc } from '@server/middlewares/doc' |
21 | import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' | 20 | import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' |
22 | import { Uploadx } from '@uploadx/core' | 21 | import { getLowercaseExtension } from '@shared/core-utils' |
23 | import { VideoCreate, VideoState } from '../../../../shared' | 22 | import { isAudioFile, uuidToShort } from '@shared/extra-utils' |
24 | import { HttpStatusCode } from '../../../../shared/models' | 23 | import { HttpStatusCode, VideoCreate, VideoResolution, VideoState } from '@shared/models' |
25 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | 24 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' |
26 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 25 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
27 | import { createReqFiles } from '../../../helpers/express-utils' | 26 | import { createReqFiles } from '../../../helpers/express-utils' |
28 | import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' | 27 | import { ffprobePromise, getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' |
29 | import { logger, loggerTagsFactory } from '../../../helpers/logger' | 28 | import { logger, loggerTagsFactory } from '../../../helpers/logger' |
30 | import { CONFIG } from '../../../initializers/config' | 29 | import { CONFIG } from '../../../initializers/config' |
31 | import { DEFAULT_AUDIO_RESOLUTION, MIMETYPES } from '../../../initializers/constants' | 30 | import { MIMETYPES } from '../../../initializers/constants' |
32 | import { sequelizeTypescript } from '../../../initializers/database' | 31 | import { sequelizeTypescript } from '../../../initializers/database' |
33 | import { federateVideoIfNeeded } from '../../../lib/activitypub/videos' | 32 | import { federateVideoIfNeeded } from '../../../lib/activitypub/videos' |
34 | import { Notifier } from '../../../lib/notifier' | 33 | import { Notifier } from '../../../lib/notifier' |
@@ -41,8 +40,8 @@ import { | |||
41 | authenticate, | 40 | authenticate, |
42 | videosAddLegacyValidator, | 41 | videosAddLegacyValidator, |
43 | videosAddResumableInitValidator, | 42 | videosAddResumableInitValidator, |
44 | videosResumableUploadIdValidator, | 43 | videosAddResumableValidator, |
45 | videosAddResumableValidator | 44 | videosResumableUploadIdValidator |
46 | } from '../../../middlewares' | 45 | } from '../../../middlewares' |
47 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | 46 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' |
48 | import { VideoModel } from '../../../models/video/video' | 47 | import { VideoModel } from '../../../models/video/video' |
@@ -52,9 +51,6 @@ const lTags = loggerTagsFactory('api', 'video') | |||
52 | const auditLogger = auditLoggerFactory('videos') | 51 | const auditLogger = auditLoggerFactory('videos') |
53 | const uploadRouter = express.Router() | 52 | const uploadRouter = express.Router() |
54 | 53 | ||
55 | const uploadx = new Uploadx({ directory: getResumableUploadPath() }) | ||
56 | uploadx.getUserId = (_, res: express.Response) => res.locals.oauth?.token.user.id | ||
57 | |||
58 | const reqVideoFileAdd = createReqFiles( | 54 | const reqVideoFileAdd = createReqFiles( |
59 | [ 'videofile', 'thumbnailfile', 'previewfile' ], | 55 | [ 'videofile', 'thumbnailfile', 'previewfile' ], |
60 | Object.assign({}, MIMETYPES.VIDEO.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT), | 56 | Object.assign({}, MIMETYPES.VIDEO.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT), |
@@ -156,7 +152,8 @@ async function addVideo (options: { | |||
156 | const videoChannel = res.locals.videoChannel | 152 | const videoChannel = res.locals.videoChannel |
157 | const user = res.locals.oauth.token.User | 153 | const user = res.locals.oauth.token.User |
158 | 154 | ||
159 | const videoData = buildLocalVideoFromReq(videoInfo, videoChannel.id) | 155 | let videoData = buildLocalVideoFromReq(videoInfo, videoChannel.id) |
156 | videoData = await Hooks.wrapObject(videoData, 'filter:api.video.upload.video-attribute.result') | ||
160 | 157 | ||
161 | videoData.state = buildNextVideoState() | 158 | videoData.state = buildNextVideoState() |
162 | videoData.duration = videoPhysicalFile.duration // duration was added by a previous middleware | 159 | videoData.duration = videoPhysicalFile.duration // duration was added by a previous middleware |
@@ -255,11 +252,13 @@ async function buildNewFile (videoPhysicalFile: express.VideoUploadFile) { | |||
255 | metadata: await getMetadataFromFile(videoPhysicalFile.path) | 252 | metadata: await getMetadataFromFile(videoPhysicalFile.path) |
256 | }) | 253 | }) |
257 | 254 | ||
258 | if (videoFile.isAudio()) { | 255 | const probe = await ffprobePromise(videoPhysicalFile.path) |
259 | videoFile.resolution = DEFAULT_AUDIO_RESOLUTION | 256 | |
257 | if (await isAudioFile(videoPhysicalFile.path, probe)) { | ||
258 | videoFile.resolution = VideoResolution.H_NOVIDEO | ||
260 | } else { | 259 | } else { |
261 | videoFile.fps = await getVideoFileFPS(videoPhysicalFile.path) | 260 | videoFile.fps = await getVideoFileFPS(videoPhysicalFile.path, probe) |
262 | videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path)).resolution | 261 | videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path, probe)).resolution |
263 | } | 262 | } |
264 | 263 | ||
265 | videoFile.filename = generateWebTorrentVideoFilename(videoFile.resolution, videoFile.extname) | 264 | videoFile.filename = generateWebTorrentVideoFilename(videoFile.resolution, videoFile.extname) |
diff --git a/server/controllers/api/videos/watching.ts b/server/controllers/api/videos/watching.ts index e8c28b613..3fd22caac 100644 --- a/server/controllers/api/videos/watching.ts +++ b/server/controllers/api/videos/watching.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { UserWatchingVideo } from '../../../../shared' | 2 | import { HttpStatusCode, UserWatchingVideo } from '@shared/models' |
3 | import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' | ||
4 | import { | 3 | import { |
5 | asyncMiddleware, | 4 | asyncMiddleware, |
6 | asyncRetryTransactionMiddleware, | 5 | asyncRetryTransactionMiddleware, |