aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-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/uuid.ts32
-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.ts37
-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
53 files changed, 283 insertions, 288 deletions
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/uuid.ts b/server/helpers/uuid.ts
deleted file mode 100644
index f3c80e046..000000000
--- a/server/helpers/uuid.ts
+++ /dev/null
@@ -1,32 +0,0 @@
1import short, { uuid } from 'short-uuid'
2
3const translator = short()
4
5function buildUUID () {
6 return uuid()
7}
8
9function uuidToShort (uuid: string) {
10 if (!uuid) return uuid
11
12 return translator.fromUUID(uuid)
13}
14
15function shortToUUID (shortUUID: string) {
16 if (!shortUUID) return shortUUID
17
18 return translator.toUUID(shortUUID)
19}
20
21function isShortUUID (value: string) {
22 if (!value) return false
23
24 return value.length === translator.maxLength
25}
26
27export {
28 buildUUID,
29 uuidToShort,
30 shortToUUID,
31 isShortUUID
32}
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/server/tests/shared/requests.ts b/server/tests/shared/requests.ts
new file mode 100644
index 000000000..9eb596029
--- /dev/null
+++ b/server/tests/shared/requests.ts
@@ -0,0 +1,37 @@
1import { doRequest } from '@server/helpers/requests'
2import { activityPubContextify } from '@server/helpers/activitypub'
3import { HTTP_SIGNATURE } from '@server/initializers/constants'
4import { buildGlobalHeaders } from '@server/lib/job-queue/handlers/utils/activitypub-http-utils'
5
6export function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) {
7 const options = {
8 method: 'POST' as 'POST',
9 json: body,
10 httpSignature,
11 headers
12 }
13
14 return doRequest(url, options)
15}
16
17export async function makeFollowRequest (to: { url: string }, by: { url: string, privateKey }) {
18 const follow = {
19 type: 'Follow',
20 id: by.url + '/' + new Date().getTime(),
21 actor: by.url,
22 object: to.url
23 }
24
25 const body = activityPubContextify(follow)
26
27 const httpSignature = {
28 algorithm: HTTP_SIGNATURE.ALGORITHM,
29 authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
30 keyId: by.url,
31 key: by.privateKey,
32 headers: HTTP_SIGNATURE.HEADERS_TO_SIGN
33 }
34 const headers = buildGlobalHeaders(body)
35
36 return makePOSTAPRequest(to.url + '/inbox', body, httpSignature, headers)
37}
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}