diff options
Diffstat (limited to 'server/lib')
-rw-r--r-- | server/lib/activitypub/crawl.ts | 2 | ||||
-rw-r--r-- | server/lib/activitypub/videos.ts | 9 | ||||
-rw-r--r-- | server/lib/blocklist.ts | 40 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-file.ts | 4 | ||||
-rw-r--r-- | server/lib/redis.ts | 54 | ||||
-rw-r--r-- | server/lib/video-comment.ts | 6 | ||||
-rw-r--r-- | server/lib/video-transcoding.ts | 9 |
7 files changed, 98 insertions, 26 deletions
diff --git a/server/lib/activitypub/crawl.ts b/server/lib/activitypub/crawl.ts index 55912341c..db9ce3293 100644 --- a/server/lib/activitypub/crawl.ts +++ b/server/lib/activitypub/crawl.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { ACTIVITY_PUB, JOB_REQUEST_TIMEOUT } from '../../initializers' | 1 | import { ACTIVITY_PUB, JOB_REQUEST_TIMEOUT } from '../../initializers' |
2 | import { doRequest } from '../../helpers/requests' | 2 | import { doRequest } from '../../helpers/requests' |
3 | import { logger } from '../../helpers/logger' | 3 | import { logger } from '../../helpers/logger' |
4 | import Bluebird = require('bluebird') | 4 | import * as Bluebird from 'bluebird' |
5 | 5 | ||
6 | async function crawlCollectionPage <T> (uri: string, handler: (items: T[]) => Promise<any> | Bluebird<any>) { | 6 | async function crawlCollectionPage <T> (uri: string, handler: (items: T[]) => Promise<any> | Bluebird<any>) { |
7 | logger.info('Crawling ActivityPub data on %s.', uri) | 7 | logger.info('Crawling ActivityPub data on %s.', uri) |
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 54cea542f..3da363c0a 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -310,7 +310,8 @@ export { | |||
310 | function isActivityVideoUrlObject (url: ActivityUrlObject): url is ActivityVideoUrlObject { | 310 | function isActivityVideoUrlObject (url: ActivityUrlObject): url is ActivityVideoUrlObject { |
311 | const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT) | 311 | const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT) |
312 | 312 | ||
313 | return mimeTypes.indexOf(url.mimeType) !== -1 && url.mimeType.startsWith('video/') | 313 | const urlMediaType = url.mediaType || url.mimeType |
314 | return mimeTypes.indexOf(urlMediaType) !== -1 && urlMediaType.startsWith('video/') | ||
314 | } | 315 | } |
315 | 316 | ||
316 | async function createVideo (videoObject: VideoTorrentObject, channelActor: ActorModel, waitThumbnail = false) { | 317 | async function createVideo (videoObject: VideoTorrentObject, channelActor: ActorModel, waitThumbnail = false) { |
@@ -468,7 +469,8 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid | |||
468 | for (const fileUrl of fileUrls) { | 469 | for (const fileUrl of fileUrls) { |
469 | // Fetch associated magnet uri | 470 | // Fetch associated magnet uri |
470 | const magnet = videoObject.url.find(u => { | 471 | const magnet = videoObject.url.find(u => { |
471 | return u.mimeType === 'application/x-bittorrent;x-scheme-handler/magnet' && u.height === fileUrl.height | 472 | const mediaType = u.mediaType || u.mimeType |
473 | return mediaType === 'application/x-bittorrent;x-scheme-handler/magnet' && (u as any).height === fileUrl.height | ||
472 | }) | 474 | }) |
473 | 475 | ||
474 | if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + fileUrl.href) | 476 | if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + fileUrl.href) |
@@ -478,8 +480,9 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid | |||
478 | throw new Error('Cannot parse magnet URI ' + magnet.href) | 480 | throw new Error('Cannot parse magnet URI ' + magnet.href) |
479 | } | 481 | } |
480 | 482 | ||
483 | const mediaType = fileUrl.mediaType || fileUrl.mimeType | ||
481 | const attribute = { | 484 | const attribute = { |
482 | extname: VIDEO_MIMETYPE_EXT[ fileUrl.mimeType ], | 485 | extname: VIDEO_MIMETYPE_EXT[ mediaType ], |
483 | infoHash: parsed.infoHash, | 486 | infoHash: parsed.infoHash, |
484 | resolution: fileUrl.height, | 487 | resolution: fileUrl.height, |
485 | size: fileUrl.size, | 488 | size: fileUrl.size, |
diff --git a/server/lib/blocklist.ts b/server/lib/blocklist.ts new file mode 100644 index 000000000..1633e500c --- /dev/null +++ b/server/lib/blocklist.ts | |||
@@ -0,0 +1,40 @@ | |||
1 | import { sequelizeTypescript } from '../initializers' | ||
2 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | ||
3 | import { ServerBlocklistModel } from '../models/server/server-blocklist' | ||
4 | |||
5 | function addAccountInBlocklist (byAccountId: number, targetAccountId: number) { | ||
6 | return sequelizeTypescript.transaction(async t => { | ||
7 | return AccountBlocklistModel.upsert({ | ||
8 | accountId: byAccountId, | ||
9 | targetAccountId: targetAccountId | ||
10 | }, { transaction: t }) | ||
11 | }) | ||
12 | } | ||
13 | |||
14 | function addServerInBlocklist (byAccountId: number, targetServerId: number) { | ||
15 | return sequelizeTypescript.transaction(async t => { | ||
16 | return ServerBlocklistModel.upsert({ | ||
17 | accountId: byAccountId, | ||
18 | targetServerId | ||
19 | }, { transaction: t }) | ||
20 | }) | ||
21 | } | ||
22 | |||
23 | function removeAccountFromBlocklist (accountBlock: AccountBlocklistModel) { | ||
24 | return sequelizeTypescript.transaction(async t => { | ||
25 | return accountBlock.destroy({ transaction: t }) | ||
26 | }) | ||
27 | } | ||
28 | |||
29 | function removeServerFromBlocklist (serverBlock: ServerBlocklistModel) { | ||
30 | return sequelizeTypescript.transaction(async t => { | ||
31 | return serverBlock.destroy({ transaction: t }) | ||
32 | }) | ||
33 | } | ||
34 | |||
35 | export { | ||
36 | addAccountInBlocklist, | ||
37 | addServerInBlocklist, | ||
38 | removeAccountFromBlocklist, | ||
39 | removeServerFromBlocklist | ||
40 | } | ||
diff --git a/server/lib/job-queue/handlers/video-file.ts b/server/lib/job-queue/handlers/video-file.ts index 1463c93fc..adc0a2a15 100644 --- a/server/lib/job-queue/handlers/video-file.ts +++ b/server/lib/job-queue/handlers/video-file.ts | |||
@@ -8,7 +8,7 @@ import { retryTransactionWrapper } from '../../../helpers/database-utils' | |||
8 | import { sequelizeTypescript } from '../../../initializers' | 8 | import { sequelizeTypescript } from '../../../initializers' |
9 | import * as Bluebird from 'bluebird' | 9 | import * as Bluebird from 'bluebird' |
10 | import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' | 10 | import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' |
11 | import { importVideoFile, transcodeOriginalVideofile, optimizeOriginalVideofile } from '../../video-transcoding' | 11 | import { importVideoFile, transcodeOriginalVideofile, optimizeVideofile } from '../../video-transcoding' |
12 | 12 | ||
13 | export type VideoFilePayload = { | 13 | export type VideoFilePayload = { |
14 | videoUUID: string | 14 | videoUUID: string |
@@ -56,7 +56,7 @@ async function processVideoFile (job: Bull.Job) { | |||
56 | 56 | ||
57 | await retryTransactionWrapper(onVideoFileTranscoderOrImportSuccess, video) | 57 | await retryTransactionWrapper(onVideoFileTranscoderOrImportSuccess, video) |
58 | } else { | 58 | } else { |
59 | await optimizeOriginalVideofile(video) | 59 | await optimizeVideofile(video) |
60 | 60 | ||
61 | await retryTransactionWrapper(onVideoFileOptimizerSuccess, video, payload.isNewVideo) | 61 | await retryTransactionWrapper(onVideoFileOptimizerSuccess, video, payload.isNewVideo) |
62 | } | 62 | } |
diff --git a/server/lib/redis.ts b/server/lib/redis.ts index e4e435659..abd75d512 100644 --- a/server/lib/redis.ts +++ b/server/lib/redis.ts | |||
@@ -48,6 +48,8 @@ class Redis { | |||
48 | ) | 48 | ) |
49 | } | 49 | } |
50 | 50 | ||
51 | /************* Forgot password *************/ | ||
52 | |||
51 | async setResetPasswordVerificationString (userId: number) { | 53 | async setResetPasswordVerificationString (userId: number) { |
52 | const generatedString = await generateRandomString(32) | 54 | const generatedString = await generateRandomString(32) |
53 | 55 | ||
@@ -60,6 +62,8 @@ class Redis { | |||
60 | return this.getValue(this.generateResetPasswordKey(userId)) | 62 | return this.getValue(this.generateResetPasswordKey(userId)) |
61 | } | 63 | } |
62 | 64 | ||
65 | /************* Email verification *************/ | ||
66 | |||
63 | async setVerifyEmailVerificationString (userId: number) { | 67 | async setVerifyEmailVerificationString (userId: number) { |
64 | const generatedString = await generateRandomString(32) | 68 | const generatedString = await generateRandomString(32) |
65 | 69 | ||
@@ -72,16 +76,20 @@ class Redis { | |||
72 | return this.getValue(this.generateVerifyEmailKey(userId)) | 76 | return this.getValue(this.generateVerifyEmailKey(userId)) |
73 | } | 77 | } |
74 | 78 | ||
79 | /************* Views per IP *************/ | ||
80 | |||
75 | setIPVideoView (ip: string, videoUUID: string) { | 81 | setIPVideoView (ip: string, videoUUID: string) { |
76 | return this.setValue(this.buildViewKey(ip, videoUUID), '1', VIDEO_VIEW_LIFETIME) | 82 | return this.setValue(this.generateViewKey(ip, videoUUID), '1', VIDEO_VIEW_LIFETIME) |
77 | } | 83 | } |
78 | 84 | ||
79 | async isVideoIPViewExists (ip: string, videoUUID: string) { | 85 | async isVideoIPViewExists (ip: string, videoUUID: string) { |
80 | return this.exists(this.buildViewKey(ip, videoUUID)) | 86 | return this.exists(this.generateViewKey(ip, videoUUID)) |
81 | } | 87 | } |
82 | 88 | ||
89 | /************* API cache *************/ | ||
90 | |||
83 | async getCachedRoute (req: express.Request) { | 91 | async getCachedRoute (req: express.Request) { |
84 | const cached = await this.getObject(this.buildCachedRouteKey(req)) | 92 | const cached = await this.getObject(this.generateCachedRouteKey(req)) |
85 | 93 | ||
86 | return cached as CachedRoute | 94 | return cached as CachedRoute |
87 | } | 95 | } |
@@ -94,9 +102,11 @@ class Redis { | |||
94 | (statusCode) ? { statusCode: statusCode.toString() } : null | 102 | (statusCode) ? { statusCode: statusCode.toString() } : null |
95 | ) | 103 | ) |
96 | 104 | ||
97 | return this.setObject(this.buildCachedRouteKey(req), cached, lifetime) | 105 | return this.setObject(this.generateCachedRouteKey(req), cached, lifetime) |
98 | } | 106 | } |
99 | 107 | ||
108 | /************* Video views *************/ | ||
109 | |||
100 | addVideoView (videoId: number) { | 110 | addVideoView (videoId: number) { |
101 | const keyIncr = this.generateVideoViewKey(videoId) | 111 | const keyIncr = this.generateVideoViewKey(videoId) |
102 | const keySet = this.generateVideosViewKey() | 112 | const keySet = this.generateVideosViewKey() |
@@ -131,33 +141,37 @@ class Redis { | |||
131 | ]) | 141 | ]) |
132 | } | 142 | } |
133 | 143 | ||
134 | generateVideosViewKey (hour?: number) { | 144 | /************* Keys generation *************/ |
145 | |||
146 | generateCachedRouteKey (req: express.Request) { | ||
147 | return req.method + '-' + req.originalUrl | ||
148 | } | ||
149 | |||
150 | private generateVideosViewKey (hour?: number) { | ||
135 | if (!hour) hour = new Date().getHours() | 151 | if (!hour) hour = new Date().getHours() |
136 | 152 | ||
137 | return `videos-view-h${hour}` | 153 | return `videos-view-h${hour}` |
138 | } | 154 | } |
139 | 155 | ||
140 | generateVideoViewKey (videoId: number, hour?: number) { | 156 | private generateVideoViewKey (videoId: number, hour?: number) { |
141 | if (!hour) hour = new Date().getHours() | 157 | if (!hour) hour = new Date().getHours() |
142 | 158 | ||
143 | return `video-view-${videoId}-h${hour}` | 159 | return `video-view-${videoId}-h${hour}` |
144 | } | 160 | } |
145 | 161 | ||
146 | generateResetPasswordKey (userId: number) { | 162 | private generateResetPasswordKey (userId: number) { |
147 | return 'reset-password-' + userId | 163 | return 'reset-password-' + userId |
148 | } | 164 | } |
149 | 165 | ||
150 | generateVerifyEmailKey (userId: number) { | 166 | private generateVerifyEmailKey (userId: number) { |
151 | return 'verify-email-' + userId | 167 | return 'verify-email-' + userId |
152 | } | 168 | } |
153 | 169 | ||
154 | buildViewKey (ip: string, videoUUID: string) { | 170 | private generateViewKey (ip: string, videoUUID: string) { |
155 | return videoUUID + '-' + ip | 171 | return videoUUID + '-' + ip |
156 | } | 172 | } |
157 | 173 | ||
158 | buildCachedRouteKey (req: express.Request) { | 174 | /************* Redis helpers *************/ |
159 | return req.method + '-' + req.originalUrl | ||
160 | } | ||
161 | 175 | ||
162 | private getValue (key: string) { | 176 | private getValue (key: string) { |
163 | return new Promise<string>((res, rej) => { | 177 | return new Promise<string>((res, rej) => { |
@@ -197,6 +211,12 @@ class Redis { | |||
197 | }) | 211 | }) |
198 | } | 212 | } |
199 | 213 | ||
214 | private deleteFieldInHash (key: string, field: string) { | ||
215 | return new Promise<void>((res, rej) => { | ||
216 | this.client.hdel(this.prefix + key, field, err => err ? rej(err) : res()) | ||
217 | }) | ||
218 | } | ||
219 | |||
200 | private setValue (key: string, value: string, expirationMilliseconds: number) { | 220 | private setValue (key: string, value: string, expirationMilliseconds: number) { |
201 | return new Promise<void>((res, rej) => { | 221 | return new Promise<void>((res, rej) => { |
202 | this.client.set(this.prefix + key, value, 'PX', expirationMilliseconds, (err, ok) => { | 222 | this.client.set(this.prefix + key, value, 'PX', expirationMilliseconds, (err, ok) => { |
@@ -235,6 +255,16 @@ class Redis { | |||
235 | }) | 255 | }) |
236 | } | 256 | } |
237 | 257 | ||
258 | private setValueInHash (key: string, field: string, value: string) { | ||
259 | return new Promise<void>((res, rej) => { | ||
260 | this.client.hset(this.prefix + key, field, value, (err) => { | ||
261 | if (err) return rej(err) | ||
262 | |||
263 | return res() | ||
264 | }) | ||
265 | }) | ||
266 | } | ||
267 | |||
238 | private increment (key: string) { | 268 | private increment (key: string) { |
239 | return new Promise<number>((res, rej) => { | 269 | return new Promise<number>((res, rej) => { |
240 | this.client.incr(this.prefix + key, (err, value) => { | 270 | this.client.incr(this.prefix + key, (err, value) => { |
diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts index 70ba7c303..59bce7520 100644 --- a/server/lib/video-comment.ts +++ b/server/lib/video-comment.ts | |||
@@ -64,10 +64,8 @@ function buildFormattedCommentTree (resultList: ResultList<VideoCommentModel>): | |||
64 | } | 64 | } |
65 | 65 | ||
66 | const parentCommentThread = idx[childComment.inReplyToCommentId] | 66 | const parentCommentThread = idx[childComment.inReplyToCommentId] |
67 | if (!parentCommentThread) { | 67 | // Maybe the parent comment was blocked by the admin/user |
68 | const msg = `Cannot format video thread tree, parent ${childComment.inReplyToCommentId} not found for child ${childComment.id}` | 68 | if (!parentCommentThread) continue |
69 | throw new Error(msg) | ||
70 | } | ||
71 | 69 | ||
72 | parentCommentThread.children.push(childCommentThread) | 70 | parentCommentThread.children.push(childCommentThread) |
73 | idx[childComment.id] = childCommentThread | 71 | idx[childComment.id] = childCommentThread |
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts index bf3ff78c2..a78de61e5 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/video-transcoding.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { CONFIG } from '../initializers' | 1 | import { CONFIG } from '../initializers' |
2 | import { join, extname } from 'path' | 2 | import { extname, join } from 'path' |
3 | import { getVideoFileFPS, getVideoFileResolution, transcode } from '../helpers/ffmpeg-utils' | 3 | import { getVideoFileFPS, getVideoFileResolution, transcode } from '../helpers/ffmpeg-utils' |
4 | import { copy, remove, rename, stat } from 'fs-extra' | 4 | import { copy, remove, rename, stat } from 'fs-extra' |
5 | import { logger } from '../helpers/logger' | 5 | import { logger } from '../helpers/logger' |
@@ -7,10 +7,11 @@ import { VideoResolution } from '../../shared/models/videos' | |||
7 | import { VideoFileModel } from '../models/video/video-file' | 7 | import { VideoFileModel } from '../models/video/video-file' |
8 | import { VideoModel } from '../models/video/video' | 8 | import { VideoModel } from '../models/video/video' |
9 | 9 | ||
10 | async function optimizeOriginalVideofile (video: VideoModel) { | 10 | async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) { |
11 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 11 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
12 | const newExtname = '.mp4' | 12 | const newExtname = '.mp4' |
13 | const inputVideoFile = video.getOriginalFile() | 13 | |
14 | const inputVideoFile = inputVideoFileArg ? inputVideoFileArg : video.getOriginalFile() | ||
14 | const videoInputPath = join(videosDirectory, video.getVideoFilename(inputVideoFile)) | 15 | const videoInputPath = join(videosDirectory, video.getVideoFilename(inputVideoFile)) |
15 | const videoTranscodedPath = join(videosDirectory, video.id + '-transcoded' + newExtname) | 16 | const videoTranscodedPath = join(videosDirectory, video.id + '-transcoded' + newExtname) |
16 | 17 | ||
@@ -124,7 +125,7 @@ async function importVideoFile (video: VideoModel, inputFilePath: string) { | |||
124 | } | 125 | } |
125 | 126 | ||
126 | export { | 127 | export { |
127 | optimizeOriginalVideofile, | 128 | optimizeVideofile, |
128 | transcodeOriginalVideofile, | 129 | transcodeOriginalVideofile, |
129 | importVideoFile | 130 | importVideoFile |
130 | } | 131 | } |