]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
chore(refactor): remove shared folder dependencies to the server
authorlutangar <johan.dufour@gmail.com>
Tue, 2 Nov 2021 18:11:20 +0000 (19:11 +0100)
committerChocobozzz <chocobozzz@cpy.re>
Thu, 16 Dec 2021 09:08:43 +0000 (10:08 +0100)
Many files from the `shared` folder were importing files from the `server` folder.
When attempting to use Typescript project references to describe dependencies,
it highlighted a circular dependency beetween `shared` <-> `server`.

The Typescript project forbid such usages.
Using project references greatly improve performance by rebuilding only
the updated project and not all source files.
> see https://www.typescriptlang.org/docs/handbook/project-references.html

78 files changed:
.eslintrc.json
.gitignore
scripts/build/server.sh
scripts/client-build-stats.ts
scripts/setup/cli.sh
scripts/tsconfig.json [new file with mode: 0644]
server/controllers/api/users/token.ts
server/controllers/api/video-playlist.ts
server/controllers/api/videos/live.ts
server/controllers/api/videos/upload.ts
server/controllers/client.ts
server/controllers/static.ts
server/helpers/core-utils.ts
server/helpers/custom-validators/misc.ts
server/helpers/express-utils.ts
server/helpers/ffprobe-utils.ts
server/helpers/image-utils.ts
server/helpers/peertube-crypto.ts
server/helpers/utils.ts
server/helpers/webtorrent.ts
server/initializers/config.ts
server/initializers/constants.ts
server/initializers/migrations/0080-video-channels.ts
server/initializers/migrations/0345-video-playlists.ts
server/initializers/migrations/0560-user-feed-token.ts
server/lib/activitypub/actors/shared/object-to-model-attributes.ts
server/lib/auth/oauth.ts
server/lib/client-html.ts
server/lib/emailer.ts
server/lib/hls.ts
server/lib/job-queue/handlers/video-file-import.ts
server/lib/job-queue/handlers/video-import.ts
server/lib/local-actor.ts
server/lib/paths.ts
server/lib/user.ts
server/lib/video-path-manager.ts
server/models/actor/actor.ts
server/models/user/user-notification.ts
server/models/video/formatter/video-format-utils.ts
server/models/video/video-caption.ts
server/models/video/video-playlist.ts
server/models/video/video-streaming-playlist.ts
server/models/video/video.ts
server/tests/api/activitypub/security.ts
server/tests/api/notifications/moderation-notifications.ts
server/tests/api/notifications/user-notifications.ts
server/tests/api/server/follows.ts
server/tests/api/server/handle-down.ts
server/tests/api/videos/multiple-servers.ts
server/tests/api/videos/single-server.ts
server/tests/cli/prune-storage.ts
server/tests/shared/index.ts [new file with mode: 0644]
server/tests/shared/requests.ts [moved from shared/extra-utils/requests/activitypub.ts with 54% similarity]
server/tests/shared/video.ts [new file with mode: 0644]
server/tools/cli.ts
server/tools/peertube-import-videos.ts
server/tools/tsconfig.json
server/tsconfig.json [new file with mode: 0644]
shared/core-utils/crypto.ts [new file with mode: 0644]
shared/core-utils/index.ts
shared/core-utils/path.ts [new file with mode: 0644]
shared/core-utils/uuid.ts [moved from server/helpers/uuid.ts with 100% similarity]
shared/extra-utils/ffprobe.ts [new file with mode: 0644]
shared/extra-utils/miscs/checks.ts
shared/extra-utils/miscs/generate.ts
shared/extra-utils/server/directories.ts
shared/extra-utils/server/plugins-command.ts
shared/extra-utils/server/server.ts
shared/extra-utils/server/servers-command.ts
shared/extra-utils/server/tracker.ts
shared/extra-utils/users/actors.ts
shared/extra-utils/videos/streaming-playlists.ts
shared/extra-utils/videos/videos-command.ts
shared/extra-utils/videos/videos.ts
shared/tsconfig.json [new file with mode: 0644]
tsconfig.base.json [new file with mode: 0644]
tsconfig.json
tsconfig.types.json [new file with mode: 0644]

index a49a9e71b4a190cfc2704f039d0c94b5c60c8bab..c7597cef3dbe5bc45c3b41f4f2c3889a8d60b8d1 100644 (file)
   "parserOptions": {
     "project": [
       "./tsconfig.json",
+      "./shared/tsconfig.json",
+      "./scripts/tsconfig.json",
+      "./server/tsconfig.json",
       "./server/tools/tsconfig.json"
     ]
   }
index 3027b6058064a3a6aa4f7a35731aa73710e1b73a..cd7d3f59be9b0f84d6ee9e2e9e5bd8c52068f9db 100644 (file)
@@ -50,3 +50,6 @@ yarn-error.log
 /server/tools/import-mediacore.ts
 /docker-volume/
 /init.mp4
+
+# TypeScript
+*.tsbuildinfo
index b903f8250b3b410b1cbf4c1de27015f163a708e5..865bdd095d3ec593691870975af48b119cad5553 100755 (executable)
@@ -4,7 +4,10 @@ set -eu
 
 rm -rf ./dist
 
-npm run tsc
-cp "./tsconfig.json" "./dist"
+npm run tsc -- -b --verbose
+cp "./tsconfig.base.json" "./tsconfig.json" "./dist/"
+cp "./scripts/tsconfig.json" "./dist/scripts/"
+cp "./server/tsconfig.json" "./dist/server/"
+cp "./shared/tsconfig.json" "./dist/shared/"
 cp -r "./server/static" "./server/assets" "./dist/server"
 cp -r "./server/lib/emails" "./dist/server/lib"
index 70ceda975a25dd0faaefb5f8975a47cd7f996741..91844dfcd7f6938dfdae4d7e382de8c14e70644b 100644 (file)
@@ -3,7 +3,7 @@ registerTSPaths()
 
 import { readdir, stat } from 'fs-extra'
 import { join } from 'path'
-import { root } from '@server/helpers/core-utils'
+import { root } from '@shared/core-utils'
 
 async function run () {
   const result = {
index ea327e5a1eb354ceeb4ae6e26170429e2fe7adfb..d0ad2ec5f33067763a84edc30805560ed8fec73e 100755 (executable)
@@ -11,6 +11,6 @@ rm -rf ./dist/server/tools/
     yarn install --pure-lockfile
 )
 
-npm run tsc -- --build ./server/tools/tsconfig.json
+npm run tsc -- --build --verbose ./server/tools/tsconfig.json
 cp -r "./server/tools/node_modules" "./dist/server/tools"
 cp "./tsconfig.json" "./dist"
diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json
new file mode 100644 (file)
index 0000000..0d9716f
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "extends": "../tsconfig.base.json",
+  "compilerOptions": {
+    "outDir": "../dist/scripts",
+  },
+  "references": [
+    { "path": "../shared" },
+    { "path": "../server" }
+  ]
+}
index 1d4004ce02db4554fa8b8c73215017ccb50095b9..42f4f6096ff26af67b461a13ee9adeb64d7c39d4 100644 (file)
@@ -1,7 +1,7 @@
 import express from 'express'
 import RateLimit from 'express-rate-limit'
 import { logger } from '@server/helpers/logger'
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 import { CONFIG } from '@server/initializers/config'
 import { getAuthNameFromRefreshGrant, getBypassFromExternalAuth, getBypassFromPasswordGrant } from '@server/lib/auth/external-auth'
 import { handleOAuthToken } from '@server/lib/auth/oauth'
index 8b7a76718dcd7b2e4a1109770680cd8d875b1409..f8f07b1c6363b35c1c8621159522bfcf43791743 100644 (file)
@@ -1,6 +1,6 @@
 import express from 'express'
 import { join } from 'path'
-import { uuidToShort } from '@server/helpers/uuid'
+import { uuidToShort } from '@shared/core-utils/uuid'
 import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists'
 import { Hooks } from '@server/lib/plugins/hooks'
 import { getServerActor } from '@server/models/application/application'
index 3e1480cf234140f494d6bf6a6163f70ea63b4beb..e466d041bed3c3520d6177c6882d9c45839b4f88 100644 (file)
@@ -1,6 +1,6 @@
 import express from 'express'
 import { createReqFiles } from '@server/helpers/express-utils'
-import { buildUUID, uuidToShort } from '@server/helpers/uuid'
+import { buildUUID, uuidToShort } from '@shared/core-utils/uuid'
 import { CONFIG } from '@server/initializers/config'
 import { ASSETS_PATH, MIMETYPES } from '@server/initializers/constants'
 import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
index 1be87f746d96df0368aefddd4f91756daef2f57c..a4d0f980fb21b3be5ffdb17889cdd731c97e7198 100644 (file)
@@ -1,9 +1,9 @@
 import express from 'express'
 import { move } from 'fs-extra'
 import { basename } from 'path'
-import { getLowercaseExtension } from '@server/helpers/core-utils'
+import { getLowercaseExtension } from '@shared/core-utils'
 import { getResumableUploadPath } from '@server/helpers/upload'
-import { uuidToShort } from '@server/helpers/uuid'
+import { uuidToShort } from '@shared/core-utils/uuid'
 import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
 import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
 import { generateWebTorrentVideoFilename } from '@server/lib/paths'
index 2157ae533d74961a4b897e297125042b2bf455b6..8a56f2f753f9e5ff313b31b05cae8824563fb3b9 100644 (file)
@@ -7,7 +7,7 @@ import { CONFIG } from '@server/initializers/config'
 import { Hooks } from '@server/lib/plugins/hooks'
 import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n'
 import { HttpStatusCode } from '@shared/models'
