aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.json3
-rw-r--r--.gitignore3
-rwxr-xr-xscripts/build/server.sh7
-rw-r--r--scripts/client-build-stats.ts2
-rwxr-xr-xscripts/setup/cli.sh2
-rw-r--r--scripts/tsconfig.json10
-rw-r--r--server/controllers/api/users/token.ts2
-rw-r--r--server/controllers/api/video-playlist.ts2
-rw-r--r--server/controllers/api/videos/live.ts2
-rw-r--r--server/controllers/api/videos/upload.ts4
-rw-r--r--server/controllers/client.ts2
-rw-r--r--server/controllers/static.ts2
-rw-r--r--server/helpers/core-utils.ts47
-rw-r--r--server/helpers/custom-validators/misc.ts2
-rw-r--r--server/helpers/express-utils.ts6
-rw-r--r--server/helpers/ffprobe-utils.ts173
-rw-r--r--server/helpers/image-utils.ts4
-rw-r--r--server/helpers/peertube-crypto.ts3
-rw-r--r--server/helpers/utils.ts3
-rw-r--r--server/helpers/webtorrent.ts3
-rw-r--r--server/initializers/config.ts3
-rw-r--r--server/initializers/constants.ts3
-rw-r--r--server/initializers/migrations/0080-video-channels.ts2
-rw-r--r--server/initializers/migrations/0345-video-playlists.ts2
-rw-r--r--server/initializers/migrations/0560-user-feed-token.ts2
-rw-r--r--server/lib/activitypub/actors/shared/object-to-model-attributes.ts4
-rw-r--r--server/lib/auth/oauth.ts3
-rw-r--r--server/lib/client-html.ts3
-rw-r--r--server/lib/emailer.ts3
-rw-r--r--server/lib/hls.ts2
-rw-r--r--server/lib/job-queue/handlers/video-file-import.ts2
-rw-r--r--server/lib/job-queue/handlers/video-import.ts2
-rw-r--r--server/lib/local-actor.ts4
-rw-r--r--server/lib/paths.ts2
-rw-r--r--server/lib/user.ts2
-rw-r--r--server/lib/video-path-manager.ts2
-rw-r--r--server/models/actor/actor.ts3
-rw-r--r--server/models/user/user-notification.ts2
-rw-r--r--server/models/video/formatter/video-format-utils.ts2
-rw-r--r--server/models/video/video-caption.ts2
-rw-r--r--server/models/video/video-playlist.ts2
-rw-r--r--server/models/video/video-streaming-playlist.ts2
-rw-r--r--server/models/video/video.ts2
-rw-r--r--server/tests/api/activitypub/security.ts2
-rw-r--r--server/tests/api/notifications/moderation-notifications.ts2
-rw-r--r--server/tests/api/notifications/user-notifications.ts2
-rw-r--r--server/tests/api/server/follows.ts2
-rw-r--r--server/tests/api/server/handle-down.ts2
-rw-r--r--server/tests/api/videos/multiple-servers.ts2
-rw-r--r--server/tests/api/videos/single-server.ts2
-rw-r--r--server/tests/cli/prune-storage.ts2
-rw-r--r--server/tests/shared/index.ts2
-rw-r--r--server/tests/shared/requests.ts (renamed from shared/extra-utils/requests/activitypub.ts)17
-rw-r--r--server/tests/shared/video.ts148
-rw-r--r--server/tools/cli.ts5
-rw-r--r--server/tools/peertube-import-videos.ts2
-rw-r--r--server/tools/tsconfig.json6
-rw-r--r--server/tsconfig.json12
-rw-r--r--shared/core-utils/crypto.ts14
-rw-r--r--shared/core-utils/index.ts2
-rw-r--r--shared/core-utils/path.ts34
-rw-r--r--shared/core-utils/uuid.ts (renamed from server/helpers/uuid.ts)0
-rw-r--r--shared/extra-utils/ffprobe.ts180
-rw-r--r--shared/extra-utils/miscs/checks.ts2
-rw-r--r--shared/extra-utils/miscs/generate.ts2
-rw-r--r--shared/extra-utils/server/directories.ts2
-rw-r--r--shared/extra-utils/server/plugins-command.ts2
-rw-r--r--shared/extra-utils/server/server.ts3
-rw-r--r--shared/extra-utils/server/servers-command.ts2
-rw-r--r--shared/extra-utils/server/tracker.ts2
-rw-r--r--shared/extra-utils/users/actors.ts2
-rw-r--r--shared/extra-utils/videos/streaming-playlists.ts2
-rw-r--r--shared/extra-utils/videos/videos-command.ts12
-rw-r--r--shared/extra-utils/videos/videos.ts150
-rw-r--r--shared/tsconfig.json6
-rw-r--r--tsconfig.base.json35
-rw-r--r--tsconfig.json51
-rw-r--r--tsconfig.types.json19
78 files changed, 583 insertions, 485 deletions
diff --git a/.eslintrc.json b/.eslintrc.json
index a49a9e71b..c7597cef3 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -102,6 +102,9 @@
102 "parserOptions": { 102 "parserOptions": {
103 "project": [ 103 "project": [
104 "./tsconfig.json", 104 "./tsconfig.json",
105 "./shared/tsconfig.json",
106 "./scripts/tsconfig.json",
107 "./server/tsconfig.json",
105 "./server/tools/tsconfig.json" 108 "./server/tools/tsconfig.json"
106 ] 109 ]
107 } 110 }
diff --git a/.gitignore b/.gitignore
index 3027b6058..cd7d3f59b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,3 +50,6 @@ yarn-error.log
50/server/tools/import-mediacore.ts 50/server/tools/import-mediacore.ts
51/docker-volume/ 51/docker-volume/
52/init.mp4 52/init.mp4
53
54# TypeScript
55*.tsbuildinfo
diff --git a/scripts/build/server.sh b/scripts/build/server.sh
index b903f8250..865bdd095 100755
--- a/scripts/build/server.sh
+++ b/scripts/build/server.sh
@@ -4,7 +4,10 @@ set -eu
4 4
5rm -rf ./dist 5rm -rf ./dist
6 6
7npm run tsc 7npm run tsc -- -b --verbose
8cp "./tsconfig.json" "./dist" 8cp "./tsconfig.base.json" "./tsconfig.json" "./dist/"
9cp "./scripts/tsconfig.json" "./dist/scripts/"
10cp "./server/tsconfig.json" "./dist/server/"
11cp "./shared/tsconfig.json" "./dist/shared/"
9cp -r "./server/static" "./server/assets" "./dist/server" 12cp -r "./server/static" "./server/assets" "./dist/server"
10cp -r "./server/lib/emails" "./dist/server/lib" 13cp -r "./server/lib/emails" "./dist/server/lib"
diff --git a/scripts/client-build-stats.ts b/scripts/client-build-stats.ts
index 70ceda975..91844dfcd 100644
--- a/scripts/client-build-stats.ts
+++ b/scripts/client-build-stats.ts
@@ -3,7 +3,7 @@ registerTSPaths()
3 3
4import { readdir, stat } from 'fs-extra' 4import { readdir, stat } from 'fs-extra'
5import { join } from 'path' 5import { join } from 'path'
6import { root } from '@server/helpers/core-utils' 6import { root } from '@shared/core-utils'
7 7
8async function run () { 8async function run () {
9 const result = { 9 const result = {
diff --git a/scripts/setup/cli.sh b/scripts/setup/cli.sh
index ea327e5a1..d0ad2ec5f 100755
--- a/scripts/setup/cli.sh
+++ b/scripts/setup/cli.sh
@@ -11,6 +11,6 @@ rm -rf ./dist/server/tools/
11 yarn install --pure-lockfile 11 yarn install --pure-lockfile
12) 12)
13 13
14npm run tsc -- --build ./server/tools/tsconfig.json 14npm run tsc -- --build --verbose ./server/tools/tsconfig.json
15cp -r "./server/tools/node_modules" "./dist/server/tools" 15cp -r "./server/tools/node_modules" "./dist/server/tools"
16cp "./tsconfig.json" "./dist" 16cp "./tsconfig.json" "./dist"
diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json
new file mode 100644
index 000000000..0d9716f2d
--- /dev/null
+++ b/scripts/tsconfig.json
@@ -0,0 +1,10 @@
1{
2 "extends": "../tsconfig.base.json",
3 "compilerOptions": {
4 "outDir": "../dist/scripts",
5 },
6 "references": [
7 { "path": "../shared" },
8 { "path": "../server" }
9 ]
10}
diff --git a/server/controllers/api/users/token.ts b/server/controllers/api/users/token.ts
index 1d4004ce0..42f4f6096 100644
--- a/server/controllers/api/users/token.ts
+++ b/server/controllers/api/users/token.ts
@@ -1,7 +1,7 @@
1import express from 'express' 1import express from 'express'
2import RateLimit from 'express-rate-limit' 2import RateLimit from 'express-rate-limit'
3import { logger } from '@server/helpers/logger' 3import { logger } from '@server/helpers/logger'
4import { buildUUID } from '@server/helpers/uuid' 4import { buildUUID } from '@shared/core-utils/uuid'
5import { CONFIG } from '@server/initializers/config' 5import { CONFIG } from '@server/initializers/config'
6import { getAuthNameFromRefreshGrant, getBypassFromExternalAuth, getBypassFromPasswordGrant } from '@server/lib/auth/external-auth' 6import { getAuthNameFromRefreshGrant, getBypassFromExternalAuth, getBypassFromPasswordGrant } from '@server/lib/auth/external-auth'
7import { handleOAuthToken } from '@server/lib/auth/oauth' 7import { handleOAuthToken } from '@server/lib/auth/oauth'
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts
index 8b7a76718..f8f07b1c6 100644
--- a/server/controllers/api/video-playlist.ts
+++ b/server/controllers/api/video-playlist.ts
@@ -1,6 +1,6 @@
1import express from 'express' 1import express from 'express'
2import { join } from 'path' 2import { join } from 'path'
3import { uuidToShort } from '@server/helpers/uuid' 3import { uuidToShort } from '@shared/core-utils/uuid'
4import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists' 4import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists'
5import { Hooks } from '@server/lib/plugins/hooks' 5import { Hooks } from '@server/lib/plugins/hooks'
6import { getServerActor } from '@server/models/application/application' 6import { getServerActor } from '@server/models/application/application'
diff --git a/server/controllers/api/videos/live.ts b/server/controllers/api/videos/live.ts
index 3e1480cf2..e466d041b 100644
--- a/server/controllers/api/videos/live.ts
+++ b/server/controllers/api/videos/live.ts
@@ -1,6 +1,6 @@
1import express from 'express' 1import express from 'express'
2import { createReqFiles } from '@server/helpers/express-utils' 2import { createReqFiles } from '@server/helpers/express-utils'
3import { buildUUID, uuidToShort } from '@server/helpers/uuid' 3import { buildUUID, uuidToShort } from '@shared/core-utils/uuid'
4import { CONFIG } from '@server/initializers/config' 4import { CONFIG } from '@server/initializers/config'
5import { ASSETS_PATH, MIMETYPES } from '@server/initializers/constants' 5import { ASSETS_PATH, MIMETYPES } from '@server/initializers/constants'
6import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' 6import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
diff --git a/server/controllers/api/videos/upload.ts b/server/controllers/api/videos/upload.ts
index 1be87f746..a4d0f980f 100644
--- a/server/controllers/api/videos/upload.ts
+++ b/server/controllers/api/videos/upload.ts
@@ -1,9 +1,9 @@
1import express from 'express' 1import express from 'express'
2import { move } from 'fs-extra' 2import { move } from 'fs-extra'
3import { basename } from 'path' 3import { basename } from 'path'
4import { getLowercaseExtension } from '@server/helpers/core-utils' 4import { getLowercaseExtension } from '@shared/core-utils'
5import { getResumableUploadPath } from '@server/helpers/upload' 5import { getResumableUploadPath } from '@server/helpers/upload'
6import { uuidToShort } from '@server/helpers/uuid' 6import { uuidToShort } from '@shared/core-utils/uuid'
7import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' 7import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
8import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' 8import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
9import { generateWebTorrentVideoFilename } from '@server/lib/paths' 9import { generateWebTorrentVideoFilename } from '@server/lib/paths'
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index 2157ae533..8a56f2f75 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -7,7 +7,7 @@ import { CONFIG } from '@server/initializers/config'
7import { Hooks } from '@server/lib/plugins/hooks' 7import { Hooks } from '@server/lib/plugins/hooks'
8import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n' 8import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n'
9import { HttpStatusCode } from '@shared/models' 9import { HttpStatusCode } from '@shared/models'
10import { root } from '../helpers/core-utils' 10import { root } from '@shared/core-utils'
11import { STATIC_MAX_AGE } from '../initializers/constants' 11import { STATIC_MAX_AGE } from '../initializers/constants'
12import { ClientHtml, sendHTML, serveIndexHTML } from '../lib/client-html' 12import { ClientHtml, sendHTML, serveIndexHTML } from '../lib/client-html'
13import { asyncMiddleware, embedCSP } from '../middlewares' 13import { asyncMiddleware, embedCSP } from '../middlewares'
diff --git a/server/controllers/static.ts b/server/controllers/static.ts
index 0d94cac9b..87bceba7a 100644
--- a/server/controllers/static.ts
+++ b/server/controllers/static.ts
@@ -5,7 +5,7 @@ import { serveIndexHTML } from '@server/lib/client-html'
5import { ServerConfigManager } from '@server/lib/server-config-manager' 5import { ServerConfigManager } from '@server/lib/server-config-manager'
6import { HttpStatusCode } from '@shared/models' 6import { HttpStatusCode } from '@shared/models'
7import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model' 7import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model'
8import { root } from '../helpers/core-utils' 8import { root } from '@shared/core-utils'
9import { CONFIG, isEmailEnabled } from '../initializers/config' 9import { CONFIG, isEmailEnabled } from '../initializers/config'
10import { 10import {
11 CONSTRAINTS_FIELDS, 11 CONSTRAINTS_FIELDS,
diff --git a/server/helpers/core-utils.ts b/server/helpers/core-utils.ts
index 2cbf0f8fe..531ccfba9 100644
--- a/server/helpers/core-utils.ts
+++ b/server/helpers/core-utils.ts
@@ -6,9 +6,8 @@
6*/ 6*/
7 7
8import { exec, ExecOptions } from 'child_process' 8import { exec, ExecOptions } from 'child_process'
9import { BinaryToTextEncoding, createHash, randomBytes } from 'crypto' 9import { randomBytes } from 'crypto'
10import { truncate } from 'lodash' 10import { truncate } from 'lodash'
11import { basename, extname, isAbsolute, join, resolve } from 'path'
12import { createPrivateKey as createPrivateKey_1, getPublicKey as getPublicKey_1 } from 'pem' 11import { createPrivateKey as createPrivateKey_1, getPublicKey as getPublicKey_1 } from 'pem'
13import { pipeline } from 'stream' 12import { pipeline } from 'stream'
14import { URL } from 'url' 13import { URL } from 'url'
@@ -159,34 +158,6 @@ function getAppNumber () {
159 158
160// --------------------------------------------------------------------------- 159// ---------------------------------------------------------------------------
161 160
162let rootPath: string
163
164function root () {
165 if (rootPath) return rootPath
166
167 rootPath = __dirname
168
169 if (basename(rootPath) === 'helpers') rootPath = resolve(rootPath, '..')
170 if (basename(rootPath) === 'server') rootPath = resolve(rootPath, '..')
171 if (basename(rootPath) === 'dist') rootPath = resolve(rootPath, '..')
172
173 return rootPath
174}
175
176function buildPath (path: string) {
177 if (isAbsolute(path)) return path
178
179 return join(root(), path)
180}
181
182function getLowercaseExtension (filename: string) {
183 const ext = extname(filename) || ''
184
185 return ext.toLowerCase()
186}
187
188// ---------------------------------------------------------------------------
189
190// Consistent with .length, lodash truncate function is not 161// Consistent with .length, lodash truncate function is not
191function peertubeTruncate (str: string, options: { length: number, separator?: RegExp, omission?: string }) { 162function peertubeTruncate (str: string, options: { length: number, separator?: RegExp, omission?: string }) {
192 const truncatedStr = truncate(str, options) 163 const truncatedStr = truncate(str, options)
@@ -221,16 +192,6 @@ function parseSemVersion (s: string) {
221 192
222// --------------------------------------------------------------------------- 193// ---------------------------------------------------------------------------
223 194
224function sha256 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') {
225 return createHash('sha256').update(str).digest(encoding)
226}
227
228function sha1 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') {
229 return createHash('sha1').update(str).digest(encoding)
230}
231
232// ---------------------------------------------------------------------------
233
234function execShell (command: string, options?: ExecOptions) { 195function execShell (command: string, options?: ExecOptions) {
235 return new Promise<{ err?: Error, stdout: string, stderr: string }>((res, rej) => { 196 return new Promise<{ err?: Error, stdout: string, stderr: string }>((res, rej) => {
236 exec(command, options, (err, stdout, stderr) => { 197 exec(command, options, (err, stdout, stderr) => {
@@ -298,9 +259,6 @@ export {
298 objectConverter, 259 objectConverter,
299 mapToJSON, 260 mapToJSON,
300 261
301 root,
302 buildPath,
303 getLowercaseExtension,
304 sanitizeUrl, 262 sanitizeUrl,
305 sanitizeHost, 263 sanitizeHost,
306 264
@@ -309,9 +267,6 @@ export {
309 pageToStartAndCount, 267 pageToStartAndCount,
310 peertubeTruncate, 268 peertubeTruncate,
311 269
312 sha256,
313 sha1,
314
315 promisify0, 270 promisify0,
316 promisify1, 271 promisify1,
317 promisify2, 272 promisify2,
diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts
index c19a3e5eb..eaabdbbea 100644
--- a/server/helpers/custom-validators/misc.ts
+++ b/server/helpers/custom-validators/misc.ts
@@ -2,7 +2,7 @@ import 'multer'
2import { UploadFilesForCheck } from 'express' 2import { UploadFilesForCheck } from 'express'
3import { sep } from 'path' 3import { sep } from 'path'
4import validator from 'validator' 4import validator from 'validator'
5import { isShortUUID, shortToUUID } from '../uuid' 5import { isShortUUID, shortToUUID } from '@shared/core-utils/uuid'
6 6
7function exists (value: any) { 7function exists (value: any) {
8 return value !== undefined && value !== null 8 return value !== undefined && value !== null
diff --git a/server/helpers/express-utils.ts b/server/helpers/express-utils.ts
index 7b81ed71b..780fd6345 100644
--- a/server/helpers/express-utils.ts
+++ b/server/helpers/express-utils.ts
@@ -1,9 +1,9 @@
1import express from 'express' 1import express, { RequestHandler } from 'express'
2import multer, { diskStorage } from 'multer' 2import multer, { diskStorage } from 'multer'
3import { HttpStatusCode } from '../../shared/models/http/http-error-codes' 3import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
4import { CONFIG } from '../initializers/config' 4import { CONFIG } from '../initializers/config'
5import { REMOTE_SCHEME } from '../initializers/constants' 5import { REMOTE_SCHEME } from '../initializers/constants'
6import { getLowercaseExtension } from './core-utils' 6import { getLowercaseExtension } from '@shared/core-utils'
7import { isArray } from './custom-validators/misc' 7import { isArray } from './custom-validators/misc'
8import { logger } from './logger' 8import { logger } from './logger'
9import { deleteFileAndCatch, generateRandomString } from './utils' 9import { deleteFileAndCatch, generateRandomString } from './utils'
@@ -69,7 +69,7 @@ function createReqFiles (
69 fieldNames: string[], 69 fieldNames: string[],
70 mimeTypes: { [id: string]: string | string[] }, 70 mimeTypes: { [id: string]: string | string[] },
71 destinations: { [fieldName: string]: string } 71 destinations: { [fieldName: string]: string }
72) { 72): RequestHandler {
73 const storage = diskStorage({ 73 const storage = diskStorage({
74 destination: (req, file, cb) => { 74 destination: (req, file, cb) => {
75 cb(null, destinations[file.fieldname]) 75 cb(null, destinations[file.fieldname])
diff --git a/server/helpers/ffprobe-utils.ts b/server/helpers/ffprobe-utils.ts
index e15628e2a..595112bce 100644
--- a/server/helpers/ffprobe-utils.ts
+++ b/server/helpers/ffprobe-utils.ts
@@ -1,9 +1,22 @@
1import { ffprobe, FfprobeData } from 'fluent-ffmpeg' 1import { FfprobeData } from 'fluent-ffmpeg'
2import { getMaxBitrate } from '@shared/core-utils' 2import { getMaxBitrate } from '@shared/core-utils'
3import { VideoFileMetadata, VideoResolution, VideoTranscodingFPS } from '../../shared/models/videos' 3import { VideoResolution, VideoTranscodingFPS } from '../../shared/models/videos'
4import { CONFIG } from '../initializers/config' 4import { CONFIG } from '../initializers/config'
5import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' 5import { VIDEO_TRANSCODING_FPS } from '../initializers/constants'
6import { logger } from './logger' 6import { logger } from './logger'
7import {
8 canDoQuickAudioTranscode,
9 ffprobePromise,
10 getDurationFromVideoFile,
11 getAudioStream,
12 getMaxAudioBitrate,
13 getMetadataFromFile,
14 getVideoFileBitrate,
15 getVideoFileFPS,
16 getVideoFileResolution,
17 getVideoStreamFromFile,
18 getVideoStreamSize
19} from '@shared/extra-utils/ffprobe'
7 20
8/** 21/**
9 * 22 *
@@ -11,79 +24,6 @@ import { logger } from './logger'
11 * 24 *
12 */ 25 */
13 26
14function ffprobePromise (path: string) {
15 return new Promise<FfprobeData>((res, rej) => {
16 ffprobe(path, (err, data) => {
17 if (err) return rej(err)
18
19 return res(data)
20 })
21 })
22}
23
24async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) {
25 // without position, ffprobe considers the last input only
26 // we make it consider the first input only
27 // if you pass a file path to pos, then ffprobe acts on that file directly
28 const data = existingProbe || await ffprobePromise(videoPath)
29
30 if (Array.isArray(data.streams)) {
31 const audioStream = data.streams.find(stream => stream['codec_type'] === 'audio')
32
33 if (audioStream) {
34 return {
35 absolutePath: data.format.filename,
36 audioStream,
37 bitrate: parseInt(audioStream['bit_rate'] + '', 10)
38 }
39 }
40 }
41
42 return { absolutePath: data.format.filename }
43}
44
45function getMaxAudioBitrate (type: 'aac' | 'mp3' | string, bitrate: number) {
46 const maxKBitrate = 384
47 const kToBits = (kbits: number) => kbits * 1000
48
49 // If we did not manage to get the bitrate, use an average value
50 if (!bitrate) return 256
51
52 if (type === 'aac') {
53 switch (true) {
54 case bitrate > kToBits(maxKBitrate):
55 return maxKBitrate
56
57 default:
58 return -1 // we interpret it as a signal to copy the audio stream as is
59 }
60 }
61
62 /*
63 a 192kbit/sec mp3 doesn't hold as much information as a 192kbit/sec aac.
64 That's why, when using aac, we can go to lower kbit/sec. The equivalences
65 made here are not made to be accurate, especially with good mp3 encoders.
66 */
67 switch (true) {
68 case bitrate <= kToBits(192):
69 return 128
70
71 case bitrate <= kToBits(384):
72 return 256
73
74 default:
75 return maxKBitrate
76 }
77}
78
79async function getVideoStreamSize (path: string, existingProbe?: FfprobeData): Promise<{ width: number, height: number }> {
80 const videoStream = await getVideoStreamFromFile(path, existingProbe)
81
82 return videoStream === null
83 ? { width: 0, height: 0 }
84 : { width: videoStream.width, height: videoStream.height }
85}
86
87async function getVideoStreamCodec (path: string) { 27async function getVideoStreamCodec (path: string) {
88 const videoStream = await getVideoStreamFromFile(path) 28 const videoStream = await getVideoStreamFromFile(path)
89 29
@@ -143,69 +83,6 @@ async function getAudioStreamCodec (path: string, existingProbe?: FfprobeData) {
143 return 'mp4a.40.2' // Fallback 83 return 'mp4a.40.2' // Fallback
144} 84}
145 85
146async function getVideoFileResolution (path: string, existingProbe?: FfprobeData) {
147 const size = await getVideoStreamSize(path, existingProbe)
148
149 return {
150 width: size.width,
151 height: size.height,
152 ratio: Math.max(size.height, size.width) / Math.min(size.height, size.width),
153 resolution: Math.min(size.height, size.width),
154 isPortraitMode: size.height > size.width
155 }
156}
157
158async function getVideoFileFPS (path: string, existingProbe?: FfprobeData) {
159 const videoStream = await getVideoStreamFromFile(path, existingProbe)
160 if (videoStream === null) return 0
161
162 for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) {
163 const valuesText: string = videoStream[key]
164 if (!valuesText) continue
165
166 const [ frames, seconds ] = valuesText.split('/')
167 if (!frames || !seconds) continue
168
169 const result = parseInt(frames, 10) / parseInt(seconds, 10)
170 if (result > 0) return Math.round(result)
171 }
172
173 return 0
174}
175
176async function getMetadataFromFile (path: string, existingProbe?: FfprobeData) {
177 const metadata = existingProbe || await ffprobePromise(path)
178
179 return new VideoFileMetadata(metadata)
180}
181
182async function getVideoFileBitrate (path: string, existingProbe?: FfprobeData): Promise<number> {
183 const metadata = await getMetadataFromFile(path, existingProbe)
184
185 let bitrate = metadata.format.bit_rate as number
186 if (bitrate && !isNaN(bitrate)) return bitrate
187
188 const videoStream = await getVideoStreamFromFile(path, existingProbe)
189 if (!videoStream) return undefined
190
191 bitrate = videoStream?.bit_rate
192 if (bitrate && !isNaN(bitrate)) return bitrate
193
194 return undefined
195}
196
197async function getDurationFromVideoFile (path: string, existingProbe?: FfprobeData) {
198 const metadata = await getMetadataFromFile(path, existingProbe)
199
200 return Math.round(metadata.format.duration)
201}
202
203async function getVideoStreamFromFile (path: string, existingProbe?: FfprobeData) {
204 const metadata = await getMetadataFromFile(path, existingProbe)
205
206 return metadata.streams.find(s => s.codec_type === 'video') || null
207}
208
209function computeLowerResolutionsToTranscode (videoFileResolution: number, type: 'vod' | 'live') { 86function computeLowerResolutionsToTranscode (videoFileResolution: number, type: 'vod' | 'live') {
210 const configResolutions = type === 'vod' 87 const configResolutions = type === 'vod'
211 ? CONFIG.TRANSCODING.RESOLUTIONS 88 ? CONFIG.TRANSCODING.RESOLUTIONS
@@ -263,26 +140,6 @@ async function canDoQuickVideoTranscode (path: string, probe?: FfprobeData): Pro
263 return true 140 return true
264} 141}
265 142
266async function canDoQuickAudioTranscode (path: string, probe?: FfprobeData): Promise<boolean> {
267 const parsedAudio = await getAudioStream(path, probe)
268
269 if (!parsedAudio.audioStream) return true
270
271 if (parsedAudio.audioStream['codec_name'] !== 'aac') return false
272
273 const audioBitrate = parsedAudio.bitrate
274 if (!audioBitrate) return false
275
276 const maxAudioBitrate = getMaxAudioBitrate('aac', audioBitrate)
277 if (maxAudioBitrate !== -1 && audioBitrate > maxAudioBitrate) return false
278
279 const channelLayout = parsedAudio.audioStream['channel_layout']
280 // Causes playback issues with Chrome
281 if (!channelLayout || channelLayout === 'unknown') return false
282
283 return true
284}
285
286function getClosestFramerateStandard <K extends keyof Pick<VideoTranscodingFPS, 'HD_STANDARD' | 'STANDARD'>> (fps: number, type: K) { 143function getClosestFramerateStandard <K extends keyof Pick<VideoTranscodingFPS, 'HD_STANDARD' | 'STANDARD'>> (fps: number, type: K) {
287 return VIDEO_TRANSCODING_FPS[type].slice(0) 144 return VIDEO_TRANSCODING_FPS[type].slice(0)
288 .sort((a, b) => fps % a - fps % b)[0] 145 .sort((a, b) => fps % a - fps % b)[0]
diff --git a/server/helpers/image-utils.ts b/server/helpers/image-utils.ts
index 4305584d5..ced288045 100644
--- a/server/helpers/image-utils.ts
+++ b/server/helpers/image-utils.ts
@@ -1,9 +1,9 @@
1import { copy, readFile, remove, rename } from 'fs-extra' 1import { copy, readFile, remove, rename } from 'fs-extra'
2import Jimp, { read } from 'jimp' 2import Jimp, { read } from 'jimp'
3import { getLowercaseExtension } from './core-utils' 3import { getLowercaseExtension } from '@shared/core-utils'
4import { convertWebPToJPG, processGIF } from './ffmpeg-utils' 4import { convertWebPToJPG, processGIF } from './ffmpeg-utils'
5import { logger } from './logger' 5import { logger } from './logger'
6import { buildUUID } from './uuid' 6import { buildUUID } from '@shared/core-utils/uuid'
7 7
8function generateImageFilename (extension = '.jpg') { 8function generateImageFilename (extension = '.jpg') {
9 return buildUUID() + extension 9 return buildUUID() + extension
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts
index 44d90d9f1..31705e7fa 100644
--- a/server/helpers/peertube-crypto.ts
+++ b/server/helpers/peertube-crypto.ts
@@ -4,7 +4,8 @@ import { Request } from 'express'
4import { cloneDeep } from 'lodash' 4import { cloneDeep } from 'lodash'
5import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants' 5import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants'
6import { MActor } from '../types/models' 6import { MActor } from '../types/models'
7import { createPrivateKey, getPublicKey, promisify1, promisify2, sha256 } from './core-utils' 7import { sha256 } from '@shared/core-utils/crypto'
8import { createPrivateKey, getPublicKey, promisify1, promisify2 } from './core-utils'
8import { jsonld } from './custom-jsonld-signature' 9import { jsonld } from './custom-jsonld-signature'
9import { logger } from './logger' 10import { logger } from './logger'
10 11
diff --git a/server/helpers/utils.ts b/server/helpers/utils.ts
index 6c95a43b6..882f808ab 100644
--- a/server/helpers/utils.ts
+++ b/server/helpers/utils.ts
@@ -3,7 +3,8 @@ import { Instance as ParseTorrent } from 'parse-torrent'
3import { join } from 'path' 3import { join } from 'path'
4import { ResultList } from '../../shared' 4import { ResultList } from '../../shared'
5import { CONFIG } from '../initializers/config' 5import { CONFIG } from '../initializers/config'
6import { execPromise, execPromise2, randomBytesPromise, sha256 } from './core-utils' 6import { sha256 } from '@shared/core-utils/crypto'
7import { execPromise, execPromise2, randomBytesPromise } from './core-utils'
7import { logger } from './logger' 8import { logger } from './logger'
8 9
9function deleteFileAndCatch (path: string) { 10function deleteFileAndCatch (path: string) {
diff --git a/server/helpers/webtorrent.ts b/server/helpers/webtorrent.ts
index ecc703646..67cb3971d 100644
--- a/server/helpers/webtorrent.ts
+++ b/server/helpers/webtorrent.ts
@@ -14,7 +14,8 @@ import { MVideo } from '@server/types/models/video/video'
14import { MVideoFile, MVideoFileRedundanciesOpt } from '@server/types/models/video/video-file' 14import { MVideoFile, MVideoFileRedundanciesOpt } from '@server/types/models/video/video-file'
15import { MStreamingPlaylistVideo } from '@server/types/models/video/video-streaming-playlist' 15import { MStreamingPlaylistVideo } from '@server/types/models/video/video-streaming-playlist'
16import { CONFIG } from '../initializers/config' 16import { CONFIG } from '../initializers/config'
17import { promisify2, sha1 } from './core-utils' 17import { promisify2 } from './core-utils'
18import { sha1 } from '@shared/core-utils/crypto'
18import { logger } from './logger' 19import { logger } from './logger'
19import { generateVideoImportTmpPath } from './utils' 20import { generateVideoImportTmpPath } from './utils'
20import { extractVideo } from './video' 21import { extractVideo } from './video'
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index 70179d25c..e3e8c426e 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -6,7 +6,8 @@ import { VideoRedundancyConfigFilter } from '@shared/models/redundancy/video-red
6import { BroadcastMessageLevel } from '@shared/models/server' 6import { BroadcastMessageLevel } from '@shared/models/server'
7import { VideoPrivacy, VideosRedundancyStrategy } from '../../shared/models' 7import { VideoPrivacy, VideosRedundancyStrategy } from '../../shared/models'
8import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type' 8import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
9import { buildPath, parseBytes, parseDurationToMs, root } from '../helpers/core-utils' 9import { buildPath, root } from '../../shared/core-utils'
10import { parseBytes, parseDurationToMs } from '../helpers/core-utils'
10 11
11// Use a variable to reload the configuration if we need 12// Use a variable to reload the configuration if we need
12let config: IConfig = require('config') 13let config: IConfig = require('config')
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 70b8e3d27..026c715c2 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -18,8 +18,9 @@ import { FollowState } from '../../shared/models/actors'
18import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type' 18import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
19import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model' 19import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model'
20import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model' 20import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model'
21import { root } from '../../shared/core-utils'
21// Do not use barrels, remain constants as independent as possible 22// Do not use barrels, remain constants as independent as possible
22import { isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils' 23import { isTestInstance, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
23import { CONFIG, registerConfigChangedHandler } from './config' 24import { CONFIG, registerConfigChangedHandler } from './config'
24 25
25// --------------------------------------------------------------------------- 26// ---------------------------------------------------------------------------
diff --git a/server/initializers/migrations/0080-video-channels.ts b/server/initializers/migrations/0080-video-channels.ts
index 0e6952350..82971c9f5 100644
--- a/server/initializers/migrations/0080-video-channels.ts
+++ b/server/initializers/migrations/0080-video-channels.ts
@@ -1,4 +1,4 @@
1import { buildUUID } from '@server/helpers/uuid' 1import { buildUUID } from '@shared/core-utils/uuid'
2import * as Sequelize from 'sequelize' 2import * as Sequelize from 'sequelize'
3 3
4async function up (utils: { 4async function up (utils: {
diff --git a/server/initializers/migrations/0345-video-playlists.ts b/server/initializers/migrations/0345-video-playlists.ts
index 8dd631dff..5cc52e7ee 100644
--- a/server/initializers/migrations/0345-video-playlists.ts
+++ b/server/initializers/migrations/0345-video-playlists.ts
@@ -1,5 +1,5 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { buildUUID } from '@server/helpers/uuid' 2import { buildUUID } from '@shared/core-utils/uuid'
3import { VideoPlaylistPrivacy, VideoPlaylistType } from '../../../shared/models/videos' 3import { VideoPlaylistPrivacy, VideoPlaylistType } from '../../../shared/models/videos'
4import { WEBSERVER } from '../constants' 4import { WEBSERVER } from '../constants'
5 5
diff --git a/server/initializers/migrations/0560-user-feed-token.ts b/server/initializers/migrations/0560-user-feed-token.ts
index 042301352..961777e35 100644
--- a/server/initializers/migrations/0560-user-feed-token.ts
+++ b/server/initializers/migrations/0560-user-feed-token.ts
@@ -1,5 +1,5 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { buildUUID } from '@server/helpers/uuid' 2import { buildUUID } from '@shared/core-utils/uuid'
3 3
4async function up (utils: { 4async function up (utils: {
5 transaction: Sequelize.Transaction 5 transaction: Sequelize.Transaction
diff --git a/server/lib/activitypub/actors/shared/object-to-model-attributes.ts b/server/lib/activitypub/actors/shared/object-to-model-attributes.ts
index 1612b3ad0..1ad89ac56 100644
--- a/server/lib/activitypub/actors/shared/object-to-model-attributes.ts
+++ b/server/lib/activitypub/actors/shared/object-to-model-attributes.ts
@@ -1,6 +1,6 @@
1import { getLowercaseExtension } from '@server/helpers/core-utils' 1import { getLowercaseExtension } from '@shared/core-utils'
2import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc' 2import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc'
3import { buildUUID } from '@server/helpers/uuid' 3import { buildUUID } from '@shared/core-utils/uuid'
4import { MIMETYPES } from '@server/initializers/constants' 4import { MIMETYPES } from '@server/initializers/constants'
5import { ActorModel } from '@server/models/actor/actor' 5import { ActorModel } from '@server/models/actor/actor'
6import { FilteredModelAttributes } from '@server/types' 6import { FilteredModelAttributes } from '@server/types'
diff --git a/server/lib/auth/oauth.ts b/server/lib/auth/oauth.ts
index 497773536..47bc8c055 100644
--- a/server/lib/auth/oauth.ts
+++ b/server/lib/auth/oauth.ts
@@ -8,7 +8,8 @@ import {
8 UnauthorizedClientError, 8 UnauthorizedClientError,
9 UnsupportedGrantTypeError 9 UnsupportedGrantTypeError
10} from 'oauth2-server' 10} from 'oauth2-server'
11import { randomBytesPromise, sha1 } from '@server/helpers/core-utils' 11import { sha1 } from '@shared/core-utils/crypto'
12import { randomBytesPromise } from '@server/helpers/core-utils'
12import { MOAuthClient } from '@server/types/models' 13import { MOAuthClient } from '@server/types/models'
13import { OAUTH_LIFETIME } from '../../initializers/constants' 14import { OAUTH_LIFETIME } from '../../initializers/constants'
14import { BypassLogin, getClient, getRefreshToken, getUser, revokeToken, saveToken } from './oauth-model' 15import { BypassLogin, getClient, getRefreshToken, getUser, revokeToken, saveToken } from './oauth-model'
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index adc3d712e..dee7ca8ed 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -8,7 +8,8 @@ import { HTMLServerConfig } from '@shared/models'
8import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n' 8import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n'
9import { HttpStatusCode } from '../../shared/models/http/http-error-codes' 9import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
10import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos' 10import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos'
11import { isTestInstance, sha256 } from '../helpers/core-utils' 11import { isTestInstance } from '../helpers/core-utils'
12import { sha256 } from '@shared/core-utils/crypto'
12import { logger } from '../helpers/logger' 13import { logger } from '../helpers/logger'
13import { mdToPlainText } from '../helpers/markdown' 14import { mdToPlainText } from '../helpers/markdown'
14import { CONFIG } from '../initializers/config' 15import { CONFIG } from '../initializers/config'
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts
index 60284ea28..ebad43650 100644
--- a/server/lib/emailer.ts
+++ b/server/lib/emailer.ts
@@ -4,7 +4,8 @@ import { createTransport, Transporter } from 'nodemailer'
4import { join } from 'path' 4import { join } from 'path'
5import { EmailPayload } from '@shared/models' 5import { EmailPayload } from '@shared/models'
6import { SendEmailDefaultOptions } from '../../shared/models/server/emailer.model' 6import { SendEmailDefaultOptions } from '../../shared/models/server/emailer.model'
7import { isTestInstance, root } from '../helpers/core-utils' 7import { isTestInstance } from '../helpers/core-utils'
8import { root } from '@shared/core-utils'
8import { bunyanLogger, logger } from '../helpers/logger' 9import { bunyanLogger, logger } from '../helpers/logger'
9import { CONFIG, isEmailEnabled } from '../initializers/config' 10import { CONFIG, isEmailEnabled } from '../initializers/config'
10import { WEBSERVER } from '../initializers/constants' 11import { WEBSERVER } from '../initializers/constants'
diff --git a/server/lib/hls.ts b/server/lib/hls.ts
index f2fe893a9..220b7733b 100644
--- a/server/lib/hls.ts
+++ b/server/lib/hls.ts
@@ -2,7 +2,7 @@ import { close, ensureDir, move, open, outputJSON, read, readFile, remove, stat,
2import { flatten, uniq } from 'lodash' 2import { flatten, uniq } from 'lodash'
3import { basename, dirname, join } from 'path' 3import { basename, dirname, join } from 'path'
4import { MStreamingPlaylistFilesVideo, MVideo, MVideoUUID } from '@server/types/models' 4import { MStreamingPlaylistFilesVideo, MVideo, MVideoUUID } from '@server/types/models'
5import { sha256 } from '../helpers/core-utils' 5import { sha256 } from '@shared/core-utils/crypto'
6import { getAudioStreamCodec, getVideoStreamCodec, getVideoStreamSize } from '../helpers/ffprobe-utils' 6import { getAudioStreamCodec, getVideoStreamCodec, getVideoStreamSize } from '../helpers/ffprobe-utils'
7import { logger } from '../helpers/logger' 7import { logger } from '../helpers/logger'
8import { doRequest, doRequestAndSaveToFile } from '../helpers/requests' 8import { doRequest, doRequestAndSaveToFile } from '../helpers/requests'
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts
index a91c2ef80..0d9e80cb8 100644
--- a/server/lib/job-queue/handlers/video-file-import.ts
+++ b/server/lib/job-queue/handlers/video-file-import.ts
@@ -1,6 +1,6 @@
1import { Job } from 'bull' 1import { Job } from 'bull'
2import { copy, stat } from 'fs-extra' 2import { copy, stat } from 'fs-extra'
3import { getLowercaseExtension } from '@server/helpers/core-utils' 3import { getLowercaseExtension } from '@shared/core-utils'
4import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' 4import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
5import { CONFIG } from '@server/initializers/config' 5import { CONFIG } from '@server/initializers/config'
6import { federateVideoIfNeeded } from '@server/lib/activitypub/videos' 6import { federateVideoIfNeeded } from '@server/lib/activitypub/videos'
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts
index 4ce1a6c30..e5730e746 100644
--- a/server/lib/job-queue/handlers/video-import.ts
+++ b/server/lib/job-queue/handlers/video-import.ts
@@ -1,6 +1,6 @@
1import { Job } from 'bull' 1import { Job } from 'bull'
2import { move, remove, stat } from 'fs-extra' 2import { move, remove, stat } from 'fs-extra'
3import { getLowercaseExtension } from '@server/helpers/core-utils' 3import { getLowercaseExtension } from '@shared/core-utils'
4import { retryTransactionWrapper } from '@server/helpers/database-utils' 4import { retryTransactionWrapper } from '@server/helpers/database-utils'
5import { YoutubeDLWrapper } from '@server/helpers/youtube-dl' 5import { YoutubeDLWrapper } from '@server/helpers/youtube-dl'
6import { isPostImportVideoAccepted } from '@server/lib/moderation' 6import { isPostImportVideoAccepted } from '@server/lib/moderation'
diff --git a/server/lib/local-actor.ts b/server/lib/local-actor.ts
index 821a92b91..572696f2a 100644
--- a/server/lib/local-actor.ts
+++ b/server/lib/local-actor.ts
@@ -2,8 +2,8 @@ import 'multer'
2import { queue } from 'async' 2import { queue } from 'async'
3import LRUCache from 'lru-cache' 3import LRUCache from 'lru-cache'
4import { join } from 'path' 4import { join } from 'path'
5import { getLowercaseExtension } from '@server/helpers/core-utils' 5import { getLowercaseExtension } from '@shared/core-utils'
6import { buildUUID } from '@server/helpers/uuid' 6import { buildUUID } from '@shared/core-utils/uuid'
7import { ActorModel } from '@server/models/actor/actor' 7import { ActorModel } from '@server/models/actor/actor'
8import { ActivityPubActorType, ActorImageType } from '@shared/models' 8import { ActivityPubActorType, ActorImageType } from '@shared/models'
9import { retryTransactionWrapper } from '../helpers/database-utils' 9import { retryTransactionWrapper } from '../helpers/database-utils'
diff --git a/server/lib/paths.ts b/server/lib/paths.ts
index 434e637c6..d8cf812e3 100644
--- a/server/lib/paths.ts
+++ b/server/lib/paths.ts
@@ -1,5 +1,5 @@
1import { join } from 'path' 1import { join } from 'path'
2import { buildUUID } from '@server/helpers/uuid' 2import { buildUUID } from '@shared/core-utils/uuid'
3import { CONFIG } from '@server/initializers/config' 3import { CONFIG } from '@server/initializers/config'
4import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY } from '@server/initializers/constants' 4import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY } from '@server/initializers/constants'
5import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/types/models' 5import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/types/models'
diff --git a/server/lib/user.ts b/server/lib/user.ts
index 936403692..230bf37d0 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -1,5 +1,5 @@
1import { Transaction } from 'sequelize/types' 1import { Transaction } from 'sequelize/types'
2import { buildUUID } from '@server/helpers/uuid' 2import { buildUUID } from '@shared/core-utils/uuid'
3import { UserModel } from '@server/models/user/user' 3import { UserModel } from '@server/models/user/user'
4import { MActorDefault } from '@server/types/models/actor' 4import { MActorDefault } from '@server/types/models/actor'
5import { ActivityPubActorType } from '../../shared/models/activitypub' 5import { ActivityPubActorType } from '../../shared/models/activitypub'
diff --git a/server/lib/video-path-manager.ts b/server/lib/video-path-manager.ts
index 27058005c..429b36df9 100644
--- a/server/lib/video-path-manager.ts
+++ b/server/lib/video-path-manager.ts
@@ -1,6 +1,6 @@
1import { remove } from 'fs-extra' 1import { remove } from 'fs-extra'
2import { extname, join } from 'path' 2import { extname, join } from 'path'
3import { buildUUID } from '@server/helpers/uuid' 3import { buildUUID } from '@shared/core-utils/uuid'
4import { extractVideo } from '@server/helpers/video' 4import { extractVideo } from '@server/helpers/video'
5import { CONFIG } from '@server/initializers/config' 5import { CONFIG } from '@server/initializers/config'
6import { 6import {
diff --git a/server/models/actor/actor.ts b/server/models/actor/actor.ts
index 8df49951d..cbd3f0e4a 100644
--- a/server/models/actor/actor.ts
+++ b/server/models/actor/actor.ts
@@ -16,9 +16,8 @@ import {
16 Table, 16 Table,
17 UpdatedAt 17 UpdatedAt
18} from 'sequelize-typescript' 18} from 'sequelize-typescript'
19import { getLowercaseExtension } from '@server/helpers/core-utils'
20import { ModelCache } from '@server/models/model-cache' 19import { ModelCache } from '@server/models/model-cache'
21import { AttributesOnly } from '@shared/core-utils' 20import { getLowercaseExtension, AttributesOnly } from '@shared/core-utils'
22import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub' 21import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub'
23import { ActorImage } from '../../../shared/models/actors/actor-image.model' 22import { ActorImage } from '../../../shared/models/actors/actor-image.model'
24import { activityPubContextify } from '../../helpers/activitypub' 23import { activityPubContextify } from '../../helpers/activitypub'
diff --git a/server/models/user/user-notification.ts b/server/models/user/user-notification.ts
index 04c5513a9..55d65d6b2 100644
--- a/server/models/user/user-notification.ts
+++ b/server/models/user/user-notification.ts
@@ -1,6 +1,6 @@
1import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize' 1import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize'
2import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
3import { uuidToShort } from '@server/helpers/uuid' 3import { uuidToShort } from '@shared/core-utils/uuid'
4import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user' 4import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user'
5import { AttributesOnly } from '@shared/core-utils' 5import { AttributesOnly } from '@shared/core-utils'
6import { UserNotification, UserNotificationType } from '../../../shared' 6import { UserNotification, UserNotificationType } from '../../../shared'
diff --git a/server/models/video/formatter/video-format-utils.ts b/server/models/video/formatter/video-format-utils.ts
index fd4da68ed..f6c750ccf 100644
--- a/server/models/video/formatter/video-format-utils.ts
+++ b/server/models/video/formatter/video-format-utils.ts
@@ -1,4 +1,4 @@
1import { uuidToShort } from '@server/helpers/uuid' 1import { uuidToShort } from '@shared/core-utils/uuid'
2import { generateMagnetUri } from '@server/helpers/webtorrent' 2import { generateMagnetUri } from '@server/helpers/webtorrent'
3import { getLocalVideoFileMetadataUrl } from '@server/lib/video-urls' 3import { getLocalVideoFileMetadataUrl } from '@server/lib/video-urls'
4import { VideoViews } from '@server/lib/video-views' 4import { VideoViews } from '@server/lib/video-views'
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts
index d24be56c3..590e72e52 100644
--- a/server/models/video/video-caption.ts
+++ b/server/models/video/video-caption.ts
@@ -15,7 +15,7 @@ import {
15 Table, 15 Table,
16 UpdatedAt 16 UpdatedAt
17} from 'sequelize-typescript' 17} from 'sequelize-typescript'
18import { buildUUID } from '@server/helpers/uuid' 18import { buildUUID } from '@shared/core-utils/uuid'
19import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models' 19import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models'
20import { AttributesOnly } from '@shared/core-utils' 20import { AttributesOnly } from '@shared/core-utils'
21import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' 21import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model'
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index 630684a88..d0c73cbd1 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -17,7 +17,7 @@ import {
17 Table, 17 Table,
18 UpdatedAt 18 UpdatedAt
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { buildUUID, uuidToShort } from '@server/helpers/uuid' 20import { buildUUID, uuidToShort } from '@shared/core-utils/uuid'
21import { MAccountId, MChannelId } from '@server/types/models' 21import { MAccountId, MChannelId } from '@server/types/models'
22import { AttributesOnly, buildPlaylistEmbedPath, buildPlaylistWatchPath, pick } from '@shared/core-utils' 22import { AttributesOnly, buildPlaylistEmbedPath, buildPlaylistWatchPath, pick } from '@shared/core-utils'
23import { ActivityIconObject } from '../../../shared/models/activitypub/objects' 23import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts
index e36852cad..23b3fbcbe 100644
--- a/server/models/video/video-streaming-playlist.ts
+++ b/server/models/video/video-streaming-playlist.ts
@@ -21,7 +21,7 @@ import { MStreamingPlaylist, MVideo } from '@server/types/models'
21import { AttributesOnly } from '@shared/core-utils' 21import { AttributesOnly } from '@shared/core-utils'
22import { VideoStorage } from '@shared/models' 22import { VideoStorage } from '@shared/models'
23import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 23import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
24import { sha1 } from '../../helpers/core-utils' 24import { sha1 } from '@shared/core-utils/crypto'
25import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 25import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
26import { isArrayOf } from '../../helpers/custom-validators/misc' 26import { isArrayOf } from '../../helpers/custom-validators/misc'
27import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' 27import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 1050463d2..efd4d8462 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -25,7 +25,7 @@ import {
25 UpdatedAt 25 UpdatedAt
26} from 'sequelize-typescript' 26} from 'sequelize-typescript'
27import { buildNSFWFilter } from '@server/helpers/express-utils' 27import { buildNSFWFilter } from '@server/helpers/express-utils'
28import { uuidToShort } from '@server/helpers/uuid' 28import { uuidToShort } from '@shared/core-utils/uuid'
29import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video' 29import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
30import { LiveManager } from '@server/lib/live/live-manager' 30import { LiveManager } from '@server/lib/live/live-manager'
31import { removeHLSObjectStorage, removeWebTorrentObjectStorage } from '@server/lib/object-storage' 31import { removeHLSObjectStorage, removeWebTorrentObjectStorage } from '@server/lib/object-storage'
diff --git a/server/tests/api/activitypub/security.ts b/server/tests/api/activitypub/security.ts
index 94d946563..2700cff13 100644
--- a/server/tests/api/activitypub/security.ts
+++ b/server/tests/api/activitypub/security.ts
@@ -7,7 +7,7 @@ import { buildDigest } from '@server/helpers/peertube-crypto'
7import { HTTP_SIGNATURE } from '@server/initializers/constants' 7import { HTTP_SIGNATURE } from '@server/initializers/constants'
8import { buildGlobalHeaders } from '@server/lib/job-queue/handlers/utils/activitypub-http-utils' 8import { buildGlobalHeaders } from '@server/lib/job-queue/handlers/utils/activitypub-http-utils'
9import { buildAbsoluteFixturePath, cleanupTests, createMultipleServers, killallServers, PeerTubeServer, wait } from '@shared/extra-utils' 9import { buildAbsoluteFixturePath, cleanupTests, createMultipleServers, killallServers, PeerTubeServer, wait } from '@shared/extra-utils'
10import { makeFollowRequest, makePOSTAPRequest } from '@shared/extra-utils/requests/activitypub' 10import { makeFollowRequest, makePOSTAPRequest } from '@server/tests/shared'
11import { HttpStatusCode } from '@shared/models' 11import { HttpStatusCode } from '@shared/models'
12 12
13const expect = chai.expect 13const expect = chai.expect
diff --git a/server/tests/api/notifications/moderation-notifications.ts b/server/tests/api/notifications/moderation-notifications.ts
index 81ce8061b..6d8e5359b 100644
--- a/server/tests/api/notifications/moderation-notifications.ts
+++ b/server/tests/api/notifications/moderation-notifications.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
3import 'mocha' 3import 'mocha'
4import { buildUUID } from '@server/helpers/uuid' 4import { buildUUID } from '@shared/core-utils/uuid'
5import { 5import {
6 checkAbuseStateChange, 6 checkAbuseStateChange,
7 checkAutoInstanceFollowing, 7 checkAutoInstanceFollowing,
diff --git a/server/tests/api/notifications/user-notifications.ts b/server/tests/api/notifications/user-notifications.ts
index 9af20843e..6db0347cc 100644
--- a/server/tests/api/notifications/user-notifications.ts
+++ b/server/tests/api/notifications/user-notifications.ts
@@ -2,7 +2,7 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { buildUUID } from '@server/helpers/uuid' 5import { buildUUID } from '@shared/core-utils/uuid'
6import { 6import {
7 CheckerBaseParams, 7 CheckerBaseParams,
8 checkMyVideoImportIsFinished, 8 checkMyVideoImportIsFinished,
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
index 832ba561a..748f4cd35 100644
--- a/server/tests/api/server/follows.ts
+++ b/server/tests/api/server/follows.ts
@@ -4,7 +4,6 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 completeVideoCheck,
8 createMultipleServers, 7 createMultipleServers,
9 dateIsValid, 8 dateIsValid,
10 expectAccountFollows, 9 expectAccountFollows,
@@ -15,6 +14,7 @@ import {
15 waitJobs 14 waitJobs
16} from '@shared/extra-utils' 15} from '@shared/extra-utils'
17import { VideoCreateResult, VideoPrivacy } from '@shared/models' 16import { VideoCreateResult, VideoPrivacy } from '@shared/models'
17import { completeVideoCheck } from '@server/tests/shared/video'
18 18
19const expect = chai.expect 19const expect = chai.expect
20 20
diff --git a/server/tests/api/server/handle-down.ts b/server/tests/api/server/handle-down.ts
index fa1da8fe0..2d059c0ed 100644
--- a/server/tests/api/server/handle-down.ts
+++ b/server/tests/api/server/handle-down.ts
@@ -5,7 +5,6 @@ import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 CommentsCommand, 7 CommentsCommand,
8 completeVideoCheck,
9 createMultipleServers, 8 createMultipleServers,
10 killallServers, 9 killallServers,
11 PeerTubeServer, 10 PeerTubeServer,
@@ -14,6 +13,7 @@ import {
14 waitJobs 13 waitJobs
15} from '@shared/extra-utils' 14} from '@shared/extra-utils'
16import { HttpStatusCode, JobState, VideoCreateResult, VideoPrivacy } from '@shared/models' 15import { HttpStatusCode, JobState, VideoCreateResult, VideoPrivacy } from '@shared/models'
16import { completeVideoCheck } from '@server/tests/shared/video'
17 17
18const expect = chai.expect 18const expect = chai.expect
19 19
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index c6c279064..9e7b39cfa 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -8,7 +8,6 @@ import {
8 checkTmpIsEmpty, 8 checkTmpIsEmpty,
9 checkVideoFilesWereRemoved, 9 checkVideoFilesWereRemoved,
10 cleanupTests, 10 cleanupTests,
11 completeVideoCheck,
12 createMultipleServers, 11 createMultipleServers,
13 dateIsValid, 12 dateIsValid,
14 doubleFollow, 13 doubleFollow,
@@ -21,6 +20,7 @@ import {
21 webtorrentAdd 20 webtorrentAdd
22} from '@shared/extra-utils' 21} from '@shared/extra-utils'
23import { HttpStatusCode, VideoCommentThreadTree, VideoPrivacy } from '@shared/models' 22import { HttpStatusCode, VideoCommentThreadTree, VideoPrivacy } from '@shared/models'
23import { completeVideoCheck } from '@server/tests/shared/video'
24 24
25const expect = chai.expect 25const expect = chai.expect
26 26
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
index a0e4a156c..100067f18 100644
--- a/server/tests/api/videos/single-server.ts
+++ b/server/tests/api/videos/single-server.ts
@@ -5,7 +5,6 @@ import * as chai from 'chai'
5import { 5import {
6 checkVideoFilesWereRemoved, 6 checkVideoFilesWereRemoved,
7 cleanupTests, 7 cleanupTests,
8 completeVideoCheck,
9 createSingleServer, 8 createSingleServer,
10 PeerTubeServer, 9 PeerTubeServer,
11 setAccessTokensToServers, 10 setAccessTokensToServers,
@@ -13,6 +12,7 @@ import {
13 wait 12 wait
14} from '@shared/extra-utils' 13} from '@shared/extra-utils'
15import { Video, VideoPrivacy } from '@shared/models' 14import { Video, VideoPrivacy } from '@shared/models'
15import { completeVideoCheck } from '@server/tests/shared/video'
16 16
17const expect = chai.expect 17const expect = chai.expect
18 18
diff --git a/server/tests/cli/prune-storage.ts b/server/tests/cli/prune-storage.ts
index 1c0282da9..4b4f75527 100644
--- a/server/tests/cli/prune-storage.ts
+++ b/server/tests/cli/prune-storage.ts
@@ -4,7 +4,7 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { createFile, readdir } from 'fs-extra' 5import { createFile, readdir } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { buildUUID } from '@server/helpers/uuid' 7import { buildUUID } from '@shared/core-utils/uuid'
8import { 8import {
9 cleanupTests, 9 cleanupTests,
10 CLICommand, 10 CLICommand,
diff --git a/server/tests/shared/index.ts b/server/tests/shared/index.ts
new file mode 100644
index 000000000..938817268
--- /dev/null
+++ b/server/tests/shared/index.ts
@@ -0,0 +1,2 @@
1export * from './requests'
2export * from './video'
diff --git a/shared/extra-utils/requests/activitypub.ts b/server/tests/shared/requests.ts
index 4ae878384..9eb596029 100644
--- a/shared/extra-utils/requests/activitypub.ts
+++ b/server/tests/shared/requests.ts
@@ -1,9 +1,9 @@
1import { activityPubContextify } from '../../../server/helpers/activitypub' 1import { doRequest } from '@server/helpers/requests'
2import { doRequest } from '../../../server/helpers/requests' 2import { activityPubContextify } from '@server/helpers/activitypub'
3import { HTTP_SIGNATURE } from '../../../server/initializers/constants' 3import { HTTP_SIGNATURE } from '@server/initializers/constants'
4import { buildGlobalHeaders } from '../../../server/lib/job-queue/handlers/utils/activitypub-http-utils' 4import { buildGlobalHeaders } from '@server/lib/job-queue/handlers/utils/activitypub-http-utils'
5 5
6function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) { 6export function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) {
7 const options = { 7 const options = {
8 method: 'POST' as 'POST', 8 method: 'POST' as 'POST',
9 json: body, 9 json: body,
@@ -14,7 +14,7 @@ function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers:
14 return doRequest(url, options) 14 return doRequest(url, options)
15} 15}
16 16
17async function makeFollowRequest (to: { url: string }, by: { url: string, privateKey }) { 17export async function makeFollowRequest (to: { url: string }, by: { url: string, privateKey }) {
18 const follow = { 18 const follow = {
19 type: 'Follow', 19 type: 'Follow',
20 id: by.url + '/' + new Date().getTime(), 20 id: by.url + '/' + new Date().getTime(),
@@ -35,8 +35,3 @@ async function makeFollowRequest (to: { url: string }, by: { url: string, privat
35 35
36 return makePOSTAPRequest(to.url + '/inbox', body, httpSignature, headers) 36 return makePOSTAPRequest(to.url + '/inbox', body, httpSignature, headers)
37} 37}
38
39export {
40 makePOSTAPRequest,
41 makeFollowRequest
42}
diff --git a/server/tests/shared/video.ts b/server/tests/shared/video.ts
new file mode 100644
index 000000000..0e6a00f5c
--- /dev/null
+++ b/server/tests/shared/video.ts
@@ -0,0 +1,148 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions */
2import { dateIsValid, makeRawRequest, PeerTubeServer, testImage, webtorrentAdd } from '@shared/extra-utils'
3import { expect } from 'chai'
4import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '@server/initializers/constants'
5import { getLowercaseExtension, uuidRegex } from '@shared/core-utils'
6
7export async function completeVideoCheck (
8 server: PeerTubeServer,
9 video: any,
10 attributes: {
11 name: string
12 category: number
13 licence: number
14 language: string
15 nsfw: boolean
16 commentsEnabled: boolean
17 downloadEnabled: boolean
18 description: string
19 publishedAt?: string
20 support: string
21 originallyPublishedAt?: string
22 account: {
23 name: string
24 host: string
25 }
26 isLocal: boolean
27 tags: string[]
28 privacy: number
29 likes?: number
30 dislikes?: number
31 duration: number
32 channel: {
33 displayName: string
34 name: string
35 description: string
36 isLocal: boolean
37 }
38 fixture: string
39 files: {
40 resolution: number
41 size: number
42 }[]
43 thumbnailfile?: string
44 previewfile?: string
45 }
46) {
47 if (!attributes.likes) attributes.likes = 0
48 if (!attributes.dislikes) attributes.dislikes = 0
49
50 const host = new URL(server.url).host
51 const originHost = attributes.account.host
52
53 expect(video.name).to.equal(attributes.name)
54 expect(video.category.id).to.equal(attributes.category)
55 expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc')
56 expect(video.licence.id).to.equal(attributes.licence)
57 expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
58 expect(video.language.id).to.equal(attributes.language)
59 expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
60 expect(video.privacy.id).to.deep.equal(attributes.privacy)
61 expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
62 expect(video.nsfw).to.equal(attributes.nsfw)
63 expect(video.description).to.equal(attributes.description)
64 expect(video.account.id).to.be.a('number')
65 expect(video.account.host).to.equal(attributes.account.host)
66 expect(video.account.name).to.equal(attributes.account.name)
67 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
68 expect(video.channel.name).to.equal(attributes.channel.name)
69 expect(video.likes).to.equal(attributes.likes)
70 expect(video.dislikes).to.equal(attributes.dislikes)
71 expect(video.isLocal).to.equal(attributes.isLocal)
72 expect(video.duration).to.equal(attributes.duration)
73 expect(video.url).to.contain(originHost)
74 expect(dateIsValid(video.createdAt)).to.be.true
75 expect(dateIsValid(video.publishedAt)).to.be.true
76 expect(dateIsValid(video.updatedAt)).to.be.true
77
78 if (attributes.publishedAt) {
79 expect(video.publishedAt).to.equal(attributes.publishedAt)
80 }
81
82 if (attributes.originallyPublishedAt) {
83 expect(video.originallyPublishedAt).to.equal(attributes.originallyPublishedAt)
84 } else {
85 expect(video.originallyPublishedAt).to.be.null
86 }
87
88 const videoDetails = await server.videos.get({ id: video.uuid })
89
90 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
91 expect(videoDetails.tags).to.deep.equal(attributes.tags)
92 expect(videoDetails.account.name).to.equal(attributes.account.name)
93 expect(videoDetails.account.host).to.equal(attributes.account.host)
94 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
95 expect(video.channel.name).to.equal(attributes.channel.name)
96 expect(videoDetails.channel.host).to.equal(attributes.account.host)
97 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
98 expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
99 expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
100 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
101 expect(videoDetails.downloadEnabled).to.equal(attributes.downloadEnabled)
102
103 for (const attributeFile of attributes.files) {
104 const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)
105 expect(file).not.to.be.undefined
106
107 let extension = getLowercaseExtension(attributes.fixture)
108 // Transcoding enabled: extension will always be .mp4
109 if (attributes.files.length > 1) extension = '.mp4'
110
111 expect(file.magnetUri).to.have.lengthOf.above(2)
112
113 expect(file.torrentDownloadUrl).to.match(new RegExp(`http://${host}/download/torrents/${uuidRegex}-${file.resolution.id}.torrent`))
114 expect(file.torrentUrl).to.match(new RegExp(`http://${host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}.torrent`))
115
116 expect(file.fileUrl).to.match(new RegExp(`http://${originHost}/static/webseed/${uuidRegex}-${file.resolution.id}${extension}`))
117 expect(file.fileDownloadUrl).to.match(new RegExp(`http://${originHost}/download/videos/${uuidRegex}-${file.resolution.id}${extension}`))
118
119 await Promise.all([
120 makeRawRequest(file.torrentUrl, 200),
121 makeRawRequest(file.torrentDownloadUrl, 200),
122 makeRawRequest(file.metadataUrl, 200)
123 ])
124
125 expect(file.resolution.id).to.equal(attributeFile.resolution)
126 expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
127
128 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
129 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
130 expect(
131 file.size,
132 'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')'
133 ).to.be.above(minSize).and.below(maxSize)
134
135 const torrent = await webtorrentAdd(file.magnetUri, true)
136 expect(torrent.files).to.be.an('array')
137 expect(torrent.files.length).to.equal(1)
138 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
139 }
140
141 expect(videoDetails.thumbnailPath).to.exist
142 await testImage(server.url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
143
144 if (attributes.previewfile) {
145 expect(videoDetails.previewPath).to.exist
146 await testImage(server.url, attributes.previewfile, videoDetails.previewPath)
147 }
148}
diff --git a/server/tools/cli.ts b/server/tools/cli.ts
index 52e6ea593..7c763734f 100644
--- a/server/tools/cli.ts
+++ b/server/tools/cli.ts
@@ -5,7 +5,9 @@ import { createLogger, format, transports } from 'winston'
5import { PeerTubeServer } from '@shared/extra-utils' 5import { PeerTubeServer } from '@shared/extra-utils'
6import { UserRole } from '@shared/models' 6import { UserRole } from '@shared/models'
7import { VideoPrivacy } from '../../shared/models/videos' 7import { VideoPrivacy } from '../../shared/models/videos'
8import { getAppNumber, isTestInstance, root } from '../helpers/core-utils' 8import { getAppNumber, isTestInstance } from '../helpers/core-utils'
9import { root } from '@shared/core-utils'
10import { loadLanguages } from '@server/initializers/constants'
9 11
10let configName = 'PeerTube/CLI' 12let configName = 'PeerTube/CLI'
11if (isTestInstance()) configName += `-${getAppNumber()}` 13if (isTestInstance()) configName += `-${getAppNumber()}`
@@ -180,6 +182,7 @@ function getServerCredentials (program: Command) {
180} 182}
181 183
182function buildServer (url: string) { 184function buildServer (url: string) {
185 loadLanguages()
183 return new PeerTubeServer({ url }) 186 return new PeerTubeServer({ url })
184} 187}
185 188
diff --git a/server/tools/peertube-import-videos.ts b/server/tools/peertube-import-videos.ts
index a758beef9..223bf7f1b 100644
--- a/server/tools/peertube-import-videos.ts
+++ b/server/tools/peertube-import-videos.ts
@@ -5,7 +5,7 @@ import { program } from 'commander'
5import { accessSync, constants } from 'fs' 5import { accessSync, constants } from 'fs'
6import { remove } from 'fs-extra' 6import { remove } from 'fs-extra'
7import { join } from 'path' 7import { join } from 'path'
8import { sha256 } from '../helpers/core-utils' 8import { sha256 } from '@shared/core-utils/crypto'
9import { doRequestAndSaveToFile } from '../helpers/requests' 9import { doRequestAndSaveToFile } from '../helpers/requests'
10import { 10import {
11 assignToken, 11 assignToken,
diff --git a/server/tools/tsconfig.json b/server/tools/tsconfig.json
index 156a8ed22..575133ec8 100644
--- a/server/tools/tsconfig.json
+++ b/server/tools/tsconfig.json
@@ -1,5 +1,11 @@
1{ 1{
2 "extends": "../../tsconfig.json", 2 "extends": "../../tsconfig.json",
3 "compilerOptions": {
4 "outDir": "../../dist/server/tools"
5 },
3 "include": [ ".", "../typings" ], 6 "include": [ ".", "../typings" ],
7 "references": [
8 { "path": "../" },
9 ],
4 "exclude": [ ] // Overwrite exclude property 10 "exclude": [ ] // Overwrite exclude property
5} 11}
diff --git a/server/tsconfig.json b/server/tsconfig.json
new file mode 100644
index 000000000..4be7ae2f4
--- /dev/null
+++ b/server/tsconfig.json
@@ -0,0 +1,12 @@
1{
2 "extends": "../tsconfig.base.json",
3 "compilerOptions": {
4 "outDir": "../dist/server"
5 },
6 "references": [
7 { "path": "../shared" }
8 ],
9 "exclude": [
10 "tools/"
11 ]
12}
diff --git a/shared/core-utils/crypto.ts b/shared/core-utils/crypto.ts
new file mode 100644
index 000000000..d6d1150d0
--- /dev/null
+++ b/shared/core-utils/crypto.ts
@@ -0,0 +1,14 @@
1import { BinaryToTextEncoding, createHash } from 'crypto'
2
3function sha256 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') {
4 return createHash('sha256').update(str).digest(encoding)
5}
6
7function sha1 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') {
8 return createHash('sha1').update(str).digest(encoding)
9}
10
11export {
12 sha256,
13 sha1
14}
diff --git a/shared/core-utils/index.ts b/shared/core-utils/index.ts
index e0a6a8087..ee5cd4412 100644
--- a/shared/core-utils/index.ts
+++ b/shared/core-utils/index.ts
@@ -1,8 +1,10 @@
1export * from './abuse' 1export * from './abuse'
2export * from './common' 2export * from './common'
3export * from './i18n' 3export * from './i18n'
4export * from './path'
4export * from './plugins' 5export * from './plugins'
5export * from './renderer' 6export * from './renderer'
6export * from './users' 7export * from './users'
7export * from './utils' 8export * from './utils'
8export * from './videos' 9export * from './videos'
10export * from './uuid'
diff --git a/shared/core-utils/path.ts b/shared/core-utils/path.ts
new file mode 100644
index 000000000..b1a45d69b
--- /dev/null
+++ b/shared/core-utils/path.ts
@@ -0,0 +1,34 @@
1import { basename, extname, isAbsolute, join, resolve } from 'path'
2
3let rootPath: string
4
5function root () {
6 if (rootPath) return rootPath
7
8 rootPath = __dirname
9
10 if (basename(rootPath) === 'core-utils') rootPath = resolve(rootPath, '..')
11 if (basename(rootPath) === 'shared') rootPath = resolve(rootPath, '..')
12 if (basename(rootPath) === 'server') rootPath = resolve(rootPath, '..')
13 if (basename(rootPath) === 'dist') rootPath = resolve(rootPath, '..')
14
15 return rootPath
16}
17
18function buildPath (path: string) {
19 if (isAbsolute(path)) return path
20
21 return join(root(), path)
22}
23
24function getLowercaseExtension (filename: string) {
25 const ext = extname(filename) || ''
26
27 return ext.toLowerCase()
28}
29
30export {
31 root,
32 buildPath,
33 getLowercaseExtension
34}
diff --git a/server/helpers/uuid.ts b/shared/core-utils/uuid.ts
index f3c80e046..f3c80e046 100644
--- a/server/helpers/uuid.ts
+++ b/shared/core-utils/uuid.ts
diff --git a/shared/extra-utils/ffprobe.ts b/shared/extra-utils/ffprobe.ts
new file mode 100644
index 000000000..9257bbd5f
--- /dev/null
+++ b/shared/extra-utils/ffprobe.ts
@@ -0,0 +1,180 @@
1import { ffprobe, FfprobeData } from 'fluent-ffmpeg'
2import { VideoFileMetadata } from '@shared/models/videos'
3
4/**
5 *
6 * Helpers to run ffprobe and extract data from the JSON output
7 *
8 */
9
10function ffprobePromise (path: string) {
11 return new Promise<FfprobeData>((res, rej) => {
12 ffprobe(path, (err, data) => {
13 if (err) return rej(err)
14
15 return res(data)
16 })
17 })
18}
19
20async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) {
21 // without position, ffprobe considers the last input only
22 // we make it consider the first input only
23 // if you pass a file path to pos, then ffprobe acts on that file directly
24 const data = existingProbe || await ffprobePromise(videoPath)
25
26 if (Array.isArray(data.streams)) {
27 const audioStream = data.streams.find(stream => stream['codec_type'] === 'audio')
28
29 if (audioStream) {
30 return {
31 absolutePath: data.format.filename,
32 audioStream,
33 bitrate: parseInt(audioStream['bit_rate'] + '', 10)
34 }
35 }
36 }
37
38 return { absolutePath: data.format.filename }
39}
40
41function getMaxAudioBitrate (type: 'aac' | 'mp3' | string, bitrate: number) {
42 const maxKBitrate = 384
43 const kToBits = (kbits: number) => kbits * 1000
44
45 // If we did not manage to get the bitrate, use an average value
46 if (!bitrate) return 256
47
48 if (type === 'aac') {
49 switch (true) {
50 case bitrate > kToBits(maxKBitrate):
51 return maxKBitrate
52
53 default:
54 return -1 // we interpret it as a signal to copy the audio stream as is
55 }
56 }
57
58 /*
59 a 192kbit/sec mp3 doesn't hold as much information as a 192kbit/sec aac.
60 That's why, when using aac, we can go to lower kbit/sec. The equivalences
61 made here are not made to be accurate, especially with good mp3 encoders.
62 */
63 switch (true) {
64 case bitrate <= kToBits(192):
65 return 128
66
67 case bitrate <= kToBits(384):
68 return 256
69
70 default:
71 return maxKBitrate
72 }
73}
74
75async function getVideoStreamSize (path: string, existingProbe?: FfprobeData): Promise<{ width: number, height: number }> {
76 const videoStream = await getVideoStreamFromFile(path, existingProbe)
77
78 return videoStream === null
79 ? { width: 0, height: 0 }
80 : { width: videoStream.width, height: videoStream.height }
81}
82
83async function getVideoFileResolution (path: string, existingProbe?: FfprobeData) {
84 const size = await getVideoStreamSize(path, existingProbe)
85
86 return {
87 width: size.width,
88 height: size.height,
89 ratio: Math.max(size.height, size.width) / Math.min(size.height, size.width),
90 resolution: Math.min(size.height, size.width),
91 isPortraitMode: size.height > size.width
92 }
93}
94
95async function getVideoFileFPS (path: string, existingProbe?: FfprobeData) {
96 const videoStream = await getVideoStreamFromFile(path, existingProbe)
97 if (videoStream === null) return 0
98
99 for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) {
100 const valuesText: string = videoStream[key]
101 if (!valuesText) continue
102
103 const [ frames, seconds ] = valuesText.split('/')
104 if (!frames || !seconds) continue
105
106 const result = parseInt(frames, 10) / parseInt(seconds, 10)
107 if (result > 0) return Math.round(result)
108 }
109
110 return 0
111}
112
113async function getMetadataFromFile (path: string, existingProbe?: FfprobeData) {
114 const metadata = existingProbe || await ffprobePromise(path)
115
116 return new VideoFileMetadata(metadata)
117}
118
119async function getVideoFileBitrate (path: string, existingProbe?: FfprobeData): Promise<number> {
120 const metadata = await getMetadataFromFile(path, existingProbe)
121
122 let bitrate = metadata.format.bit_rate as number
123 if (bitrate && !isNaN(bitrate)) return bitrate
124
125 const videoStream = await getVideoStreamFromFile(path, existingProbe)
126 if (!videoStream) return undefined
127
128 bitrate = videoStream?.bit_rate
129 if (bitrate && !isNaN(bitrate)) return bitrate
130
131 return undefined
132}
133
134async function getDurationFromVideoFile (path: string, existingProbe?: FfprobeData) {
135 const metadata = await getMetadataFromFile(path, existingProbe)
136
137 return Math.round(metadata.format.duration)
138}
139
140async function getVideoStreamFromFile (path: string, existingProbe?: FfprobeData) {
141 const metadata = await getMetadataFromFile(path, existingProbe)
142
143 return metadata.streams.find(s => s.codec_type === 'video') || null
144}
145
146async function canDoQuickAudioTranscode (path: string, probe?: FfprobeData): Promise<boolean> {
147 const parsedAudio = await getAudioStream(path, probe)
148
149 if (!parsedAudio.audioStream) return true
150
151 if (parsedAudio.audioStream['codec_name'] !== 'aac') return false
152
153 const audioBitrate = parsedAudio.bitrate
154 if (!audioBitrate) return false
155
156 const maxAudioBitrate = getMaxAudioBitrate('aac', audioBitrate)
157 if (maxAudioBitrate !== -1 && audioBitrate > maxAudioBitrate) return false
158
159 const channelLayout = parsedAudio.audioStream['channel_layout']
160 // Causes playback issues with Chrome
161 if (!channelLayout || channelLayout === 'unknown') return false
162
163 return true
164}
165
166// ---------------------------------------------------------------------------
167
168export {
169 getVideoStreamSize,
170 getVideoFileResolution,
171 getMetadataFromFile,
172 getMaxAudioBitrate,
173 getVideoStreamFromFile,
174 getDurationFromVideoFile,
175 getAudioStream,
176 getVideoFileFPS,
177 ffprobePromise,
178 getVideoFileBitrate,
179 canDoQuickAudioTranscode
180}
diff --git a/shared/extra-utils/miscs/checks.ts b/shared/extra-utils/miscs/checks.ts
index b1be214b1..589928997 100644
--- a/shared/extra-utils/miscs/checks.ts
+++ b/shared/extra-utils/miscs/checks.ts
@@ -3,7 +3,7 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { pathExists, readFile } from 'fs-extra' 4import { pathExists, readFile } from 'fs-extra'
5import { join } from 'path' 5import { join } from 'path'
6import { root } from '@server/helpers/core-utils' 6import { root } from '@shared/core-utils'
7import { HttpStatusCode } from '@shared/models' 7import { HttpStatusCode } from '@shared/models'
8import { makeGetRequest } from '../requests' 8import { makeGetRequest } from '../requests'
9import { PeerTubeServer } from '../server' 9import { PeerTubeServer } from '../server'
diff --git a/shared/extra-utils/miscs/generate.ts b/shared/extra-utils/miscs/generate.ts
index 3b29c0ad4..93673a063 100644
--- a/shared/extra-utils/miscs/generate.ts
+++ b/shared/extra-utils/miscs/generate.ts
@@ -2,7 +2,7 @@ import { expect } from 'chai'
2import ffmpeg from 'fluent-ffmpeg' 2import ffmpeg from 'fluent-ffmpeg'
3import { ensureDir, pathExists } from 'fs-extra' 3import { ensureDir, pathExists } from 'fs-extra'
4import { dirname } from 'path' 4import { dirname } from 'path'
5import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '@server/helpers/ffprobe-utils' 5import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '@shared/extra-utils/ffprobe'
6import { getMaxBitrate } from '@shared/core-utils' 6import { getMaxBitrate } from '@shared/core-utils'
7import { buildAbsoluteFixturePath } from './tests' 7import { buildAbsoluteFixturePath } from './tests'
8 8
diff --git a/shared/extra-utils/server/directories.ts b/shared/extra-utils/server/directories.ts
index b6465cbf4..e6f72d6fc 100644
--- a/shared/extra-utils/server/directories.ts
+++ b/shared/extra-utils/server/directories.ts
@@ -3,7 +3,7 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { pathExists, readdir } from 'fs-extra' 4import { pathExists, readdir } from 'fs-extra'
5import { join } from 'path' 5import { join } from 'path'
6import { root } from '@server/helpers/core-utils' 6import { root } from '@shared/core-utils'
7import { PeerTubeServer } from './server' 7import { PeerTubeServer } from './server'
8 8
9async function checkTmpIsEmpty (server: PeerTubeServer) { 9async function checkTmpIsEmpty (server: PeerTubeServer) {
diff --git a/shared/extra-utils/server/plugins-command.ts b/shared/extra-utils/server/plugins-command.ts
index 9bf24afff..1c44711da 100644
--- a/shared/extra-utils/server/plugins-command.ts
+++ b/shared/extra-utils/server/plugins-command.ts
@@ -2,7 +2,7 @@
2 2
3import { readJSON, writeJSON } from 'fs-extra' 3import { readJSON, writeJSON } from 'fs-extra'
4import { join } from 'path' 4import { join } from 'path'
5import { root } from '@server/helpers/core-utils' 5import { root } from '@shared/core-utils'
6import { 6import {
7 HttpStatusCode, 7 HttpStatusCode,
8 PeerTubePlugin, 8 PeerTubePlugin,
diff --git a/shared/extra-utils/server/server.ts b/shared/extra-utils/server/server.ts
index 9da293877..339b9cabb 100644
--- a/shared/extra-utils/server/server.ts
+++ b/shared/extra-utils/server/server.ts
@@ -1,8 +1,7 @@
1import { ChildProcess, fork } from 'child_process' 1import { ChildProcess, fork } from 'child_process'
2import { copy } from 'fs-extra' 2import { copy } from 'fs-extra'
3import { join } from 'path' 3import { join } from 'path'
4import { root } from '@server/helpers/core-utils' 4import { root, randomInt } from '@shared/core-utils'
5import { randomInt } from '@shared/core-utils'
6import { Video, VideoChannel, VideoCreateResult, VideoDetails } from '../../models/videos' 5import { Video, VideoChannel, VideoCreateResult, VideoDetails } from '../../models/videos'
7import { BulkCommand } from '../bulk' 6import { BulkCommand } from '../bulk'
8import { CLICommand } from '../cli' 7import { CLICommand } from '../cli'
diff --git a/shared/extra-utils/server/servers-command.ts b/shared/extra-utils/server/servers-command.ts
index 776d2123c..47420c95f 100644
--- a/shared/extra-utils/server/servers-command.ts
+++ b/shared/extra-utils/server/servers-command.ts
@@ -1,7 +1,7 @@
1import { exec } from 'child_process' 1import { exec } from 'child_process'
2import { copy, ensureDir, readFile, remove } from 'fs-extra' 2import { copy, ensureDir, readFile, remove } from 'fs-extra'
3import { basename, join } from 'path' 3import { basename, join } from 'path'
4import { root } from '@server/helpers/core-utils' 4import { root } from '@shared/core-utils'
5import { HttpStatusCode } from '@shared/models' 5import { HttpStatusCode } from '@shared/models'
6import { getFileSize, isGithubCI, wait } from '../miscs' 6import { getFileSize, isGithubCI, wait } from '../miscs'
7import { AbstractCommand, OverrideCommandOptions } from '../shared' 7import { AbstractCommand, OverrideCommandOptions } from '../shared'
diff --git a/shared/extra-utils/server/tracker.ts b/shared/extra-utils/server/tracker.ts
index f04e8f8a1..ed43a5924 100644
--- a/shared/extra-utils/server/tracker.ts
+++ b/shared/extra-utils/server/tracker.ts
@@ -1,5 +1,5 @@
1import { expect } from 'chai' 1import { expect } from 'chai'
2import { sha1 } from '@server/helpers/core-utils' 2import { sha1 } from '@shared/core-utils/crypto'
3import { makeGetRequest } from '../requests' 3import { makeGetRequest } from '../requests'
4 4
5async function hlsInfohashExist (serverUrl: string, masterPlaylistUrl: string, fileNumber: number) { 5async function hlsInfohashExist (serverUrl: string, masterPlaylistUrl: string, fileNumber: number) {
diff --git a/shared/extra-utils/users/actors.ts b/shared/extra-utils/users/actors.ts
index cfcc7d0a7..12c3e078a 100644
--- a/shared/extra-utils/users/actors.ts
+++ b/shared/extra-utils/users/actors.ts
@@ -3,7 +3,7 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { pathExists, readdir } from 'fs-extra' 4import { pathExists, readdir } from 'fs-extra'
5import { join } from 'path' 5import { join } from 'path'
6import { root } from '@server/helpers/core-utils' 6import { root } from '@shared/core-utils'
7import { Account, VideoChannel } from '@shared/models' 7import { Account, VideoChannel } from '@shared/models'
8import { PeerTubeServer } from '../server' 8import { PeerTubeServer } from '../server'
9 9
diff --git a/shared/extra-utils/videos/streaming-playlists.ts b/shared/extra-utils/videos/streaming-playlists.ts
index 6671e3fa6..0451c0efe 100644
--- a/shared/extra-utils/videos/streaming-playlists.ts
+++ b/shared/extra-utils/videos/streaming-playlists.ts
@@ -1,6 +1,6 @@
1import { expect } from 'chai' 1import { expect } from 'chai'
2import { basename } from 'path' 2import { basename } from 'path'
3import { sha256 } from '@server/helpers/core-utils' 3import { sha256 } from '@shared/core-utils/crypto'
4import { removeFragmentedMP4Ext } from '@shared/core-utils' 4import { removeFragmentedMP4Ext } from '@shared/core-utils'
5import { HttpStatusCode, VideoStreamingPlaylist } from '@shared/models' 5import { HttpStatusCode, VideoStreamingPlaylist } from '@shared/models'
6import { PeerTubeServer } from '../server' 6import { PeerTubeServer } from '../server'
diff --git a/shared/extra-utils/videos/videos-command.ts b/shared/extra-utils/videos/videos-command.ts
index 7ec9c3647..8ea828b40 100644
--- a/shared/extra-utils/videos/videos-command.ts
+++ b/shared/extra-utils/videos/videos-command.ts
@@ -5,8 +5,7 @@ import { createReadStream, stat } from 'fs-extra'
5import got, { Response as GotResponse } from 'got' 5import got, { Response as GotResponse } from 'got'
6import { omit } from 'lodash' 6import { omit } from 'lodash'
7import validator from 'validator' 7import validator from 'validator'
8import { buildUUID } from '@server/helpers/uuid' 8import { buildUUID } from '@shared/core-utils/uuid'
9import { loadLanguages } from '@server/initializers/constants'
10import { pick } from '@shared/core-utils' 9import { pick } from '@shared/core-utils'
11import { 10import {
12 HttpStatusCode, 11 HttpStatusCode,
@@ -23,7 +22,7 @@ import {
23} from '@shared/models' 22} from '@shared/models'
24import { buildAbsoluteFixturePath, wait } from '../miscs' 23import { buildAbsoluteFixturePath, wait } from '../miscs'
25import { unwrapBody } from '../requests' 24import { unwrapBody } from '../requests'
26import { PeerTubeServer, waitJobs } from '../server' 25import { waitJobs } from '../server'
27import { AbstractCommand, OverrideCommandOptions } from '../shared' 26import { AbstractCommand, OverrideCommandOptions } from '../shared'
28 27
29export type VideoEdit = Partial<Omit<VideoCreate, 'thumbnailfile' | 'previewfile'>> & { 28export type VideoEdit = Partial<Omit<VideoCreate, 'thumbnailfile' | 'previewfile'>> & {
@@ -33,13 +32,6 @@ export type VideoEdit = Partial<Omit<VideoCreate, 'thumbnailfile' | 'previewfile
33} 32}
34 33
35export class VideosCommand extends AbstractCommand { 34export class VideosCommand extends AbstractCommand {
36
37 constructor (server: PeerTubeServer) {
38 super(server)
39
40 loadLanguages()
41 }
42
43 getCategories (options: OverrideCommandOptions = {}) { 35 getCategories (options: OverrideCommandOptions = {}) {
44 const path = '/api/v1/videos/categories' 36 const path = '/api/v1/videos/categories'
45 37
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts
index c05c2be6c..2c3464aa8 100644
--- a/shared/extra-utils/videos/videos.ts
+++ b/shared/extra-utils/videos/videos.ts
@@ -3,12 +3,7 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { pathExists, readdir } from 'fs-extra' 4import { pathExists, readdir } from 'fs-extra'
5import { basename, join } from 'path' 5import { basename, join } from 'path'
6import { getLowercaseExtension } from '@server/helpers/core-utils'
7import { uuidRegex } from '@shared/core-utils'
8import { HttpStatusCode, VideoCaption, VideoDetails } from '@shared/models' 6import { HttpStatusCode, VideoCaption, VideoDetails } from '@shared/models'
9import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
10import { dateIsValid, testImage, webtorrentAdd } from '../miscs'
11import { makeRawRequest } from '../requests/requests'
12import { waitJobs } from '../server' 7import { waitJobs } from '../server'
13import { PeerTubeServer } from '../server/server' 8import { PeerTubeServer } from '../server/server'
14import { VideoEdit } from './videos-command' 9import { VideoEdit } from './videos-command'
@@ -85,150 +80,6 @@ function checkUploadVideoParam (
85 : server.videos.buildResumeUpload({ token, attributes, expectedStatus }) 80 : server.videos.buildResumeUpload({ token, attributes, expectedStatus })
86} 81}
87 82
88async function completeVideoCheck (
89 server: PeerTubeServer,
90 video: any,
91 attributes: {
92 name: string
93 category: number
94 licence: number
95 language: string
96 nsfw: boolean
97 commentsEnabled: boolean
98 downloadEnabled: boolean
99 description: string
100 publishedAt?: string
101 support: string
102 originallyPublishedAt?: string
103 account: {
104 name: string
105 host: string
106 }
107 isLocal: boolean
108 tags: string[]
109 privacy: number
110 likes?: number
111 dislikes?: number
112 duration: number
113 channel: {
114 displayName: string
115 name: string
116 description: string
117 isLocal: boolean
118 }
119 fixture: string
120 files: {
121 resolution: number
122 size: number
123 }[]
124 thumbnailfile?: string
125 previewfile?: string
126 }
127) {
128 if (!attributes.likes) attributes.likes = 0
129 if (!attributes.dislikes) attributes.dislikes = 0
130
131 const host = new URL(server.url).host
132 const originHost = attributes.account.host
133
134 expect(video.name).to.equal(attributes.name)
135 expect(video.category.id).to.equal(attributes.category)
136 expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc')
137 expect(video.licence.id).to.equal(attributes.licence)
138 expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
139 expect(video.language.id).to.equal(attributes.language)
140 expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
141 expect(video.privacy.id).to.deep.equal(attributes.privacy)
142 expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
143 expect(video.nsfw).to.equal(attributes.nsfw)
144 expect(video.description).to.equal(attributes.description)
145 expect(video.account.id).to.be.a('number')
146 expect(video.account.host).to.equal(attributes.account.host)
147 expect(video.account.name).to.equal(attributes.account.name)
148 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
149 expect(video.channel.name).to.equal(attributes.channel.name)
150 expect(video.likes).to.equal(attributes.likes)
151 expect(video.dislikes).to.equal(attributes.dislikes)
152 expect(video.isLocal).to.equal(attributes.isLocal)
153 expect(video.duration).to.equal(attributes.duration)
154 expect(video.url).to.contain(originHost)
155 expect(dateIsValid(video.createdAt)).to.be.true
156 expect(dateIsValid(video.publishedAt)).to.be.true
157 expect(dateIsValid(video.updatedAt)).to.be.true
158
159 if (attributes.publishedAt) {
160 expect(video.publishedAt).to.equal(attributes.publishedAt)
161 }
162
163 if (attributes.originallyPublishedAt) {
164 expect(video.originallyPublishedAt).to.equal(attributes.originallyPublishedAt)
165 } else {
166 expect(video.originallyPublishedAt).to.be.null
167 }
168
169 const videoDetails = await server.videos.get({ id: video.uuid })
170
171 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
172 expect(videoDetails.tags).to.deep.equal(attributes.tags)
173 expect(videoDetails.account.name).to.equal(attributes.account.name)
174 expect(videoDetails.account.host).to.equal(attributes.account.host)
175 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
176 expect(video.channel.name).to.equal(attributes.channel.name)
177 expect(videoDetails.channel.host).to.equal(attributes.account.host)
178 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
179 expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
180 expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
181 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
182 expect(videoDetails.downloadEnabled).to.equal(attributes.downloadEnabled)
183
184 for (const attributeFile of attributes.files) {
185 const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)
186 expect(file).not.to.be.undefined
187
188 let extension = getLowercaseExtension(attributes.fixture)
189 // Transcoding enabled: extension will always be .mp4
190 if (attributes.files.length > 1) extension = '.mp4'
191
192 expect(file.magnetUri).to.have.lengthOf.above(2)
193
194 expect(file.torrentDownloadUrl).to.match(new RegExp(`http://${host}/download/torrents/${uuidRegex}-${file.resolution.id}.torrent`))
195 expect(file.torrentUrl).to.match(new RegExp(`http://${host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}.torrent`))
196
197 expect(file.fileUrl).to.match(new RegExp(`http://${originHost}/static/webseed/${uuidRegex}-${file.resolution.id}${extension}`))
198 expect(file.fileDownloadUrl).to.match(new RegExp(`http://${originHost}/download/videos/${uuidRegex}-${file.resolution.id}${extension}`))
199
200 await Promise.all([
201 makeRawRequest(file.torrentUrl, 200),
202 makeRawRequest(file.torrentDownloadUrl, 200),
203 makeRawRequest(file.metadataUrl, 200)
204 ])
205
206 expect(file.resolution.id).to.equal(attributeFile.resolution)
207 expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
208
209 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
210 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
211 expect(
212 file.size,
213 'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')'
214 ).to.be.above(minSize).and.below(maxSize)
215
216 const torrent = await webtorrentAdd(file.magnetUri, true)
217 expect(torrent.files).to.be.an('array')
218 expect(torrent.files.length).to.equal(1)
219 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
220 expect(torrent.files[0].name).to.equal(`${videoDetails.name} ${file.resolution.id}p${extension}`)
221 }
222
223 expect(videoDetails.thumbnailPath).to.exist
224 await testImage(server.url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
225
226 if (attributes.previewfile) {
227 expect(videoDetails.previewPath).to.exist
228 await testImage(server.url, attributes.previewfile, videoDetails.previewPath)
229 }
230}
231
232// serverNumber starts from 1 83// serverNumber starts from 1
233async function uploadRandomVideoOnServers ( 84async function uploadRandomVideoOnServers (
234 servers: PeerTubeServer[], 85 servers: PeerTubeServer[],
@@ -247,7 +98,6 @@ async function uploadRandomVideoOnServers (
247 98
248export { 99export {
249 checkUploadVideoParam, 100 checkUploadVideoParam,
250 completeVideoCheck,
251 uploadRandomVideoOnServers, 101 uploadRandomVideoOnServers,
252 checkVideoFilesWereRemoved, 102 checkVideoFilesWereRemoved,
253 saveVideoInServers 103 saveVideoInServers
diff --git a/shared/tsconfig.json b/shared/tsconfig.json
new file mode 100644
index 000000000..95892077b
--- /dev/null
+++ b/shared/tsconfig.json
@@ -0,0 +1,6 @@
1{
2 "extends": "../tsconfig.base.json",
3 "compilerOptions": {
4 "outDir": "../dist/shared"
5 }
6}
diff --git a/tsconfig.base.json b/tsconfig.base.json
new file mode 100644
index 000000000..ef86b9797
--- /dev/null
+++ b/tsconfig.base.json
@@ -0,0 +1,35 @@
1{
2 "compilerOptions": {
3 "module": "commonjs",
4 "target": "es2015",
5 "noImplicitAny": false,
6 "sourceMap": false,
7 "experimentalDecorators": true,
8 "emitDecoratorMetadata": true,
9 "importHelpers": true,
10 "removeComments": true,
11 "strictBindCallApply": true,
12 "esModuleInterop": true,
13 "forceConsistentCasingInFileNames": true,
14 "lib": [
15 "es2015",
16 "es2016",
17 "es2017",
18 "es2018",
19 "es2019"
20 ],
21 "typeRoots": [
22 "node_modules/@types",
23 ],
24 "baseUrl": "./",
25 "outDir": "./dist/",
26 "paths": {
27 "@server/*": [ "server/*" ],
28 "@shared/*": [ "shared/*" ]
29 },
30 "resolveJsonModule": true,
31 "strict": false,
32 "skipLibCheck": true,
33 "composite": true
34 }
35}
diff --git a/tsconfig.json b/tsconfig.json
index 075a3d86e..a14a97dfb 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,46 +1,9 @@
1{ 1{
2 "compilerOptions": { 2 "extends": "./tsconfig.base.json",
3 "module": "commonjs", 3 "references": [
4 "target": "es2015", 4 { "path": "./shared" },
5 "noImplicitAny": false, 5 { "path": "./server" },
6 "sourceMap": false, 6 { "path": "./scripts" }
7 "experimentalDecorators": true, 7 ],
8 "emitDecoratorMetadata": true, 8 "files": ["server.ts"]
9 "importHelpers": true,
10 "removeComments": true,
11 "strictBindCallApply": true,
12 "esModuleInterop": true,
13 "forceConsistentCasingInFileNames": true,
14 "outDir": "./dist",
15 "lib": [
16 "dom",
17 "es2015",
18 "es2016",
19 "es2017",
20 "es2018",
21 "es2019"
22 ],
23 "typeRoots": [
24 "node_modules/@types",
25 "server/typings"
26 ],
27 "baseUrl": "./",
28 "paths": {
29 "@server/*": [ "server/*" ],
30 "@shared/*": [ "shared/*" ]
31 }
32 },
33 "exclude": [
34 "server/tools/",
35 "node_modules",
36 "dist",
37 "storage",
38 "client",
39 "test1",
40 "test2",
41 "test3",
42 "test4",
43 "test5",
44 "test6"
45 ]
46} 9}
diff --git a/tsconfig.types.json b/tsconfig.types.json
new file mode 100644
index 000000000..c9447d86d
--- /dev/null
+++ b/tsconfig.types.json
@@ -0,0 +1,19 @@
1{
2 "extends": "./tsconfig.base.json",
3 "compilerOptions": {
4 "incremental": true,
5 "sourceMap": true,
6 "stripInternal": true,
7 "removeComments": false,
8 "declaration": true,
9 "declarationMap": true,
10 "emitDeclarationOnly": true
11 },
12 "references": [
13 { "path": "./shared/tsconfig.types.json" },
14 { "path": "./server/tsconfig.types.json" },
15 { "path": "./scripts/tsconfig.types.json" }
16 ],
17 "files": []
18}
19