aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared/extra-utils
diff options
context:
space:
mode:
Diffstat (limited to 'shared/extra-utils')
-rw-r--r--shared/extra-utils/miscs/checks.ts46
-rw-r--r--shared/extra-utils/miscs/generate.ts61
-rw-r--r--shared/extra-utils/miscs/index.ts6
-rw-r--r--shared/extra-utils/miscs/miscs.ts170
-rw-r--r--shared/extra-utils/miscs/sql-command.ts2
-rw-r--r--shared/extra-utils/miscs/stubs.ts7
-rw-r--r--shared/extra-utils/miscs/tests.ts62
-rw-r--r--shared/extra-utils/miscs/webtorrent.ts16
-rw-r--r--shared/extra-utils/mock-servers/mock-email.ts4
-rw-r--r--shared/extra-utils/requests/check-api-params.ts11
-rw-r--r--shared/extra-utils/requests/requests.ts4
-rw-r--r--shared/extra-utils/server/directories.ts34
-rw-r--r--shared/extra-utils/server/index.ts2
-rw-r--r--shared/extra-utils/server/jobs.ts2
-rw-r--r--shared/extra-utils/server/plugins-command.ts3
-rw-r--r--shared/extra-utils/server/servers-command.ts81
-rw-r--r--shared/extra-utils/server/servers.ts108
-rw-r--r--shared/extra-utils/shared/abstract-command.ts2
-rw-r--r--shared/extra-utils/videos/captions-command.ts4
-rw-r--r--shared/extra-utils/videos/live-command.ts7
-rw-r--r--shared/extra-utils/videos/live.ts4
-rw-r--r--shared/extra-utils/videos/playlists.ts2
-rw-r--r--shared/extra-utils/videos/videos.ts30
23 files changed, 345 insertions, 323 deletions
diff --git a/shared/extra-utils/miscs/checks.ts b/shared/extra-utils/miscs/checks.ts
new file mode 100644
index 000000000..86b861cd2
--- /dev/null
+++ b/shared/extra-utils/miscs/checks.ts
@@ -0,0 +1,46 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
2
3import { expect } from 'chai'
4import { pathExists, readFile } from 'fs-extra'
5import { join } from 'path'
6import { root } from '@server/helpers/core-utils'
7import { HttpStatusCode } from '@shared/core-utils'
8import { makeGetRequest } from '../requests'
9import { ServerInfo } from '../server'
10
11// Default interval -> 5 minutes
12function dateIsValid (dateString: string, interval = 300000) {
13 const dateToCheck = new Date(dateString)
14 const now = new Date()
15
16 return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
17}
18
19async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
20 const res = await makeGetRequest({
21 url,
22 path: imagePath,
23 statusCodeExpected: HttpStatusCode.OK_200
24 })
25
26 const body = res.body
27
28 const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension))
29 const minLength = body.length - ((30 * body.length) / 100)
30 const maxLength = body.length + ((30 * body.length) / 100)
31
32 expect(data.length).to.be.above(minLength, 'the generated image is way smaller than the recorded fixture')
33 expect(data.length).to.be.below(maxLength, 'the generated image is way larger than the recorded fixture')
34}
35
36async function testFileExistsOrNot (server: ServerInfo, directory: string, filePath: string, exist: boolean) {
37 const base = server.serversCommand.buildDirectory(directory)
38
39 expect(await pathExists(join(base, filePath))).to.equal(exist)
40}
41
42export {
43 dateIsValid,
44 testImage,
45 testFileExistsOrNot
46}
diff --git a/shared/extra-utils/miscs/generate.ts b/shared/extra-utils/miscs/generate.ts
new file mode 100644
index 000000000..4e70ab853
--- /dev/null
+++ b/shared/extra-utils/miscs/generate.ts
@@ -0,0 +1,61 @@
1import { ensureDir, pathExists } from 'fs-extra'
2import { dirname } from 'path'
3import { buildAbsoluteFixturePath } from './tests'
4import * as ffmpeg from 'fluent-ffmpeg'
5
6async function generateHighBitrateVideo () {
7 const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true)
8
9 await ensureDir(dirname(tempFixturePath))
10
11 const exists = await pathExists(tempFixturePath)
12 if (!exists) {
13 console.log('Generating high bitrate video.')
14
15 // Generate a random, high bitrate video on the fly, so we don't have to include
16 // a large file in the repo. The video needs to have a certain minimum length so
17 // that FFmpeg properly applies bitrate limits.
18 // https://stackoverflow.com/a/15795112
19 return new Promise<string>((res, rej) => {
20 ffmpeg()
21 .outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ])
22 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
23 .outputOptions([ '-maxrate 10M', '-bufsize 10M' ])
24 .output(tempFixturePath)
25 .on('error', rej)
26 .on('end', () => res(tempFixturePath))
27 .run()
28 })
29 }
30
31 return tempFixturePath
32}
33
34async function generateVideoWithFramerate (fps = 60) {
35 const tempFixturePath = buildAbsoluteFixturePath(`video_${fps}fps.mp4`, true)
36
37 await ensureDir(dirname(tempFixturePath))
38
39 const exists = await pathExists(tempFixturePath)
40 if (!exists) {
41 console.log('Generating video with framerate %d.', fps)
42
43 return new Promise<string>((res, rej) => {
44 ffmpeg()
45 .outputOptions([ '-f rawvideo', '-video_size 1280x720', '-i /dev/urandom' ])
46 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
47 .outputOptions([ `-r ${fps}` ])
48 .output(tempFixturePath)
49 .on('error', rej)
50 .on('end', () => res(tempFixturePath))
51 .run()
52 })
53 }
54
55 return tempFixturePath
56}
57
58export {
59 generateHighBitrateVideo,
60 generateVideoWithFramerate
61}
diff --git a/shared/extra-utils/miscs/index.ts b/shared/extra-utils/miscs/index.ts
index 7e236c329..4474661de 100644
--- a/shared/extra-utils/miscs/index.ts
+++ b/shared/extra-utils/miscs/index.ts
@@ -1,3 +1,5 @@
1export * from './miscs' 1export * from './checks'
2export * from './generate'
2export * from './sql-command' 3export * from './sql-command'
3export * from './stubs' 4export * from './tests'
5export * from './webtorrent'
diff --git a/shared/extra-utils/miscs/miscs.ts b/shared/extra-utils/miscs/miscs.ts
deleted file mode 100644
index 462b914d4..000000000
--- a/shared/extra-utils/miscs/miscs.ts
+++ /dev/null
@@ -1,170 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import * as chai from 'chai'
4import * as ffmpeg from 'fluent-ffmpeg'
5import { ensureDir, pathExists, readFile, stat } from 'fs-extra'
6import { basename, dirname, isAbsolute, join, resolve } from 'path'
7import * as request from 'supertest'
8import * as WebTorrent from 'webtorrent'
9import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
10
11const expect = chai.expect
12let webtorrent: WebTorrent.Instance
13
14function immutableAssign<T, U> (target: T, source: U) {
15 return Object.assign<{}, T, U>({}, target, source)
16}
17
18// Default interval -> 5 minutes
19function dateIsValid (dateString: string, interval = 300000) {
20 const dateToCheck = new Date(dateString)
21 const now = new Date()
22
23 return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
24}
25
26function wait (milliseconds: number) {
27 return new Promise(resolve => setTimeout(resolve, milliseconds))
28}
29
30function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
31 const WebTorrent = require('webtorrent')
32
33 if (!webtorrent) webtorrent = new WebTorrent()
34 if (refreshWebTorrent === true) webtorrent = new WebTorrent()
35
36 return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
37}
38
39function root () {
40 // We are in /miscs
41 let root = join(__dirname, '..', '..', '..')
42
43 if (basename(root) === 'dist') root = resolve(root, '..')
44
45 return root
46}
47
48function buildServerDirectory (server: { internalServerNumber: number }, directory: string) {
49 return join(root(), 'test' + server.internalServerNumber, directory)
50}
51
52async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
53 const res = await request(url)
54 .get(imagePath)
55 .expect(HttpStatusCode.OK_200)
56
57 const body = res.body
58
59 const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension))
60 const minLength = body.length - ((30 * body.length) / 100)
61 const maxLength = body.length + ((30 * body.length) / 100)
62
63 expect(data.length).to.be.above(minLength, 'the generated image is way smaller than the recorded fixture')
64 expect(data.length).to.be.below(maxLength, 'the generated image is way larger than the recorded fixture')
65}
66
67async function testFileExistsOrNot (server: { internalServerNumber: number }, directory: string, filePath: string, exist: boolean) {
68 const base = buildServerDirectory(server, directory)
69
70 expect(await pathExists(join(base, filePath))).to.equal(exist)
71}
72
73function isGithubCI () {
74 return !!process.env.GITHUB_WORKSPACE
75}
76
77function buildAbsoluteFixturePath (path: string, customCIPath = false) {
78 if (isAbsolute(path)) return path
79
80 if (customCIPath && process.env.GITHUB_WORKSPACE) {
81 return join(process.env.GITHUB_WORKSPACE, 'fixtures', path)
82 }
83
84 return join(root(), 'server', 'tests', 'fixtures', path)
85}
86
87function areHttpImportTestsDisabled () {
88 const disabled = process.env.DISABLE_HTTP_IMPORT_TESTS === 'true'
89
90 if (disabled) console.log('Import tests are disabled')
91
92 return disabled
93}
94
95async function generateHighBitrateVideo () {
96 const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true)
97
98 await ensureDir(dirname(tempFixturePath))
99
100 const exists = await pathExists(tempFixturePath)
101 if (!exists) {
102 console.log('Generating high bitrate video.')
103
104 // Generate a random, high bitrate video on the fly, so we don't have to include
105 // a large file in the repo. The video needs to have a certain minimum length so
106 // that FFmpeg properly applies bitrate limits.
107 // https://stackoverflow.com/a/15795112
108 return new Promise<string>((res, rej) => {
109 ffmpeg()
110 .outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ])
111 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
112 .outputOptions([ '-maxrate 10M', '-bufsize 10M' ])
113 .output(tempFixturePath)
114 .on('error', rej)
115 .on('end', () => res(tempFixturePath))
116 .run()
117 })
118 }
119
120 return tempFixturePath
121}
122
123async function generateVideoWithFramerate (fps = 60) {
124 const tempFixturePath = buildAbsoluteFixturePath(`video_${fps}fps.mp4`, true)
125
126 await ensureDir(dirname(tempFixturePath))
127
128 const exists = await pathExists(tempFixturePath)
129 if (!exists) {
130 console.log('Generating video with framerate %d.', fps)
131
132 return new Promise<string>((res, rej) => {
133 ffmpeg()
134 .outputOptions([ '-f rawvideo', '-video_size 1280x720', '-i /dev/urandom' ])
135 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
136 .outputOptions([ `-r ${fps}` ])
137 .output(tempFixturePath)
138 .on('error', rej)
139 .on('end', () => res(tempFixturePath))
140 .run()
141 })
142 }
143
144 return tempFixturePath
145}
146
147async function getFileSize (path: string) {
148 const stats = await stat(path)
149
150 return stats.size
151}
152
153// ---------------------------------------------------------------------------
154
155export {
156 dateIsValid,
157 wait,
158 areHttpImportTestsDisabled,
159 buildServerDirectory,
160 webtorrentAdd,
161 getFileSize,
162 immutableAssign,
163 testImage,
164 isGithubCI,
165 buildAbsoluteFixturePath,
166 testFileExistsOrNot,
167 root,
168 generateHighBitrateVideo,
169 generateVideoWithFramerate
170}
diff --git a/shared/extra-utils/miscs/sql-command.ts b/shared/extra-utils/miscs/sql-command.ts
index 2a3e9e607..80c8cd271 100644
--- a/shared/extra-utils/miscs/sql-command.ts
+++ b/shared/extra-utils/miscs/sql-command.ts
@@ -1,5 +1,5 @@
1import { QueryTypes, Sequelize } from 'sequelize' 1import { QueryTypes, Sequelize } from 'sequelize'
2import { AbstractCommand } from '../shared' 2import { AbstractCommand } from '../shared/abstract-command'
3 3
4export class SQLCommand extends AbstractCommand { 4export class SQLCommand extends AbstractCommand {
5 private sequelize: Sequelize 5 private sequelize: Sequelize
diff --git a/shared/extra-utils/miscs/stubs.ts b/shared/extra-utils/miscs/stubs.ts
deleted file mode 100644
index 940e4bf29..000000000
--- a/shared/extra-utils/miscs/stubs.ts
+++ /dev/null
@@ -1,7 +0,0 @@
1function buildRequestStub (): any {
2 return { }
3}
4
5export {
6 buildRequestStub
7}
diff --git a/shared/extra-utils/miscs/tests.ts b/shared/extra-utils/miscs/tests.ts
new file mode 100644
index 000000000..8f7a2f92b
--- /dev/null
+++ b/shared/extra-utils/miscs/tests.ts
@@ -0,0 +1,62 @@
1import { stat } from 'fs-extra'
2import { basename, isAbsolute, join, resolve } from 'path'
3
4function parallelTests () {
5 return process.env.MOCHA_PARALLEL === 'true'
6}
7
8function isGithubCI () {
9 return !!process.env.GITHUB_WORKSPACE
10}
11
12function areHttpImportTestsDisabled () {
13 const disabled = process.env.DISABLE_HTTP_IMPORT_TESTS === 'true'
14
15 if (disabled) console.log('Import tests are disabled')
16
17 return disabled
18}
19
20function buildAbsoluteFixturePath (path: string, customCIPath = false) {
21 if (isAbsolute(path)) return path
22
23 if (customCIPath && process.env.GITHUB_WORKSPACE) {
24 return join(process.env.GITHUB_WORKSPACE, 'fixtures', path)
25 }
26
27 return join(root(), 'server', 'tests', 'fixtures', path)
28}
29
30function root () {
31 // We are in /miscs
32 let root = join(__dirname, '..', '..', '..')
33
34 if (basename(root) === 'dist') root = resolve(root, '..')
35
36 return root
37}
38
39function wait (milliseconds: number) {
40 return new Promise(resolve => setTimeout(resolve, milliseconds))
41}
42
43async function getFileSize (path: string) {
44 const stats = await stat(path)
45
46 return stats.size
47}
48
49function buildRequestStub (): any {
50 return { }
51}
52
53export {
54 parallelTests,
55 isGithubCI,
56 areHttpImportTestsDisabled,
57 buildAbsoluteFixturePath,
58 getFileSize,
59 buildRequestStub,
60 wait,
61 root
62}
diff --git a/shared/extra-utils/miscs/webtorrent.ts b/shared/extra-utils/miscs/webtorrent.ts
new file mode 100644
index 000000000..82548946d
--- /dev/null
+++ b/shared/extra-utils/miscs/webtorrent.ts
@@ -0,0 +1,16 @@
1import * as WebTorrent from 'webtorrent'
2
3let webtorrent: WebTorrent.Instance
4
5function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
6 const WebTorrent = require('webtorrent')
7
8 if (!webtorrent) webtorrent = new WebTorrent()
9 if (refreshWebTorrent === true) webtorrent = new WebTorrent()
10
11 return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
12}
13
14export {
15 webtorrentAdd
16}
diff --git a/shared/extra-utils/mock-servers/mock-email.ts b/shared/extra-utils/mock-servers/mock-email.ts
index 9fc9a5ad0..ffd62e325 100644
--- a/shared/extra-utils/mock-servers/mock-email.ts
+++ b/shared/extra-utils/mock-servers/mock-email.ts
@@ -1,6 +1,6 @@
1import { ChildProcess } from 'child_process' 1import { ChildProcess } from 'child_process'
2import { randomInt } from '../../core-utils/miscs/miscs' 2import { randomInt } from '@shared/core-utils'
3import { parallelTests } from '../server/servers' 3import { parallelTests } from '../miscs'
4 4
5const MailDev = require('maildev') 5const MailDev = require('maildev')
6 6
diff --git a/shared/extra-utils/requests/check-api-params.ts b/shared/extra-utils/requests/check-api-params.ts
index 7f5ff775c..7df63b004 100644
--- a/shared/extra-utils/requests/check-api-params.ts
+++ b/shared/extra-utils/requests/check-api-params.ts
@@ -1,13 +1,12 @@
1import { HttpStatusCode } from '@shared/core-utils'
1import { makeGetRequest } from './requests' 2import { makeGetRequest } from './requests'
2import { immutableAssign } from '../miscs/miscs'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4 3
5function checkBadStartPagination (url: string, path: string, token?: string, query = {}) { 4function checkBadStartPagination (url: string, path: string, token?: string, query = {}) {
6 return makeGetRequest({ 5 return makeGetRequest({
7 url, 6 url,
8 path, 7 path,
9 token, 8 token,
10 query: immutableAssign(query, { start: 'hello' }), 9 query: { ...query, start: 'hello' },
11 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 10 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400
12 }) 11 })
13} 12}
@@ -17,7 +16,7 @@ async function checkBadCountPagination (url: string, path: string, token?: strin
17 url, 16 url,
18 path, 17 path,
19 token, 18 token,
20 query: immutableAssign(query, { count: 'hello' }), 19 query: { ...query, count: 'hello' },
21 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 20 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400
22 }) 21 })
23 22
@@ -25,7 +24,7 @@ async function checkBadCountPagination (url: string, path: string, token?: strin
25 url, 24 url,
26 path, 25 path,
27 token, 26 token,
28 query: immutableAssign(query, { count: 2000 }), 27 query: { ...query, count: 2000 },
29 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 28 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400
30 }) 29 })
31} 30}
@@ -35,7 +34,7 @@ function checkBadSortPagination (url: string, path: string, token?: string, quer
35 url, 34 url,
36 path, 35 path,
37 token, 36 token,
38 query: immutableAssign(query, { sort: 'hello' }), 37 query: { ...query, sort: 'hello' },
39 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 38 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400
40 }) 39 })
41} 40}
diff --git a/shared/extra-utils/requests/requests.ts b/shared/extra-utils/requests/requests.ts
index 3fbaa31d6..f9d112aca 100644
--- a/shared/extra-utils/requests/requests.ts
+++ b/shared/extra-utils/requests/requests.ts
@@ -4,8 +4,8 @@ import { isAbsolute, join } from 'path'
4import { decode } from 'querystring' 4import { decode } from 'querystring'
5import * as request from 'supertest' 5import * as request from 'supertest'
6import { URL } from 'url' 6import { URL } from 'url'
7import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 7import { HttpStatusCode } from '@shared/core-utils'
8import { buildAbsoluteFixturePath, root } from '../miscs/miscs' 8import { buildAbsoluteFixturePath, root } from '../miscs/tests'
9 9
10function get4KFileUrl () { 10function get4KFileUrl () {
11 return 'https://download.cpy.re/peertube/4k_file.txt' 11 return 'https://download.cpy.re/peertube/4k_file.txt'
diff --git a/shared/extra-utils/server/directories.ts b/shared/extra-utils/server/directories.ts
new file mode 100644
index 000000000..3cd38a561
--- /dev/null
+++ b/shared/extra-utils/server/directories.ts
@@ -0,0 +1,34 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { pathExists, readdir } from 'fs-extra'
5import { join } from 'path'
6import { root } from '@server/helpers/core-utils'
7import { ServerInfo } from './servers'
8
9async function checkTmpIsEmpty (server: ServerInfo) {
10 await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls', 'resumable-uploads' ])
11
12 if (await pathExists(join('test' + server.internalServerNumber, 'tmp', 'hls'))) {
13 await checkDirectoryIsEmpty(server, 'tmp/hls')
14 }
15}
16
17async function checkDirectoryIsEmpty (server: ServerInfo, directory: string, exceptions: string[] = []) {
18 const testDirectory = 'test' + server.internalServerNumber
19
20 const directoryPath = join(root(), testDirectory, directory)
21
22 const directoryExists = await pathExists(directoryPath)
23 expect(directoryExists).to.be.true
24
25 const files = await readdir(directoryPath)
26 const filtered = files.filter(f => exceptions.includes(f) === false)
27
28 expect(filtered).to.have.lengthOf(0)
29}
30
31export {
32 checkTmpIsEmpty,
33 checkDirectoryIsEmpty
34}
diff --git a/shared/extra-utils/server/index.ts b/shared/extra-utils/server/index.ts
index 03c3b0123..669b004cd 100644
--- a/shared/extra-utils/server/index.ts
+++ b/shared/extra-utils/server/index.ts
@@ -1,6 +1,7 @@
1export * from './config-command' 1export * from './config-command'
2export * from './contact-form-command' 2export * from './contact-form-command'
3export * from './debug-command' 3export * from './debug-command'
4export * from './directories'
4export * from './follows-command' 5export * from './follows-command'
5export * from './follows' 6export * from './follows'
6export * from './jobs' 7export * from './jobs'
@@ -8,5 +9,6 @@ export * from './jobs-command'
8export * from './plugins-command' 9export * from './plugins-command'
9export * from './plugins' 10export * from './plugins'
10export * from './redundancy-command' 11export * from './redundancy-command'
12export * from './servers-command'
11export * from './servers' 13export * from './servers'
12export * from './stats-command' 14export * from './stats-command'
diff --git a/shared/extra-utils/server/jobs.ts b/shared/extra-utils/server/jobs.ts
index b4b3d52e7..36ef882b3 100644
--- a/shared/extra-utils/server/jobs.ts
+++ b/shared/extra-utils/server/jobs.ts
@@ -1,6 +1,6 @@
1 1
2import { JobState } from '../../models' 2import { JobState } from '../../models'
3import { wait } from '../miscs/miscs' 3import { wait } from '../miscs'
4import { ServerInfo } from './servers' 4import { ServerInfo } from './servers'
5 5
6async function waitJobs (serversArg: ServerInfo[] | ServerInfo) { 6async function waitJobs (serversArg: ServerInfo[] | ServerInfo) {
diff --git a/shared/extra-utils/server/plugins-command.ts b/shared/extra-utils/server/plugins-command.ts
index ff49d58c4..5bed51d1a 100644
--- a/shared/extra-utils/server/plugins-command.ts
+++ b/shared/extra-utils/server/plugins-command.ts
@@ -15,7 +15,6 @@ import {
15 RegisteredServerSettings, 15 RegisteredServerSettings,
16 ResultList 16 ResultList
17} from '@shared/models' 17} from '@shared/models'
18import { buildServerDirectory } from '../miscs'
19import { AbstractCommand, OverrideCommandOptions } from '../shared' 18import { AbstractCommand, OverrideCommandOptions } from '../shared'
20 19
21export class PluginsCommand extends AbstractCommand { 20export class PluginsCommand extends AbstractCommand {
@@ -252,6 +251,6 @@ export class PluginsCommand extends AbstractCommand {
252 } 251 }
253 252
254 private getPackageJSONPath (npmName: string) { 253 private getPackageJSONPath (npmName: string) {
255 return buildServerDirectory(this.server, join('plugins', 'node_modules', npmName, 'package.json')) 254 return this.server.serversCommand.buildDirectory(join('plugins', 'node_modules', npmName, 'package.json'))
256 } 255 }
257} 256}
diff --git a/shared/extra-utils/server/servers-command.ts b/shared/extra-utils/server/servers-command.ts
new file mode 100644
index 000000000..9ef68fede
--- /dev/null
+++ b/shared/extra-utils/server/servers-command.ts
@@ -0,0 +1,81 @@
1import { exec } from 'child_process'
2import { copy, ensureDir, readFile, remove } from 'fs-extra'
3import { join } from 'path'
4import { root } from '@server/helpers/core-utils'
5import { HttpStatusCode } from '@shared/core-utils'
6import { getFileSize } from '@uploadx/core'
7import { isGithubCI, wait } from '../miscs'
8import { AbstractCommand, OverrideCommandOptions } from '../shared'
9
10export class ServersCommand extends AbstractCommand {
11
12 static flushTests (internalServerNumber: number) {
13 return new Promise<void>((res, rej) => {
14 const suffix = ` -- ${internalServerNumber}`
15
16 return exec('npm run clean:server:test' + suffix, (err, _stdout, stderr) => {
17 if (err || stderr) return rej(err || new Error(stderr))
18
19 return res()
20 })
21 })
22 }
23
24 ping (options: OverrideCommandOptions = {}) {
25 return this.getRequestBody({
26 ...options,
27
28 path: '/api/v1/ping',
29 implicitToken: false,
30 defaultExpectedStatus: HttpStatusCode.OK_200
31 })
32 }
33
34 async cleanupTests () {
35 const p: Promise<any>[] = []
36
37 if (isGithubCI()) {
38 await ensureDir('artifacts')
39
40 const origin = this.server.serversCommand.buildDirectory('logs/peertube.log')
41 const destname = `peertube-${this.server.internalServerNumber}.log`
42 console.log('Saving logs %s.', destname)
43
44 await copy(origin, join('artifacts', destname))
45 }
46
47 if (this.server.parallel) {
48 p.push(ServersCommand.flushTests(this.server.internalServerNumber))
49 }
50
51 if (this.server.customConfigFile) {
52 p.push(remove(this.server.customConfigFile))
53 }
54
55 return p
56 }
57
58 async waitUntilLog (str: string, count = 1, strictCount = true) {
59 const logfile = this.server.serversCommand.buildDirectory('logs/peertube.log')
60
61 while (true) {
62 const buf = await readFile(logfile)
63
64 const matches = buf.toString().match(new RegExp(str, 'g'))
65 if (matches && matches.length === count) return
66 if (matches && strictCount === false && matches.length >= count) return
67
68 await wait(1000)
69 }
70 }
71
72 buildDirectory (directory: string) {
73 return join(root(), 'test' + this.server.internalServerNumber, directory)
74 }
75
76 async getServerFileSize (subPath: string) {
77 const path = this.server.serversCommand.buildDirectory(subPath)
78
79 return getFileSize(path)
80 }
81}
diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts
index e0e49d2c4..f5dc0326f 100644
--- a/shared/extra-utils/server/servers.ts
+++ b/shared/extra-utils/server/servers.ts
@@ -1,9 +1,9 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
2 2
3import { expect } from 'chai' 3import { ChildProcess, fork } from 'child_process'
4import { ChildProcess, exec, fork } from 'child_process' 4import { copy, ensureDir } from 'fs-extra'
5import { copy, ensureDir, pathExists, readdir, readFile, remove } from 'fs-extra'
6import { join } from 'path' 5import { join } from 'path'
6import { root } from '@server/helpers/core-utils'
7import { randomInt } from '../../core-utils/miscs/miscs' 7import { randomInt } from '../../core-utils/miscs/miscs'
8import { VideoChannel } from '../../models/videos' 8import { VideoChannel } from '../../models/videos'
9import { BulkCommand } from '../bulk' 9import { BulkCommand } from '../bulk'
@@ -11,11 +11,9 @@ import { CLICommand } from '../cli'
11import { CustomPagesCommand } from '../custom-pages' 11import { CustomPagesCommand } from '../custom-pages'
12import { FeedCommand } from '../feeds' 12import { FeedCommand } from '../feeds'
13import { LogsCommand } from '../logs' 13import { LogsCommand } from '../logs'
14import { SQLCommand } from '../miscs' 14import { isGithubCI, parallelTests, SQLCommand } from '../miscs'
15import { buildServerDirectory, getFileSize, isGithubCI, root, wait } from '../miscs/miscs'
16import { AbusesCommand } from '../moderation' 15import { AbusesCommand } from '../moderation'
17import { OverviewsCommand } from '../overviews' 16import { OverviewsCommand } from '../overviews'
18import { makeGetRequest } from '../requests/requests'
19import { SearchCommand } from '../search' 17import { SearchCommand } from '../search'
20import { SocketIOCommand } from '../socket' 18import { SocketIOCommand } from '../socket'
21import { AccountsCommand, BlocklistCommand, NotificationsCommand, SubscriptionsCommand } from '../users' 19import { AccountsCommand, BlocklistCommand, NotificationsCommand, SubscriptionsCommand } from '../users'
@@ -39,6 +37,7 @@ import { FollowsCommand } from './follows-command'
39import { JobsCommand } from './jobs-command' 37import { JobsCommand } from './jobs-command'
40import { PluginsCommand } from './plugins-command' 38import { PluginsCommand } from './plugins-command'
41import { RedundancyCommand } from './redundancy-command' 39import { RedundancyCommand } from './redundancy-command'
40import { ServersCommand } from './servers-command'
42import { StatsCommand } from './stats-command' 41import { StatsCommand } from './stats-command'
43 42
44interface ServerInfo { 43interface ServerInfo {
@@ -126,10 +125,7 @@ interface ServerInfo {
126 commentsCommand?: CommentsCommand 125 commentsCommand?: CommentsCommand
127 sqlCommand?: SQLCommand 126 sqlCommand?: SQLCommand
128 notificationsCommand?: NotificationsCommand 127 notificationsCommand?: NotificationsCommand
129} 128 serversCommand?: ServersCommand
130
131function parallelTests () {
132 return process.env.MOCHA_PARALLEL === 'true'
133} 129}
134 130
135function flushAndRunMultipleServers (totalServers: number, configOverride?: Object) { 131function flushAndRunMultipleServers (totalServers: number, configOverride?: Object) {
@@ -151,18 +147,6 @@ function flushAndRunMultipleServers (totalServers: number, configOverride?: Obje
151 }) 147 })
152} 148}
153 149
154function flushTests (serverNumber?: number) {
155 return new Promise<void>((res, rej) => {
156 const suffix = serverNumber ? ` -- ${serverNumber}` : ''
157
158 return exec('npm run clean:server:test' + suffix, (err, _stdout, stderr) => {
159 if (err || stderr) return rej(err || new Error(stderr))
160
161 return res()
162 })
163 })
164}
165
166function randomServer () { 150function randomServer () {
167 const low = 10 151 const low = 10
168 const high = 10000 152 const high = 10000
@@ -189,7 +173,7 @@ async function flushAndRunServer (serverNumber: number, configOverride?: Object,
189 const rtmpPort = parallel ? randomRTMP() : 1936 173 const rtmpPort = parallel ? randomRTMP() : 1936
190 const port = 9000 + internalServerNumber 174 const port = 9000 + internalServerNumber
191 175
192 await flushTests(internalServerNumber) 176 await ServersCommand.flushTests(internalServerNumber)
193 177
194 const server: ServerInfo = { 178 const server: ServerInfo = {
195 app: null, 179 app: null,
@@ -372,6 +356,7 @@ function assignCommands (server: ServerInfo) {
372 server.commentsCommand = new CommentsCommand(server) 356 server.commentsCommand = new CommentsCommand(server)
373 server.sqlCommand = new SQLCommand(server) 357 server.sqlCommand = new SQLCommand(server)
374 server.notificationsCommand = new NotificationsCommand(server) 358 server.notificationsCommand = new NotificationsCommand(server)
359 server.serversCommand = new ServersCommand(server)
375} 360}
376 361
377async function reRunServer (server: ServerInfo, configOverride?: any) { 362async function reRunServer (server: ServerInfo, configOverride?: any) {
@@ -381,28 +366,6 @@ async function reRunServer (server: ServerInfo, configOverride?: any) {
381 return server 366 return server
382} 367}
383 368
384async function checkTmpIsEmpty (server: ServerInfo) {
385 await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls', 'resumable-uploads' ])
386
387 if (await pathExists(join('test' + server.internalServerNumber, 'tmp', 'hls'))) {
388 await checkDirectoryIsEmpty(server, 'tmp/hls')
389 }
390}
391
392async function checkDirectoryIsEmpty (server: ServerInfo, directory: string, exceptions: string[] = []) {
393 const testDirectory = 'test' + server.internalServerNumber
394
395 const directoryPath = join(root(), testDirectory, directory)
396
397 const directoryExists = await pathExists(directoryPath)
398 expect(directoryExists).to.be.true
399
400 const files = await readdir(directoryPath)
401 const filtered = files.filter(f => exceptions.includes(f) === false)
402
403 expect(filtered).to.have.lengthOf(0)
404}
405
406async function killallServers (servers: ServerInfo[]) { 369async function killallServers (servers: ServerInfo[]) {
407 for (const server of servers) { 370 for (const server of servers) {
408 if (!server.app) continue 371 if (!server.app) continue
@@ -422,71 +385,22 @@ async function cleanupTests (servers: ServerInfo[]) {
422 await ensureDir('artifacts') 385 await ensureDir('artifacts')
423 } 386 }
424 387
425 const p: Promise<any>[] = [] 388 let p: Promise<any>[] = []
426 for (const server of servers) { 389 for (const server of servers) {
427 if (isGithubCI()) { 390 p = p.concat(server.serversCommand.cleanupTests())
428 const origin = await buildServerDirectory(server, 'logs/peertube.log')
429 const destname = `peertube-${server.internalServerNumber}.log`
430 console.log('Saving logs %s.', destname)
431
432 await copy(origin, join('artifacts', destname))
433 }
434
435 if (server.parallel) {
436 p.push(flushTests(server.internalServerNumber))
437 }
438
439 if (server.customConfigFile) {
440 p.push(remove(server.customConfigFile))
441 }
442 } 391 }
443 392
444 return Promise.all(p) 393 return Promise.all(p)
445} 394}
446 395
447async function waitUntilLog (server: ServerInfo, str: string, count = 1, strictCount = true) {
448 const logfile = buildServerDirectory(server, 'logs/peertube.log')
449
450 while (true) {
451 const buf = await readFile(logfile)
452
453 const matches = buf.toString().match(new RegExp(str, 'g'))
454 if (matches && matches.length === count) return
455 if (matches && strictCount === false && matches.length >= count) return
456
457 await wait(1000)
458 }
459}
460
461async function getServerFileSize (server: ServerInfo, subPath: string) {
462 const path = buildServerDirectory(server, subPath)
463
464 return getFileSize(path)
465}
466
467function makePingRequest (server: ServerInfo) {
468 return makeGetRequest({
469 url: server.url,
470 path: '/api/v1/ping',
471 statusCodeExpected: 200
472 })
473}
474
475// --------------------------------------------------------------------------- 396// ---------------------------------------------------------------------------
476 397
477export { 398export {
478 checkDirectoryIsEmpty,
479 checkTmpIsEmpty,
480 getServerFileSize,
481 ServerInfo, 399 ServerInfo,
482 parallelTests,
483 cleanupTests, 400 cleanupTests,
484 flushAndRunMultipleServers, 401 flushAndRunMultipleServers,
485 flushTests,
486 makePingRequest,
487 flushAndRunServer, 402 flushAndRunServer,
488 killallServers, 403 killallServers,
489 reRunServer, 404 reRunServer,
490 assignCommands, 405 assignCommands
491 waitUntilLog
492} 406}
diff --git a/shared/extra-utils/shared/abstract-command.ts b/shared/extra-utils/shared/abstract-command.ts
index fd2deb57e..4e61554a2 100644
--- a/shared/extra-utils/shared/abstract-command.ts
+++ b/shared/extra-utils/shared/abstract-command.ts
@@ -1,6 +1,6 @@
1import { isAbsolute, join } from 'path' 1import { isAbsolute, join } from 'path'
2import { HttpStatusCode } from '@shared/core-utils' 2import { HttpStatusCode } from '@shared/core-utils'
3import { root } from '../miscs/miscs' 3import { root } from '../miscs/tests'
4import { 4import {
5 makeDeleteRequest, 5 makeDeleteRequest,
6 makeGetRequest, 6 makeGetRequest,
diff --git a/shared/extra-utils/videos/captions-command.ts b/shared/extra-utils/videos/captions-command.ts
index 908b6dae6..ac3bde7a9 100644
--- a/shared/extra-utils/videos/captions-command.ts
+++ b/shared/extra-utils/videos/captions-command.ts
@@ -1,6 +1,6 @@
1import { HttpStatusCode } from '@shared/core-utils'
1import { ResultList, VideoCaption } from '@shared/models' 2import { ResultList, VideoCaption } from '@shared/models'
2import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes' 3import { buildAbsoluteFixturePath } from '../miscs'
3import { buildAbsoluteFixturePath } from '../miscs/miscs'
4import { AbstractCommand, OverrideCommandOptions } from '../shared' 4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5 5
6export class CaptionsCommand extends AbstractCommand { 6export class CaptionsCommand extends AbstractCommand {
diff --git a/shared/extra-utils/videos/live-command.ts b/shared/extra-utils/videos/live-command.ts
index 4f03c9127..a494e60fa 100644
--- a/shared/extra-utils/videos/live-command.ts
+++ b/shared/extra-utils/videos/live-command.ts
@@ -5,9 +5,8 @@ import { omit } from 'lodash'
5import { join } from 'path' 5import { join } from 'path'
6import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models' 6import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models'
7import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes' 7import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes'
8import { buildServerDirectory, wait } from '../miscs/miscs' 8import { wait } from '../miscs'
9import { unwrapBody } from '../requests' 9import { unwrapBody } from '../requests'
10import { waitUntilLog } from '../server/servers'
11import { AbstractCommand, OverrideCommandOptions } from '../shared' 10import { AbstractCommand, OverrideCommandOptions } from '../shared'
12import { sendRTMPStream, testFfmpegStreamError } from './live' 11import { sendRTMPStream, testFfmpegStreamError } from './live'
13import { getVideoWithToken } from './videos' 12import { getVideoWithToken } from './videos'
@@ -116,7 +115,7 @@ export class LiveCommand extends AbstractCommand {
116 const { resolution, segment, videoUUID } = options 115 const { resolution, segment, videoUUID } = options
117 const segmentName = `${resolution}-00000${segment}.ts` 116 const segmentName = `${resolution}-00000${segment}.ts`
118 117
119 return waitUntilLog(this.server, `${videoUUID}/${segmentName}`, 2, false) 118 return this.server.serversCommand.waitUntilLog(`${videoUUID}/${segmentName}`, 2, false)
120 } 119 }
121 120
122 async waitUntilSaved (options: OverrideCommandOptions & { 121 async waitUntilSaved (options: OverrideCommandOptions & {
@@ -135,7 +134,7 @@ export class LiveCommand extends AbstractCommand {
135 async countPlaylists (options: OverrideCommandOptions & { 134 async countPlaylists (options: OverrideCommandOptions & {
136 videoUUID: string 135 videoUUID: string
137 }) { 136 }) {
138 const basePath = buildServerDirectory(this.server, 'streaming-playlists') 137 const basePath = this.server.serversCommand.buildDirectory('streaming-playlists')
139 const hlsPath = join(basePath, 'hls', options.videoUUID) 138 const hlsPath = join(basePath, 'hls', options.videoUUID)
140 139
141 const files = await readdir(hlsPath) 140 const files = await readdir(hlsPath)
diff --git a/shared/extra-utils/videos/live.ts b/shared/extra-utils/videos/live.ts
index 92cb9104c..0efcc2883 100644
--- a/shared/extra-utils/videos/live.ts
+++ b/shared/extra-utils/videos/live.ts
@@ -4,7 +4,7 @@ import { expect } from 'chai'
4import * as ffmpeg from 'fluent-ffmpeg' 4import * as ffmpeg from 'fluent-ffmpeg'
5import { pathExists, readdir } from 'fs-extra' 5import { pathExists, readdir } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { buildAbsoluteFixturePath, buildServerDirectory, wait } from '../miscs/miscs' 7import { buildAbsoluteFixturePath, wait } from '../miscs'
8import { ServerInfo } from '../server/servers' 8import { ServerInfo } from '../server/servers'
9 9
10function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') { 10function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') {
@@ -77,7 +77,7 @@ async function waitUntilLivePublishedOnAllServers (servers: ServerInfo[], videoI
77} 77}
78 78
79async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resolutions: number[] = []) { 79async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resolutions: number[] = []) {
80 const basePath = buildServerDirectory(server, 'streaming-playlists') 80 const basePath = server.serversCommand.buildDirectory('streaming-playlists')
81 const hlsPath = join(basePath, 'hls', videoUUID) 81 const hlsPath = join(basePath, 'hls', videoUUID)
82 82
83 if (resolutions.length === 0) { 83 if (resolutions.length === 0) {
diff --git a/shared/extra-utils/videos/playlists.ts b/shared/extra-utils/videos/playlists.ts
index 023333c87..3dde52bb9 100644
--- a/shared/extra-utils/videos/playlists.ts
+++ b/shared/extra-utils/videos/playlists.ts
@@ -1,7 +1,7 @@
1import { expect } from 'chai' 1import { expect } from 'chai'
2import { readdir } from 'fs-extra' 2import { readdir } from 'fs-extra'
3import { join } from 'path' 3import { join } from 'path'
4import { root } from '../' 4import { root } from '../miscs'
5 5
6async function checkPlaylistFilesWereRemoved ( 6async function checkPlaylistFilesWereRemoved (
7 playlistUUID: string, 7 playlistUUID: string,
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts
index 920c93072..5dd71ce8b 100644
--- a/shared/extra-utils/videos/videos.ts
+++ b/shared/extra-utils/videos/videos.ts
@@ -13,15 +13,7 @@ import { HttpStatusCode } from '@shared/core-utils'
13import { BooleanBothQuery, VideosCommonQuery } from '@shared/models' 13import { BooleanBothQuery, VideosCommonQuery } from '@shared/models'
14import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants' 14import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
15import { VideoDetails, VideoPrivacy } from '../../models/videos' 15import { VideoDetails, VideoPrivacy } from '../../models/videos'
16import { 16import { buildAbsoluteFixturePath, dateIsValid, testImage, wait, webtorrentAdd } from '../miscs'
17 buildAbsoluteFixturePath,
18 buildServerDirectory,
19 dateIsValid,
20 immutableAssign,
21 testImage,
22 wait,
23 webtorrentAdd
24} from '../miscs/miscs'
25import { makeGetRequest, makePutBodyRequest, makeRawRequest, makeUploadRequest } from '../requests/requests' 17import { makeGetRequest, makePutBodyRequest, makeRawRequest, makeUploadRequest } from '../requests/requests'
26import { waitJobs } from '../server/jobs' 18import { waitJobs } from '../server/jobs'
27import { ServerInfo } from '../server/servers' 19import { ServerInfo } from '../server/servers'
@@ -165,7 +157,7 @@ function getVideosListWithToken (url: string, token: string, query: { nsfw?: Boo
165 return request(url) 157 return request(url)
166 .get(path) 158 .get(path)
167 .set('Authorization', 'Bearer ' + token) 159 .set('Authorization', 'Bearer ' + token)
168 .query(immutableAssign(query, { sort: 'name' })) 160 .query({ sort: 'name', ...query })
169 .set('Accept', 'application/json') 161 .set('Accept', 'application/json')
170 .expect(HttpStatusCode.OK_200) 162 .expect(HttpStatusCode.OK_200)
171 .expect('Content-Type', /json/) 163 .expect('Content-Type', /json/)
@@ -228,11 +220,7 @@ function getAccountVideos (
228 return makeGetRequest({ 220 return makeGetRequest({
229 url, 221 url,
230 path, 222 path,
231 query: immutableAssign(query, { 223 query: { ...query, start, count, sort },
232 start,
233 count,
234 sort
235 }),
236 token: accessToken, 224 token: accessToken,
237 statusCodeExpected: HttpStatusCode.OK_200 225 statusCodeExpected: HttpStatusCode.OK_200
238 }) 226 })
@@ -252,11 +240,7 @@ function getVideoChannelVideos (
252 return makeGetRequest({ 240 return makeGetRequest({
253 url, 241 url,
254 path, 242 path,
255 query: immutableAssign(query, { 243 query: { ...query, start, count, sort },
256 start,
257 count,
258 sort
259 }),
260 token: accessToken, 244 token: accessToken,
261 statusCodeExpected: HttpStatusCode.OK_200 245 statusCodeExpected: HttpStatusCode.OK_200
262 }) 246 })
@@ -320,7 +304,7 @@ async function removeAllVideos (server: ServerInfo) {
320 304
321async function checkVideoFilesWereRemoved ( 305async function checkVideoFilesWereRemoved (
322 videoUUID: string, 306 videoUUID: string,
323 serverNumber: number, 307 server: ServerInfo,
324 directories = [ 308 directories = [
325 'redundancy', 309 'redundancy',
326 'videos', 310 'videos',
@@ -333,7 +317,7 @@ async function checkVideoFilesWereRemoved (
333 ] 317 ]
334) { 318) {
335 for (const directory of directories) { 319 for (const directory of directories) {
336 const directoryPath = buildServerDirectory({ internalServerNumber: serverNumber }, directory) 320 const directoryPath = server.serversCommand.buildDirectory(directory)
337 321
338 const directoryExists = await pathExists(directoryPath) 322 const directoryExists = await pathExists(directoryPath)
339 if (directoryExists === false) continue 323 if (directoryExists === false) continue
@@ -607,7 +591,7 @@ function rateVideo (url: string, accessToken: string, id: number | string, ratin
607function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) { 591function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
608 return new Promise<any>((res, rej) => { 592 return new Promise<any>((res, rej) => {
609 const torrentName = videoUUID + '-' + resolution + '.torrent' 593 const torrentName = videoUUID + '-' + resolution + '.torrent'
610 const torrentPath = buildServerDirectory(server, join('torrents', torrentName)) 594 const torrentPath = server.serversCommand.buildDirectory(join('torrents', torrentName))
611 595
612 readFile(torrentPath, (err, data) => { 596 readFile(torrentPath, (err, data) => {
613 if (err) return rej(err) 597 if (err) return rej(err)