-import { root } from '../helpers/core-utils'
+import { root } from '@shared/core-utils'
 import { STATIC_MAX_AGE } from '../initializers/constants'
 import { ClientHtml, sendHTML, serveIndexHTML } from '../lib/client-html'
 import { asyncMiddleware, embedCSP } from '../middlewares'
index 0d94cac9b0beec4366ac7d27d99d1f7fdda6177a..87bceba7afa23a39ee3ac3916eaa1f15561e66c3 100644 (file)
@@ -5,7 +5,7 @@ import { serveIndexHTML } from '@server/lib/client-html'
 import { ServerConfigManager } from '@server/lib/server-config-manager'
 import { HttpStatusCode } from '@shared/models'
 import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model'
-import { root } from '../helpers/core-utils'
+import { root } from '@shared/core-utils'
 import { CONFIG, isEmailEnabled } from '../initializers/config'
 import {
   CONSTRAINTS_FIELDS,
index 2cbf0f8fe5eabf19df83ef29a3206063db99a57a..531ccfba958f05eeb503207795292d8defaf439c 100644 (file)
@@ -6,9 +6,8 @@
 */
 
 import { exec, ExecOptions } from 'child_process'
-import { BinaryToTextEncoding, createHash, randomBytes } from 'crypto'
+import { randomBytes } from 'crypto'
 import { truncate } from 'lodash'
-import { basename, extname, isAbsolute, join, resolve } from 'path'
 import { createPrivateKey as createPrivateKey_1, getPublicKey as getPublicKey_1 } from 'pem'
 import { pipeline } from 'stream'
 import { URL } from 'url'
@@ -159,34 +158,6 @@ function getAppNumber () {
 
 // ---------------------------------------------------------------------------
 
-let rootPath: string
-
-function root () {
-  if (rootPath) return rootPath
-
-  rootPath = __dirname
-
-  if (basename(rootPath) === 'helpers') rootPath = resolve(rootPath, '..')
-  if (basename(rootPath) === 'server') rootPath = resolve(rootPath, '..')
-  if (basename(rootPath) === 'dist') rootPath = resolve(rootPath, '..')
-
-  return rootPath
-}
-
-function buildPath (path: string) {
-  if (isAbsolute(path)) return path
-
-  return join(root(), path)
-}
-
-function getLowercaseExtension (filename: string) {
-  const ext = extname(filename) || ''
-
-  return ext.toLowerCase()
-}
-
-// ---------------------------------------------------------------------------
-
 // Consistent with .length, lodash truncate function is not
 function peertubeTruncate (str: string, options: { length: number, separator?: RegExp, omission?: string }) {
   const truncatedStr = truncate(str, options)
@@ -221,16 +192,6 @@ function parseSemVersion (s: string) {
 
 // ---------------------------------------------------------------------------
 
-function sha256 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') {
-  return createHash('sha256').update(str).digest(encoding)
-}
-
-function sha1 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') {
-  return createHash('sha1').update(str).digest(encoding)
-}
-
-// ---------------------------------------------------------------------------
-
 function execShell (command: string, options?: ExecOptions) {
   return new Promise<{ err?: Error, stdout: string, stderr: string }>((res, rej) => {
     exec(command, options, (err, stdout, stderr) => {
@@ -298,9 +259,6 @@ export {
   objectConverter,
   mapToJSON,
 
-  root,
-  buildPath,
-  getLowercaseExtension,
   sanitizeUrl,
   sanitizeHost,
 
@@ -309,9 +267,6 @@ export {
   pageToStartAndCount,
   peertubeTruncate,
 
-  sha256,
-  sha1,
-
   promisify0,
   promisify1,
   promisify2,
index c19a3e5eba2692e7f1e87075d20509ede9bba365..eaabdbbea2b94d72e45cf903eca8274cdf535a32 100644 (file)
@@ -2,7 +2,7 @@ import 'multer'
 import { UploadFilesForCheck } from 'express'
 import { sep } from 'path'
 import validator from 'validator'
-import { isShortUUID, shortToUUID } from '../uuid'
+import { isShortUUID, shortToUUID } from '@shared/core-utils/uuid'
 
 function exists (value: any) {
   return value !== undefined && value !== null
index 7b81ed71b03e091f1be20707322a8bbe326ae461..780fd6345e850df4735f70d066c3f65616e8d926 100644 (file)
@@ -1,9 +1,9 @@
-import express from 'express'
+import express, { RequestHandler } from 'express'
 import multer, { diskStorage } from 'multer'
 import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
 import { CONFIG } from '../initializers/config'
 import { REMOTE_SCHEME } from '../initializers/constants'
-import { getLowercaseExtension } from './core-utils'
+import { getLowercaseExtension } from '@shared/core-utils'
 import { isArray } from './custom-validators/misc'
 import { logger } from './logger'
 import { deleteFileAndCatch, generateRandomString } from './utils'
@@ -69,7 +69,7 @@ function createReqFiles (
   fieldNames: string[],
   mimeTypes: { [id: string]: string | string[] },
   destinations: { [fieldName: string]: string }
-) {
+): RequestHandler {
   const storage = diskStorage({
     destination: (req, file, cb) => {
       cb(null, destinations[file.fieldname])
index e15628e2a7ff428251eab933e803e8cc1b800717..595112bce602b321a9a1c19ced49c5d81ad9d183 100644 (file)
@@ -1,9 +1,22 @@
-import { ffprobe, FfprobeData } from 'fluent-ffmpeg'
+import { FfprobeData } from 'fluent-ffmpeg'
 import { getMaxBitrate } from '@shared/core-utils'
-import { VideoFileMetadata, VideoResolution, VideoTranscodingFPS } from '../../shared/models/videos'
+import { VideoResolution, VideoTranscodingFPS } from '../../shared/models/videos'
 import { CONFIG } from '../initializers/config'
 import { VIDEO_TRANSCODING_FPS } from '../initializers/constants'
 import { logger } from './logger'
+import {
+  canDoQuickAudioTranscode,
+  ffprobePromise,
+  getDurationFromVideoFile,
+  getAudioStream,
+  getMaxAudioBitrate,
+  getMetadataFromFile,
+  getVideoFileBitrate,
+  getVideoFileFPS,
+  getVideoFileResolution,
+  getVideoStreamFromFile,
+  getVideoStreamSize
+} from '@shared/extra-utils/ffprobe'
 
 /**
  *
@@ -11,79 +24,6 @@ import { logger } from './logger'
  *
  */
 
-function ffprobePromise (path: string) {
-  return new Promise<FfprobeData>((res, rej) => {
-    ffprobe(path, (err, data) => {
-      if (err) return rej(err)
-
-      return res(data)
-    })
-  })
-}
-
-async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) {
-  // without position, ffprobe considers the last input only
-  // we make it consider the first input only
-  // if you pass a file path to pos, then ffprobe acts on that file directly
-  const data = existingProbe || await ffprobePromise(videoPath)
-
-  if (Array.isArray(data.streams)) {
-    const audioStream = data.streams.find(stream => stream['codec_type'] === 'audio')
-
-    if (audioStream) {
-      return {
-        absolutePath: data.format.filename,
-        audioStream,
-        bitrate: parseInt(audioStream['bit_rate'] + '', 10)
-      }
-    }
-  }
-
-  return { absolutePath: data.format.filename }
-}
-
-function getMaxAudioBitrate (type: 'aac' | 'mp3' | string, bitrate: number) {
-  const maxKBitrate = 384
-  const kToBits = (kbits: number) => kbits * 1000
-
-  // If we did not manage to get the bitrate, use an average value
-  if (!bitrate) return 256
-
-  if (type === 'aac') {
-    switch (true) {
-      case bitrate > kToBits(maxKBitrate):
-        return maxKBitrate
-
-      default:
-        return -1 // we interpret it as a signal to copy the audio stream as is
-    }
-  }
-
-  /*
-    a 192kbit/sec mp3 doesn't hold as much information as a 192kbit/sec aac.
-    That's why, when using aac, we can go to lower kbit/sec. The equivalences
-    made here are not made to be accurate, especially with good mp3 encoders.
-    */
-  switch (true) {
-    case bitrate <= kToBits(192):
-      return 128
-
-    case bitrate <= kToBits(384):
-      return 256
-
-    default:
-      return maxKBitrate
-  }
-}
-
-async function getVideoStreamSize (path: string, existingProbe?: FfprobeData): Promise<{ width: number, height: number }> {
-  const videoStream = await getVideoStreamFromFile(path, existingProbe)
-
-  return videoStream === null
-    ? { width: 0, height: 0 }
-    : { width: videoStream.width, height: videoStream.height }
-}
-
 async function getVideoStreamCodec (path: string) {
   const videoStream = await getVideoStreamFromFile(path)
 
@@ -143,69 +83,6 @@ async function getAudioStreamCodec (path: string, existingProbe?: FfprobeData) {
   return 'mp4a.40.2' // Fallback
 }
 
-async function getVideoFileResolution (path: string, existingProbe?: FfprobeData) {
-  const size = await getVideoStreamSize(path, existingProbe)
-
-  return {
-    width: size.width,
-    height: size.height,
-    ratio: Math.max(size.height, size.width) / Math.min(size.height, size.width),
-    resolution: Math.min(size.height, size.width),
-    isPortraitMode: size.height > size.width
-  }
-}
-
-async function getVideoFileFPS (path: string, existingProbe?: FfprobeData) {
-  const videoStream = await getVideoStreamFromFile(path, existingProbe)
-  if (videoStream === null) return 0
-
-  for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) {
-    const valuesText: string = videoStream[key]
-    if (!valuesText) continue
-
-    const [ frames, seconds ] = valuesText.split('/')
-    if (!frames || !seconds) continue
-
-    const result = parseInt(frames, 10) / parseInt(seconds, 10)
-    if (result > 0) return Math.round(result)
-  }
-
-  return 0
-}
-
-async function getMetadataFromFile (path: string, existingProbe?: FfprobeData) {
-  const metadata = existingProbe || await ffprobePromise(path)
-
-  return new VideoFileMetadata(metadata)
-}
-
-async function getVideoFileBitrate (path: string, existingProbe?: FfprobeData): Promise<number> {
-  const metadata = await getMetadataFromFile(path, existingProbe)
-
-  let bitrate = metadata.format.bit_rate as number
-  if (bitrate && !isNaN(bitrate)) return bitrate
-
-  const videoStream = await getVideoStreamFromFile(path, existingProbe)
-  if (!videoStream) return undefined
-
-  bitrate = videoStream?.bit_rate
-  if (bitrate && !isNaN(bitrate)) return bitrate
-
-  return undefined
-}
-
-async function getDurationFromVideoFile (path: string, existingProbe?: FfprobeData) {
-  const metadata = await getMetadataFromFile(path, existingProbe)
-
-  return Math.round(metadata.format.duration)
-}
-
-async function getVideoStreamFromFile (path: string, existingProbe?: FfprobeData) {
-  const metadata = await getMetadataFromFile(path, existingProbe)
-
-  return metadata.streams.find(s => s.codec_type === 'video') || null
-}
-
 function computeLowerResolutionsToTranscode (videoFileResolution: number, type: 'vod' | 'live') {
   const configResolutions = type === 'vod'
     ? CONFIG.TRANSCODING.RESOLUTIONS
@@ -263,26 +140,6 @@ async function canDoQuickVideoTranscode (path: string, probe?: FfprobeData): Pro
   return true
 }
 
-async function canDoQuickAudioTranscode (path: string, probe?: FfprobeData): Promise<boolean> {
-  const parsedAudio = await getAudioStream(path, probe)
-
-  if (!parsedAudio.audioStream) return true
-
-  if (parsedAudio.audioStream['codec_name'] !== 'aac') return false
-
-  const audioBitrate = parsedAudio.bitrate
-  if (!audioBitrate) return false
-
-  const maxAudioBitrate = getMaxAudioBitrate('aac', audioBitrate)
-  if (maxAudioBitrate !== -1 && audioBitrate > maxAudioBitrate) return false
-
-  const channelLayout = parsedAudio.audioStream['channel_layout']
-  // Causes playback issues with Chrome
-  if (!channelLayout || channelLayout === 'unknown') return false
-
-  return true
-}
-
 function getClosestFramerateStandard <K extends keyof Pick<VideoTranscodingFPS, 'HD_STANDARD' | 'STANDARD'>> (fps: number, type: K) {
   return VIDEO_TRANSCODING_FPS[type].slice(0)
                                     .sort((a, b) => fps % a - fps % b)[0]
index 4305584d58f962727726a6cc35b03bf857e2111e..ced288045159dee629c2759a01da997e83f59fac 100644 (file)
@@ -1,9 +1,9 @@
 import { copy, readFile, remove, rename } from 'fs-extra'
 import Jimp, { read } from 'jimp'
-import { getLowercaseExtension } from './core-utils'
+import { getLowercaseExtension } from '@shared/core-utils'
 import { convertWebPToJPG, processGIF } from './ffmpeg-utils'
 import { logger } from './logger'
-import { buildUUID } from './uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 
 function generateImageFilename (extension = '.jpg') {
   return buildUUID() + extension
index 44d90d9f12fdc9fdeaaaeff8ebf0c23cce37c7f5..31705e7fa700bb373e16fa13b51a0a093d5b5ade 100644 (file)
@@ -4,7 +4,8 @@ import { Request } from 'express'
 import { cloneDeep } from 'lodash'
 import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants'
 import { MActor } from '../types/models'
-import { createPrivateKey, getPublicKey, promisify1, promisify2, sha256 } from './core-utils'
+import { sha256 } from '@shared/core-utils/crypto'
+import { createPrivateKey, getPublicKey, promisify1, promisify2 } from './core-utils'
 import { jsonld } from './custom-jsonld-signature'
 import { logger } from './logger'
 
index 6c95a43b62afea04ac21c4636ce6eaecd572ff14..882f808abaa9583fd68b3ec87335f0355a21236d 100644 (file)
@@ -3,7 +3,8 @@ import { Instance as ParseTorrent } from 'parse-torrent'
 import { join } from 'path'
 import { ResultList } from '../../shared'
 import { CONFIG } from '../initializers/config'
-import { execPromise, execPromise2, randomBytesPromise, sha256 } from './core-utils'
+import { sha256 } from '@shared/core-utils/crypto'
+import { execPromise, execPromise2, randomBytesPromise } from './core-utils'
 import { logger } from './logger'
 
 function deleteFileAndCatch (path: string) {
index ecc703646afb879621ce6403a6510e8204e701e0..67cb3971d4a685e3d910f584daa30dac71643f5e 100644 (file)
@@ -14,7 +14,8 @@ import { MVideo } from '@server/types/models/video/video'
 import { MVideoFile, MVideoFileRedundanciesOpt } from '@server/types/models/video/video-file'
 import { MStreamingPlaylistVideo } from '@server/types/models/video/video-streaming-playlist'
 import { CONFIG } from '../initializers/config'
-import { promisify2, sha1 } from './core-utils'
+import { promisify2 } from './core-utils'
+import { sha1 } from '@shared/core-utils/crypto'
 import { logger } from './logger'
 import { generateVideoImportTmpPath } from './utils'
 import { extractVideo } from './video'
index 70179d25c0c62556762331bfcc4a08fa1a20c18b..e3e8c426e001b4e343cec4f45f851966726d813a 100644 (file)
@@ -6,7 +6,8 @@ import { VideoRedundancyConfigFilter } from '@shared/models/redundancy/video-red
 import { BroadcastMessageLevel } from '@shared/models/server'
 import { VideoPrivacy, VideosRedundancyStrategy } from '../../shared/models'
 import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
-import { buildPath, parseBytes, parseDurationToMs, root } from '../helpers/core-utils'
+import { buildPath, root } from '../../shared/core-utils'
+import { parseBytes, parseDurationToMs } from '../helpers/core-utils'
 
 // Use a variable to reload the configuration if we need
 let config: IConfig = require('config')
index 70b8e3d27a8579824ab3c1e118c7e14edacf2566..026c715c2d4e2daf887a645ce178e42ae0c00061 100644 (file)
@@ -18,8 +18,9 @@ import { FollowState } from '../../shared/models/actors'
 import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
 import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model'
 import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model'
+import { root } from '../../shared/core-utils'
 // Do not use barrels, remain constants as independent as possible
-import { isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
+import { isTestInstance, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
 import { CONFIG, registerConfigChangedHandler } from './config'
 
 // ---------------------------------------------------------------------------
index 0e6952350260906c49261829590d42a471b63f22..82971c9f57adf0e4f830f690e0c159c6221e8c4e 100644 (file)
@@ -1,4 +1,4 @@
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 import * as Sequelize from 'sequelize'
 
 async function up (utils: {
index 8dd631dff78c52f9218a4df534d8e6dc5a0977a2..5cc52e7ee9a81a6ca666cecf7d62a802fa7ab235 100644 (file)
@@ -1,5 +1,5 @@
 import * as Sequelize from 'sequelize'
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 import { VideoPlaylistPrivacy, VideoPlaylistType } from '../../../shared/models/videos'
 import { WEBSERVER } from '../constants'
 
index 0423013522a3e9be327be8ccabe99f81ce2c4df9..961777e357334b23df27939090c972ca0ec8be54 100644 (file)
@@ -1,5 +1,5 @@
 import * as Sequelize from 'sequelize'
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 
 async function up (utils: {
   transaction: Sequelize.Transaction
index 1612b3ad0cc0bb946466233865b73a7f0225038f..1ad89ac568dfc1e720b5fe4847e28f5ad1bce255 100644 (file)
@@ -1,6 +1,6 @@
-import { getLowercaseExtension } from '@server/helpers/core-utils'
+import { getLowercaseExtension } from '@shared/core-utils'
 import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc'
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 import { MIMETYPES } from '@server/initializers/constants'
 import { ActorModel } from '@server/models/actor/actor'
 import { FilteredModelAttributes } from '@server/types'
index 497773536e126857f9e7b55406a711b1123f395e..47bc8c05566774098671fec01393457e97ed2707 100644 (file)
@@ -8,7 +8,8 @@ import {
   UnauthorizedClientError,
   UnsupportedGrantTypeError
 } from 'oauth2-server'
-import { randomBytesPromise, sha1 } from '@server/helpers/core-utils'
+import { sha1 } from '@shared/core-utils/crypto'
+import { randomBytesPromise } from '@server/helpers/core-utils'
 import { MOAuthClient } from '@server/types/models'
 import { OAUTH_LIFETIME } from '../../initializers/constants'
 import { BypassLogin, getClient, getRefreshToken, getUser, revokeToken, saveToken } from './oauth-model'
index adc3d712e98eda336e10e5f81a4b7cfbac98a9c4..dee7ca8edd09e68a6d5bfb25337d63895178ee0f 100644 (file)
@@ -8,7 +8,8 @@ import { HTMLServerConfig } from '@shared/models'
 import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n'
 import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
 import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos'
-import { isTestInstance, sha256 } from '../helpers/core-utils'
+import { isTestInstance } from '../helpers/core-utils'
+import { sha256 } from '@shared/core-utils/crypto'
 import { logger } from '../helpers/logger'
 import { mdToPlainText } from '../helpers/markdown'
 import { CONFIG } from '../initializers/config'
index 60284ea2817c909b63f8038faea752591ca99416..ebad43650284c9e92ba9ba94a51551d4dd2d7750 100644 (file)
@@ -4,7 +4,8 @@ import { createTransport, Transporter } from 'nodemailer'
 import { join } from 'path'
 import { EmailPayload } from '@shared/models'
 import { SendEmailDefaultOptions } from '../../shared/models/server/emailer.model'
-import { isTestInstance, root } from '../helpers/core-utils'
+import { isTestInstance } from '../helpers/core-utils'
+import { root } from '@shared/core-utils'
 import { bunyanLogger, logger } from '../helpers/logger'
 import { CONFIG, isEmailEnabled } from '../initializers/config'
 import { WEBSERVER } from '../initializers/constants'
index f2fe893a94333694a2c5205652d0fcbf526a307c..220b7733b56d3b2dc15c5c613527edd0f3b38d68 100644 (file)
@@ -2,7 +2,7 @@ import { close, ensureDir, move, open, outputJSON, read, readFile, remove, stat,
 import { flatten, uniq } from 'lodash'
 import { basename, dirname, join } from 'path'
 import { MStreamingPlaylistFilesVideo, MVideo, MVideoUUID } from '@server/types/models'
-import { sha256 } from '../helpers/core-utils'
+import { sha256 } from '@shared/core-utils/crypto'
 import { getAudioStreamCodec, getVideoStreamCodec, getVideoStreamSize } from '../helpers/ffprobe-utils'
 import { logger } from '../helpers/logger'
 import { doRequest, doRequestAndSaveToFile } from '../helpers/requests'
index a91c2ef8020f2ce62b5d74a4d1c8b3aed9ca03d7..0d9e80cb86420f299469cd092120c3f6b08aaeee 100644 (file)
@@ -1,6 +1,6 @@
 import { Job } from 'bull'
 import { copy, stat } from 'fs-extra'
-import { getLowercaseExtension } from '@server/helpers/core-utils'
+import { getLowercaseExtension } from '@shared/core-utils'
 import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
 import { CONFIG } from '@server/initializers/config'
 import { federateVideoIfNeeded } from '@server/lib/activitypub/videos'
index 4ce1a6c30d9b52d22dc0e1a6fdeaacee353cfe7a..e5730e746a288ca97b918187e9324ab68215c642 100644 (file)
@@ -1,6 +1,6 @@
 import { Job } from 'bull'
 import { move, remove, stat } from 'fs-extra'
-import { getLowercaseExtension } from '@server/helpers/core-utils'
+import { getLowercaseExtension } from '@shared/core-utils'
 import { retryTransactionWrapper } from '@server/helpers/database-utils'
 import { YoutubeDLWrapper } from '@server/helpers/youtube-dl'
 import { isPostImportVideoAccepted } from '@server/lib/moderation'
index 821a92b9123c13467846bd28b9a0dd31a8ee6245..572696f2acfca3f991e7bddd7e8e00efb26c1def 100644 (file)
@@ -2,8 +2,8 @@ import 'multer'
 import { queue } from 'async'
 import LRUCache from 'lru-cache'
 import { join } from 'path'
-import { getLowercaseExtension } from '@server/helpers/core-utils'
-import { buildUUID } from '@server/helpers/uuid'
+import { getLowercaseExtension } from '@shared/core-utils'
+import { buildUUID } from '@shared/core-utils/uuid'
 import { ActorModel } from '@server/models/actor/actor'
 import { ActivityPubActorType, ActorImageType } from '@shared/models'
 import { retryTransactionWrapper } from '../helpers/database-utils'
index 434e637c6f97c9cd326c6bb4214797479778f5d8..d8cf812e3bce15d2384fbf26489c32c3159da4b6 100644 (file)
@@ -1,5 +1,5 @@
 import { join } from 'path'
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 import { CONFIG } from '@server/initializers/config'
 import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY } from '@server/initializers/constants'
 import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/types/models'
index 936403692c79ebc15fee2573beb9de9d9a6e2f5e..230bf37d0f55cb1351de2603900ec4dd01870412 100644 (file)
@@ -1,5 +1,5 @@
 import { Transaction } from 'sequelize/types'
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 import { UserModel } from '@server/models/user/user'
 import { MActorDefault } from '@server/types/models/actor'
 import { ActivityPubActorType } from '../../shared/models/activitypub'
index 27058005c887781f57ea39e332d4c5be776a2d9f..429b36df989fe8ccbef3457c2767cb011dcc03aa 100644 (file)
@@ -1,6 +1,6 @@
 import { remove } from 'fs-extra'
 import { extname, join } from 'path'
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 import { extractVideo } from '@server/helpers/video'
 import { CONFIG } from '@server/initializers/config'
 import {
index 8df49951d561ce66743f72c17f6260e442fccb97..cbd3f0e4a6c1329df8ce4b2b0da6e3cadb67bcee 100644 (file)
@@ -16,9 +16,8 @@ import {
   Table,
   UpdatedAt
 } from 'sequelize-typescript'
-import { getLowercaseExtension } from '@server/helpers/core-utils'
 import { ModelCache } from '@server/models/model-cache'
-import { AttributesOnly } from '@shared/core-utils'
+import { getLowercaseExtension, AttributesOnly } from '@shared/core-utils'
 import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub'
 import { ActorImage } from '../../../shared/models/actors/actor-image.model'
 import { activityPubContextify } from '../../helpers/activitypub'
index 04c5513a9b92b500496994ed5b822c6887575be9..55d65d6b2c1430f56f0934d38e5d5a1dc3750546 100644 (file)
@@ -1,6 +1,6 @@
 import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize'
 import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
-import { uuidToShort } from '@server/helpers/uuid'
+import { uuidToShort } from '@shared/core-utils/uuid'
 import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user'
 import { AttributesOnly } from '@shared/core-utils'
 import { UserNotification, UserNotificationType } from '../../../shared'
index fd4da68ed37433ffe7d1b3eaaf3acdb50be0cbd8..f6c750ccf5df977936d5c4bffa28217bea456c51 100644 (file)
@@ -1,4 +1,4 @@
-import { uuidToShort } from '@server/helpers/uuid'
+import { uuidToShort } from '@shared/core-utils/uuid'
 import { generateMagnetUri } from '@server/helpers/webtorrent'
 import { getLocalVideoFileMetadataUrl } from '@server/lib/video-urls'
 import { VideoViews } from '@server/lib/video-views'
index d24be56c3ac8c79a570cfd8797acf791988fc39e..590e72e527d3d8310ba8cfcfc9d53d51f7e735a3 100644 (file)
@@ -15,7 +15,7 @@ import {
   Table,
   UpdatedAt
 } from 'sequelize-typescript'
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models'
 import { AttributesOnly } from '@shared/core-utils'
 import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model'
index 630684a88350d04f5ffe9d20026c5ecdf5622144..d0c73cbd1f05cf9ab371370220549913baea215d 100644 (file)
@@ -17,7 +17,7 @@ import {
   Table,
   UpdatedAt
 } from 'sequelize-typescript'
-import { buildUUID, uuidToShort } from '@server/helpers/uuid'
+import { buildUUID, uuidToShort } from '@shared/core-utils/uuid'
 import { MAccountId, MChannelId } from '@server/types/models'
 import { AttributesOnly, buildPlaylistEmbedPath, buildPlaylistWatchPath, pick } from '@shared/core-utils'
 import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
index e36852cad62498d79d156f3b7ae46d25120e3ac0..23b3fbcbe4ae308693e3a50f53732945d24a4ba0 100644 (file)
@@ -21,7 +21,7 @@ import { MStreamingPlaylist, MVideo } from '@server/types/models'
 import { AttributesOnly } from '@shared/core-utils'
 import { VideoStorage } from '@shared/models'
 import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
-import { sha1 } from '../../helpers/core-utils'
+import { sha1 } from '@shared/core-utils/crypto'
 import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
 import { isArrayOf } from '../../helpers/custom-validators/misc'
 import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
index 1050463d20dd7dba28c4a8f03364efd0c9e47891..efd4d84624dd14d54a5209483e2b14cb660a427e 100644 (file)
@@ -25,7 +25,7 @@ import {
   UpdatedAt
 } from 'sequelize-typescript'
 import { buildNSFWFilter } from '@server/helpers/express-utils'
-import { uuidToShort } from '@server/helpers/uuid'
+import { uuidToShort } from '@shared/core-utils/uuid'
 import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
 import { LiveManager } from '@server/lib/live/live-manager'
 import { removeHLSObjectStorage, removeWebTorrentObjectStorage } from '@server/lib/object-storage'
index 94d946563c2d62631110a3e17e412c840413bb89..2700cff1383f5b532614e1657e42aec8b5c4c3fd 100644 (file)
@@ -7,7 +7,7 @@ import { buildDigest } from '@server/helpers/peertube-crypto'
 import { HTTP_SIGNATURE } from '@server/initializers/constants'
 import { buildGlobalHeaders } from '@server/lib/job-queue/handlers/utils/activitypub-http-utils'
 import { buildAbsoluteFixturePath, cleanupTests, createMultipleServers, killallServers, PeerTubeServer, wait } from '@shared/extra-utils'
-import { makeFollowRequest, makePOSTAPRequest } from '@shared/extra-utils/requests/activitypub'
+import { makeFollowRequest, makePOSTAPRequest } from '@server/tests/shared'
 import { HttpStatusCode } from '@shared/models'
 
 const expect = chai.expect
index 81ce8061b9a2ddab610c1619de24e4612a69b737..6d8e5359bd7dd16f937ddb8f8e17f7e9cc6fd8e6 100644 (file)
@@ -1,7 +1,7 @@
 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
 
 import 'mocha'
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 import {
   checkAbuseStateChange,
   checkAutoInstanceFollowing,
index 9af20843ecaca8e34b744fd85f8724b89873fe44..6db0347cc14d03d9306f61e955a63c3225683205 100644 (file)
@@ -2,7 +2,7 @@
 
 import 'mocha'
 import * as chai from 'chai'
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 import {
   CheckerBaseParams,
   checkMyVideoImportIsFinished,
index 832ba561ade5ed963a97d2d3dcd6f94a89a5e650..748f4cd35689ffb8dc7c81ed204ead0aa3c411fc 100644 (file)
@@ -4,7 +4,6 @@ import 'mocha'
 import * as chai from 'chai'
 import {
   cleanupTests,
-  completeVideoCheck,
   createMultipleServers,
   dateIsValid,
   expectAccountFollows,
@@ -15,6 +14,7 @@ import {
   waitJobs
 } from '@shared/extra-utils'
 import { VideoCreateResult, VideoPrivacy } from '@shared/models'
+import { completeVideoCheck } from '@server/tests/shared/video'
 
 const expect = chai.expect
 
index fa1da8fe085d974d66b462f030cf335c4db2e3f9..2d059c0ed8775e7a30f57404daf967916aa15287 100644 (file)
@@ -5,7 +5,6 @@ import * as chai from 'chai'
 import {
   cleanupTests,
   CommentsCommand,
-  completeVideoCheck,
   createMultipleServers,
   killallServers,
   PeerTubeServer,
@@ -14,6 +13,7 @@ import {
   waitJobs
 } from '@shared/extra-utils'
 import { HttpStatusCode, JobState, VideoCreateResult, VideoPrivacy } from '@shared/models'
+import { completeVideoCheck } from '@server/tests/shared/video'
 
 const expect = chai.expect
 
index c6c2790649b6c74fb78167802a6ec020c21afea5..9e7b39cfa432587b3b0c6e3f24c32eebf2ac2bab 100644 (file)
@@ -8,7 +8,6 @@ import {
   checkTmpIsEmpty,
   checkVideoFilesWereRemoved,
   cleanupTests,
-  completeVideoCheck,
   createMultipleServers,
   dateIsValid,
   doubleFollow,
@@ -21,6 +20,7 @@ import {
   webtorrentAdd
 } from '@shared/extra-utils'
 import { HttpStatusCode, VideoCommentThreadTree, VideoPrivacy } from '@shared/models'
+import { completeVideoCheck } from '@server/tests/shared/video'
 
 const expect = chai.expect
 
index a0e4a156c584cc495ad29855c6a0ab8feee0207b..100067f1813028722367082aa1e49d9959e3c11d 100644 (file)
@@ -5,7 +5,6 @@ import * as chai from 'chai'
 import {
   checkVideoFilesWereRemoved,
   cleanupTests,
-  completeVideoCheck,
   createSingleServer,
   PeerTubeServer,
   setAccessTokensToServers,
@@ -13,6 +12,7 @@ import {
   wait
 } from '@shared/extra-utils'
 import { Video, VideoPrivacy } from '@shared/models'
+import { completeVideoCheck } from '@server/tests/shared/video'
 
 const expect = chai.expect
 
index 1c0282da99bc90d9759f8a83c13b62c9c41662ba..4b4f755271a6a0ae51660e869a51c84ca9c9901a 100644 (file)
@@ -4,7 +4,7 @@ import 'mocha'
 import * as chai from 'chai'
 import { createFile, readdir } from 'fs-extra'
 import { join } from 'path'
-import { buildUUID } from '@server/helpers/uuid'
+import { buildUUID } from '@shared/core-utils/uuid'
 import {
   cleanupTests,
   CLICommand,
diff --git a/server/tests/shared/index.ts b/server/tests/shared/index.ts
new file mode 100644 (file)
index 0000000..9388172
--- /dev/null
@@ -0,0 +1,2 @@
+export * from './requests'
+export * from './video'
similarity index 54%
rename from shared/extra-utils/requests/activitypub.ts
rename to server/tests/shared/requests.ts
index 4ae878384bba5543e59ca6bde62a42121b73db32..9eb5960298b74615554cea76295600d446a52bea 100644 (file)
@@ -1,9 +1,9 @@
-import { activityPubContextify } from '../../../server/helpers/activitypub'
-import { doRequest } from '../../../server/helpers/requests'
-import { HTTP_SIGNATURE } from '../../../server/initializers/constants'
-import { buildGlobalHeaders } from '../../../server/lib/job-queue/handlers/utils/activitypub-http-utils'
+import { doRequest } from '@server/helpers/requests'
+import { activityPubContextify } from '@server/helpers/activitypub'
+import { HTTP_SIGNATURE } from '@server/initializers/constants'
+import { buildGlobalHeaders } from '@server/lib/job-queue/handlers/utils/activitypub-http-utils'
 
-function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) {
+export function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) {
   const options = {
     method: 'POST' as 'POST',
     json: body,
@@ -14,7 +14,7 @@ function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers:
   return doRequest(url, options)
 }
 
-async function makeFollowRequest (to: { url: string }, by: { url: string, privateKey }) {
+export async function makeFollowRequest (to: { url: string }, by: { url: string, privateKey }) {
   const follow = {
     type: 'Follow',
     id: by.url + '/' + new Date().getTime(),
@@ -35,8 +35,3 @@ async function makeFollowRequest (to: { url: string }, by: { url: string, privat
 
   return makePOSTAPRequest(to.url + '/inbox', body, httpSignature, headers)
 }
-
-export {
-  makePOSTAPRequest,
-  makeFollowRequest
-}
diff --git a/server/tests/shared/video.ts b/server/tests/shared/video.ts
new file mode 100644 (file)
index 0000000..0e6a00f
--- /dev/null
@@ -0,0 +1,148 @@
+/* eslint-disable @typescript-eslint/no-unused-expressions */
+import { dateIsValid, makeRawRequest, PeerTubeServer, testImage, webtorrentAdd } from '@shared/extra-utils'
+import { expect } from 'chai'
+import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '@server/initializers/constants'
+import { getLowercaseExtension, uuidRegex } from '@shared/core-utils'
+
+export async function completeVideoCheck (
+  server: PeerTubeServer,
+  video: any,
+  attributes: {
+    name: string
+    category: number
+    licence: number
+    language: string
+    nsfw: boolean
+    commentsEnabled: boolean
+    downloadEnabled: boolean
+    description: string
+    publishedAt?: string
+    support: string
+    originallyPublishedAt?: string
+    account: {
+      name: string
+      host: string
+    }
+    isLocal: boolean
+    tags: string[]
+    privacy: number
+    likes?: number
+    dislikes?: number
+    duration: number
+    channel: {
+      displayName: string
+      name: string
+      description: string
+      isLocal: boolean
+    }
+    fixture: string
+    files: {
+      resolution: number
+      size: number
+    }[]
+    thumbnailfile?: string
+    previewfile?: string
+  }
+) {
+  if (!attributes.likes) attributes.likes = 0
+  if (!attributes.dislikes) attributes.dislikes = 0
+
+  const host = new URL(server.url).host
+  const originHost = attributes.account.host
+
+  expect(video.name).to.equal(attributes.name)
+  expect(video.category.id).to.equal(attributes.category)
+  expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc')
+  expect(video.licence.id).to.equal(attributes.licence)
+  expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
+  expect(video.language.id).to.equal(attributes.language)
+  expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
+  expect(video.privacy.id).to.deep.equal(attributes.privacy)
+  expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
+  expect(video.nsfw).to.equal(attributes.nsfw)
+  expect(video.description).to.equal(attributes.description)
+  expect(video.account.id).to.be.a('number')
+  expect(video.account.host).to.equal(attributes.account.host)
+  expect(video.account.name).to.equal(attributes.account.name)
+  expect(video.channel.displayName).to.equal(attributes.channel.displayName)
+  expect(video.channel.name).to.equal(attributes.channel.name)
+  expect(video.likes).to.equal(attributes.likes)
+  expect(video.dislikes).to.equal(attributes.dislikes)
+  expect(video.isLocal).to.equal(attributes.isLocal)
+  expect(video.duration).to.equal(attributes.duration)
+  expect(video.url).to.contain(originHost)
+  expect(dateIsValid(video.createdAt)).to.be.true
+  expect(dateIsValid(video.publishedAt)).to.be.true
+  expect(dateIsValid(video.updatedAt)).to.be.true
+
+  if (attributes.publishedAt) {
+    expect(video.publishedAt).to.equal(attributes.publishedAt)
+  }
+
+  if (attributes.originallyPublishedAt) {
+    expect(video.originallyPublishedAt).to.equal(attributes.originallyPublishedAt)
+  } else {
+    expect(video.originallyPublishedAt).to.be.null
+  }
+
+  const videoDetails = await server.videos.get({ id: video.uuid })
+
+  expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
+  expect(videoDetails.tags).to.deep.equal(attributes.tags)
+  expect(videoDetails.account.name).to.equal(attributes.account.name)
+  expect(videoDetails.account.host).to.equal(attributes.account.host)
+  expect(video.channel.displayName).to.equal(attributes.channel.displayName)
+  expect(video.channel.name).to.equal(attributes.channel.name)
+  expect(videoDetails.channel.host).to.equal(attributes.account.host)
+  expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
+  expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
+  expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
+  expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
+  expect(videoDetails.downloadEnabled).to.equal(attributes.downloadEnabled)
+
+  for (const attributeFile of attributes.files) {
+    const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)
+    expect(file).not.to.be.undefined
+
+    let extension = getLowercaseExtension(attributes.fixture)
+    // Transcoding enabled: extension will always be .mp4
+    if (attributes.files.length > 1) extension = '.mp4'
+
+    expect(file.magnetUri).to.have.lengthOf.above(2)
+
+    expect(file.torrentDownloadUrl).to.match(new RegExp(`http://${host}/download/torrents/${uuidRegex}-${file.resolution.id}.torrent`))
+    expect(file.torrentUrl).to.match(new RegExp(`http://${host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}.torrent`))
+
+    expect(file.fileUrl).to.match(new RegExp(`http://${originHost}/static/webseed/${uuidRegex}-${file.resolution.id}${extension}`))
+    expect(file.fileDownloadUrl).to.match(new RegExp(`http://${originHost}/download/videos/${uuidRegex}-${file.resolution.id}${extension}`))
+
+    await Promise.all([
+      makeRawRequest(file.torrentUrl, 200),
+      makeRawRequest(file.torrentDownloadUrl, 200),
+      makeRawRequest(file.metadataUrl, 200)
+    ])
+
+    expect(file.resolution.id).to.equal(attributeFile.resolution)
+    expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
+
+    const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
+    const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
+    expect(
+      file.size,
+      'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')'
+    ).to.be.above(minSize).and.below(maxSize)
+
+    const torrent = await webtorrentAdd(file.magnetUri, true)
+    expect(torrent.files).to.be.an('array')
+    expect(torrent.files.length).to.equal(1)
+    expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+  }
+
+  expect(videoDetails.thumbnailPath).to.exist
+  await testImage(server.url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
+
+  if (attributes.previewfile) {
+    expect(videoDetails.previewPath).to.exist
+    await testImage(server.url, attributes.previewfile, videoDetails.previewPath)
+  }
+}
index 52e6ea593ef662811f828efa061275118a33559b..7c763734f61e9dd1c59fa8183d083ebc2c0c0a9f 100644 (file)
@@ -5,7 +5,9 @@ import { createLogger, format, transports } from 'winston'
 import { PeerTubeServer } from '@shared/extra-utils'
 import { UserRole } from '@shared/models'
 import { VideoPrivacy } from '../../shared/models/videos'
-import { getAppNumber, isTestInstance, root } from '../helpers/core-utils'
+import { getAppNumber, isTestInstance } from '../helpers/core-utils'
+import { root } from '@shared/core-utils'
+import { loadLanguages } from '@server/initializers/constants'
 
 let configName = 'PeerTube/CLI'
 if (isTestInstance()) configName += `-${getAppNumber()}`
@@ -180,6 +182,7 @@ function getServerCredentials (program: Command) {
 }
 
 function buildServer (url: string) {
+  loadLanguages()
   return new PeerTubeServer({ url })
 }
 
index a758beef9cac3fa0a49099d8058fe1973dd36add..223bf7f1b97432df73d4c9dd585b869bf8aeb741 100644 (file)
@@ -5,7 +5,7 @@ import { program } from 'commander'
 import { accessSync, constants } from 'fs'
 import { remove } from 'fs-extra'
 import { join } from 'path'
-import { sha256 } from '../helpers/core-utils'
+import { sha256 } from '@shared/core-utils/crypto'
 import { doRequestAndSaveToFile } from '../helpers/requests'
 import {
   assignToken,
index 156a8ed2238460bc41bc6b324d922d8767605834..575133ec8c99bacbeb84bed577c6c6d3fd5cd726 100644 (file)
@@ -1,5 +1,11 @@
 {
   "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../../dist/server/tools"
+  },
   "include": [ ".", "../typings" ],
+  "references": [
+    { "path": "../" },
+  ],
   "exclude": [ ] // Overwrite exclude property
 }
diff --git a/server/tsconfig.json b/server/tsconfig.json
new file mode 100644 (file)
index 0000000..4be7ae2
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "extends": "../tsconfig.base.json",
+  "compilerOptions": {
+    "outDir": "../dist/server"
+  },
+  "references": [
+    { "path": "../shared" }
+  ],
+  "exclude": [
+    "tools/"
+  ]
+}
diff --git a/shared/core-utils/crypto.ts b/shared/core-utils/crypto.ts
new file mode 100644 (file)
index 0000000..d6d1150
--- /dev/null
@@ -0,0 +1,14 @@
+import { BinaryToTextEncoding, createHash } from 'crypto'
+
+function sha256 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') {
+  return createHash('sha256').update(str).digest(encoding)
+}
+
+function sha1 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') {
+  return createHash('sha1').update(str).digest(encoding)
+}
+
+export {
+  sha256,
+  sha1
+}
index e0a6a8087167090861b80ae30a4281fe2a9fe2b4..ee5cd4412af1f4fcc98b88a27a15e306b51a77e1 100644 (file)
@@ -1,8 +1,10 @@
 export * from './abuse'
 export * from './common'
 export * from './i18n'
+export * from './path'
 export * from './plugins'
 export * from './renderer'
 export * from './users'
 export * from './utils'
 export * from './videos'
+export * from './uuid'
diff --git a/shared/core-utils/path.ts b/shared/core-utils/path.ts
new file mode 100644 (file)
index 0000000..b1a45d6
--- /dev/null
@@ -0,0 +1,34 @@
+import { basename, extname, isAbsolute, join, resolve } from 'path'
+
+let rootPath: string
+
+function root () {
+  if (rootPath) return rootPath
+
+  rootPath = __dirname
+
+  if (basename(rootPath) === 'core-utils') rootPath = resolve(rootPath, '..')
+  if (basename(rootPath) === 'shared') rootPath = resolve(rootPath, '..')
+  if (basename(rootPath) === 'server') rootPath = resolve(rootPath, '..')
+  if (basename(rootPath) === 'dist') rootPath = resolve(rootPath, '..')
+
+  return rootPath
+}
+
+function buildPath (path: string) {
+  if (isAbsolute(path)) return path
+
+  return join(root(), path)
+}
+
+function getLowercaseExtension (filename: string) {
+  const ext = extname(filename) || ''
+
+  return ext.toLowerCase()
+}
+
+export {
+  root,
+  buildPath,
+  getLowercaseExtension
+}
diff --git a/shared/extra-utils/ffprobe.ts b/shared/extra-utils/ffprobe.ts
new file mode 100644 (file)
index 0000000..9257bbd
--- /dev/null
@@ -0,0 +1,180 @@
+import { ffprobe, FfprobeData } from 'fluent-ffmpeg'
+import { VideoFileMetadata } from '@shared/models/videos'
+
+/**
+ *
+ * Helpers to run ffprobe and extract data from the JSON output
+ *
+ */
+
+function ffprobePromise (path: string) {
+  return new Promise<FfprobeData>((res, rej) => {
+    ffprobe(path, (err, data) => {
+      if (err) return rej(err)
+
+      return res(data)
+    })
+  })
+}
+
+async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) {
+  // without position, ffprobe considers the last input only
+  // we make it consider the first input only
+  // if you pass a file path to pos, then ffprobe acts on that file directly
+  const data = existingProbe || await ffprobePromise(videoPath)
+
+  if (Array.isArray(data.streams)) {
+    const audioStream = data.streams.find(stream => stream['codec_type'] === 'audio')
+
+    if (audioStream) {
+      return {
+        absolutePath: data.format.filename,
+        audioStream,
+        bitrate: parseInt(audioStream['bit_rate'] + '', 10)
+      }
+    }
+  }
+
+  return { absolutePath: data.format.filename }
+}
+
+function getMaxAudioBitrate (type: 'aac' | 'mp3' | string, bitrate: number) {
+  const maxKBitrate = 384
+  const kToBits = (kbits: number) => kbits * 1000
+
+  // If we did not manage to get the bitrate, use an average value
+  if (!bitrate) return 256
+
+  if (type === 'aac') {
+    switch (true) {
+      case bitrate > kToBits(maxKBitrate):
+        return maxKBitrate
+
+      default:
+        return -1 // we interpret it as a signal to copy the audio stream as is
+    }
+  }
+
+  /*
+    a 192kbit/sec mp3 doesn't hold as much information as a 192kbit/sec aac.
+    That's why, when using aac, we can go to lower kbit/sec. The equivalences
+    made here are not made to be accurate, especially with good mp3 encoders.
+    */
+  switch (true) {
+    case bitrate <= kToBits(192):
+      return 128
+
+    case bitrate <= kToBits(384):
+      return 256
+
+    default:
+      return maxKBitrate
+  }
+}
+
+async function getVideoStreamSize (path: string, existingProbe?: FfprobeData): Promise<{ width: number, height: number }> {
+  const videoStream = await getVideoStreamFromFile(path, existingProbe)
+
+  return videoStream === null
+    ? { width: 0, height: 0 }
+    : { width: videoStream.width, height: videoStream.height }
+}
+
+async function getVideoFileResolution (path: string, existingProbe?: FfprobeData) {
+  const size = await getVideoStreamSize(path, existingProbe)
+
+  return {
+    width: size.width,
+    height: size.height,
+    ratio: Math.max(size.height, size.width) / Math.min(size.height, size.width),
+    resolution: Math.min(size.height, size.width),
+    isPortraitMode: size.height > size.width
+  }
+}
+
+async function getVideoFileFPS (path: string, existingProbe?: FfprobeData) {
+  const videoStream = await getVideoStreamFromFile(path, existingProbe)
+  if (videoStream === null) return 0
+
+  for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) {
+    const valuesText: string = videoStream[key]
+    if (!valuesText) continue
+
+    const [ frames, seconds ] = valuesText.split('/')
+    if (!frames || !seconds) continue
+
+    const result = parseInt(frames, 10) / parseInt(seconds, 10)
+    if (result > 0) return Math.round(result)
+  }
+
+  return 0
+}
+
+async function getMetadataFromFile (path: string, existingProbe?: FfprobeData) {
+  const metadata = existingProbe || await ffprobePromise(path)
+
+  return new VideoFileMetadata(metadata)
+}
+
+async function getVideoFileBitrate (path: string, existingProbe?: FfprobeData): Promise<number> {
+  const metadata = await getMetadataFromFile(path, existingProbe)
+
+  let bitrate = metadata.format.bit_rate as number
+  if (bitrate && !isNaN(bitrate)) return bitrate
+
+  const videoStream = await getVideoStreamFromFile(path, existingProbe)
+  if (!videoStream) return undefined
+
+  bitrate = videoStream?.bit_rate
+  if (bitrate && !isNaN(bitrate)) return bitrate
+
+  return undefined
+}
+
+async function getDurationFromVideoFile (path: string, existingProbe?: FfprobeData) {
+  const metadata = await getMetadataFromFile(path, existingProbe)
+
+  return Math.round(metadata.format.duration)
+}
+
+async function getVideoStreamFromFile (path: string, existingProbe?: FfprobeData) {
+  const metadata = await getMetadataFromFile(path, existingProbe)
+
+  return metadata.streams.find(s => s.codec_type === 'video') || null
+}
+
+async function canDoQuickAudioTranscode (path: string, probe?: FfprobeData): Promise<boolean> {
+  const parsedAudio = await getAudioStream(path, probe)
+
+  if (!parsedAudio.audioStream) return true
+
+  if (parsedAudio.audioStream['codec_name'] !== 'aac') return false
+
+  const audioBitrate = parsedAudio.bitrate
+  if (!audioBitrate) return false
+
+  const maxAudioBitrate = getMaxAudioBitrate('aac', audioBitrate)
+  if (maxAudioBitrate !== -1 && audioBitrate > maxAudioBitrate) return false
+
+  const channelLayout = parsedAudio.audioStream['channel_layout']
+  // Causes playback issues with Chrome
+  if (!channelLayout || channelLayout === 'unknown') return false
+
+  return true
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  getVideoStreamSize,
+  getVideoFileResolution,
+  getMetadataFromFile,
+  getMaxAudioBitrate,
+  getVideoStreamFromFile,
+  getDurationFromVideoFile,
+  getAudioStream,
+  getVideoFileFPS,
+  ffprobePromise,
+  getVideoFileBitrate,
+  canDoQuickAudioTranscode
+}
index b1be214b1848f04a9925379749338dbb5eff778c..58992899780de339c0577662c4327e78094273ad 100644 (file)
@@ -3,7 +3,7 @@
 import { expect } from 'chai'
 import { pathExists, readFile } from 'fs-extra'
 import { join } from 'path'
-import { root } from '@server/helpers/core-utils'
+import { root } from '@shared/core-utils'
 import { HttpStatusCode } from '@shared/models'
 import { makeGetRequest } from '../requests'
 import { PeerTubeServer } from '../server'
index 3b29c0ad49f1b75f62a293fb51d1b53f9489535a..93673a0632f4d5b03e898c191e23172df422ff8d 100644 (file)
@@ -2,7 +2,7 @@ import { expect } from 'chai'
 import ffmpeg from 'fluent-ffmpeg'
 import { ensureDir, pathExists } from 'fs-extra'
 import { dirname } from 'path'
-import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '@server/helpers/ffprobe-utils'
+import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '@shared/extra-utils/ffprobe'
 import { getMaxBitrate } from '@shared/core-utils'
 import { buildAbsoluteFixturePath } from './tests'
 
index b6465cbf458d1094e3a6ae09b397f36f54058dfe..e6f72d6fcbc0acabafd6cbe53ad6547fc6a4383e 100644 (file)
@@ -3,7 +3,7 @@
 import { expect } from 'chai'
 import { pathExists, readdir } from 'fs-extra'
 import { join } from 'path'
-import { root } from '@server/helpers/core-utils'
+import { root } from '@shared/core-utils'
 import { PeerTubeServer } from './server'
 
 async function checkTmpIsEmpty (server: PeerTubeServer) {
index 9bf24afffc3aab16a98b92e6ce99ec0700123d00..1c44711da8a969259e48b80146bd459a32385039 100644 (file)
@@ -2,7 +2,7 @@
 
 import { readJSON, writeJSON } from 'fs-extra'
 import { join } from 'path'
-import { root } from '@server/helpers/core-utils'
+import { root } from '@shared/core-utils'
 import {
   HttpStatusCode,
   PeerTubePlugin,
index 9da2938772fdb904f7a6d37fe4c6778ae55d2c37..339b9cabb8a13abb14b63e0a89cd80f7e3de5b65 100644 (file)
@@ -1,8 +1,7 @@
 import { ChildProcess, fork } from 'child_process'
 import { copy } from 'fs-extra'
 import { join } from 'path'
-import { root } from '@server/helpers/core-utils'
-import { randomInt } from '@shared/core-utils'
+import { root, randomInt } from '@shared/core-utils'
 import { Video, VideoChannel, VideoCreateResult, VideoDetails } from '../../models/videos'
 import { BulkCommand } from '../bulk'
 import { CLICommand } from '../cli'
index 776d2123c20aba0757080e176ca7a9f9590cc211..47420c95f209891574bebc798d97e2965ef1c104 100644 (file)
@@ -1,7 +1,7 @@
 import { exec } from 'child_process'
 import { copy, ensureDir, readFile, remove } from 'fs-extra'
 import { basename, join } from 'path'
-import { root } from '@server/helpers/core-utils'
+import { root } from '@shared/core-utils'
 import { HttpStatusCode } from '@shared/models'
 import { getFileSize, isGithubCI, wait } from '../miscs'
 import { AbstractCommand, OverrideCommandOptions } from '../shared'
index f04e8f8a1eb4cefb1ba8f1a179352498636ed8e2..ed43a59245210b56ff917e3159025d1501d31d56 100644 (file)
@@ -1,5 +1,5 @@
 import { expect } from 'chai'
-import { sha1 } from '@server/helpers/core-utils'
+import { sha1 } from '@shared/core-utils/crypto'
 import { makeGetRequest } from '../requests'
 
 async function hlsInfohashExist (serverUrl: string, masterPlaylistUrl: string, fileNumber: number) {
index cfcc7d0a73c7d0cc64f5ed71ce9aa0f2ce69a47e..12c3e078a6c8784a0958bb9d3a56c83e4285db1b 100644 (file)
@@ -3,7 +3,7 @@
 import { expect } from 'chai'
 import { pathExists, readdir } from 'fs-extra'
 import { join } from 'path'
-import { root } from '@server/helpers/core-utils'
+import { root } from '@shared/core-utils'
 import { Account, VideoChannel } from '@shared/models'
 import { PeerTubeServer } from '../server'
 
index 6671e3fa6bc6306fff222a11e40477a3425e1445..0451c0efefb7b370fdd69d5165aaf85849df6c8e 100644 (file)
@@ -1,6 +1,6 @@
 import { expect } from 'chai'
 import { basename } from 'path'
-import { sha256 } from '@server/helpers/core-utils'
+import { sha256 } from '@shared/core-utils/crypto'
 import { removeFragmentedMP4Ext } from '@shared/core-utils'
 import { HttpStatusCode, VideoStreamingPlaylist } from '@shared/models'
 import { PeerTubeServer } from '../server'
index 7ec9c3647cbc67a419d67c5e2454014de5887771..8ea828b40dbc13ffb723d6594451ace77d1740e5 100644 (file)
@@ -5,8 +5,7 @@ import { createReadStream, stat } from 'fs-extra'
 import got, { Response as GotResponse } from 'got'
 import { omit } from 'lodash'
 import validator from 'validator'
-import { buildUUID } from '@server/helpers/uuid'
-import { loadLanguages } from '@server/initializers/constants'
+import { buildUUID } from '@shared/core-utils/uuid'
 import { pick } from '@shared/core-utils'
 import {
   HttpStatusCode,
@@ -23,7 +22,7 @@ import {
 } from '@shared/models'
 import { buildAbsoluteFixturePath, wait } from '../miscs'
 import { unwrapBody } from '../requests'
-import { PeerTubeServer, waitJobs } from '../server'
+import { waitJobs } from '../server'
 import { AbstractCommand, OverrideCommandOptions } from '../shared'
 
 export type VideoEdit = Partial<Omit<VideoCreate, 'thumbnailfile' | 'previewfile'>> & {
@@ -33,13 +32,6 @@ export type VideoEdit = Partial<Omit<VideoCreate, 'thumbnailfile' | 'previewfile
 }
 
 export class VideosCommand extends AbstractCommand {
-
-  constructor (server: PeerTubeServer) {
-    super(server)
-
-    loadLanguages()
-  }
-
   getCategories (options: OverrideCommandOptions = {}) {
     const path = '/api/v1/videos/categories'
 
index c05c2be6c08f01454308c99e19284493240bb505..2c3464aa83efc472282737a43ff7f029efde8300 100644 (file)
@@ -3,12 +3,7 @@
 import { expect } from 'chai'
 import { pathExists, readdir } from 'fs-extra'
 import { basename, join } from 'path'
-import { getLowercaseExtension } from '@server/helpers/core-utils'
-import { uuidRegex } from '@shared/core-utils'
 import { HttpStatusCode, VideoCaption, VideoDetails } from '@shared/models'
-import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
-import { dateIsValid, testImage, webtorrentAdd } from '../miscs'
-import { makeRawRequest } from '../requests/requests'
 import { waitJobs } from '../server'
 import { PeerTubeServer } from '../server/server'
 import { VideoEdit } from './videos-command'
@@ -85,150 +80,6 @@ function checkUploadVideoParam (
     : server.videos.buildResumeUpload({ token, attributes, expectedStatus })
 }
 
-async function completeVideoCheck (
-  server: PeerTubeServer,
-  video: any,
-  attributes: {
-    name: string
-    category: number
-    licence: number
-    language: string
-    nsfw: boolean
-    commentsEnabled: boolean
-    downloadEnabled: boolean
-    description: string
-    publishedAt?: string
-    support: string
-    originallyPublishedAt?: string
-    account: {
-      name: string
-      host: string
-    }
-    isLocal: boolean
-    tags: string[]
-    privacy: number
-    likes?: number
-    dislikes?: number
-    duration: number
-    channel: {
-      displayName: string
-      name: string
-      description: string
-      isLocal: boolean
-    }
-    fixture: string
-    files: {
-      resolution: number
-      size: number
-    }[]
-    thumbnailfile?: string
-    previewfile?: string
-  }
-) {
-  if (!attributes.likes) attributes.likes = 0
-  if (!attributes.dislikes) attributes.dislikes = 0
-
-  const host = new URL(server.url).host
-  const originHost = attributes.account.host
-
-  expect(video.name).to.equal(attributes.name)
-  expect(video.category.id).to.equal(attributes.category)
-  expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc')
-  expect(video.licence.id).to.equal(attributes.licence)
-  expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
-  expect(video.language.id).to.equal(attributes.language)
-  expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
-  expect(video.privacy.id).to.deep.equal(attributes.privacy)
-  expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
-  expect(video.nsfw).to.equal(attributes.nsfw)
-  expect(video.description).to.equal(attributes.description)
-  expect(video.account.id).to.be.a('number')
-  expect(video.account.host).to.equal(attributes.account.host)
-  expect(video.account.name).to.equal(attributes.account.name)
-  expect(video.channel.displayName).to.equal(attributes.channel.displayName)
-  expect(video.channel.name).to.equal(attributes.channel.name)
-  expect(video.likes).to.equal(attributes.likes)
-  expect(video.dislikes).to.equal(attributes.dislikes)
-  expect(video.isLocal).to.equal(attributes.isLocal)
-  expect(video.duration).to.equal(attributes.duration)
-  expect(video.url).to.contain(originHost)
-  expect(dateIsValid(video.createdAt)).to.be.true
-  expect(dateIsValid(video.publishedAt)).to.be.true
-  expect(dateIsValid(video.updatedAt)).to.be.true
-
-  if (attributes.publishedAt) {
-    expect(video.publishedAt).to.equal(attributes.publishedAt)
-  }
-
-  if (attributes.originallyPublishedAt) {
-    expect(video.originallyPublishedAt).to.equal(attributes.originallyPublishedAt)
-  } else {
-    expect(video.originallyPublishedAt).to.be.null
-  }
-
-  const videoDetails = await server.videos.get({ id: video.uuid })
-
-  expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
-  expect(videoDetails.tags).to.deep.equal(attributes.tags)
-  expect(videoDetails.account.name).to.equal(attributes.account.name)
-  expect(videoDetails.account.host).to.equal(attributes.account.host)
-  expect(video.channel.displayName).to.equal(attributes.channel.displayName)
-  expect(video.channel.name).to.equal(attributes.channel.name)
-  expect(videoDetails.channel.host).to.equal(attributes.account.host)
-  expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
-  expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
-  expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
-  expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
-  expect(videoDetails.downloadEnabled).to.equal(attributes.downloadEnabled)
-
-  for (const attributeFile of attributes.files) {
-    const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)
-    expect(file).not.to.be.undefined
-
-    let extension = getLowercaseExtension(attributes.fixture)
-    // Transcoding enabled: extension will always be .mp4
-    if (attributes.files.length > 1) extension = '.mp4'
-
-    expect(file.magnetUri).to.have.lengthOf.above(2)
-
-    expect(file.torrentDownloadUrl).to.match(new RegExp(`http://${host}/download/torrents/${uuidRegex}-${file.resolution.id}.torrent`))
-    expect(file.torrentUrl).to.match(new RegExp(`http://${host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}.torrent`))
-
-    expect(file.fileUrl).to.match(new RegExp(`http://${originHost}/static/webseed/${uuidRegex}-${file.resolution.id}${extension}`))
-    expect(file.fileDownloadUrl).to.match(new RegExp(`http://${originHost}/download/videos/${uuidRegex}-${file.resolution.id}${extension}`))
-
-    await Promise.all([
-      makeRawRequest(file.torrentUrl, 200),
-      makeRawRequest(file.torrentDownloadUrl, 200),
-      makeRawRequest(file.metadataUrl, 200)
-    ])
-
-    expect(file.resolution.id).to.equal(attributeFile.resolution)
-    expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
-
-    const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
-    const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
-    expect(
-      file.size,
-      'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')'
-    ).to.be.above(minSize).and.below(maxSize)
-
-    const torrent = await webtorrentAdd(file.magnetUri, true)
-    expect(torrent.files).to.be.an('array')
-    expect(torrent.files.length).to.equal(1)
-    expect(torrent.files[0].path).to.exist.and.to.not.equal('')
-    expect(torrent.files[0].name).to.equal(`${videoDetails.name} ${file.resolution.id}p${extension}`)
-  }
-
-  expect(videoDetails.thumbnailPath).to.exist
-  await testImage(server.url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
-
-  if (attributes.previewfile) {
-    expect(videoDetails.previewPath).to.exist
-    await testImage(server.url, attributes.previewfile, videoDetails.previewPath)
-  }
-}
-
 // serverNumber starts from 1
 async function uploadRandomVideoOnServers (
   servers: PeerTubeServer[],
@@ -247,7 +98,6 @@ async function uploadRandomVideoOnServers (
 
 export {
   checkUploadVideoParam,
-  completeVideoCheck,
   uploadRandomVideoOnServers,
   checkVideoFilesWereRemoved,
   saveVideoInServers
diff --git a/shared/tsconfig.json b/shared/tsconfig.json
new file mode 100644 (file)
index 0000000..9589207
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "extends": "../tsconfig.base.json",
+  "compilerOptions": {
+    "outDir": "../dist/shared"
+  }
+}
diff --git a/tsconfig.base.json b/tsconfig.base.json
new file mode 100644 (file)
index 0000000..ef86b97
--- /dev/null
@@ -0,0 +1,35 @@
+{
+  "compilerOptions": {
+    "module": "commonjs",
+    "target": "es2015",
+    "noImplicitAny": false,
+    "sourceMap": false,
+    "experimentalDecorators": true,
+    "emitDecoratorMetadata": true,
+    "importHelpers": true,
+    "removeComments": true,
+    "strictBindCallApply": true,
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true,
+    "lib": [
+      "es2015",
+      "es2016",
+      "es2017",
+      "es2018",
+      "es2019"
+    ],
+    "typeRoots": [
+      "node_modules/@types",
+    ],
+    "baseUrl": "./",
+    "outDir": "./dist/",
+    "paths": {
+      "@server/*": [ "server/*" ],
+      "@shared/*": [ "shared/*" ]
+    },
+    "resolveJsonModule": true,
+    "strict": false,
+    "skipLibCheck": true,
+    "composite": true
+  }
+}
index 075a3d86eccb416e9946e92a51b4005ecb70cb0d..a14a97dfb2355ee4f7084582f773991e930210dc 100644 (file)
@@ -1,46 +1,9 @@
 {
-  "compilerOptions": {
-    "module": "commonjs",
-    "target": "es2015",
-    "noImplicitAny": false,
-    "sourceMap": false,
-    "experimentalDecorators": true,
-    "emitDecoratorMetadata": true,
-    "importHelpers": true,
-    "removeComments": true,
-    "strictBindCallApply": true,
-    "esModuleInterop": true,
-    "forceConsistentCasingInFileNames": true,
-    "outDir": "./dist",
-    "lib": [
-      "dom",
-      "es2015",
-      "es2016",
-      "es2017",
-      "es2018",
-      "es2019"
-    ],
-    "typeRoots": [
-      "node_modules/@types",
-      "server/typings"
-    ],
-    "baseUrl": "./",
-    "paths": {
-      "@server/*": [ "server/*" ],
-      "@shared/*": [ "shared/*" ]
-    }
-  },
-  "exclude": [
-    "server/tools/",
-    "node_modules",
-    "dist",
-    "storage",
-    "client",
-    "test1",
-    "test2",
-    "test3",
-    "test4",
-    "test5",
-    "test6"
-  ]
+  "extends": "./tsconfig.base.json",
+  "references": [
+    { "path": "./shared" },
+    { "path": "./server" },
+    { "path": "./scripts" }
+  ],
+  "files": ["server.ts"]
 }
diff --git a/tsconfig.types.json b/tsconfig.types.json
new file mode 100644 (file)
index 0000000..c9447d8
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "extends": "./tsconfig.base.json",
+  "compilerOptions": {
+    "incremental": true,
+    "sourceMap": true,
+    "stripInternal": true,
+    "removeComments": false,
+    "declaration": true,
+    "declarationMap": true,
+    "emitDeclarationOnly": true
+  },
+  "references": [
+    { "path": "./shared/tsconfig.types.json" },
+    { "path": "./server/tsconfig.types.json" },
+    { "path": "./scripts/tsconfig.types.json" }
+  ],
+  "files": []
+}
+