From bf54587a3e2ad9c2c186828f2a5682b91ee2cc00 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 17 Dec 2021 09:29:23 +0100 Subject: shared/ typescript types dir server-commands --- shared/extra-utils/bulk/bulk-command.ts | 20 - shared/extra-utils/bulk/index.ts | 1 - shared/extra-utils/cli/cli-command.ts | 27 - shared/extra-utils/cli/index.ts | 1 - .../custom-pages/custom-pages-command.ts | 33 - shared/extra-utils/custom-pages/index.ts | 1 - shared/extra-utils/feeds/feeds-command.ts | 44 -- shared/extra-utils/feeds/index.ts | 1 - shared/extra-utils/index.ts | 16 +- shared/extra-utils/logs/index.ts | 1 - shared/extra-utils/logs/logs-command.ts | 44 -- shared/extra-utils/miscs/checks.ts | 58 -- shared/extra-utils/miscs/generate.ts | 75 -- shared/extra-utils/miscs/index.ts | 5 - shared/extra-utils/miscs/sql-command.ts | 141 ---- shared/extra-utils/miscs/tests.ts | 101 --- shared/extra-utils/miscs/webtorrent.ts | 46 -- shared/extra-utils/mock-servers/index.ts | 5 - shared/extra-utils/mock-servers/mock-429.ts | 33 - shared/extra-utils/mock-servers/mock-email.ts | 63 -- .../mock-servers/mock-instances-index.ts | 46 -- .../mock-servers/mock-joinpeertube-versions.ts | 34 - .../mock-servers/mock-object-storage.ts | 41 -- .../mock-servers/mock-plugin-blocklist.ts | 36 - shared/extra-utils/mock-servers/mock-proxy.ts | 25 - shared/extra-utils/mock-servers/utils.ts | 33 - shared/extra-utils/moderation/abuses-command.ts | 228 ------ shared/extra-utils/moderation/index.ts | 1 - shared/extra-utils/overviews/index.ts | 1 - shared/extra-utils/overviews/overviews-command.ts | 23 - shared/extra-utils/requests/check-api-params.ts | 48 -- shared/extra-utils/requests/index.ts | 3 - shared/extra-utils/requests/requests.ts | 208 ------ shared/extra-utils/search/index.ts | 1 - shared/extra-utils/search/search-command.ts | 98 --- shared/extra-utils/server/config-command.ts | 353 --------- shared/extra-utils/server/contact-form-command.ts | 31 - shared/extra-utils/server/debug-command.ts | 33 - shared/extra-utils/server/directories.ts | 34 - shared/extra-utils/server/follows-command.ts | 139 ---- shared/extra-utils/server/follows.ts | 20 - shared/extra-utils/server/index.ts | 17 - shared/extra-utils/server/jobs-command.ts | 61 -- shared/extra-utils/server/jobs.ts | 84 --- .../extra-utils/server/object-storage-command.ts | 77 -- shared/extra-utils/server/plugins-command.ts | 257 ------- shared/extra-utils/server/plugins.ts | 18 - shared/extra-utils/server/redundancy-command.ts | 80 --- shared/extra-utils/server/server.ts | 392 ---------- shared/extra-utils/server/servers-command.ts | 92 --- shared/extra-utils/server/servers.ts | 49 -- shared/extra-utils/server/stats-command.ts | 25 - shared/extra-utils/server/tracker.ts | 27 - shared/extra-utils/shared/abstract-command.ts | 211 ------ shared/extra-utils/shared/index.ts | 1 - shared/extra-utils/socket/index.ts | 1 - shared/extra-utils/socket/socket-io-command.ts | 15 - shared/extra-utils/users/accounts-command.ts | 78 -- shared/extra-utils/users/actors.ts | 73 -- shared/extra-utils/users/blocklist-command.ts | 162 ----- shared/extra-utils/users/index.ts | 9 - shared/extra-utils/users/login-command.ts | 132 ---- shared/extra-utils/users/login.ts | 19 - shared/extra-utils/users/notifications-command.ts | 86 --- shared/extra-utils/users/notifications.ts | 795 --------------------- shared/extra-utils/users/subscriptions-command.ts | 99 --- shared/extra-utils/users/users-command.ts | 416 ----------- shared/extra-utils/videos/blacklist-command.ts | 76 -- shared/extra-utils/videos/captions-command.ts | 65 -- shared/extra-utils/videos/captions.ts | 21 - .../extra-utils/videos/change-ownership-command.ts | 68 -- shared/extra-utils/videos/channels-command.ts | 178 ----- shared/extra-utils/videos/channels.ts | 18 - shared/extra-utils/videos/comments-command.ts | 152 ---- shared/extra-utils/videos/history-command.ts | 58 -- shared/extra-utils/videos/imports-command.ts | 47 -- shared/extra-utils/videos/index.ts | 19 - shared/extra-utils/videos/live-command.ts | 155 ---- shared/extra-utils/videos/live.ts | 137 ---- shared/extra-utils/videos/playlists-command.ts | 280 -------- shared/extra-utils/videos/playlists.ts | 25 - shared/extra-utils/videos/services-command.ts | 29 - .../videos/streaming-playlists-command.ts | 44 -- shared/extra-utils/videos/streaming-playlists.ts | 77 -- shared/extra-utils/videos/videos-command.ts | 679 ------------------ shared/extra-utils/videos/videos.ts | 104 --- 86 files changed, 1 insertion(+), 7659 deletions(-) delete mode 100644 shared/extra-utils/bulk/bulk-command.ts delete mode 100644 shared/extra-utils/bulk/index.ts delete mode 100644 shared/extra-utils/cli/cli-command.ts delete mode 100644 shared/extra-utils/cli/index.ts delete mode 100644 shared/extra-utils/custom-pages/custom-pages-command.ts delete mode 100644 shared/extra-utils/custom-pages/index.ts delete mode 100644 shared/extra-utils/feeds/feeds-command.ts delete mode 100644 shared/extra-utils/feeds/index.ts delete mode 100644 shared/extra-utils/logs/index.ts delete mode 100644 shared/extra-utils/logs/logs-command.ts delete mode 100644 shared/extra-utils/miscs/checks.ts delete mode 100644 shared/extra-utils/miscs/generate.ts delete mode 100644 shared/extra-utils/miscs/index.ts delete mode 100644 shared/extra-utils/miscs/sql-command.ts delete mode 100644 shared/extra-utils/miscs/tests.ts delete mode 100644 shared/extra-utils/miscs/webtorrent.ts delete mode 100644 shared/extra-utils/mock-servers/index.ts delete mode 100644 shared/extra-utils/mock-servers/mock-429.ts delete mode 100644 shared/extra-utils/mock-servers/mock-email.ts delete mode 100644 shared/extra-utils/mock-servers/mock-instances-index.ts delete mode 100644 shared/extra-utils/mock-servers/mock-joinpeertube-versions.ts delete mode 100644 shared/extra-utils/mock-servers/mock-object-storage.ts delete mode 100644 shared/extra-utils/mock-servers/mock-plugin-blocklist.ts delete mode 100644 shared/extra-utils/mock-servers/mock-proxy.ts delete mode 100644 shared/extra-utils/mock-servers/utils.ts delete mode 100644 shared/extra-utils/moderation/abuses-command.ts delete mode 100644 shared/extra-utils/moderation/index.ts delete mode 100644 shared/extra-utils/overviews/index.ts delete mode 100644 shared/extra-utils/overviews/overviews-command.ts delete mode 100644 shared/extra-utils/requests/check-api-params.ts delete mode 100644 shared/extra-utils/requests/index.ts delete mode 100644 shared/extra-utils/requests/requests.ts delete mode 100644 shared/extra-utils/search/index.ts delete mode 100644 shared/extra-utils/search/search-command.ts delete mode 100644 shared/extra-utils/server/config-command.ts delete mode 100644 shared/extra-utils/server/contact-form-command.ts delete mode 100644 shared/extra-utils/server/debug-command.ts delete mode 100644 shared/extra-utils/server/directories.ts delete mode 100644 shared/extra-utils/server/follows-command.ts delete mode 100644 shared/extra-utils/server/follows.ts delete mode 100644 shared/extra-utils/server/index.ts delete mode 100644 shared/extra-utils/server/jobs-command.ts delete mode 100644 shared/extra-utils/server/jobs.ts delete mode 100644 shared/extra-utils/server/object-storage-command.ts delete mode 100644 shared/extra-utils/server/plugins-command.ts delete mode 100644 shared/extra-utils/server/plugins.ts delete mode 100644 shared/extra-utils/server/redundancy-command.ts delete mode 100644 shared/extra-utils/server/server.ts delete mode 100644 shared/extra-utils/server/servers-command.ts delete mode 100644 shared/extra-utils/server/servers.ts delete mode 100644 shared/extra-utils/server/stats-command.ts delete mode 100644 shared/extra-utils/server/tracker.ts delete mode 100644 shared/extra-utils/shared/abstract-command.ts delete mode 100644 shared/extra-utils/shared/index.ts delete mode 100644 shared/extra-utils/socket/index.ts delete mode 100644 shared/extra-utils/socket/socket-io-command.ts delete mode 100644 shared/extra-utils/users/accounts-command.ts delete mode 100644 shared/extra-utils/users/actors.ts delete mode 100644 shared/extra-utils/users/blocklist-command.ts delete mode 100644 shared/extra-utils/users/index.ts delete mode 100644 shared/extra-utils/users/login-command.ts delete mode 100644 shared/extra-utils/users/login.ts delete mode 100644 shared/extra-utils/users/notifications-command.ts delete mode 100644 shared/extra-utils/users/notifications.ts delete mode 100644 shared/extra-utils/users/subscriptions-command.ts delete mode 100644 shared/extra-utils/users/users-command.ts delete mode 100644 shared/extra-utils/videos/blacklist-command.ts delete mode 100644 shared/extra-utils/videos/captions-command.ts delete mode 100644 shared/extra-utils/videos/captions.ts delete mode 100644 shared/extra-utils/videos/change-ownership-command.ts delete mode 100644 shared/extra-utils/videos/channels-command.ts delete mode 100644 shared/extra-utils/videos/channels.ts delete mode 100644 shared/extra-utils/videos/comments-command.ts delete mode 100644 shared/extra-utils/videos/history-command.ts delete mode 100644 shared/extra-utils/videos/imports-command.ts delete mode 100644 shared/extra-utils/videos/index.ts delete mode 100644 shared/extra-utils/videos/live-command.ts delete mode 100644 shared/extra-utils/videos/live.ts delete mode 100644 shared/extra-utils/videos/playlists-command.ts delete mode 100644 shared/extra-utils/videos/playlists.ts delete mode 100644 shared/extra-utils/videos/services-command.ts delete mode 100644 shared/extra-utils/videos/streaming-playlists-command.ts delete mode 100644 shared/extra-utils/videos/streaming-playlists.ts delete mode 100644 shared/extra-utils/videos/videos-command.ts delete mode 100644 shared/extra-utils/videos/videos.ts (limited to 'shared/extra-utils') diff --git a/shared/extra-utils/bulk/bulk-command.ts b/shared/extra-utils/bulk/bulk-command.ts deleted file mode 100644 index b5c5673ce..000000000 --- a/shared/extra-utils/bulk/bulk-command.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { BulkRemoveCommentsOfBody, HttpStatusCode } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class BulkCommand extends AbstractCommand { - - removeCommentsOf (options: OverrideCommandOptions & { - attributes: BulkRemoveCommentsOfBody - }) { - const { attributes } = options - - return this.postBodyRequest({ - ...options, - - path: '/api/v1/bulk/remove-comments-of', - fields: attributes, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } -} diff --git a/shared/extra-utils/bulk/index.ts b/shared/extra-utils/bulk/index.ts deleted file mode 100644 index 391597243..000000000 --- a/shared/extra-utils/bulk/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './bulk-command' diff --git a/shared/extra-utils/cli/cli-command.ts b/shared/extra-utils/cli/cli-command.ts deleted file mode 100644 index ab9738174..000000000 --- a/shared/extra-utils/cli/cli-command.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { exec } from 'child_process' -import { AbstractCommand } from '../shared' - -export class CLICommand extends AbstractCommand { - - static exec (command: string) { - return new Promise((res, rej) => { - exec(command, (err, stdout, _stderr) => { - if (err) return rej(err) - - return res(stdout) - }) - }) - } - - getEnv () { - return `NODE_ENV=test NODE_APP_INSTANCE=${this.server.internalServerNumber}` - } - - async execWithEnv (command: string, configOverride?: any) { - const prefix = configOverride - ? `NODE_CONFIG='${JSON.stringify(configOverride)}'` - : '' - - return CLICommand.exec(`${prefix} ${this.getEnv()} ${command}`) - } -} diff --git a/shared/extra-utils/cli/index.ts b/shared/extra-utils/cli/index.ts deleted file mode 100644 index 91b5abfbe..000000000 --- a/shared/extra-utils/cli/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './cli-command' diff --git a/shared/extra-utils/custom-pages/custom-pages-command.ts b/shared/extra-utils/custom-pages/custom-pages-command.ts deleted file mode 100644 index cd869a8de..000000000 --- a/shared/extra-utils/custom-pages/custom-pages-command.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { CustomPage, HttpStatusCode } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class CustomPagesCommand extends AbstractCommand { - - getInstanceHomepage (options: OverrideCommandOptions = {}) { - const path = '/api/v1/custom-pages/homepage/instance' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - updateInstanceHomepage (options: OverrideCommandOptions & { - content: string - }) { - const { content } = options - const path = '/api/v1/custom-pages/homepage/instance' - - return this.putBodyRequest({ - ...options, - - path, - fields: { content }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } -} diff --git a/shared/extra-utils/custom-pages/index.ts b/shared/extra-utils/custom-pages/index.ts deleted file mode 100644 index 58aed04f2..000000000 --- a/shared/extra-utils/custom-pages/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './custom-pages-command' diff --git a/shared/extra-utils/feeds/feeds-command.ts b/shared/extra-utils/feeds/feeds-command.ts deleted file mode 100644 index 3c95f9536..000000000 --- a/shared/extra-utils/feeds/feeds-command.ts +++ /dev/null @@ -1,44 +0,0 @@ - -import { HttpStatusCode } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -type FeedType = 'videos' | 'video-comments' | 'subscriptions' - -export class FeedCommand extends AbstractCommand { - - getXML (options: OverrideCommandOptions & { - feed: FeedType - format?: string - }) { - const { feed, format } = options - const path = '/feeds/' + feed + '.xml' - - return this.getRequestText({ - ...options, - - path, - query: format ? { format } : undefined, - accept: 'application/xml', - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getJSON (options: OverrideCommandOptions & { - feed: FeedType - query?: { [ id: string ]: any } - }) { - const { feed, query } = options - const path = '/feeds/' + feed + '.json' - - return this.getRequestText({ - ...options, - - path, - query, - accept: 'application/json', - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/feeds/index.ts b/shared/extra-utils/feeds/index.ts deleted file mode 100644 index 662a22b6f..000000000 --- a/shared/extra-utils/feeds/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './feeds-command' diff --git a/shared/extra-utils/index.ts b/shared/extra-utils/index.ts index 4b3636d06..5710c5ab5 100644 --- a/shared/extra-utils/index.ts +++ b/shared/extra-utils/index.ts @@ -1,15 +1 @@ -export * from './bulk' -export * from './cli' -export * from './custom-pages' -export * from './feeds' -export * from './logs' -export * from './miscs' -export * from './mock-servers' -export * from './moderation' -export * from './overviews' -export * from './requests' -export * from './search' -export * from './server' -export * from './socket' -export * from './users' -export * from './videos' +export * from './ffprobe' diff --git a/shared/extra-utils/logs/index.ts b/shared/extra-utils/logs/index.ts deleted file mode 100644 index 69452d7f0..000000000 --- a/shared/extra-utils/logs/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './logs-command' diff --git a/shared/extra-utils/logs/logs-command.ts b/shared/extra-utils/logs/logs-command.ts deleted file mode 100644 index 7b5c66c0c..000000000 --- a/shared/extra-utils/logs/logs-command.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { HttpStatusCode } from '@shared/models' -import { LogLevel } from '../../models/server/log-level.type' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class LogsCommand extends AbstractCommand { - - getLogs (options: OverrideCommandOptions & { - startDate: Date - endDate?: Date - level?: LogLevel - tagsOneOf?: string[] - }) { - const { startDate, endDate, tagsOneOf, level } = options - const path = '/api/v1/server/logs' - - return this.getRequestBody({ - ...options, - - path, - query: { startDate, endDate, level, tagsOneOf }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getAuditLogs (options: OverrideCommandOptions & { - startDate: Date - endDate?: Date - }) { - const { startDate, endDate } = options - - const path = '/api/v1/server/audit-logs' - - return this.getRequestBody({ - ...options, - - path, - query: { startDate, endDate }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - -} diff --git a/shared/extra-utils/miscs/checks.ts b/shared/extra-utils/miscs/checks.ts deleted file mode 100644 index 589928997..000000000 --- a/shared/extra-utils/miscs/checks.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ - -import { expect } from 'chai' -import { pathExists, readFile } from 'fs-extra' -import { join } from 'path' -import { root } from '@shared/core-utils' -import { HttpStatusCode } from '@shared/models' -import { makeGetRequest } from '../requests' -import { PeerTubeServer } from '../server' - -// Default interval -> 5 minutes -function dateIsValid (dateString: string, interval = 300000) { - const dateToCheck = new Date(dateString) - const now = new Date() - - return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval -} - -function expectStartWith (str: string, start: string) { - expect(str.startsWith(start), `${str} does not start with ${start}`).to.be.true -} - -async function expectLogDoesNotContain (server: PeerTubeServer, str: string) { - const content = await server.servers.getLogContent() - - expect(content.toString()).to.not.contain(str) -} - -async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') { - const res = await makeGetRequest({ - url, - path: imagePath, - expectedStatus: HttpStatusCode.OK_200 - }) - - const body = res.body - - const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension)) - const minLength = body.length - ((30 * body.length) / 100) - const maxLength = body.length + ((30 * body.length) / 100) - - expect(data.length).to.be.above(minLength, 'the generated image is way smaller than the recorded fixture') - expect(data.length).to.be.below(maxLength, 'the generated image is way larger than the recorded fixture') -} - -async function testFileExistsOrNot (server: PeerTubeServer, directory: string, filePath: string, exist: boolean) { - const base = server.servers.buildDirectory(directory) - - expect(await pathExists(join(base, filePath))).to.equal(exist) -} - -export { - dateIsValid, - testImage, - expectLogDoesNotContain, - testFileExistsOrNot, - expectStartWith -} diff --git a/shared/extra-utils/miscs/generate.ts b/shared/extra-utils/miscs/generate.ts deleted file mode 100644 index 93673a063..000000000 --- a/shared/extra-utils/miscs/generate.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { expect } from 'chai' -import ffmpeg from 'fluent-ffmpeg' -import { ensureDir, pathExists } from 'fs-extra' -import { dirname } from 'path' -import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '@shared/extra-utils/ffprobe' -import { getMaxBitrate } from '@shared/core-utils' -import { buildAbsoluteFixturePath } from './tests' - -async function ensureHasTooBigBitrate (fixturePath: string) { - const bitrate = await getVideoFileBitrate(fixturePath) - const dataResolution = await getVideoFileResolution(fixturePath) - const fps = await getVideoFileFPS(fixturePath) - - const maxBitrate = getMaxBitrate({ ...dataResolution, fps }) - expect(bitrate).to.be.above(maxBitrate) -} - -async function generateHighBitrateVideo () { - const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true) - - await ensureDir(dirname(tempFixturePath)) - - const exists = await pathExists(tempFixturePath) - if (!exists) { - console.log('Generating high bitrate video.') - - // Generate a random, high bitrate video on the fly, so we don't have to include - // a large file in the repo. The video needs to have a certain minimum length so - // that FFmpeg properly applies bitrate limits. - // https://stackoverflow.com/a/15795112 - return new Promise((res, rej) => { - ffmpeg() - .outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ]) - .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ]) - .outputOptions([ '-maxrate 10M', '-bufsize 10M' ]) - .output(tempFixturePath) - .on('error', rej) - .on('end', () => res(tempFixturePath)) - .run() - }) - } - - await ensureHasTooBigBitrate(tempFixturePath) - - return tempFixturePath -} - -async function generateVideoWithFramerate (fps = 60) { - const tempFixturePath = buildAbsoluteFixturePath(`video_${fps}fps.mp4`, true) - - await ensureDir(dirname(tempFixturePath)) - - const exists = await pathExists(tempFixturePath) - if (!exists) { - console.log('Generating video with framerate %d.', fps) - - return new Promise((res, rej) => { - ffmpeg() - .outputOptions([ '-f rawvideo', '-video_size 1280x720', '-i /dev/urandom' ]) - .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ]) - .outputOptions([ `-r ${fps}` ]) - .output(tempFixturePath) - .on('error', rej) - .on('end', () => res(tempFixturePath)) - .run() - }) - } - - return tempFixturePath -} - -export { - generateHighBitrateVideo, - generateVideoWithFramerate -} diff --git a/shared/extra-utils/miscs/index.ts b/shared/extra-utils/miscs/index.ts deleted file mode 100644 index 4474661de..000000000 --- a/shared/extra-utils/miscs/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './checks' -export * from './generate' -export * from './sql-command' -export * from './tests' -export * from './webtorrent' diff --git a/shared/extra-utils/miscs/sql-command.ts b/shared/extra-utils/miscs/sql-command.ts deleted file mode 100644 index bedb3349b..000000000 --- a/shared/extra-utils/miscs/sql-command.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { QueryTypes, Sequelize } from 'sequelize' -import { AbstractCommand } from '../shared/abstract-command' - -export class SQLCommand extends AbstractCommand { - private sequelize: Sequelize - - deleteAll (table: string) { - const seq = this.getSequelize() - - const options = { type: QueryTypes.DELETE } - - return seq.query(`DELETE FROM "${table}"`, options) - } - - async getCount (table: string) { - const seq = this.getSequelize() - - const options = { type: QueryTypes.SELECT as QueryTypes.SELECT } - - const [ { total } ] = await seq.query<{ total: string }>(`SELECT COUNT(*) as total FROM "${table}"`, options) - if (total === null) return 0 - - return parseInt(total, 10) - } - - setActorField (to: string, field: string, value: string) { - const seq = this.getSequelize() - - const options = { type: QueryTypes.UPDATE } - - return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options) - } - - setVideoField (uuid: string, field: string, value: string) { - const seq = this.getSequelize() - - const options = { type: QueryTypes.UPDATE } - - return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options) - } - - setPlaylistField (uuid: string, field: string, value: string) { - const seq = this.getSequelize() - - const options = { type: QueryTypes.UPDATE } - - return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options) - } - - async countVideoViewsOf (uuid: string) { - const seq = this.getSequelize() - - const query = 'SELECT SUM("videoView"."views") AS "total" FROM "videoView" ' + - `INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'` - - const options = { type: QueryTypes.SELECT as QueryTypes.SELECT } - const [ { total } ] = await seq.query<{ total: number }>(query, options) - - if (!total) return 0 - - return parseInt(total + '', 10) - } - - getActorImage (filename: string) { - return this.selectQuery(`SELECT * FROM "actorImage" WHERE filename = '${filename}'`) - .then(rows => rows[0]) - } - - selectQuery (query: string) { - const seq = this.getSequelize() - const options = { type: QueryTypes.SELECT as QueryTypes.SELECT } - - return seq.query(query, options) - } - - updateQuery (query: string) { - const seq = this.getSequelize() - const options = { type: QueryTypes.UPDATE as QueryTypes.UPDATE } - - return seq.query(query, options) - } - - setPluginField (pluginName: string, field: string, value: string) { - const seq = this.getSequelize() - - const options = { type: QueryTypes.UPDATE } - - return seq.query(`UPDATE "plugin" SET "${field}" = '${value}' WHERE "name" = '${pluginName}'`, options) - } - - setPluginVersion (pluginName: string, newVersion: string) { - return this.setPluginField(pluginName, 'version', newVersion) - } - - setPluginLatestVersion (pluginName: string, newVersion: string) { - return this.setPluginField(pluginName, 'latestVersion', newVersion) - } - - setActorFollowScores (newScore: number) { - const seq = this.getSequelize() - - const options = { type: QueryTypes.UPDATE } - - return seq.query(`UPDATE "actorFollow" SET "score" = ${newScore}`, options) - } - - setTokenField (accessToken: string, field: string, value: string) { - const seq = this.getSequelize() - - const options = { type: QueryTypes.UPDATE } - - return seq.query(`UPDATE "oAuthToken" SET "${field}" = '${value}' WHERE "accessToken" = '${accessToken}'`, options) - } - - async cleanup () { - if (!this.sequelize) return - - await this.sequelize.close() - this.sequelize = undefined - } - - private getSequelize () { - if (this.sequelize) return this.sequelize - - const dbname = 'peertube_test' + this.server.internalServerNumber - const username = 'peertube' - const password = 'peertube' - const host = 'localhost' - const port = 5432 - - this.sequelize = new Sequelize(dbname, username, password, { - dialect: 'postgres', - host, - port, - logging: false - }) - - return this.sequelize - } - -} diff --git a/shared/extra-utils/miscs/tests.ts b/shared/extra-utils/miscs/tests.ts deleted file mode 100644 index 658fe5fd3..000000000 --- a/shared/extra-utils/miscs/tests.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { stat } from 'fs-extra' -import { basename, isAbsolute, join, resolve } from 'path' - -const FIXTURE_URLS = { - peertube_long: 'https://peertube2.cpy.re/videos/watch/122d093a-1ede-43bd-bd34-59d2931ffc5e', - peertube_short: 'https://peertube2.cpy.re/w/3fbif9S3WmtTP8gGsC5HBd', - - youtube: 'https://www.youtube.com/watch?v=msX3jv1XdvM', - - /** - * The video is used to check format-selection correctness wrt. HDR, - * which brings its own set of oddities outside of a MediaSource. - * - * The video needs to have the following format_ids: - * (which you can check by using `youtube-dl -F`): - * - (webm vp9) - * - (mp4 avc1) - * - (webm vp9.2 HDR) - */ - youtubeHDR: 'https://www.youtube.com/watch?v=RQgnBB9z_N4', - - // eslint-disable-next-line max-len - magnet: 'magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Flazy-static%2Ftorrents%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.torrent&xt=urn:btih:0f498834733e8057ed5c6f2ee2b4efd8d84a76ee&dn=super+peertube2+video&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.mp4', - - badVideo: 'https://download.cpy.re/peertube/bad_video.mp4', - goodVideo: 'https://download.cpy.re/peertube/good_video.mp4', - goodVideo720: 'https://download.cpy.re/peertube/good_video_720.mp4', - - file4K: 'https://download.cpy.re/peertube/4k_file.txt' -} - -function parallelTests () { - return process.env.MOCHA_PARALLEL === 'true' -} - -function isGithubCI () { - return !!process.env.GITHUB_WORKSPACE -} - -function areHttpImportTestsDisabled () { - const disabled = process.env.DISABLE_HTTP_IMPORT_TESTS === 'true' - - if (disabled) console.log('DISABLE_HTTP_IMPORT_TESTS env set to "true" so import tests are disabled') - - return disabled -} - -function areObjectStorageTestsDisabled () { - const disabled = process.env.ENABLE_OBJECT_STORAGE_TESTS !== 'true' - - if (disabled) console.log('ENABLE_OBJECT_STORAGE_TESTS env is not set to "true" so object storage tests are disabled') - - return disabled -} - -function buildAbsoluteFixturePath (path: string, customCIPath = false) { - if (isAbsolute(path)) return path - - if (customCIPath && process.env.GITHUB_WORKSPACE) { - return join(process.env.GITHUB_WORKSPACE, 'fixtures', path) - } - - return join(root(), 'server', 'tests', 'fixtures', path) -} - -function root () { - // We are in /miscs - let root = join(__dirname, '..', '..', '..') - - if (basename(root) === 'dist') root = resolve(root, '..') - - return root -} - -function wait (milliseconds: number) { - return new Promise(resolve => setTimeout(resolve, milliseconds)) -} - -async function getFileSize (path: string) { - const stats = await stat(path) - - return stats.size -} - -function buildRequestStub (): any { - return { } -} - -export { - FIXTURE_URLS, - - parallelTests, - isGithubCI, - areHttpImportTestsDisabled, - buildAbsoluteFixturePath, - getFileSize, - buildRequestStub, - areObjectStorageTestsDisabled, - wait, - root -} diff --git a/shared/extra-utils/miscs/webtorrent.ts b/shared/extra-utils/miscs/webtorrent.ts deleted file mode 100644 index 0683f8893..000000000 --- a/shared/extra-utils/miscs/webtorrent.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { readFile } from 'fs-extra' -import parseTorrent from 'parse-torrent' -import { basename, join } from 'path' -import * as WebTorrent from 'webtorrent' -import { VideoFile } from '@shared/models' -import { PeerTubeServer } from '../server' - -let webtorrent: WebTorrent.Instance - -function webtorrentAdd (torrentId: string, refreshWebTorrent = false) { - const WebTorrent = require('webtorrent') - - if (webtorrent && refreshWebTorrent) webtorrent.destroy() - if (!webtorrent || refreshWebTorrent) webtorrent = new WebTorrent() - - webtorrent.on('error', err => console.error('Error in webtorrent', err)) - - return new Promise(res => { - const torrent = webtorrent.add(torrentId, res) - - torrent.on('error', err => console.error('Error in webtorrent torrent', err)) - torrent.on('warning', warn => { - const msg = typeof warn === 'string' - ? warn - : warn.message - - if (msg.includes('Unsupported')) return - - console.error('Warning in webtorrent torrent', warn) - }) - }) -} - -async function parseTorrentVideo (server: PeerTubeServer, file: VideoFile) { - const torrentName = basename(file.torrentUrl) - const torrentPath = server.servers.buildDirectory(join('torrents', torrentName)) - - const data = await readFile(torrentPath) - - return parseTorrent(data) -} - -export { - webtorrentAdd, - parseTorrentVideo -} diff --git a/shared/extra-utils/mock-servers/index.ts b/shared/extra-utils/mock-servers/index.ts deleted file mode 100644 index 93c00c788..000000000 --- a/shared/extra-utils/mock-servers/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './mock-email' -export * from './mock-instances-index' -export * from './mock-joinpeertube-versions' -export * from './mock-plugin-blocklist' -export * from './mock-object-storage' diff --git a/shared/extra-utils/mock-servers/mock-429.ts b/shared/extra-utils/mock-servers/mock-429.ts deleted file mode 100644 index 9e0d1281a..000000000 --- a/shared/extra-utils/mock-servers/mock-429.ts +++ /dev/null @@ -1,33 +0,0 @@ -import express from 'express' -import { Server } from 'http' -import { getPort, randomListen, terminateServer } from './utils' - -export class Mock429 { - private server: Server - private responseSent = false - - async initialize () { - const app = express() - - app.get('/', (req: express.Request, res: express.Response, next: express.NextFunction) => { - - if (!this.responseSent) { - this.responseSent = true - - // Retry after 5 seconds - res.header('retry-after', '2') - return res.sendStatus(429) - } - - return res.sendStatus(200) - }) - - this.server = await randomListen(app) - - return getPort(this.server) - } - - terminate () { - return terminateServer(this.server) - } -} diff --git a/shared/extra-utils/mock-servers/mock-email.ts b/shared/extra-utils/mock-servers/mock-email.ts deleted file mode 100644 index f646c1621..000000000 --- a/shared/extra-utils/mock-servers/mock-email.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { ChildProcess } from 'child_process' -import MailDev from '@peertube/maildev' -import { randomInt } from '@shared/core-utils' -import { parallelTests } from '../miscs' - -class MockSmtpServer { - - private static instance: MockSmtpServer - private started = false - private emailChildProcess: ChildProcess - private emails: object[] - - private constructor () { } - - collectEmails (emailsCollection: object[]) { - return new Promise((res, rej) => { - const port = parallelTests() ? randomInt(1000, 2000) : 1025 - this.emails = emailsCollection - - if (this.started) { - return res(undefined) - } - - const maildev = new MailDev({ - ip: '127.0.0.1', - smtp: port, - disableWeb: true, - silent: true - }) - - maildev.on('new', email => { - this.emails.push(email) - }) - - maildev.listen(err => { - if (err) return rej(err) - - this.started = true - - return res(port) - }) - }) - } - - kill () { - if (!this.emailChildProcess) return - - process.kill(this.emailChildProcess.pid) - - this.emailChildProcess = null - MockSmtpServer.instance = null - } - - static get Instance () { - return this.instance || (this.instance = new this()) - } -} - -// --------------------------------------------------------------------------- - -export { - MockSmtpServer -} diff --git a/shared/extra-utils/mock-servers/mock-instances-index.ts b/shared/extra-utils/mock-servers/mock-instances-index.ts deleted file mode 100644 index 92b12d6f3..000000000 --- a/shared/extra-utils/mock-servers/mock-instances-index.ts +++ /dev/null @@ -1,46 +0,0 @@ -import express from 'express' -import { Server } from 'http' -import { getPort, randomListen, terminateServer } from './utils' - -export class MockInstancesIndex { - private server: Server - - private readonly indexInstances: { host: string, createdAt: string }[] = [] - - async initialize () { - const app = express() - - app.use('/', (req: express.Request, res: express.Response, next: express.NextFunction) => { - if (process.env.DEBUG) console.log('Receiving request on mocked server %s.', req.url) - - return next() - }) - - app.get('/api/v1/instances/hosts', (req: express.Request, res: express.Response) => { - const since = req.query.since - - const filtered = this.indexInstances.filter(i => { - if (!since) return true - - return i.createdAt > since - }) - - return res.json({ - total: filtered.length, - data: filtered - }) - }) - - this.server = await randomListen(app) - - return getPort(this.server) - } - - addInstance (host: string) { - this.indexInstances.push({ host, createdAt: new Date().toISOString() }) - } - - terminate () { - return terminateServer(this.server) - } -} diff --git a/shared/extra-utils/mock-servers/mock-joinpeertube-versions.ts b/shared/extra-utils/mock-servers/mock-joinpeertube-versions.ts deleted file mode 100644 index e7906ea56..000000000 --- a/shared/extra-utils/mock-servers/mock-joinpeertube-versions.ts +++ /dev/null @@ -1,34 +0,0 @@ -import express from 'express' -import { Server } from 'http' -import { getPort, randomListen } from './utils' - -export class MockJoinPeerTubeVersions { - private server: Server - private latestVersion: string - - async initialize () { - const app = express() - - app.use('/', (req: express.Request, res: express.Response, next: express.NextFunction) => { - if (process.env.DEBUG) console.log('Receiving request on mocked server %s.', req.url) - - return next() - }) - - app.get('/versions.json', (req: express.Request, res: express.Response) => { - return res.json({ - peertube: { - latestVersion: this.latestVersion - } - }) - }) - - this.server = await randomListen(app) - - return getPort(this.server) - } - - setLatestVersion (latestVersion: string) { - this.latestVersion = latestVersion - } -} diff --git a/shared/extra-utils/mock-servers/mock-object-storage.ts b/shared/extra-utils/mock-servers/mock-object-storage.ts deleted file mode 100644 index d135c2631..000000000 --- a/shared/extra-utils/mock-servers/mock-object-storage.ts +++ /dev/null @@ -1,41 +0,0 @@ -import express from 'express' -import got, { RequestError } from 'got' -import { Server } from 'http' -import { pipeline } from 'stream' -import { ObjectStorageCommand } from '../server' -import { getPort, randomListen, terminateServer } from './utils' - -export class MockObjectStorage { - private server: Server - - async initialize () { - const app = express() - - app.get('/:bucketName/:path(*)', (req: express.Request, res: express.Response, next: express.NextFunction) => { - const url = `http://${req.params.bucketName}.${ObjectStorageCommand.getEndpointHost()}/${req.params.path}` - - if (process.env.DEBUG) { - console.log('Receiving request on mocked server %s.', req.url) - console.log('Proxifying request to %s', url) - } - - return pipeline( - got.stream(url, { throwHttpErrors: false }), - res, - (err: RequestError) => { - if (!err) return - - console.error('Pipeline failed.', err) - } - ) - }) - - this.server = await randomListen(app) - - return getPort(this.server) - } - - terminate () { - return terminateServer(this.server) - } -} diff --git a/shared/extra-utils/mock-servers/mock-plugin-blocklist.ts b/shared/extra-utils/mock-servers/mock-plugin-blocklist.ts deleted file mode 100644 index f8a271cba..000000000 --- a/shared/extra-utils/mock-servers/mock-plugin-blocklist.ts +++ /dev/null @@ -1,36 +0,0 @@ -import express, { Request, Response } from 'express' -import { Server } from 'http' -import { getPort, randomListen, terminateServer } from './utils' - -type BlocklistResponse = { - data: { - value: string - action?: 'add' | 'remove' - updatedAt?: string - }[] -} - -export class MockBlocklist { - private body: BlocklistResponse - private server: Server - - async initialize () { - const app = express() - - app.get('/blocklist', (req: Request, res: Response) => { - return res.json(this.body) - }) - - this.server = await randomListen(app) - - return getPort(this.server) - } - - replace (body: BlocklistResponse) { - this.body = body - } - - terminate () { - return terminateServer(this.server) - } -} diff --git a/shared/extra-utils/mock-servers/mock-proxy.ts b/shared/extra-utils/mock-servers/mock-proxy.ts deleted file mode 100644 index 75ac79055..000000000 --- a/shared/extra-utils/mock-servers/mock-proxy.ts +++ /dev/null @@ -1,25 +0,0 @@ - -import { createServer, Server } from 'http' -import proxy from 'proxy' -import { getPort, terminateServer } from './utils' - -class MockProxy { - private server: Server - - initialize () { - return new Promise(res => { - this.server = proxy(createServer()) - this.server.listen(0, () => res(getPort(this.server))) - }) - } - - terminate () { - return terminateServer(this.server) - } -} - -// --------------------------------------------------------------------------- - -export { - MockProxy -} diff --git a/shared/extra-utils/mock-servers/utils.ts b/shared/extra-utils/mock-servers/utils.ts deleted file mode 100644 index 235642439..000000000 --- a/shared/extra-utils/mock-servers/utils.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Express } from 'express' -import { Server } from 'http' -import { AddressInfo } from 'net' - -function randomListen (app: Express) { - return new Promise(res => { - const server = app.listen(0, () => res(server)) - }) -} - -function getPort (server: Server) { - const address = server.address() as AddressInfo - - return address.port -} - -function terminateServer (server: Server) { - if (!server) return Promise.resolve() - - return new Promise((res, rej) => { - server.close(err => { - if (err) return rej(err) - - return res() - }) - }) -} - -export { - randomListen, - getPort, - terminateServer -} diff --git a/shared/extra-utils/moderation/abuses-command.ts b/shared/extra-utils/moderation/abuses-command.ts deleted file mode 100644 index 0db32ba46..000000000 --- a/shared/extra-utils/moderation/abuses-command.ts +++ /dev/null @@ -1,228 +0,0 @@ -import { pick } from '@shared/core-utils' -import { - AbuseFilter, - AbuseMessage, - AbusePredefinedReasonsString, - AbuseState, - AbuseUpdate, - AbuseVideoIs, - AdminAbuse, - HttpStatusCode, - ResultList, - UserAbuse -} from '@shared/models' -import { unwrapBody } from '../requests/requests' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class AbusesCommand extends AbstractCommand { - - report (options: OverrideCommandOptions & { - reason: string - - accountId?: number - videoId?: number - commentId?: number - - predefinedReasons?: AbusePredefinedReasonsString[] - - startAt?: number - endAt?: number - }) { - const path = '/api/v1/abuses' - - const video = options.videoId - ? { - id: options.videoId, - startAt: options.startAt, - endAt: options.endAt - } - : undefined - - const comment = options.commentId - ? { id: options.commentId } - : undefined - - const account = options.accountId - ? { id: options.accountId } - : undefined - - const body = { - account, - video, - comment, - - reason: options.reason, - predefinedReasons: options.predefinedReasons - } - - return unwrapBody<{ abuse: { id: number } }>(this.postBodyRequest({ - ...options, - - path, - fields: body, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - } - - getAdminList (options: OverrideCommandOptions & { - start?: number - count?: number - sort?: string - - id?: number - predefinedReason?: AbusePredefinedReasonsString - search?: string - filter?: AbuseFilter - state?: AbuseState - videoIs?: AbuseVideoIs - searchReporter?: string - searchReportee?: string - searchVideo?: string - searchVideoChannel?: string - } = {}) { - const toPick: (keyof typeof options)[] = [ - 'count', - 'filter', - 'id', - 'predefinedReason', - 'search', - 'searchReportee', - 'searchReporter', - 'searchVideo', - 'searchVideoChannel', - 'sort', - 'start', - 'state', - 'videoIs' - ] - - const path = '/api/v1/abuses' - - const defaultQuery = { sort: 'createdAt' } - const query = { ...defaultQuery, ...pick(options, toPick) } - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getUserList (options: OverrideCommandOptions & { - start?: number - count?: number - sort?: string - - id?: number - search?: string - state?: AbuseState - }) { - const toPick: (keyof typeof options)[] = [ - 'id', - 'search', - 'state', - 'start', - 'count', - 'sort' - ] - - const path = '/api/v1/users/me/abuses' - - const defaultQuery = { sort: 'createdAt' } - const query = { ...defaultQuery, ...pick(options, toPick) } - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - update (options: OverrideCommandOptions & { - abuseId: number - body: AbuseUpdate - }) { - const { abuseId, body } = options - const path = '/api/v1/abuses/' + abuseId - - return this.putBodyRequest({ - ...options, - - path, - fields: body, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - delete (options: OverrideCommandOptions & { - abuseId: number - }) { - const { abuseId } = options - const path = '/api/v1/abuses/' + abuseId - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - listMessages (options: OverrideCommandOptions & { - abuseId: number - }) { - const { abuseId } = options - const path = '/api/v1/abuses/' + abuseId + '/messages' - - return this.getRequestBody>({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - deleteMessage (options: OverrideCommandOptions & { - abuseId: number - messageId: number - }) { - const { abuseId, messageId } = options - const path = '/api/v1/abuses/' + abuseId + '/messages/' + messageId - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - addMessage (options: OverrideCommandOptions & { - abuseId: number - message: string - }) { - const { abuseId, message } = options - const path = '/api/v1/abuses/' + abuseId + '/messages' - - return this.postBodyRequest({ - ...options, - - path, - fields: { message }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - -} diff --git a/shared/extra-utils/moderation/index.ts b/shared/extra-utils/moderation/index.ts deleted file mode 100644 index b37643956..000000000 --- a/shared/extra-utils/moderation/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './abuses-command' diff --git a/shared/extra-utils/overviews/index.ts b/shared/extra-utils/overviews/index.ts deleted file mode 100644 index e19551907..000000000 --- a/shared/extra-utils/overviews/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './overviews-command' diff --git a/shared/extra-utils/overviews/overviews-command.ts b/shared/extra-utils/overviews/overviews-command.ts deleted file mode 100644 index 06b4892d2..000000000 --- a/shared/extra-utils/overviews/overviews-command.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { HttpStatusCode, VideosOverview } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class OverviewsCommand extends AbstractCommand { - - getVideos (options: OverrideCommandOptions & { - page: number - }) { - const { page } = options - const path = '/api/v1/overviews/videos' - - const query = { page } - - return this.getRequestBody({ - ...options, - - path, - query, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/requests/check-api-params.ts b/shared/extra-utils/requests/check-api-params.ts deleted file mode 100644 index 26ba1e913..000000000 --- a/shared/extra-utils/requests/check-api-params.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { HttpStatusCode } from '@shared/models' -import { makeGetRequest } from './requests' - -function checkBadStartPagination (url: string, path: string, token?: string, query = {}) { - return makeGetRequest({ - url, - path, - token, - query: { ...query, start: 'hello' }, - expectedStatus: HttpStatusCode.BAD_REQUEST_400 - }) -} - -async function checkBadCountPagination (url: string, path: string, token?: string, query = {}) { - await makeGetRequest({ - url, - path, - token, - query: { ...query, count: 'hello' }, - expectedStatus: HttpStatusCode.BAD_REQUEST_400 - }) - - await makeGetRequest({ - url, - path, - token, - query: { ...query, count: 2000 }, - expectedStatus: HttpStatusCode.BAD_REQUEST_400 - }) -} - -function checkBadSortPagination (url: string, path: string, token?: string, query = {}) { - return makeGetRequest({ - url, - path, - token, - query: { ...query, sort: 'hello' }, - expectedStatus: HttpStatusCode.BAD_REQUEST_400 - }) -} - -// --------------------------------------------------------------------------- - -export { - checkBadStartPagination, - checkBadCountPagination, - checkBadSortPagination -} diff --git a/shared/extra-utils/requests/index.ts b/shared/extra-utils/requests/index.ts deleted file mode 100644 index 501163f92..000000000 --- a/shared/extra-utils/requests/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Don't include activitypub that import stuff from server -export * from './check-api-params' -export * from './requests' diff --git a/shared/extra-utils/requests/requests.ts b/shared/extra-utils/requests/requests.ts deleted file mode 100644 index b6b9024ed..000000000 --- a/shared/extra-utils/requests/requests.ts +++ /dev/null @@ -1,208 +0,0 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ - -import { decode } from 'querystring' -import request from 'supertest' -import { URL } from 'url' -import { HttpStatusCode } from '@shared/models' -import { buildAbsoluteFixturePath } from '../miscs/tests' - -export type CommonRequestParams = { - url: string - path?: string - contentType?: string - range?: string - redirects?: number - accept?: string - host?: string - token?: string - headers?: { [ name: string ]: string } - type?: string - xForwardedFor?: string - expectedStatus?: HttpStatusCode -} - -function makeRawRequest (url: string, expectedStatus?: HttpStatusCode, range?: string) { - const { host, protocol, pathname } = new URL(url) - - return makeGetRequest({ url: `${protocol}//${host}`, path: pathname, expectedStatus, range }) -} - -function makeGetRequest (options: CommonRequestParams & { - query?: any - rawQuery?: string -}) { - const req = request(options.url).get(options.path) - - if (options.query) req.query(options.query) - if (options.rawQuery) req.query(options.rawQuery) - - return buildRequest(req, { contentType: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options }) -} - -function makeHTMLRequest (url: string, path: string) { - return makeGetRequest({ - url, - path, - accept: 'text/html', - expectedStatus: HttpStatusCode.OK_200 - }) -} - -function makeActivityPubGetRequest (url: string, path: string, expectedStatus = HttpStatusCode.OK_200) { - return makeGetRequest({ - url, - path, - expectedStatus: expectedStatus, - accept: 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8' - }) -} - -function makeDeleteRequest (options: CommonRequestParams & { - query?: any - rawQuery?: string -}) { - const req = request(options.url).delete(options.path) - - if (options.query) req.query(options.query) - if (options.rawQuery) req.query(options.rawQuery) - - return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options }) -} - -function makeUploadRequest (options: CommonRequestParams & { - method?: 'POST' | 'PUT' - - fields: { [ fieldName: string ]: any } - attaches?: { [ attachName: string ]: any | any[] } -}) { - let req = options.method === 'PUT' - ? request(options.url).put(options.path) - : request(options.url).post(options.path) - - req = buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options }) - - buildFields(req, options.fields) - - Object.keys(options.attaches || {}).forEach(attach => { - const value = options.attaches[attach] - if (!value) return - - if (Array.isArray(value)) { - req.attach(attach, buildAbsoluteFixturePath(value[0]), value[1]) - } else { - req.attach(attach, buildAbsoluteFixturePath(value)) - } - }) - - return req -} - -function makePostBodyRequest (options: CommonRequestParams & { - fields?: { [ fieldName: string ]: any } -}) { - const req = request(options.url).post(options.path) - .send(options.fields) - - return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options }) -} - -function makePutBodyRequest (options: { - url: string - path: string - token?: string - fields: { [ fieldName: string ]: any } - expectedStatus?: HttpStatusCode -}) { - const req = request(options.url).put(options.path) - .send(options.fields) - - return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options }) -} - -function decodeQueryString (path: string) { - return decode(path.split('?')[1]) -} - -function unwrapBody (test: request.Test): Promise { - return test.then(res => res.body) -} - -function unwrapText (test: request.Test): Promise { - return test.then(res => res.text) -} - -function unwrapBodyOrDecodeToJSON (test: request.Test): Promise { - return test.then(res => { - if (res.body instanceof Buffer) { - return JSON.parse(new TextDecoder().decode(res.body)) - } - - return res.body - }) -} - -function unwrapTextOrDecode (test: request.Test): Promise { - return test.then(res => res.text || new TextDecoder().decode(res.body)) -} - -// --------------------------------------------------------------------------- - -export { - makeHTMLRequest, - makeGetRequest, - decodeQueryString, - makeUploadRequest, - makePostBodyRequest, - makePutBodyRequest, - makeDeleteRequest, - makeRawRequest, - makeActivityPubGetRequest, - unwrapBody, - unwrapTextOrDecode, - unwrapBodyOrDecodeToJSON, - unwrapText -} - -// --------------------------------------------------------------------------- - -function buildRequest (req: request.Test, options: CommonRequestParams) { - if (options.contentType) req.set('Accept', options.contentType) - if (options.token) req.set('Authorization', 'Bearer ' + options.token) - if (options.range) req.set('Range', options.range) - if (options.accept) req.set('Accept', options.accept) - if (options.host) req.set('Host', options.host) - if (options.redirects) req.redirects(options.redirects) - if (options.expectedStatus) req.expect(options.expectedStatus) - if (options.xForwardedFor) req.set('X-Forwarded-For', options.xForwardedFor) - if (options.type) req.type(options.type) - - Object.keys(options.headers || {}).forEach(name => { - req.set(name, options.headers[name]) - }) - - return req -} - -function buildFields (req: request.Test, fields: { [ fieldName: string ]: any }, namespace?: string) { - if (!fields) return - - let formKey: string - - for (const key of Object.keys(fields)) { - if (namespace) formKey = `${namespace}[${key}]` - else formKey = key - - if (fields[key] === undefined) continue - - if (Array.isArray(fields[key]) && fields[key].length === 0) { - req.field(key, []) - continue - } - - if (fields[key] !== null && typeof fields[key] === 'object') { - buildFields(req, fields[key], formKey) - } else { - req.field(formKey, fields[key]) - } - } -} diff --git a/shared/extra-utils/search/index.ts b/shared/extra-utils/search/index.ts deleted file mode 100644 index 48dbe8ae9..000000000 --- a/shared/extra-utils/search/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './search-command' diff --git a/shared/extra-utils/search/search-command.ts b/shared/extra-utils/search/search-command.ts deleted file mode 100644 index 0fbbcd6ef..000000000 --- a/shared/extra-utils/search/search-command.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { - HttpStatusCode, - ResultList, - Video, - VideoChannel, - VideoChannelsSearchQuery, - VideoPlaylist, - VideoPlaylistsSearchQuery, - VideosSearchQuery -} from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class SearchCommand extends AbstractCommand { - - searchChannels (options: OverrideCommandOptions & { - search: string - }) { - return this.advancedChannelSearch({ - ...options, - - search: { search: options.search } - }) - } - - advancedChannelSearch (options: OverrideCommandOptions & { - search: VideoChannelsSearchQuery - }) { - const { search } = options - const path = '/api/v1/search/video-channels' - - return this.getRequestBody>({ - ...options, - - path, - query: search, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - searchPlaylists (options: OverrideCommandOptions & { - search: string - }) { - return this.advancedPlaylistSearch({ - ...options, - - search: { search: options.search } - }) - } - - advancedPlaylistSearch (options: OverrideCommandOptions & { - search: VideoPlaylistsSearchQuery - }) { - const { search } = options - const path = '/api/v1/search/video-playlists' - - return this.getRequestBody>({ - ...options, - - path, - query: search, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - searchVideos (options: OverrideCommandOptions & { - search: string - sort?: string - }) { - const { search, sort } = options - - return this.advancedVideoSearch({ - ...options, - - search: { - search: search, - sort: sort ?? '-publishedAt' - } - }) - } - - advancedVideoSearch (options: OverrideCommandOptions & { - search: VideosSearchQuery - }) { - const { search } = options - const path = '/api/v1/search/videos' - - return this.getRequestBody>({ - ...options, - - path, - query: search, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/server/config-command.ts b/shared/extra-utils/server/config-command.ts deleted file mode 100644 index 89ae8eb4f..000000000 --- a/shared/extra-utils/server/config-command.ts +++ /dev/null @@ -1,353 +0,0 @@ -import { merge } from 'lodash' -import { DeepPartial } from '@shared/typescript-utils' -import { About, HttpStatusCode, ServerConfig } from '@shared/models' -import { CustomConfig } from '../../models/server/custom-config.model' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class ConfigCommand extends AbstractCommand { - - static getCustomConfigResolutions (enabled: boolean) { - return { - '144p': enabled, - '240p': enabled, - '360p': enabled, - '480p': enabled, - '720p': enabled, - '1080p': enabled, - '1440p': enabled, - '2160p': enabled - } - } - - enableImports () { - return this.updateExistingSubConfig({ - newConfig: { - import: { - videos: { - http: { - enabled: true - }, - - torrent: { - enabled: true - } - } - } - } - }) - } - - enableLive (options: { - allowReplay?: boolean - transcoding?: boolean - } = {}) { - return this.updateExistingSubConfig({ - newConfig: { - live: { - enabled: true, - allowReplay: options.allowReplay ?? true, - transcoding: { - enabled: options.transcoding ?? true, - resolutions: ConfigCommand.getCustomConfigResolutions(true) - } - } - } - }) - } - - disableTranscoding () { - return this.updateExistingSubConfig({ - newConfig: { - transcoding: { - enabled: false - } - } - }) - } - - enableTranscoding (webtorrent = true, hls = true) { - return this.updateExistingSubConfig({ - newConfig: { - transcoding: { - enabled: true, - resolutions: ConfigCommand.getCustomConfigResolutions(true), - - webtorrent: { - enabled: webtorrent - }, - hls: { - enabled: hls - } - } - } - }) - } - - getConfig (options: OverrideCommandOptions = {}) { - const path = '/api/v1/config' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getAbout (options: OverrideCommandOptions = {}) { - const path = '/api/v1/config/about' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getCustomConfig (options: OverrideCommandOptions = {}) { - const path = '/api/v1/config/custom' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - updateCustomConfig (options: OverrideCommandOptions & { - newCustomConfig: CustomConfig - }) { - const path = '/api/v1/config/custom' - - return this.putBodyRequest({ - ...options, - - path, - fields: options.newCustomConfig, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - deleteCustomConfig (options: OverrideCommandOptions = {}) { - const path = '/api/v1/config/custom' - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - async updateExistingSubConfig (options: OverrideCommandOptions & { - newConfig: DeepPartial - }) { - const existing = await this.getCustomConfig(options) - - return this.updateCustomConfig({ ...options, newCustomConfig: merge({}, existing, options.newConfig) }) - } - - updateCustomSubConfig (options: OverrideCommandOptions & { - newConfig: DeepPartial - }) { - const newCustomConfig: CustomConfig = { - instance: { - name: 'PeerTube updated', - shortDescription: 'my short description', - description: 'my super description', - terms: 'my super terms', - codeOfConduct: 'my super coc', - - creationReason: 'my super creation reason', - moderationInformation: 'my super moderation information', - administrator: 'Kuja', - maintenanceLifetime: 'forever', - businessModel: 'my super business model', - hardwareInformation: '2vCore 3GB RAM', - - languages: [ 'en', 'es' ], - categories: [ 1, 2 ], - - isNSFW: true, - defaultNSFWPolicy: 'blur', - - defaultClientRoute: '/videos/recently-added', - - customizations: { - javascript: 'alert("coucou")', - css: 'body { background-color: red; }' - } - }, - theme: { - default: 'default' - }, - services: { - twitter: { - username: '@MySuperUsername', - whitelisted: true - } - }, - client: { - videos: { - miniature: { - preferAuthorDisplayName: false - } - }, - menu: { - login: { - redirectOnSingleExternalAuth: false - } - } - }, - cache: { - previews: { - size: 2 - }, - captions: { - size: 3 - }, - torrents: { - size: 4 - } - }, - signup: { - enabled: false, - limit: 5, - requiresEmailVerification: false, - minimumAge: 16 - }, - admin: { - email: 'superadmin1@example.com' - }, - contactForm: { - enabled: true - }, - user: { - videoQuota: 5242881, - videoQuotaDaily: 318742 - }, - videoChannels: { - maxPerUser: 20 - }, - transcoding: { - enabled: true, - allowAdditionalExtensions: true, - allowAudioFiles: true, - threads: 1, - concurrency: 3, - profile: 'default', - resolutions: { - '0p': false, - '144p': false, - '240p': false, - '360p': true, - '480p': true, - '720p': false, - '1080p': false, - '1440p': false, - '2160p': false - }, - webtorrent: { - enabled: true - }, - hls: { - enabled: false - } - }, - live: { - enabled: true, - allowReplay: false, - maxDuration: -1, - maxInstanceLives: -1, - maxUserLives: 50, - transcoding: { - enabled: true, - threads: 4, - profile: 'default', - resolutions: { - '144p': true, - '240p': true, - '360p': true, - '480p': true, - '720p': true, - '1080p': true, - '1440p': true, - '2160p': true - } - } - }, - import: { - videos: { - concurrency: 3, - http: { - enabled: false - }, - torrent: { - enabled: false - } - } - }, - trending: { - videos: { - algorithms: { - enabled: [ 'best', 'hot', 'most-viewed', 'most-liked' ], - default: 'hot' - } - } - }, - autoBlacklist: { - videos: { - ofUsers: { - enabled: false - } - } - }, - followers: { - instance: { - enabled: true, - manualApproval: false - } - }, - followings: { - instance: { - autoFollowBack: { - enabled: false - }, - autoFollowIndex: { - indexUrl: 'https://instances.joinpeertube.org/api/v1/instances/hosts', - enabled: false - } - } - }, - broadcastMessage: { - enabled: true, - level: 'warning', - message: 'hello', - dismissable: true - }, - search: { - remoteUri: { - users: true, - anonymous: true - }, - searchIndex: { - enabled: true, - url: 'https://search.joinpeertube.org', - disableLocalSearch: true, - isDefaultSearch: true - } - } - } - - merge(newCustomConfig, options.newConfig) - - return this.updateCustomConfig({ ...options, newCustomConfig }) - } -} diff --git a/shared/extra-utils/server/contact-form-command.ts b/shared/extra-utils/server/contact-form-command.ts deleted file mode 100644 index 0e8fd6d84..000000000 --- a/shared/extra-utils/server/contact-form-command.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { HttpStatusCode } from '@shared/models' -import { ContactForm } from '../../models/server' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class ContactFormCommand extends AbstractCommand { - - send (options: OverrideCommandOptions & { - fromEmail: string - fromName: string - subject: string - body: string - }) { - const path = '/api/v1/server/contact' - - const body: ContactForm = { - fromEmail: options.fromEmail, - fromName: options.fromName, - subject: options.subject, - body: options.body - } - - return this.postBodyRequest({ - ...options, - - path, - fields: body, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } -} diff --git a/shared/extra-utils/server/debug-command.ts b/shared/extra-utils/server/debug-command.ts deleted file mode 100644 index 3c5a785bb..000000000 --- a/shared/extra-utils/server/debug-command.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Debug, HttpStatusCode, SendDebugCommand } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class DebugCommand extends AbstractCommand { - - getDebug (options: OverrideCommandOptions = {}) { - const path = '/api/v1/server/debug' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - sendCommand (options: OverrideCommandOptions & { - body: SendDebugCommand - }) { - const { body } = options - const path = '/api/v1/server/debug/run-command' - - return this.postBodyRequest({ - ...options, - - path, - fields: body, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } -} diff --git a/shared/extra-utils/server/directories.ts b/shared/extra-utils/server/directories.ts deleted file mode 100644 index e6f72d6fc..000000000 --- a/shared/extra-utils/server/directories.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ - -import { expect } from 'chai' -import { pathExists, readdir } from 'fs-extra' -import { join } from 'path' -import { root } from '@shared/core-utils' -import { PeerTubeServer } from './server' - -async function checkTmpIsEmpty (server: PeerTubeServer) { - await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls', 'resumable-uploads' ]) - - if (await pathExists(join('test' + server.internalServerNumber, 'tmp', 'hls'))) { - await checkDirectoryIsEmpty(server, 'tmp/hls') - } -} - -async function checkDirectoryIsEmpty (server: PeerTubeServer, directory: string, exceptions: string[] = []) { - const testDirectory = 'test' + server.internalServerNumber - - const directoryPath = join(root(), testDirectory, directory) - - const directoryExists = await pathExists(directoryPath) - expect(directoryExists).to.be.true - - const files = await readdir(directoryPath) - const filtered = files.filter(f => exceptions.includes(f) === false) - - expect(filtered).to.have.lengthOf(0) -} - -export { - checkTmpIsEmpty, - checkDirectoryIsEmpty -} diff --git a/shared/extra-utils/server/follows-command.ts b/shared/extra-utils/server/follows-command.ts deleted file mode 100644 index 01ef6f179..000000000 --- a/shared/extra-utils/server/follows-command.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { pick } from '@shared/core-utils' -import { ActivityPubActorType, ActorFollow, FollowState, HttpStatusCode, ResultList, ServerFollowCreate } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' -import { PeerTubeServer } from './server' - -export class FollowsCommand extends AbstractCommand { - - getFollowers (options: OverrideCommandOptions & { - start: number - count: number - sort: string - search?: string - actorType?: ActivityPubActorType - state?: FollowState - }) { - const path = '/api/v1/server/followers' - - const query = pick(options, [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ]) - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getFollowings (options: OverrideCommandOptions & { - start?: number - count?: number - sort?: string - search?: string - actorType?: ActivityPubActorType - state?: FollowState - } = {}) { - const path = '/api/v1/server/following' - - const query = pick(options, [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ]) - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - follow (options: OverrideCommandOptions & { - hosts?: string[] - handles?: string[] - }) { - const path = '/api/v1/server/following' - - const fields: ServerFollowCreate = {} - - if (options.hosts) { - fields.hosts = options.hosts.map(f => f.replace(/^http:\/\//, '')) - } - - if (options.handles) { - fields.handles = options.handles - } - - return this.postBodyRequest({ - ...options, - - path, - fields, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - async unfollow (options: OverrideCommandOptions & { - target: PeerTubeServer | string - }) { - const { target } = options - - const handle = typeof target === 'string' - ? target - : target.host - - const path = '/api/v1/server/following/' + handle - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - acceptFollower (options: OverrideCommandOptions & { - follower: string - }) { - const path = '/api/v1/server/followers/' + options.follower + '/accept' - - return this.postBodyRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - rejectFollower (options: OverrideCommandOptions & { - follower: string - }) { - const path = '/api/v1/server/followers/' + options.follower + '/reject' - - return this.postBodyRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - removeFollower (options: OverrideCommandOptions & { - follower: PeerTubeServer - }) { - const path = '/api/v1/server/followers/peertube@' + options.follower.host - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } -} diff --git a/shared/extra-utils/server/follows.ts b/shared/extra-utils/server/follows.ts deleted file mode 100644 index 698238f29..000000000 --- a/shared/extra-utils/server/follows.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { waitJobs } from './jobs' -import { PeerTubeServer } from './server' - -async function doubleFollow (server1: PeerTubeServer, server2: PeerTubeServer) { - await Promise.all([ - server1.follows.follow({ hosts: [ server2.url ] }), - server2.follows.follow({ hosts: [ server1.url ] }) - ]) - - // Wait request propagation - await waitJobs([ server1, server2 ]) - - return true -} - -// --------------------------------------------------------------------------- - -export { - doubleFollow -} diff --git a/shared/extra-utils/server/index.ts b/shared/extra-utils/server/index.ts deleted file mode 100644 index 76a2099da..000000000 --- a/shared/extra-utils/server/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -export * from './config-command' -export * from './contact-form-command' -export * from './debug-command' -export * from './directories' -export * from './follows-command' -export * from './follows' -export * from './jobs' -export * from './jobs-command' -export * from './object-storage-command' -export * from './plugins-command' -export * from './plugins' -export * from './redundancy-command' -export * from './server' -export * from './servers-command' -export * from './servers' -export * from './stats-command' -export * from './tracker' diff --git a/shared/extra-utils/server/jobs-command.ts b/shared/extra-utils/server/jobs-command.ts deleted file mode 100644 index 6636e7e4d..000000000 --- a/shared/extra-utils/server/jobs-command.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { pick } from '@shared/core-utils' -import { HttpStatusCode } from '@shared/models' -import { Job, JobState, JobType, ResultList } from '../../models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class JobsCommand extends AbstractCommand { - - async getLatest (options: OverrideCommandOptions & { - jobType: JobType - }) { - const { data } = await this.list({ ...options, start: 0, count: 1, sort: '-createdAt' }) - - if (data.length === 0) return undefined - - return data[0] - } - - list (options: OverrideCommandOptions & { - state?: JobState - jobType?: JobType - start?: number - count?: number - sort?: string - } = {}) { - const path = this.buildJobsUrl(options.state) - - const query = pick(options, [ 'start', 'count', 'sort', 'jobType' ]) - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listFailed (options: OverrideCommandOptions & { - jobType?: JobType - }) { - const path = this.buildJobsUrl('failed') - - return this.getRequestBody>({ - ...options, - - path, - query: { start: 0, count: 50 }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - private buildJobsUrl (state?: JobState) { - let path = '/api/v1/jobs' - - if (state) path += '/' + state - - return path - } -} diff --git a/shared/extra-utils/server/jobs.ts b/shared/extra-utils/server/jobs.ts deleted file mode 100644 index 34fefd444..000000000 --- a/shared/extra-utils/server/jobs.ts +++ /dev/null @@ -1,84 +0,0 @@ - -import { expect } from 'chai' -import { JobState, JobType } from '../../models' -import { wait } from '../miscs' -import { PeerTubeServer } from './server' - -async function waitJobs (serversArg: PeerTubeServer[] | PeerTubeServer, skipDelayed = false) { - const pendingJobWait = process.env.NODE_PENDING_JOB_WAIT - ? parseInt(process.env.NODE_PENDING_JOB_WAIT, 10) - : 250 - - let servers: PeerTubeServer[] - - if (Array.isArray(serversArg) === false) servers = [ serversArg as PeerTubeServer ] - else servers = serversArg as PeerTubeServer[] - - const states: JobState[] = [ 'waiting', 'active' ] - if (!skipDelayed) states.push('delayed') - - const repeatableJobs: JobType[] = [ 'videos-views-stats', 'activitypub-cleaner' ] - let pendingRequests: boolean - - function tasksBuilder () { - const tasks: Promise[] = [] - - // Check if each server has pending request - for (const server of servers) { - for (const state of states) { - const p = server.jobs.list({ - state, - start: 0, - count: 10, - sort: '-createdAt' - }).then(body => body.data) - .then(jobs => jobs.filter(j => !repeatableJobs.includes(j.type))) - .then(jobs => { - if (jobs.length !== 0) { - pendingRequests = true - } - }) - - tasks.push(p) - } - - const p = server.debug.getDebug() - .then(obj => { - if (obj.activityPubMessagesWaiting !== 0) { - pendingRequests = true - } - }) - - tasks.push(p) - } - - return tasks - } - - do { - pendingRequests = false - await Promise.all(tasksBuilder()) - - // Retry, in case of new jobs were created - if (pendingRequests === false) { - await wait(pendingJobWait) - await Promise.all(tasksBuilder()) - } - - if (pendingRequests) { - await wait(pendingJobWait) - } - } while (pendingRequests) -} - -async function expectNoFailedTranscodingJob (server: PeerTubeServer) { - const { data } = await server.jobs.listFailed({ jobType: 'video-transcoding' }) - expect(data).to.have.lengthOf(0) -} - -// --------------------------------------------------------------------------- - -export { - waitJobs, - expectNoFailedTranscodingJob -} diff --git a/shared/extra-utils/server/object-storage-command.ts b/shared/extra-utils/server/object-storage-command.ts deleted file mode 100644 index b4de8f4cb..000000000 --- a/shared/extra-utils/server/object-storage-command.ts +++ /dev/null @@ -1,77 +0,0 @@ - -import { HttpStatusCode } from '@shared/models' -import { makePostBodyRequest } from '../requests' -import { AbstractCommand } from '../shared' - -export class ObjectStorageCommand extends AbstractCommand { - static readonly DEFAULT_PLAYLIST_BUCKET = 'streaming-playlists' - static readonly DEFAULT_WEBTORRENT_BUCKET = 'videos' - - static getDefaultConfig () { - return { - object_storage: { - enabled: true, - endpoint: 'http://' + this.getEndpointHost(), - region: this.getRegion(), - - credentials: this.getCredentialsConfig(), - - streaming_playlists: { - bucket_name: this.DEFAULT_PLAYLIST_BUCKET - }, - - videos: { - bucket_name: this.DEFAULT_WEBTORRENT_BUCKET - } - } - } - } - - static getCredentialsConfig () { - return { - access_key_id: 'AKIAIOSFODNN7EXAMPLE', - secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' - } - } - - static getEndpointHost () { - return 'localhost:9444' - } - - static getRegion () { - return 'us-east-1' - } - - static getWebTorrentBaseUrl () { - return `http://${this.DEFAULT_WEBTORRENT_BUCKET}.${this.getEndpointHost()}/` - } - - static getPlaylistBaseUrl () { - return `http://${this.DEFAULT_PLAYLIST_BUCKET}.${this.getEndpointHost()}/` - } - - static async prepareDefaultBuckets () { - await this.createBucket(this.DEFAULT_PLAYLIST_BUCKET) - await this.createBucket(this.DEFAULT_WEBTORRENT_BUCKET) - } - - static async createBucket (name: string) { - await makePostBodyRequest({ - url: this.getEndpointHost(), - path: '/ui/' + name + '?delete', - expectedStatus: HttpStatusCode.TEMPORARY_REDIRECT_307 - }) - - await makePostBodyRequest({ - url: this.getEndpointHost(), - path: '/ui/' + name + '?create', - expectedStatus: HttpStatusCode.TEMPORARY_REDIRECT_307 - }) - - await makePostBodyRequest({ - url: this.getEndpointHost(), - path: '/ui/' + name + '?make-public', - expectedStatus: HttpStatusCode.TEMPORARY_REDIRECT_307 - }) - } -} diff --git a/shared/extra-utils/server/plugins-command.ts b/shared/extra-utils/server/plugins-command.ts deleted file mode 100644 index 1c44711da..000000000 --- a/shared/extra-utils/server/plugins-command.ts +++ /dev/null @@ -1,257 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ - -import { readJSON, writeJSON } from 'fs-extra' -import { join } from 'path' -import { root } from '@shared/core-utils' -import { - HttpStatusCode, - PeerTubePlugin, - PeerTubePluginIndex, - PeertubePluginIndexList, - PluginPackageJson, - PluginTranslation, - PluginType, - PublicServerSetting, - RegisteredServerSettings, - ResultList -} from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class PluginsCommand extends AbstractCommand { - - static getPluginTestPath (suffix = '') { - return join(root(), 'server', 'tests', 'fixtures', 'peertube-plugin-test' + suffix) - } - - list (options: OverrideCommandOptions & { - start?: number - count?: number - sort?: string - pluginType?: PluginType - uninstalled?: boolean - }) { - const { start, count, sort, pluginType, uninstalled } = options - const path = '/api/v1/plugins' - - return this.getRequestBody>({ - ...options, - - path, - query: { - start, - count, - sort, - pluginType, - uninstalled - }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listAvailable (options: OverrideCommandOptions & { - start?: number - count?: number - sort?: string - pluginType?: PluginType - currentPeerTubeEngine?: string - search?: string - expectedStatus?: HttpStatusCode - }) { - const { start, count, sort, pluginType, search, currentPeerTubeEngine } = options - const path = '/api/v1/plugins/available' - - const query: PeertubePluginIndexList = { - start, - count, - sort, - pluginType, - currentPeerTubeEngine, - search - } - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - get (options: OverrideCommandOptions & { - npmName: string - }) { - const path = '/api/v1/plugins/' + options.npmName - - return this.getRequestBody({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - updateSettings (options: OverrideCommandOptions & { - npmName: string - settings: any - }) { - const { npmName, settings } = options - const path = '/api/v1/plugins/' + npmName + '/settings' - - return this.putBodyRequest({ - ...options, - - path, - fields: { settings }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - getRegisteredSettings (options: OverrideCommandOptions & { - npmName: string - }) { - const path = '/api/v1/plugins/' + options.npmName + '/registered-settings' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getPublicSettings (options: OverrideCommandOptions & { - npmName: string - }) { - const { npmName } = options - const path = '/api/v1/plugins/' + npmName + '/public-settings' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getTranslations (options: OverrideCommandOptions & { - locale: string - }) { - const { locale } = options - const path = '/plugins/translations/' + locale + '.json' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - install (options: OverrideCommandOptions & { - path?: string - npmName?: string - pluginVersion?: string - }) { - const { npmName, path, pluginVersion } = options - const apiPath = '/api/v1/plugins/install' - - return this.postBodyRequest({ - ...options, - - path: apiPath, - fields: { npmName, path, pluginVersion }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - update (options: OverrideCommandOptions & { - path?: string - npmName?: string - }) { - const { npmName, path } = options - const apiPath = '/api/v1/plugins/update' - - return this.postBodyRequest({ - ...options, - - path: apiPath, - fields: { npmName, path }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - uninstall (options: OverrideCommandOptions & { - npmName: string - }) { - const { npmName } = options - const apiPath = '/api/v1/plugins/uninstall' - - return this.postBodyRequest({ - ...options, - - path: apiPath, - fields: { npmName }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - getCSS (options: OverrideCommandOptions = {}) { - const path = '/plugins/global.css' - - return this.getRequestText({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getExternalAuth (options: OverrideCommandOptions & { - npmName: string - npmVersion: string - authName: string - query?: any - }) { - const { npmName, npmVersion, authName, query } = options - - const path = '/plugins/' + npmName + '/' + npmVersion + '/auth/' + authName - - return this.getRequest({ - ...options, - - path, - query, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200, - redirects: 0 - }) - } - - updatePackageJSON (npmName: string, json: any) { - const path = this.getPackageJSONPath(npmName) - - return writeJSON(path, json) - } - - getPackageJSON (npmName: string): Promise { - const path = this.getPackageJSONPath(npmName) - - return readJSON(path) - } - - private getPackageJSONPath (npmName: string) { - return this.server.servers.buildDirectory(join('plugins', 'node_modules', npmName, 'package.json')) - } -} diff --git a/shared/extra-utils/server/plugins.ts b/shared/extra-utils/server/plugins.ts deleted file mode 100644 index 0f5fabd5a..000000000 --- a/shared/extra-utils/server/plugins.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ - -import { expect } from 'chai' -import { PeerTubeServer } from '../server/server' - -async function testHelloWorldRegisteredSettings (server: PeerTubeServer) { - const body = await server.plugins.getRegisteredSettings({ npmName: 'peertube-plugin-hello-world' }) - - const registeredSettings = body.registeredSettings - expect(registeredSettings).to.have.length.at.least(1) - - const adminNameSettings = registeredSettings.find(s => s.name === 'admin-name') - expect(adminNameSettings).to.not.be.undefined -} - -export { - testHelloWorldRegisteredSettings -} diff --git a/shared/extra-utils/server/redundancy-command.ts b/shared/extra-utils/server/redundancy-command.ts deleted file mode 100644 index e7a8b3c29..000000000 --- a/shared/extra-utils/server/redundancy-command.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { HttpStatusCode, ResultList, VideoRedundanciesTarget, VideoRedundancy } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class RedundancyCommand extends AbstractCommand { - - updateRedundancy (options: OverrideCommandOptions & { - host: string - redundancyAllowed: boolean - }) { - const { host, redundancyAllowed } = options - const path = '/api/v1/server/redundancy/' + host - - return this.putBodyRequest({ - ...options, - - path, - fields: { redundancyAllowed }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - listVideos (options: OverrideCommandOptions & { - target: VideoRedundanciesTarget - start?: number - count?: number - sort?: string - }) { - const path = '/api/v1/server/redundancy/videos' - - const { target, start, count, sort } = options - - return this.getRequestBody>({ - ...options, - - path, - - query: { - start: start ?? 0, - count: count ?? 5, - sort: sort ?? 'name', - target - }, - - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - addVideo (options: OverrideCommandOptions & { - videoId: number - }) { - const path = '/api/v1/server/redundancy/videos' - const { videoId } = options - - return this.postBodyRequest({ - ...options, - - path, - fields: { videoId }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - removeVideo (options: OverrideCommandOptions & { - redundancyId: number - }) { - const { redundancyId } = options - const path = '/api/v1/server/redundancy/videos/' + redundancyId - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } -} diff --git a/shared/extra-utils/server/server.ts b/shared/extra-utils/server/server.ts deleted file mode 100644 index 339b9cabb..000000000 --- a/shared/extra-utils/server/server.ts +++ /dev/null @@ -1,392 +0,0 @@ -import { ChildProcess, fork } from 'child_process' -import { copy } from 'fs-extra' -import { join } from 'path' -import { root, randomInt } from '@shared/core-utils' -import { Video, VideoChannel, VideoCreateResult, VideoDetails } from '../../models/videos' -import { BulkCommand } from '../bulk' -import { CLICommand } from '../cli' -import { CustomPagesCommand } from '../custom-pages' -import { FeedCommand } from '../feeds' -import { LogsCommand } from '../logs' -import { parallelTests, SQLCommand } from '../miscs' -import { AbusesCommand } from '../moderation' -import { OverviewsCommand } from '../overviews' -import { SearchCommand } from '../search' -import { SocketIOCommand } from '../socket' -import { AccountsCommand, BlocklistCommand, LoginCommand, NotificationsCommand, SubscriptionsCommand, UsersCommand } from '../users' -import { - BlacklistCommand, - CaptionsCommand, - ChangeOwnershipCommand, - ChannelsCommand, - HistoryCommand, - ImportsCommand, - LiveCommand, - PlaylistsCommand, - ServicesCommand, - StreamingPlaylistsCommand, - VideosCommand -} from '../videos' -import { CommentsCommand } from '../videos/comments-command' -import { ConfigCommand } from './config-command' -import { ContactFormCommand } from './contact-form-command' -import { DebugCommand } from './debug-command' -import { FollowsCommand } from './follows-command' -import { JobsCommand } from './jobs-command' -import { PluginsCommand } from './plugins-command' -import { RedundancyCommand } from './redundancy-command' -import { ServersCommand } from './servers-command' -import { StatsCommand } from './stats-command' -import { ObjectStorageCommand } from './object-storage-command' - -export type RunServerOptions = { - hideLogs?: boolean - nodeArgs?: string[] - peertubeArgs?: string[] - env?: { [ id: string ]: string } -} - -export class PeerTubeServer { - app?: ChildProcess - - url: string - host?: string - hostname?: string - port?: number - - rtmpPort?: number - rtmpsPort?: number - - parallel?: boolean - internalServerNumber: number - - serverNumber?: number - customConfigFile?: string - - store?: { - client?: { - id?: string - secret?: string - } - - user?: { - username: string - password: string - email?: string - } - - channel?: VideoChannel - - video?: Video - videoCreated?: VideoCreateResult - videoDetails?: VideoDetails - - videos?: { id: number, uuid: string }[] - } - - accessToken?: string - refreshToken?: string - - bulk?: BulkCommand - cli?: CLICommand - customPage?: CustomPagesCommand - feed?: FeedCommand - logs?: LogsCommand - abuses?: AbusesCommand - overviews?: OverviewsCommand - search?: SearchCommand - contactForm?: ContactFormCommand - debug?: DebugCommand - follows?: FollowsCommand - jobs?: JobsCommand - plugins?: PluginsCommand - redundancy?: RedundancyCommand - stats?: StatsCommand - config?: ConfigCommand - socketIO?: SocketIOCommand - accounts?: AccountsCommand - blocklist?: BlocklistCommand - subscriptions?: SubscriptionsCommand - live?: LiveCommand - services?: ServicesCommand - blacklist?: BlacklistCommand - captions?: CaptionsCommand - changeOwnership?: ChangeOwnershipCommand - playlists?: PlaylistsCommand - history?: HistoryCommand - imports?: ImportsCommand - streamingPlaylists?: StreamingPlaylistsCommand - channels?: ChannelsCommand - comments?: CommentsCommand - sql?: SQLCommand - notifications?: NotificationsCommand - servers?: ServersCommand - login?: LoginCommand - users?: UsersCommand - objectStorage?: ObjectStorageCommand - videos?: VideosCommand - - constructor (options: { serverNumber: number } | { url: string }) { - if ((options as any).url) { - this.setUrl((options as any).url) - } else { - this.setServerNumber((options as any).serverNumber) - } - - this.store = { - client: { - id: null, - secret: null - }, - user: { - username: null, - password: null - } - } - - this.assignCommands() - } - - setServerNumber (serverNumber: number) { - this.serverNumber = serverNumber - - this.parallel = parallelTests() - - this.internalServerNumber = this.parallel ? this.randomServer() : this.serverNumber - this.rtmpPort = this.parallel ? this.randomRTMP() : 1936 - this.rtmpsPort = this.parallel ? this.randomRTMP() : 1937 - this.port = 9000 + this.internalServerNumber - - this.url = `http://localhost:${this.port}` - this.host = `localhost:${this.port}` - this.hostname = 'localhost' - } - - setUrl (url: string) { - const parsed = new URL(url) - - this.url = url - this.host = parsed.host - this.hostname = parsed.hostname - this.port = parseInt(parsed.port) - } - - async flushAndRun (configOverride?: Object, options: RunServerOptions = {}) { - await ServersCommand.flushTests(this.internalServerNumber) - - return this.run(configOverride, options) - } - - async run (configOverrideArg?: any, options: RunServerOptions = {}) { - // These actions are async so we need to be sure that they have both been done - const serverRunString = { - 'HTTP server listening': false - } - const key = 'Database peertube_test' + this.internalServerNumber + ' is ready' - serverRunString[key] = false - - const regexps = { - client_id: 'Client id: (.+)', - client_secret: 'Client secret: (.+)', - user_username: 'Username: (.+)', - user_password: 'User password: (.+)' - } - - await this.assignCustomConfigFile() - - const configOverride = this.buildConfigOverride() - - if (configOverrideArg !== undefined) { - Object.assign(configOverride, configOverrideArg) - } - - // Share the environment - const env = Object.create(process.env) - env['NODE_ENV'] = 'test' - env['NODE_APP_INSTANCE'] = this.internalServerNumber.toString() - env['NODE_CONFIG'] = JSON.stringify(configOverride) - - if (options.env) { - Object.assign(env, options.env) - } - - const forkOptions = { - silent: true, - env, - detached: true, - execArgv: options.nodeArgs || [] - } - - return new Promise((res, rej) => { - const self = this - let aggregatedLogs = '' - - this.app = fork(join(root(), 'dist', 'server.js'), options.peertubeArgs || [], forkOptions) - - const onPeerTubeExit = () => rej(new Error('Process exited:\n' + aggregatedLogs)) - const onParentExit = () => { - if (!this.app || !this.app.pid) return - - try { - process.kill(self.app.pid) - } catch { /* empty */ } - } - - this.app.on('exit', onPeerTubeExit) - process.on('exit', onParentExit) - - this.app.stdout.on('data', function onStdout (data) { - let dontContinue = false - - const log: string = data.toString() - aggregatedLogs += log - - // Capture things if we want to - for (const key of Object.keys(regexps)) { - const regexp = regexps[key] - const matches = log.match(regexp) - if (matches !== null) { - if (key === 'client_id') self.store.client.id = matches[1] - else if (key === 'client_secret') self.store.client.secret = matches[1] - else if (key === 'user_username') self.store.user.username = matches[1] - else if (key === 'user_password') self.store.user.password = matches[1] - } - } - - // Check if all required sentences are here - for (const key of Object.keys(serverRunString)) { - if (log.includes(key)) serverRunString[key] = true - if (serverRunString[key] === false) dontContinue = true - } - - // If no, there is maybe one thing not already initialized (client/user credentials generation...) - if (dontContinue === true) return - - if (options.hideLogs === false) { - console.log(log) - } else { - process.removeListener('exit', onParentExit) - self.app.stdout.removeListener('data', onStdout) - self.app.removeListener('exit', onPeerTubeExit) - } - - res() - }) - }) - } - - async kill () { - if (!this.app) return - - await this.sql.cleanup() - - process.kill(-this.app.pid) - - this.app = null - } - - private randomServer () { - const low = 10 - const high = 10000 - - return randomInt(low, high) - } - - private randomRTMP () { - const low = 1900 - const high = 2100 - - return randomInt(low, high) - } - - private async assignCustomConfigFile () { - if (this.internalServerNumber === this.serverNumber) return - - const basePath = join(root(), 'config') - - const tmpConfigFile = join(basePath, `test-${this.internalServerNumber}.yaml`) - await copy(join(basePath, `test-${this.serverNumber}.yaml`), tmpConfigFile) - - this.customConfigFile = tmpConfigFile - } - - private buildConfigOverride () { - if (!this.parallel) return {} - - return { - listen: { - port: this.port - }, - webserver: { - port: this.port - }, - database: { - suffix: '_test' + this.internalServerNumber - }, - storage: { - tmp: `test${this.internalServerNumber}/tmp/`, - bin: `test${this.internalServerNumber}/bin/`, - avatars: `test${this.internalServerNumber}/avatars/`, - videos: `test${this.internalServerNumber}/videos/`, - streaming_playlists: `test${this.internalServerNumber}/streaming-playlists/`, - redundancy: `test${this.internalServerNumber}/redundancy/`, - logs: `test${this.internalServerNumber}/logs/`, - previews: `test${this.internalServerNumber}/previews/`, - thumbnails: `test${this.internalServerNumber}/thumbnails/`, - torrents: `test${this.internalServerNumber}/torrents/`, - captions: `test${this.internalServerNumber}/captions/`, - cache: `test${this.internalServerNumber}/cache/`, - plugins: `test${this.internalServerNumber}/plugins/` - }, - admin: { - email: `admin${this.internalServerNumber}@example.com` - }, - live: { - rtmp: { - port: this.rtmpPort - } - } - } - } - - private assignCommands () { - this.bulk = new BulkCommand(this) - this.cli = new CLICommand(this) - this.customPage = new CustomPagesCommand(this) - this.feed = new FeedCommand(this) - this.logs = new LogsCommand(this) - this.abuses = new AbusesCommand(this) - this.overviews = new OverviewsCommand(this) - this.search = new SearchCommand(this) - this.contactForm = new ContactFormCommand(this) - this.debug = new DebugCommand(this) - this.follows = new FollowsCommand(this) - this.jobs = new JobsCommand(this) - this.plugins = new PluginsCommand(this) - this.redundancy = new RedundancyCommand(this) - this.stats = new StatsCommand(this) - this.config = new ConfigCommand(this) - this.socketIO = new SocketIOCommand(this) - this.accounts = new AccountsCommand(this) - this.blocklist = new BlocklistCommand(this) - this.subscriptions = new SubscriptionsCommand(this) - this.live = new LiveCommand(this) - this.services = new ServicesCommand(this) - this.blacklist = new BlacklistCommand(this) - this.captions = new CaptionsCommand(this) - this.changeOwnership = new ChangeOwnershipCommand(this) - this.playlists = new PlaylistsCommand(this) - this.history = new HistoryCommand(this) - this.imports = new ImportsCommand(this) - this.streamingPlaylists = new StreamingPlaylistsCommand(this) - this.channels = new ChannelsCommand(this) - this.comments = new CommentsCommand(this) - this.sql = new SQLCommand(this) - this.notifications = new NotificationsCommand(this) - this.servers = new ServersCommand(this) - this.login = new LoginCommand(this) - this.users = new UsersCommand(this) - this.videos = new VideosCommand(this) - this.objectStorage = new ObjectStorageCommand(this) - } -} diff --git a/shared/extra-utils/server/servers-command.ts b/shared/extra-utils/server/servers-command.ts deleted file mode 100644 index 47420c95f..000000000 --- a/shared/extra-utils/server/servers-command.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { exec } from 'child_process' -import { copy, ensureDir, readFile, remove } from 'fs-extra' -import { basename, join } from 'path' -import { root } from '@shared/core-utils' -import { HttpStatusCode } from '@shared/models' -import { getFileSize, isGithubCI, wait } from '../miscs' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class ServersCommand extends AbstractCommand { - - static flushTests (internalServerNumber: number) { - return new Promise((res, rej) => { - const suffix = ` -- ${internalServerNumber}` - - return exec('npm run clean:server:test' + suffix, (err, _stdout, stderr) => { - if (err || stderr) return rej(err || new Error(stderr)) - - return res() - }) - }) - } - - ping (options: OverrideCommandOptions = {}) { - return this.getRequestBody({ - ...options, - - path: '/api/v1/ping', - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - async cleanupTests () { - const p: Promise[] = [] - - if (isGithubCI()) { - await ensureDir('artifacts') - - const origin = this.buildDirectory('logs/peertube.log') - const destname = `peertube-${this.server.internalServerNumber}.log` - console.log('Saving logs %s.', destname) - - await copy(origin, join('artifacts', destname)) - } - - if (this.server.parallel) { - p.push(ServersCommand.flushTests(this.server.internalServerNumber)) - } - - if (this.server.customConfigFile) { - p.push(remove(this.server.customConfigFile)) - } - - return p - } - - async waitUntilLog (str: string, count = 1, strictCount = true) { - const logfile = this.buildDirectory('logs/peertube.log') - - while (true) { - const buf = await readFile(logfile) - - const matches = buf.toString().match(new RegExp(str, 'g')) - if (matches && matches.length === count) return - if (matches && strictCount === false && matches.length >= count) return - - await wait(1000) - } - } - - buildDirectory (directory: string) { - return join(root(), 'test' + this.server.internalServerNumber, directory) - } - - buildWebTorrentFilePath (fileUrl: string) { - return this.buildDirectory(join('videos', basename(fileUrl))) - } - - buildFragmentedFilePath (videoUUID: string, fileUrl: string) { - return this.buildDirectory(join('streaming-playlists', 'hls', videoUUID, basename(fileUrl))) - } - - getLogContent () { - return readFile(this.buildDirectory('logs/peertube.log')) - } - - async getServerFileSize (subPath: string) { - const path = this.server.servers.buildDirectory(subPath) - - return getFileSize(path) - } -} diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts deleted file mode 100644 index 21ab9405b..000000000 --- a/shared/extra-utils/server/servers.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { ensureDir } from 'fs-extra' -import { isGithubCI } from '../miscs' -import { PeerTubeServer, RunServerOptions } from './server' - -async function createSingleServer (serverNumber: number, configOverride?: Object, options: RunServerOptions = {}) { - const server = new PeerTubeServer({ serverNumber }) - - await server.flushAndRun(configOverride, options) - - return server -} - -function createMultipleServers (totalServers: number, configOverride?: Object, options: RunServerOptions = {}) { - const serverPromises: Promise[] = [] - - for (let i = 1; i <= totalServers; i++) { - serverPromises.push(createSingleServer(i, configOverride, options)) - } - - return Promise.all(serverPromises) -} - -async function killallServers (servers: PeerTubeServer[]) { - return Promise.all(servers.map(s => s.kill())) -} - -async function cleanupTests (servers: PeerTubeServer[]) { - await killallServers(servers) - - if (isGithubCI()) { - await ensureDir('artifacts') - } - - let p: Promise[] = [] - for (const server of servers) { - p = p.concat(server.servers.cleanupTests()) - } - - return Promise.all(p) -} - -// --------------------------------------------------------------------------- - -export { - createSingleServer, - createMultipleServers, - cleanupTests, - killallServers -} diff --git a/shared/extra-utils/server/stats-command.ts b/shared/extra-utils/server/stats-command.ts deleted file mode 100644 index 64a452306..000000000 --- a/shared/extra-utils/server/stats-command.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { HttpStatusCode, ServerStats } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class StatsCommand extends AbstractCommand { - - get (options: OverrideCommandOptions & { - useCache?: boolean // default false - } = {}) { - const { useCache = false } = options - const path = '/api/v1/server/stats' - - const query = { - t: useCache ? undefined : new Date().getTime() - } - - return this.getRequestBody({ - ...options, - - path, - query, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/server/tracker.ts b/shared/extra-utils/server/tracker.ts deleted file mode 100644 index ed43a5924..000000000 --- a/shared/extra-utils/server/tracker.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { expect } from 'chai' -import { sha1 } from '@shared/core-utils/crypto' -import { makeGetRequest } from '../requests' - -async function hlsInfohashExist (serverUrl: string, masterPlaylistUrl: string, fileNumber: number) { - const path = '/tracker/announce' - - const infohash = sha1(`2${masterPlaylistUrl}+V${fileNumber}`) - - // From bittorrent-tracker - const infohashBinary = escape(Buffer.from(infohash, 'hex').toString('binary')).replace(/[@*/+]/g, function (char) { - return '%' + char.charCodeAt(0).toString(16).toUpperCase() - }) - - const res = await makeGetRequest({ - url: serverUrl, - path, - rawQuery: `peer_id=-WW0105-NkvYO/egUAr4&info_hash=${infohashBinary}&port=42100`, - expectedStatus: 200 - }) - - expect(res.text).to.not.contain('failure') -} - -export { - hlsInfohashExist -} diff --git a/shared/extra-utils/shared/abstract-command.ts b/shared/extra-utils/shared/abstract-command.ts deleted file mode 100644 index a57c857fc..000000000 --- a/shared/extra-utils/shared/abstract-command.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { isAbsolute, join } from 'path' -import { root } from '../miscs/tests' -import { - makeDeleteRequest, - makeGetRequest, - makePostBodyRequest, - makePutBodyRequest, - makeUploadRequest, - unwrapBody, - unwrapText -} from '../requests/requests' -import { PeerTubeServer } from '../server/server' - -export interface OverrideCommandOptions { - token?: string - expectedStatus?: number -} - -interface InternalCommonCommandOptions extends OverrideCommandOptions { - // Default to server.url - url?: string - - path: string - // If we automatically send the server token if the token is not provided - implicitToken: boolean - defaultExpectedStatus: number - - // Common optional request parameters - contentType?: string - accept?: string - redirects?: number - range?: string - host?: string - headers?: { [ name: string ]: string } - requestType?: string - xForwardedFor?: string -} - -interface InternalGetCommandOptions extends InternalCommonCommandOptions { - query?: { [ id: string ]: any } -} - -interface InternalDeleteCommandOptions extends InternalCommonCommandOptions { - query?: { [ id: string ]: any } - rawQuery?: string -} - -abstract class AbstractCommand { - - constructor ( - protected server: PeerTubeServer - ) { - - } - - protected getRequestBody (options: InternalGetCommandOptions) { - return unwrapBody(this.getRequest(options)) - } - - protected getRequestText (options: InternalGetCommandOptions) { - return unwrapText(this.getRequest(options)) - } - - protected getRawRequest (options: Omit) { - const { url, range } = options - const { host, protocol, pathname } = new URL(url) - - return this.getRequest({ - ...options, - - token: this.buildCommonRequestToken(options), - defaultExpectedStatus: this.buildExpectedStatus(options), - - url: `${protocol}//${host}`, - path: pathname, - range - }) - } - - protected getRequest (options: InternalGetCommandOptions) { - const { query } = options - - return makeGetRequest({ - ...this.buildCommonRequestOptions(options), - - query - }) - } - - protected deleteRequest (options: InternalDeleteCommandOptions) { - const { query, rawQuery } = options - - return makeDeleteRequest({ - ...this.buildCommonRequestOptions(options), - - query, - rawQuery - }) - } - - protected putBodyRequest (options: InternalCommonCommandOptions & { - fields?: { [ fieldName: string ]: any } - }) { - const { fields } = options - - return makePutBodyRequest({ - ...this.buildCommonRequestOptions(options), - - fields - }) - } - - protected postBodyRequest (options: InternalCommonCommandOptions & { - fields?: { [ fieldName: string ]: any } - }) { - const { fields } = options - - return makePostBodyRequest({ - ...this.buildCommonRequestOptions(options), - - fields - }) - } - - protected postUploadRequest (options: InternalCommonCommandOptions & { - fields?: { [ fieldName: string ]: any } - attaches?: { [ fieldName: string ]: any } - }) { - const { fields, attaches } = options - - return makeUploadRequest({ - ...this.buildCommonRequestOptions(options), - - method: 'POST', - fields, - attaches - }) - } - - protected putUploadRequest (options: InternalCommonCommandOptions & { - fields?: { [ fieldName: string ]: any } - attaches?: { [ fieldName: string ]: any } - }) { - const { fields, attaches } = options - - return makeUploadRequest({ - ...this.buildCommonRequestOptions(options), - - method: 'PUT', - fields, - attaches - }) - } - - protected updateImageRequest (options: InternalCommonCommandOptions & { - fixture: string - fieldname: string - }) { - const filePath = isAbsolute(options.fixture) - ? options.fixture - : join(root(), 'server', 'tests', 'fixtures', options.fixture) - - return this.postUploadRequest({ - ...options, - - fields: {}, - attaches: { [options.fieldname]: filePath } - }) - } - - protected buildCommonRequestOptions (options: InternalCommonCommandOptions) { - const { url, path, redirects, contentType, accept, range, host, headers, requestType, xForwardedFor } = options - - return { - url: url ?? this.server.url, - path, - - token: this.buildCommonRequestToken(options), - expectedStatus: this.buildExpectedStatus(options), - - redirects, - contentType, - range, - host, - accept, - headers, - type: requestType, - xForwardedFor - } - } - - protected buildCommonRequestToken (options: Pick) { - const { token } = options - - const fallbackToken = options.implicitToken - ? this.server.accessToken - : undefined - - return token !== undefined ? token : fallbackToken - } - - protected buildExpectedStatus (options: Pick) { - const { expectedStatus, defaultExpectedStatus } = options - - return expectedStatus !== undefined ? expectedStatus : defaultExpectedStatus - } -} - -export { - AbstractCommand -} diff --git a/shared/extra-utils/shared/index.ts b/shared/extra-utils/shared/index.ts deleted file mode 100644 index e807ab4f7..000000000 --- a/shared/extra-utils/shared/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './abstract-command' diff --git a/shared/extra-utils/socket/index.ts b/shared/extra-utils/socket/index.ts deleted file mode 100644 index 594329b2f..000000000 --- a/shared/extra-utils/socket/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './socket-io-command' diff --git a/shared/extra-utils/socket/socket-io-command.ts b/shared/extra-utils/socket/socket-io-command.ts deleted file mode 100644 index c277ead28..000000000 --- a/shared/extra-utils/socket/socket-io-command.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { io } from 'socket.io-client' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class SocketIOCommand extends AbstractCommand { - - getUserNotificationSocket (options: OverrideCommandOptions = {}) { - return io(this.server.url + '/user-notifications', { - query: { accessToken: options.token ?? this.server.accessToken } - }) - } - - getLiveNotificationSocket () { - return io(this.server.url + '/live-videos') - } -} diff --git a/shared/extra-utils/users/accounts-command.ts b/shared/extra-utils/users/accounts-command.ts deleted file mode 100644 index 98d9d5927..000000000 --- a/shared/extra-utils/users/accounts-command.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { HttpStatusCode, ResultList } from '@shared/models' -import { Account, ActorFollow } from '../../models/actors' -import { AccountVideoRate, VideoRateType } from '../../models/videos' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class AccountsCommand extends AbstractCommand { - - list (options: OverrideCommandOptions & { - sort?: string // default -createdAt - } = {}) { - const { sort = '-createdAt' } = options - const path = '/api/v1/accounts' - - return this.getRequestBody>({ - ...options, - - path, - query: { sort }, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - get (options: OverrideCommandOptions & { - accountName: string - }) { - const path = '/api/v1/accounts/' + options.accountName - - return this.getRequestBody({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listRatings (options: OverrideCommandOptions & { - accountName: string - rating?: VideoRateType - }) { - const { rating, accountName } = options - const path = '/api/v1/accounts/' + accountName + '/ratings' - - const query = { rating } - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listFollowers (options: OverrideCommandOptions & { - accountName: string - start?: number - count?: number - sort?: string - search?: string - }) { - const { accountName, start, count, sort, search } = options - const path = '/api/v1/accounts/' + accountName + '/followers' - - const query = { start, count, sort, search } - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/users/actors.ts b/shared/extra-utils/users/actors.ts deleted file mode 100644 index 12c3e078a..000000000 --- a/shared/extra-utils/users/actors.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ - -import { expect } from 'chai' -import { pathExists, readdir } from 'fs-extra' -import { join } from 'path' -import { root } from '@shared/core-utils' -import { Account, VideoChannel } from '@shared/models' -import { PeerTubeServer } from '../server' - -async function expectChannelsFollows (options: { - server: PeerTubeServer - handle: string - followers: number - following: number -}) { - const { server } = options - const { data } = await server.channels.list() - - return expectActorFollow({ ...options, data }) -} - -async function expectAccountFollows (options: { - server: PeerTubeServer - handle: string - followers: number - following: number -}) { - const { server } = options - const { data } = await server.accounts.list() - - return expectActorFollow({ ...options, data }) -} - -async function checkActorFilesWereRemoved (filename: string, serverNumber: number) { - const testDirectory = 'test' + serverNumber - - for (const directory of [ 'avatars' ]) { - const directoryPath = join(root(), testDirectory, directory) - - const directoryExists = await pathExists(directoryPath) - expect(directoryExists).to.be.true - - const files = await readdir(directoryPath) - for (const file of files) { - expect(file).to.not.contain(filename) - } - } -} - -export { - expectAccountFollows, - expectChannelsFollows, - checkActorFilesWereRemoved -} - -// --------------------------------------------------------------------------- - -function expectActorFollow (options: { - server: PeerTubeServer - data: (Account | VideoChannel)[] - handle: string - followers: number - following: number -}) { - const { server, data, handle, followers, following } = options - - const actor = data.find(a => a.name + '@' + a.host === handle) - const message = `${handle} on ${server.url}` - - expect(actor, message).to.exist - expect(actor.followersCount).to.equal(followers, message) - expect(actor.followingCount).to.equal(following, message) -} diff --git a/shared/extra-utils/users/blocklist-command.ts b/shared/extra-utils/users/blocklist-command.ts deleted file mode 100644 index 2e7ed074d..000000000 --- a/shared/extra-utils/users/blocklist-command.ts +++ /dev/null @@ -1,162 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ - -import { AccountBlock, BlockStatus, HttpStatusCode, ResultList, ServerBlock } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -type ListBlocklistOptions = OverrideCommandOptions & { - start: number - count: number - sort: string // default -createdAt -} - -export class BlocklistCommand extends AbstractCommand { - - listMyAccountBlocklist (options: ListBlocklistOptions) { - const path = '/api/v1/users/me/blocklist/accounts' - - return this.listBlocklist(options, path) - } - - listMyServerBlocklist (options: ListBlocklistOptions) { - const path = '/api/v1/users/me/blocklist/servers' - - return this.listBlocklist(options, path) - } - - listServerAccountBlocklist (options: ListBlocklistOptions) { - const path = '/api/v1/server/blocklist/accounts' - - return this.listBlocklist(options, path) - } - - listServerServerBlocklist (options: ListBlocklistOptions) { - const path = '/api/v1/server/blocklist/servers' - - return this.listBlocklist(options, path) - } - - // --------------------------------------------------------------------------- - - getStatus (options: OverrideCommandOptions & { - accounts?: string[] - hosts?: string[] - }) { - const { accounts, hosts } = options - - const path = '/api/v1/blocklist/status' - - return this.getRequestBody({ - ...options, - - path, - query: { - accounts, - hosts - }, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - // --------------------------------------------------------------------------- - - addToMyBlocklist (options: OverrideCommandOptions & { - account?: string - server?: string - }) { - const { account, server } = options - - const path = account - ? '/api/v1/users/me/blocklist/accounts' - : '/api/v1/users/me/blocklist/servers' - - return this.postBodyRequest({ - ...options, - - path, - fields: { - accountName: account, - host: server - }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - addToServerBlocklist (options: OverrideCommandOptions & { - account?: string - server?: string - }) { - const { account, server } = options - - const path = account - ? '/api/v1/server/blocklist/accounts' - : '/api/v1/server/blocklist/servers' - - return this.postBodyRequest({ - ...options, - - path, - fields: { - accountName: account, - host: server - }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - // --------------------------------------------------------------------------- - - removeFromMyBlocklist (options: OverrideCommandOptions & { - account?: string - server?: string - }) { - const { account, server } = options - - const path = account - ? '/api/v1/users/me/blocklist/accounts/' + account - : '/api/v1/users/me/blocklist/servers/' + server - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - removeFromServerBlocklist (options: OverrideCommandOptions & { - account?: string - server?: string - }) { - const { account, server } = options - - const path = account - ? '/api/v1/server/blocklist/accounts/' + account - : '/api/v1/server/blocklist/servers/' + server - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - private listBlocklist (options: ListBlocklistOptions, path: string) { - const { start, count, sort = '-createdAt' } = options - - return this.getRequestBody>({ - ...options, - - path, - query: { start, count, sort }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - -} diff --git a/shared/extra-utils/users/index.ts b/shared/extra-utils/users/index.ts deleted file mode 100644 index 460a06f70..000000000 --- a/shared/extra-utils/users/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export * from './accounts-command' -export * from './actors' -export * from './blocklist-command' -export * from './login' -export * from './login-command' -export * from './notifications' -export * from './notifications-command' -export * from './subscriptions-command' -export * from './users-command' diff --git a/shared/extra-utils/users/login-command.ts b/shared/extra-utils/users/login-command.ts deleted file mode 100644 index 143f72a59..000000000 --- a/shared/extra-utils/users/login-command.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { HttpStatusCode, PeerTubeProblemDocument } from '@shared/models' -import { unwrapBody } from '../requests' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class LoginCommand extends AbstractCommand { - - login (options: OverrideCommandOptions & { - client?: { id?: string, secret?: string } - user?: { username: string, password?: string } - } = {}) { - const { client = this.server.store.client, user = this.server.store.user } = options - const path = '/api/v1/users/token' - - const body = { - client_id: client.id, - client_secret: client.secret, - username: user.username, - password: user.password ?? 'password', - response_type: 'code', - grant_type: 'password', - scope: 'upload' - } - - return unwrapBody<{ access_token: string, refresh_token: string } & PeerTubeProblemDocument>(this.postBodyRequest({ - ...options, - - path, - requestType: 'form', - fields: body, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - } - - getAccessToken (arg1?: { username: string, password?: string }): Promise - getAccessToken (arg1: string, password?: string): Promise - async getAccessToken (arg1?: { username: string, password?: string } | string, password?: string) { - let user: { username: string, password?: string } - - if (!arg1) user = this.server.store.user - else if (typeof arg1 === 'object') user = arg1 - else user = { username: arg1, password } - - try { - const body = await this.login({ user }) - - return body.access_token - } catch (err) { - throw new Error(`Cannot authenticate. Please check your username/password. (${err})`) - } - } - - loginUsingExternalToken (options: OverrideCommandOptions & { - username: string - externalAuthToken: string - }) { - const { username, externalAuthToken } = options - const path = '/api/v1/users/token' - - const body = { - client_id: this.server.store.client.id, - client_secret: this.server.store.client.secret, - username: username, - response_type: 'code', - grant_type: 'password', - scope: 'upload', - externalAuthToken - } - - return this.postBodyRequest({ - ...options, - - path, - requestType: 'form', - fields: body, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - logout (options: OverrideCommandOptions & { - token: string - }) { - const path = '/api/v1/users/revoke-token' - - return unwrapBody<{ redirectUrl: string }>(this.postBodyRequest({ - ...options, - - path, - requestType: 'form', - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - } - - refreshToken (options: OverrideCommandOptions & { - refreshToken: string - }) { - const path = '/api/v1/users/token' - - const body = { - client_id: this.server.store.client.id, - client_secret: this.server.store.client.secret, - refresh_token: options.refreshToken, - response_type: 'code', - grant_type: 'refresh_token' - } - - return this.postBodyRequest({ - ...options, - - path, - requestType: 'form', - fields: body, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getClient (options: OverrideCommandOptions = {}) { - const path = '/api/v1/oauth-clients/local' - - return this.getRequestBody<{ client_id: string, client_secret: string }>({ - ...options, - - path, - host: this.server.host, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/users/login.ts b/shared/extra-utils/users/login.ts deleted file mode 100644 index f1df027d3..000000000 --- a/shared/extra-utils/users/login.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { PeerTubeServer } from '../server/server' - -function setAccessTokensToServers (servers: PeerTubeServer[]) { - const tasks: Promise[] = [] - - for (const server of servers) { - const p = server.login.getAccessToken() - .then(t => { server.accessToken = t }) - tasks.push(p) - } - - return Promise.all(tasks) -} - -// --------------------------------------------------------------------------- - -export { - setAccessTokensToServers -} diff --git a/shared/extra-utils/users/notifications-command.ts b/shared/extra-utils/users/notifications-command.ts deleted file mode 100644 index 692420b8b..000000000 --- a/shared/extra-utils/users/notifications-command.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { HttpStatusCode, ResultList } from '@shared/models' -import { UserNotification, UserNotificationSetting } from '../../models/users' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class NotificationsCommand extends AbstractCommand { - - updateMySettings (options: OverrideCommandOptions & { - settings: UserNotificationSetting - }) { - const path = '/api/v1/users/me/notification-settings' - - return this.putBodyRequest({ - ...options, - - path, - fields: options.settings, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - list (options: OverrideCommandOptions & { - start?: number - count?: number - unread?: boolean - sort?: string - }) { - const { start, count, unread, sort = '-createdAt' } = options - const path = '/api/v1/users/me/notifications' - - return this.getRequestBody>({ - ...options, - - path, - query: { - start, - count, - sort, - unread - }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - markAsRead (options: OverrideCommandOptions & { - ids: number[] - }) { - const { ids } = options - const path = '/api/v1/users/me/notifications/read' - - return this.postBodyRequest({ - ...options, - - path, - fields: { ids }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - markAsReadAll (options: OverrideCommandOptions) { - const path = '/api/v1/users/me/notifications/read-all' - - return this.postBodyRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - async getLatest (options: OverrideCommandOptions = {}) { - const { total, data } = await this.list({ - ...options, - start: 0, - count: 1, - sort: '-createdAt' - }) - - if (total === 0) return undefined - - return data[0] - } -} diff --git a/shared/extra-utils/users/notifications.ts b/shared/extra-utils/users/notifications.ts deleted file mode 100644 index 07ccb0f8d..000000000 --- a/shared/extra-utils/users/notifications.ts +++ /dev/null @@ -1,795 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ - -import { expect } from 'chai' -import { inspect } from 'util' -import { AbuseState, PluginType } from '@shared/models' -import { UserNotification, UserNotificationSetting, UserNotificationSettingValue, UserNotificationType } from '../../models/users' -import { MockSmtpServer } from '../mock-servers/mock-email' -import { PeerTubeServer } from '../server' -import { doubleFollow } from '../server/follows' -import { createMultipleServers } from '../server/servers' -import { setAccessTokensToServers } from './login' - -type CheckerBaseParams = { - server: PeerTubeServer - emails: any[] - socketNotifications: UserNotification[] - token: string - check?: { web: boolean, mail: boolean } -} - -type CheckerType = 'presence' | 'absence' - -function getAllNotificationsSettings (): UserNotificationSetting { - return { - newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL - } -} - -async function checkNewVideoFromSubscription (options: CheckerBaseParams & { - videoName: string - shortUUID: string - checkType: CheckerType -}) { - const { videoName, shortUUID } = options - const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkVideo(notification.video, videoName, shortUUID) - checkActor(notification.video.channel) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.type !== UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION || n.video.name !== videoName - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - return text.indexOf(shortUUID) !== -1 && text.indexOf('Your subscription') !== -1 - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkVideoIsPublished (options: CheckerBaseParams & { - videoName: string - shortUUID: string - checkType: CheckerType -}) { - const { videoName, shortUUID } = options - const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkVideo(notification.video, videoName, shortUUID) - checkActor(notification.video.channel) - } else { - expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - return text.includes(shortUUID) && text.includes('Your video') - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkMyVideoImportIsFinished (options: CheckerBaseParams & { - videoName: string - shortUUID: string - url: string - success: boolean - checkType: CheckerType -}) { - const { videoName, shortUUID, url, success } = options - - const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.videoImport.targetUrl).to.equal(url) - - if (success) checkVideo(notification.videoImport.video, videoName, shortUUID) - } else { - expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - const toFind = success ? ' finished' : ' error' - - return text.includes(url) && text.includes(toFind) - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkUserRegistered (options: CheckerBaseParams & { - username: string - checkType: CheckerType -}) { - const { username } = options - const notificationType = UserNotificationType.NEW_USER_REGISTRATION - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkActor(notification.account) - expect(notification.account.name).to.equal(username) - } else { - expect(notification).to.satisfy(n => n.type !== notificationType || n.account.name !== username) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - - return text.includes(' registered.') && text.includes(username) - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkNewActorFollow (options: CheckerBaseParams & { - followType: 'channel' | 'account' - followerName: string - followerDisplayName: string - followingDisplayName: string - checkType: CheckerType -}) { - const { followType, followerName, followerDisplayName, followingDisplayName } = options - const notificationType = UserNotificationType.NEW_FOLLOW - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkActor(notification.actorFollow.follower) - expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName) - expect(notification.actorFollow.follower.name).to.equal(followerName) - expect(notification.actorFollow.follower.host).to.not.be.undefined - - const following = notification.actorFollow.following - expect(following.displayName).to.equal(followingDisplayName) - expect(following.type).to.equal(followType) - } else { - expect(notification).to.satisfy(n => { - return n.type !== notificationType || - (n.actorFollow.follower.name !== followerName && n.actorFollow.following !== followingDisplayName) - }) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - - return text.includes(followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName) - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkNewInstanceFollower (options: CheckerBaseParams & { - followerHost: string - checkType: CheckerType -}) { - const { followerHost } = options - const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkActor(notification.actorFollow.follower) - expect(notification.actorFollow.follower.name).to.equal('peertube') - expect(notification.actorFollow.follower.host).to.equal(followerHost) - - expect(notification.actorFollow.following.name).to.equal('peertube') - } else { - expect(notification).to.satisfy(n => { - return n.type !== notificationType || n.actorFollow.follower.host !== followerHost - }) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - - return text.includes('instance has a new follower') && text.includes(followerHost) - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkAutoInstanceFollowing (options: CheckerBaseParams & { - followerHost: string - followingHost: string - checkType: CheckerType -}) { - const { followerHost, followingHost } = options - const notificationType = UserNotificationType.AUTO_INSTANCE_FOLLOWING - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - const following = notification.actorFollow.following - checkActor(following) - expect(following.name).to.equal('peertube') - expect(following.host).to.equal(followingHost) - - expect(notification.actorFollow.follower.name).to.equal('peertube') - expect(notification.actorFollow.follower.host).to.equal(followerHost) - } else { - expect(notification).to.satisfy(n => { - return n.type !== notificationType || n.actorFollow.following.host !== followingHost - }) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - - return text.includes(' automatically followed a new instance') && text.includes(followingHost) - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkCommentMention (options: CheckerBaseParams & { - shortUUID: string - commentId: number - threadId: number - byAccountDisplayName: string - checkType: CheckerType -}) { - const { shortUUID, commentId, threadId, byAccountDisplayName } = options - const notificationType = UserNotificationType.COMMENT_MENTION - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkComment(notification.comment, commentId, threadId) - checkActor(notification.comment.account) - expect(notification.comment.account.displayName).to.equal(byAccountDisplayName) - - checkVideo(notification.comment.video, undefined, shortUUID) - } else { - expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - - return text.includes(' mentioned ') && text.includes(shortUUID) && text.includes(byAccountDisplayName) - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -let lastEmailCount = 0 - -async function checkNewCommentOnMyVideo (options: CheckerBaseParams & { - shortUUID: string - commentId: number - threadId: number - checkType: CheckerType -}) { - const { server, shortUUID, commentId, threadId, checkType, emails } = options - const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkComment(notification.comment, commentId, threadId) - checkActor(notification.comment.account) - checkVideo(notification.comment.video, undefined, shortUUID) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.comment === undefined || n.comment.id !== commentId - }) - } - } - - const commentUrl = `http://localhost:${server.port}/w/${shortUUID};threadId=${threadId}` - - function emailNotificationFinder (email: object) { - return email['text'].indexOf(commentUrl) !== -1 - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) - - if (checkType === 'presence') { - // We cannot detect email duplicates, so check we received another email - expect(emails).to.have.length.above(lastEmailCount) - lastEmailCount = emails.length - } -} - -async function checkNewVideoAbuseForModerators (options: CheckerBaseParams & { - shortUUID: string - videoName: string - checkType: CheckerType -}) { - const { shortUUID, videoName } = options - const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.abuse.id).to.be.a('number') - checkVideo(notification.abuse.video, videoName, shortUUID) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.abuse === undefined || n.abuse.video.shortUUID !== shortUUID - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - return text.indexOf(shortUUID) !== -1 && text.indexOf('abuse') !== -1 - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkNewAbuseMessage (options: CheckerBaseParams & { - abuseId: number - message: string - toEmail: string - checkType: CheckerType -}) { - const { abuseId, message, toEmail } = options - const notificationType = UserNotificationType.ABUSE_NEW_MESSAGE - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.abuse.id).to.equal(abuseId) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.type !== notificationType || n.abuse === undefined || n.abuse.id !== abuseId - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - const to = email['to'].filter(t => t.address === toEmail) - - return text.indexOf(message) !== -1 && to.length !== 0 - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkAbuseStateChange (options: CheckerBaseParams & { - abuseId: number - state: AbuseState - checkType: CheckerType -}) { - const { abuseId, state } = options - const notificationType = UserNotificationType.ABUSE_STATE_CHANGE - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.abuse.id).to.equal(abuseId) - expect(notification.abuse.state).to.equal(state) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.abuse === undefined || n.abuse.id !== abuseId - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - - const contains = state === AbuseState.ACCEPTED - ? ' accepted' - : ' rejected' - - return text.indexOf(contains) !== -1 - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkNewCommentAbuseForModerators (options: CheckerBaseParams & { - shortUUID: string - videoName: string - checkType: CheckerType -}) { - const { shortUUID, videoName } = options - const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.abuse.id).to.be.a('number') - checkVideo(notification.abuse.comment.video, videoName, shortUUID) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.abuse === undefined || n.abuse.comment.video.shortUUID !== shortUUID - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - return text.indexOf(shortUUID) !== -1 && text.indexOf('abuse') !== -1 - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkNewAccountAbuseForModerators (options: CheckerBaseParams & { - displayName: string - checkType: CheckerType -}) { - const { displayName } = options - const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.abuse.id).to.be.a('number') - expect(notification.abuse.account.displayName).to.equal(displayName) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.abuse === undefined || n.abuse.account.displayName !== displayName - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - return text.indexOf(displayName) !== -1 && text.indexOf('abuse') !== -1 - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkVideoAutoBlacklistForModerators (options: CheckerBaseParams & { - shortUUID: string - videoName: string - checkType: CheckerType -}) { - const { shortUUID, videoName } = options - const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.videoBlacklist.video.id).to.be.a('number') - checkVideo(notification.videoBlacklist.video, videoName, shortUUID) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.video === undefined || n.video.shortUUID !== shortUUID - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - return text.indexOf(shortUUID) !== -1 && email['text'].indexOf('video-auto-blacklist/list') !== -1 - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkNewBlacklistOnMyVideo (options: CheckerBaseParams & { - shortUUID: string - videoName: string - blacklistType: 'blacklist' | 'unblacklist' -}) { - const { videoName, shortUUID, blacklistType } = options - const notificationType = blacklistType === 'blacklist' - ? UserNotificationType.BLACKLIST_ON_MY_VIDEO - : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO - - function notificationChecker (notification: UserNotification) { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video - - checkVideo(video, videoName, shortUUID) - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - const blacklistText = blacklistType === 'blacklist' - ? 'blacklisted' - : 'unblacklisted' - - return text.includes(shortUUID) && text.includes(blacklistText) - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder, checkType: 'presence' }) -} - -async function checkNewPeerTubeVersion (options: CheckerBaseParams & { - latestVersion: string - checkType: CheckerType -}) { - const { latestVersion } = options - const notificationType = UserNotificationType.NEW_PEERTUBE_VERSION - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.peertube).to.exist - expect(notification.peertube.latestVersion).to.equal(latestVersion) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.peertube === undefined || n.peertube.latestVersion !== latestVersion - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - - return text.includes(latestVersion) - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function checkNewPluginVersion (options: CheckerBaseParams & { - pluginType: PluginType - pluginName: string - checkType: CheckerType -}) { - const { pluginName, pluginType } = options - const notificationType = UserNotificationType.NEW_PLUGIN_VERSION - - function notificationChecker (notification: UserNotification, checkType: CheckerType) { - if (checkType === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.plugin.name).to.equal(pluginName) - expect(notification.plugin.type).to.equal(pluginType) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.plugin === undefined || n.plugin.name !== pluginName - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - - return text.includes(pluginName) - } - - await checkNotification({ ...options, notificationChecker, emailNotificationFinder }) -} - -async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) { - const userNotifications: UserNotification[] = [] - const adminNotifications: UserNotification[] = [] - const adminNotificationsServer2: UserNotification[] = [] - const emails: object[] = [] - - const port = await MockSmtpServer.Instance.collectEmails(emails) - - const overrideConfig = { - smtp: { - hostname: 'localhost', - port - }, - signup: { - limit: 20 - } - } - const servers = await createMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg)) - - await setAccessTokensToServers(servers) - - if (serversCount > 1) { - await doubleFollow(servers[0], servers[1]) - } - - const user = { username: 'user_1', password: 'super password' } - await servers[0].users.create({ ...user, videoQuota: 10 * 1000 * 1000 }) - const userAccessToken = await servers[0].login.getAccessToken(user) - - await servers[0].notifications.updateMySettings({ token: userAccessToken, settings: getAllNotificationsSettings() }) - await servers[0].notifications.updateMySettings({ settings: getAllNotificationsSettings() }) - - if (serversCount > 1) { - await servers[1].notifications.updateMySettings({ settings: getAllNotificationsSettings() }) - } - - { - const socket = servers[0].socketIO.getUserNotificationSocket({ token: userAccessToken }) - socket.on('new-notification', n => userNotifications.push(n)) - } - { - const socket = servers[0].socketIO.getUserNotificationSocket() - socket.on('new-notification', n => adminNotifications.push(n)) - } - - if (serversCount > 1) { - const socket = servers[1].socketIO.getUserNotificationSocket() - socket.on('new-notification', n => adminNotificationsServer2.push(n)) - } - - const { videoChannels } = await servers[0].users.getMyInfo() - const channelId = videoChannels[0].id - - return { - userNotifications, - adminNotifications, - adminNotificationsServer2, - userAccessToken, - emails, - servers, - channelId - } -} - -// --------------------------------------------------------------------------- - -export { - getAllNotificationsSettings, - - CheckerBaseParams, - CheckerType, - checkMyVideoImportIsFinished, - checkUserRegistered, - checkAutoInstanceFollowing, - checkVideoIsPublished, - checkNewVideoFromSubscription, - checkNewActorFollow, - checkNewCommentOnMyVideo, - checkNewBlacklistOnMyVideo, - checkCommentMention, - checkNewVideoAbuseForModerators, - checkVideoAutoBlacklistForModerators, - checkNewAbuseMessage, - checkAbuseStateChange, - checkNewInstanceFollower, - prepareNotificationsTest, - checkNewCommentAbuseForModerators, - checkNewAccountAbuseForModerators, - checkNewPeerTubeVersion, - checkNewPluginVersion -} - -// --------------------------------------------------------------------------- - -async function checkNotification (options: CheckerBaseParams & { - notificationChecker: (notification: UserNotification, checkType: CheckerType) => void - emailNotificationFinder: (email: object) => boolean - checkType: CheckerType -}) { - const { server, token, checkType, notificationChecker, emailNotificationFinder, socketNotifications, emails } = options - - const check = options.check || { web: true, mail: true } - - if (check.web) { - const notification = await server.notifications.getLatest({ token: token }) - - if (notification || checkType !== 'absence') { - notificationChecker(notification, checkType) - } - - const socketNotification = socketNotifications.find(n => { - try { - notificationChecker(n, 'presence') - return true - } catch { - return false - } - }) - - if (checkType === 'presence') { - const obj = inspect(socketNotifications, { depth: 5 }) - expect(socketNotification, 'The socket notification is absent when it should be present. ' + obj).to.not.be.undefined - } else { - const obj = inspect(socketNotification, { depth: 5 }) - expect(socketNotification, 'The socket notification is present when it should not be present. ' + obj).to.be.undefined - } - } - - if (check.mail) { - // Last email - const email = emails - .slice() - .reverse() - .find(e => emailNotificationFinder(e)) - - if (checkType === 'presence') { - const texts = emails.map(e => e.text) - expect(email, 'The email is absent when is should be present. ' + inspect(texts)).to.not.be.undefined - } else { - expect(email, 'The email is present when is should not be present. ' + inspect(email)).to.be.undefined - } - } -} - -function checkVideo (video: any, videoName?: string, shortUUID?: string) { - if (videoName) { - expect(video.name).to.be.a('string') - expect(video.name).to.not.be.empty - expect(video.name).to.equal(videoName) - } - - if (shortUUID) { - expect(video.shortUUID).to.be.a('string') - expect(video.shortUUID).to.not.be.empty - expect(video.shortUUID).to.equal(shortUUID) - } - - expect(video.id).to.be.a('number') -} - -function checkActor (actor: any) { - expect(actor.displayName).to.be.a('string') - expect(actor.displayName).to.not.be.empty - expect(actor.host).to.not.be.undefined -} - -function checkComment (comment: any, commentId: number, threadId: number) { - expect(comment.id).to.equal(commentId) - expect(comment.threadId).to.equal(threadId) -} diff --git a/shared/extra-utils/users/subscriptions-command.ts b/shared/extra-utils/users/subscriptions-command.ts deleted file mode 100644 index edc60e612..000000000 --- a/shared/extra-utils/users/subscriptions-command.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { HttpStatusCode, ResultList, Video, VideoChannel } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class SubscriptionsCommand extends AbstractCommand { - - add (options: OverrideCommandOptions & { - targetUri: string - }) { - const path = '/api/v1/users/me/subscriptions' - - return this.postBodyRequest({ - ...options, - - path, - fields: { uri: options.targetUri }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - list (options: OverrideCommandOptions & { - sort?: string // default -createdAt - search?: string - } = {}) { - const { sort = '-createdAt', search } = options - const path = '/api/v1/users/me/subscriptions' - - return this.getRequestBody>({ - ...options, - - path, - query: { - sort, - search - }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listVideos (options: OverrideCommandOptions & { - sort?: string // default -createdAt - } = {}) { - const { sort = '-createdAt' } = options - const path = '/api/v1/users/me/subscriptions/videos' - - return this.getRequestBody>({ - ...options, - - path, - query: { sort }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - get (options: OverrideCommandOptions & { - uri: string - }) { - const path = '/api/v1/users/me/subscriptions/' + options.uri - - return this.getRequestBody({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - remove (options: OverrideCommandOptions & { - uri: string - }) { - const path = '/api/v1/users/me/subscriptions/' + options.uri - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - exist (options: OverrideCommandOptions & { - uris: string[] - }) { - const path = '/api/v1/users/me/subscriptions/exist' - - return this.getRequestBody<{ [id: string ]: boolean }>({ - ...options, - - path, - query: { 'uris[]': options.uris }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/users/users-command.ts b/shared/extra-utils/users/users-command.ts deleted file mode 100644 index 90c5f2183..000000000 --- a/shared/extra-utils/users/users-command.ts +++ /dev/null @@ -1,416 +0,0 @@ -import { omit } from 'lodash' -import { pick } from '@shared/core-utils' -import { - HttpStatusCode, - MyUser, - ResultList, - User, - UserAdminFlag, - UserCreateResult, - UserRole, - UserUpdate, - UserUpdateMe, - UserVideoQuota, - UserVideoRate -} from '@shared/models' -import { ScopedToken } from '@shared/models/users/user-scoped-token' -import { unwrapBody } from '../requests' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class UsersCommand extends AbstractCommand { - - askResetPassword (options: OverrideCommandOptions & { - email: string - }) { - const { email } = options - const path = '/api/v1/users/ask-reset-password' - - return this.postBodyRequest({ - ...options, - - path, - fields: { email }, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - resetPassword (options: OverrideCommandOptions & { - userId: number - verificationString: string - password: string - }) { - const { userId, verificationString, password } = options - const path = '/api/v1/users/' + userId + '/reset-password' - - return this.postBodyRequest({ - ...options, - - path, - fields: { password, verificationString }, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - // --------------------------------------------------------------------------- - - askSendVerifyEmail (options: OverrideCommandOptions & { - email: string - }) { - const { email } = options - const path = '/api/v1/users/ask-send-verify-email' - - return this.postBodyRequest({ - ...options, - - path, - fields: { email }, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - verifyEmail (options: OverrideCommandOptions & { - userId: number - verificationString: string - isPendingEmail?: boolean // default false - }) { - const { userId, verificationString, isPendingEmail = false } = options - const path = '/api/v1/users/' + userId + '/verify-email' - - return this.postBodyRequest({ - ...options, - - path, - fields: { - verificationString, - isPendingEmail - }, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - // --------------------------------------------------------------------------- - - banUser (options: OverrideCommandOptions & { - userId: number - reason?: string - }) { - const { userId, reason } = options - const path = '/api/v1/users' + '/' + userId + '/block' - - return this.postBodyRequest({ - ...options, - - path, - fields: { reason }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - unbanUser (options: OverrideCommandOptions & { - userId: number - }) { - const { userId } = options - const path = '/api/v1/users' + '/' + userId + '/unblock' - - return this.postBodyRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - // --------------------------------------------------------------------------- - - getMyScopedTokens (options: OverrideCommandOptions = {}) { - const path = '/api/v1/users/scoped-tokens' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - renewMyScopedTokens (options: OverrideCommandOptions = {}) { - const path = '/api/v1/users/scoped-tokens' - - return this.postBodyRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - // --------------------------------------------------------------------------- - - create (options: OverrideCommandOptions & { - username: string - password?: string - videoQuota?: number - videoQuotaDaily?: number - role?: UserRole - adminFlags?: UserAdminFlag - }) { - const { - username, - adminFlags, - password = 'password', - videoQuota = 42000000, - videoQuotaDaily = -1, - role = UserRole.USER - } = options - - const path = '/api/v1/users' - - return unwrapBody<{ user: UserCreateResult }>(this.postBodyRequest({ - ...options, - - path, - fields: { - username, - password, - role, - adminFlags, - email: username + '@example.com', - videoQuota, - videoQuotaDaily - }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - })).then(res => res.user) - } - - async generate (username: string, role?: UserRole) { - const password = 'password' - const user = await this.create({ username, password, role }) - - const token = await this.server.login.getAccessToken({ username, password }) - - const me = await this.getMyInfo({ token }) - - return { - token, - userId: user.id, - userChannelId: me.videoChannels[0].id, - userChannelName: me.videoChannels[0].name - } - } - - async generateUserAndToken (username: string, role?: UserRole) { - const password = 'password' - await this.create({ username, password, role }) - - return this.server.login.getAccessToken({ username, password }) - } - - register (options: OverrideCommandOptions & { - username: string - password?: string - displayName?: string - channel?: { - name: string - displayName: string - } - }) { - const { username, password = 'password', displayName, channel } = options - const path = '/api/v1/users/register' - - return this.postBodyRequest({ - ...options, - - path, - fields: { - username, - password, - email: username + '@example.com', - displayName, - channel - }, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - // --------------------------------------------------------------------------- - - getMyInfo (options: OverrideCommandOptions = {}) { - const path = '/api/v1/users/me' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getMyQuotaUsed (options: OverrideCommandOptions = {}) { - const path = '/api/v1/users/me/video-quota-used' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getMyRating (options: OverrideCommandOptions & { - videoId: number | string - }) { - const { videoId } = options - const path = '/api/v1/users/me/videos/' + videoId + '/rating' - - return this.getRequestBody({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - deleteMe (options: OverrideCommandOptions = {}) { - const path = '/api/v1/users/me' - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - updateMe (options: OverrideCommandOptions & UserUpdateMe) { - const path = '/api/v1/users/me' - - const toSend: UserUpdateMe = omit(options, 'url', 'accessToken') - - return this.putBodyRequest({ - ...options, - - path, - fields: toSend, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - updateMyAvatar (options: OverrideCommandOptions & { - fixture: string - }) { - const { fixture } = options - const path = '/api/v1/users/me/avatar/pick' - - return this.updateImageRequest({ - ...options, - - path, - fixture, - fieldname: 'avatarfile', - - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - // --------------------------------------------------------------------------- - - get (options: OverrideCommandOptions & { - userId: number - withStats?: boolean // default false - }) { - const { userId, withStats } = options - const path = '/api/v1/users/' + userId - - return this.getRequestBody({ - ...options, - - path, - query: { withStats }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - list (options: OverrideCommandOptions & { - start?: number - count?: number - sort?: string - search?: string - blocked?: boolean - } = {}) { - const path = '/api/v1/users' - - return this.getRequestBody>({ - ...options, - - path, - query: pick(options, [ 'start', 'count', 'sort', 'search', 'blocked' ]), - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - remove (options: OverrideCommandOptions & { - userId: number - }) { - const { userId } = options - const path = '/api/v1/users/' + userId - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - update (options: OverrideCommandOptions & { - userId: number - email?: string - emailVerified?: boolean - videoQuota?: number - videoQuotaDaily?: number - password?: string - adminFlags?: UserAdminFlag - pluginAuth?: string - role?: UserRole - }) { - const path = '/api/v1/users/' + options.userId - - const toSend: UserUpdate = {} - if (options.password !== undefined && options.password !== null) toSend.password = options.password - if (options.email !== undefined && options.email !== null) toSend.email = options.email - if (options.emailVerified !== undefined && options.emailVerified !== null) toSend.emailVerified = options.emailVerified - if (options.videoQuota !== undefined && options.videoQuota !== null) toSend.videoQuota = options.videoQuota - if (options.videoQuotaDaily !== undefined && options.videoQuotaDaily !== null) toSend.videoQuotaDaily = options.videoQuotaDaily - if (options.role !== undefined && options.role !== null) toSend.role = options.role - if (options.adminFlags !== undefined && options.adminFlags !== null) toSend.adminFlags = options.adminFlags - if (options.pluginAuth !== undefined) toSend.pluginAuth = options.pluginAuth - - return this.putBodyRequest({ - ...options, - - path, - fields: toSend, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } -} diff --git a/shared/extra-utils/videos/blacklist-command.ts b/shared/extra-utils/videos/blacklist-command.ts deleted file mode 100644 index 3a2ef89ba..000000000 --- a/shared/extra-utils/videos/blacklist-command.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import { HttpStatusCode, ResultList } from '@shared/models' -import { VideoBlacklist, VideoBlacklistType } from '../../models/videos' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class BlacklistCommand extends AbstractCommand { - - add (options: OverrideCommandOptions & { - videoId: number | string - reason?: string - unfederate?: boolean - }) { - const { videoId, reason, unfederate } = options - const path = '/api/v1/videos/' + videoId + '/blacklist' - - return this.postBodyRequest({ - ...options, - - path, - fields: { reason, unfederate }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - update (options: OverrideCommandOptions & { - videoId: number | string - reason?: string - }) { - const { videoId, reason } = options - const path = '/api/v1/videos/' + videoId + '/blacklist' - - return this.putBodyRequest({ - ...options, - - path, - fields: { reason }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - remove (options: OverrideCommandOptions & { - videoId: number | string - }) { - const { videoId } = options - const path = '/api/v1/videos/' + videoId + '/blacklist' - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - list (options: OverrideCommandOptions & { - sort?: string - type?: VideoBlacklistType - } = {}) { - const { sort, type } = options - const path = '/api/v1/videos/blacklist/' - - const query = { sort, type } - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/videos/captions-command.ts b/shared/extra-utils/videos/captions-command.ts deleted file mode 100644 index a65ea99e3..000000000 --- a/shared/extra-utils/videos/captions-command.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { HttpStatusCode, ResultList, VideoCaption } from '@shared/models' -import { buildAbsoluteFixturePath } from '../miscs' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class CaptionsCommand extends AbstractCommand { - - add (options: OverrideCommandOptions & { - videoId: string | number - language: string - fixture: string - mimeType?: string - }) { - const { videoId, language, fixture, mimeType } = options - - const path = '/api/v1/videos/' + videoId + '/captions/' + language - - const captionfile = buildAbsoluteFixturePath(fixture) - const captionfileAttach = mimeType - ? [ captionfile, { contentType: mimeType } ] - : captionfile - - return this.putUploadRequest({ - ...options, - - path, - fields: {}, - attaches: { - captionfile: captionfileAttach - }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - list (options: OverrideCommandOptions & { - videoId: string | number - }) { - const { videoId } = options - const path = '/api/v1/videos/' + videoId + '/captions' - - return this.getRequestBody>({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - delete (options: OverrideCommandOptions & { - videoId: string | number - language: string - }) { - const { videoId, language } = options - const path = '/api/v1/videos/' + videoId + '/captions/' + language - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } -} diff --git a/shared/extra-utils/videos/captions.ts b/shared/extra-utils/videos/captions.ts deleted file mode 100644 index 35e722408..000000000 --- a/shared/extra-utils/videos/captions.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { expect } from 'chai' -import request from 'supertest' -import { HttpStatusCode } from '@shared/models' - -async function testCaptionFile (url: string, captionPath: string, toTest: RegExp | string) { - const res = await request(url) - .get(captionPath) - .expect(HttpStatusCode.OK_200) - - if (toTest instanceof RegExp) { - expect(res.text).to.match(toTest) - } else { - expect(res.text).to.contain(toTest) - } -} - -// --------------------------------------------------------------------------- - -export { - testCaptionFile -} diff --git a/shared/extra-utils/videos/change-ownership-command.ts b/shared/extra-utils/videos/change-ownership-command.ts deleted file mode 100644 index ad4c726ef..000000000 --- a/shared/extra-utils/videos/change-ownership-command.ts +++ /dev/null @@ -1,68 +0,0 @@ - -import { HttpStatusCode, ResultList, VideoChangeOwnership } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class ChangeOwnershipCommand extends AbstractCommand { - - create (options: OverrideCommandOptions & { - videoId: number | string - username: string - }) { - const { videoId, username } = options - const path = '/api/v1/videos/' + videoId + '/give-ownership' - - return this.postBodyRequest({ - ...options, - - path, - fields: { username }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - list (options: OverrideCommandOptions = {}) { - const path = '/api/v1/videos/ownership' - - return this.getRequestBody>({ - ...options, - - path, - query: { sort: '-createdAt' }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - accept (options: OverrideCommandOptions & { - ownershipId: number - channelId: number - }) { - const { ownershipId, channelId } = options - const path = '/api/v1/videos/ownership/' + ownershipId + '/accept' - - return this.postBodyRequest({ - ...options, - - path, - fields: { channelId }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - refuse (options: OverrideCommandOptions & { - ownershipId: number - }) { - const { ownershipId } = options - const path = '/api/v1/videos/ownership/' + ownershipId + '/refuse' - - return this.postBodyRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } -} diff --git a/shared/extra-utils/videos/channels-command.ts b/shared/extra-utils/videos/channels-command.ts deleted file mode 100644 index e406e570b..000000000 --- a/shared/extra-utils/videos/channels-command.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { pick } from '@shared/core-utils' -import { ActorFollow, HttpStatusCode, ResultList, VideoChannel, VideoChannelCreateResult } from '@shared/models' -import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model' -import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-update.model' -import { unwrapBody } from '../requests' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class ChannelsCommand extends AbstractCommand { - - list (options: OverrideCommandOptions & { - start?: number - count?: number - sort?: string - withStats?: boolean - } = {}) { - const path = '/api/v1/video-channels' - - return this.getRequestBody>({ - ...options, - - path, - query: pick(options, [ 'start', 'count', 'sort', 'withStats' ]), - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listByAccount (options: OverrideCommandOptions & { - accountName: string - start?: number - count?: number - sort?: string - withStats?: boolean - search?: string - }) { - const { accountName, sort = 'createdAt' } = options - const path = '/api/v1/accounts/' + accountName + '/video-channels' - - return this.getRequestBody>({ - ...options, - - path, - query: { sort, ...pick(options, [ 'start', 'count', 'withStats', 'search' ]) }, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - async create (options: OverrideCommandOptions & { - attributes: Partial - }) { - const path = '/api/v1/video-channels/' - - // Default attributes - const defaultAttributes = { - displayName: 'my super video channel', - description: 'my super channel description', - support: 'my super channel support' - } - const attributes = { ...defaultAttributes, ...options.attributes } - - const body = await unwrapBody<{ videoChannel: VideoChannelCreateResult }>(this.postBodyRequest({ - ...options, - - path, - fields: attributes, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - - return body.videoChannel - } - - update (options: OverrideCommandOptions & { - channelName: string - attributes: VideoChannelUpdate - }) { - const { channelName, attributes } = options - const path = '/api/v1/video-channels/' + channelName - - return this.putBodyRequest({ - ...options, - - path, - fields: attributes, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - delete (options: OverrideCommandOptions & { - channelName: string - }) { - const path = '/api/v1/video-channels/' + options.channelName - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - get (options: OverrideCommandOptions & { - channelName: string - }) { - const path = '/api/v1/video-channels/' + options.channelName - - return this.getRequestBody({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - updateImage (options: OverrideCommandOptions & { - fixture: string - channelName: string | number - type: 'avatar' | 'banner' - }) { - const { channelName, fixture, type } = options - - const path = `/api/v1/video-channels/${channelName}/${type}/pick` - - return this.updateImageRequest({ - ...options, - - path, - fixture, - fieldname: type + 'file', - - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - deleteImage (options: OverrideCommandOptions & { - channelName: string | number - type: 'avatar' | 'banner' - }) { - const { channelName, type } = options - - const path = `/api/v1/video-channels/${channelName}/${type}` - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - listFollowers (options: OverrideCommandOptions & { - channelName: string - start?: number - count?: number - sort?: string - search?: string - }) { - const { channelName, start, count, sort, search } = options - const path = '/api/v1/video-channels/' + channelName + '/followers' - - const query = { start, count, sort, search } - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/videos/channels.ts b/shared/extra-utils/videos/channels.ts deleted file mode 100644 index 756c47453..000000000 --- a/shared/extra-utils/videos/channels.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { PeerTubeServer } from '../server/server' - -function setDefaultVideoChannel (servers: PeerTubeServer[]) { - const tasks: Promise[] = [] - - for (const server of servers) { - const p = server.users.getMyInfo() - .then(user => { server.store.channel = user.videoChannels[0] }) - - tasks.push(p) - } - - return Promise.all(tasks) -} - -export { - setDefaultVideoChannel -} diff --git a/shared/extra-utils/videos/comments-command.ts b/shared/extra-utils/videos/comments-command.ts deleted file mode 100644 index f0d163a07..000000000 --- a/shared/extra-utils/videos/comments-command.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { pick } from 'lodash' -import { HttpStatusCode, ResultList, VideoComment, VideoCommentThreads, VideoCommentThreadTree } from '@shared/models' -import { unwrapBody } from '../requests' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class CommentsCommand extends AbstractCommand { - - private lastVideoId: number | string - private lastThreadId: number - private lastReplyId: number - - listForAdmin (options: OverrideCommandOptions & { - start?: number - count?: number - sort?: string - isLocal?: boolean - search?: string - searchAccount?: string - searchVideo?: string - } = {}) { - const { sort = '-createdAt' } = options - const path = '/api/v1/videos/comments' - - const query = { sort, ...pick(options, [ 'start', 'count', 'isLocal', 'search', 'searchAccount', 'searchVideo' ]) } - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listThreads (options: OverrideCommandOptions & { - videoId: number | string - start?: number - count?: number - sort?: string - }) { - const { start, count, sort, videoId } = options - const path = '/api/v1/videos/' + videoId + '/comment-threads' - - return this.getRequestBody({ - ...options, - - path, - query: { start, count, sort }, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getThread (options: OverrideCommandOptions & { - videoId: number | string - threadId: number - }) { - const { videoId, threadId } = options - const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId - - return this.getRequestBody({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - async createThread (options: OverrideCommandOptions & { - videoId: number | string - text: string - }) { - const { videoId, text } = options - const path = '/api/v1/videos/' + videoId + '/comment-threads' - - const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({ - ...options, - - path, - fields: { text }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - - this.lastThreadId = body.comment?.id - this.lastVideoId = videoId - - return body.comment - } - - async addReply (options: OverrideCommandOptions & { - videoId: number | string - toCommentId: number - text: string - }) { - const { videoId, toCommentId, text } = options - const path = '/api/v1/videos/' + videoId + '/comments/' + toCommentId - - const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({ - ...options, - - path, - fields: { text }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - - this.lastReplyId = body.comment?.id - - return body.comment - } - - async addReplyToLastReply (options: OverrideCommandOptions & { - text: string - }) { - return this.addReply({ ...options, videoId: this.lastVideoId, toCommentId: this.lastReplyId }) - } - - async addReplyToLastThread (options: OverrideCommandOptions & { - text: string - }) { - return this.addReply({ ...options, videoId: this.lastVideoId, toCommentId: this.lastThreadId }) - } - - async findCommentId (options: OverrideCommandOptions & { - videoId: number | string - text: string - }) { - const { videoId, text } = options - const { data } = await this.listThreads({ videoId, count: 25, sort: '-createdAt' }) - - return data.find(c => c.text === text).id - } - - delete (options: OverrideCommandOptions & { - videoId: number | string - commentId: number - }) { - const { videoId, commentId } = options - const path = '/api/v1/videos/' + videoId + '/comments/' + commentId - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } -} diff --git a/shared/extra-utils/videos/history-command.ts b/shared/extra-utils/videos/history-command.ts deleted file mode 100644 index 13b7150c1..000000000 --- a/shared/extra-utils/videos/history-command.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { HttpStatusCode, ResultList, Video } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class HistoryCommand extends AbstractCommand { - - wathVideo (options: OverrideCommandOptions & { - videoId: number | string - currentTime: number - }) { - const { videoId, currentTime } = options - - const path = '/api/v1/videos/' + videoId + '/watching' - const fields = { currentTime } - - return this.putBodyRequest({ - ...options, - - path, - fields, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - list (options: OverrideCommandOptions & { - search?: string - } = {}) { - const { search } = options - const path = '/api/v1/users/me/history/videos' - - return this.getRequestBody>({ - ...options, - - path, - query: { - search - }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - remove (options: OverrideCommandOptions & { - beforeDate?: string - } = {}) { - const { beforeDate } = options - const path = '/api/v1/users/me/history/videos/remove' - - return this.postBodyRequest({ - ...options, - - path, - fields: { beforeDate }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } -} diff --git a/shared/extra-utils/videos/imports-command.ts b/shared/extra-utils/videos/imports-command.ts deleted file mode 100644 index e4944694d..000000000 --- a/shared/extra-utils/videos/imports-command.ts +++ /dev/null @@ -1,47 +0,0 @@ - -import { HttpStatusCode, ResultList } from '@shared/models' -import { VideoImport, VideoImportCreate } from '../../models/videos' -import { unwrapBody } from '../requests' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class ImportsCommand extends AbstractCommand { - - importVideo (options: OverrideCommandOptions & { - attributes: VideoImportCreate & { torrentfile?: string } - }) { - const { attributes } = options - const path = '/api/v1/videos/imports' - - let attaches: any = {} - if (attributes.torrentfile) attaches = { torrentfile: attributes.torrentfile } - - return unwrapBody(this.postUploadRequest({ - ...options, - - path, - attaches, - fields: options.attributes, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - } - - getMyVideoImports (options: OverrideCommandOptions & { - sort?: string - } = {}) { - const { sort } = options - const path = '/api/v1/users/me/videos/imports' - - const query = {} - if (sort) query['sort'] = sort - - return this.getRequestBody>({ - ...options, - - path, - query: { sort }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/videos/index.ts b/shared/extra-utils/videos/index.ts deleted file mode 100644 index 26e663f46..000000000 --- a/shared/extra-utils/videos/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -export * from './blacklist-command' -export * from './captions-command' -export * from './captions' -export * from './change-ownership-command' -export * from './channels' -export * from './channels-command' -export * from './comments-command' -export * from './history-command' -export * from './imports-command' -export * from './live-command' -export * from './live' -export * from './playlists-command' -export * from './playlists' -export * from './services-command' -export * from './streaming-playlists-command' -export * from './streaming-playlists' -export * from './comments-command' -export * from './videos-command' -export * from './videos' diff --git a/shared/extra-utils/videos/live-command.ts b/shared/extra-utils/videos/live-command.ts deleted file mode 100644 index 74f5d3089..000000000 --- a/shared/extra-utils/videos/live-command.ts +++ /dev/null @@ -1,155 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ - -import { readdir } from 'fs-extra' -import { omit } from 'lodash' -import { join } from 'path' -import { HttpStatusCode, LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models' -import { wait } from '../miscs' -import { unwrapBody } from '../requests' -import { AbstractCommand, OverrideCommandOptions } from '../shared' -import { sendRTMPStream, testFfmpegStreamError } from './live' - -export class LiveCommand extends AbstractCommand { - - get (options: OverrideCommandOptions & { - videoId: number | string - }) { - const path = '/api/v1/videos/live' - - return this.getRequestBody({ - ...options, - - path: path + '/' + options.videoId, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - update (options: OverrideCommandOptions & { - videoId: number | string - fields: LiveVideoUpdate - }) { - const { videoId, fields } = options - const path = '/api/v1/videos/live' - - return this.putBodyRequest({ - ...options, - - path: path + '/' + videoId, - fields, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - async create (options: OverrideCommandOptions & { - fields: LiveVideoCreate - }) { - const { fields } = options - const path = '/api/v1/videos/live' - - const attaches: any = {} - if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile - if (fields.previewfile) attaches.previewfile = fields.previewfile - - const body = await unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({ - ...options, - - path, - attaches, - fields: omit(fields, 'thumbnailfile', 'previewfile'), - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - - return body.video - } - - async sendRTMPStreamInVideo (options: OverrideCommandOptions & { - videoId: number | string - fixtureName?: string - copyCodecs?: boolean - }) { - const { videoId, fixtureName, copyCodecs } = options - const videoLive = await this.get({ videoId }) - - return sendRTMPStream({ rtmpBaseUrl: videoLive.rtmpUrl, streamKey: videoLive.streamKey, fixtureName, copyCodecs }) - } - - async runAndTestStreamError (options: OverrideCommandOptions & { - videoId: number | string - shouldHaveError: boolean - }) { - const command = await this.sendRTMPStreamInVideo(options) - - return testFfmpegStreamError(command, options.shouldHaveError) - } - - waitUntilPublished (options: OverrideCommandOptions & { - videoId: number | string - }) { - const { videoId } = options - return this.waitUntilState({ videoId, state: VideoState.PUBLISHED }) - } - - waitUntilWaiting (options: OverrideCommandOptions & { - videoId: number | string - }) { - const { videoId } = options - return this.waitUntilState({ videoId, state: VideoState.WAITING_FOR_LIVE }) - } - - waitUntilEnded (options: OverrideCommandOptions & { - videoId: number | string - }) { - const { videoId } = options - return this.waitUntilState({ videoId, state: VideoState.LIVE_ENDED }) - } - - waitUntilSegmentGeneration (options: OverrideCommandOptions & { - videoUUID: string - resolution: number - segment: number - }) { - const { resolution, segment, videoUUID } = options - const segmentName = `${resolution}-00000${segment}.ts` - - return this.server.servers.waitUntilLog(`${videoUUID}/${segmentName}`, 2, false) - } - - async waitUntilSaved (options: OverrideCommandOptions & { - videoId: number | string - }) { - let video: VideoDetails - - do { - video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId }) - - await wait(500) - } while (video.isLive === true || video.state.id !== VideoState.PUBLISHED) - } - - async countPlaylists (options: OverrideCommandOptions & { - videoUUID: string - }) { - const basePath = this.server.servers.buildDirectory('streaming-playlists') - const hlsPath = join(basePath, 'hls', options.videoUUID) - - const files = await readdir(hlsPath) - - return files.filter(f => f.endsWith('.m3u8')).length - } - - private async waitUntilState (options: OverrideCommandOptions & { - videoId: number | string - state: VideoState - }) { - let video: VideoDetails - - do { - video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId }) - - await wait(500) - } while (video.state.id !== options.state) - } -} diff --git a/shared/extra-utils/videos/live.ts b/shared/extra-utils/videos/live.ts deleted file mode 100644 index d3665bc90..000000000 --- a/shared/extra-utils/videos/live.ts +++ /dev/null @@ -1,137 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ - -import { expect } from 'chai' -import ffmpeg, { FfmpegCommand } from 'fluent-ffmpeg' -import { pathExists, readdir } from 'fs-extra' -import { join } from 'path' -import { buildAbsoluteFixturePath, wait } from '../miscs' -import { PeerTubeServer } from '../server/server' - -function sendRTMPStream (options: { - rtmpBaseUrl: string - streamKey: string - fixtureName?: string // default video_short.mp4 - copyCodecs?: boolean // default false -}) { - const { rtmpBaseUrl, streamKey, fixtureName = 'video_short.mp4', copyCodecs = false } = options - - const fixture = buildAbsoluteFixturePath(fixtureName) - - const command = ffmpeg(fixture) - command.inputOption('-stream_loop -1') - command.inputOption('-re') - - if (copyCodecs) { - command.outputOption('-c copy') - } else { - command.outputOption('-c:v libx264') - command.outputOption('-g 50') - command.outputOption('-keyint_min 2') - command.outputOption('-r 60') - } - - command.outputOption('-f flv') - - const rtmpUrl = rtmpBaseUrl + '/' + streamKey - command.output(rtmpUrl) - - command.on('error', err => { - if (err?.message?.includes('Exiting normally')) return - - if (process.env.DEBUG) console.error(err) - }) - - if (process.env.DEBUG) { - command.on('stderr', data => console.log(data)) - } - - command.run() - - return command -} - -function waitFfmpegUntilError (command: FfmpegCommand, successAfterMS = 10000) { - return new Promise((res, rej) => { - command.on('error', err => { - return rej(err) - }) - - setTimeout(() => { - res() - }, successAfterMS) - }) -} - -async function testFfmpegStreamError (command: FfmpegCommand, shouldHaveError: boolean) { - let error: Error - - try { - await waitFfmpegUntilError(command, 35000) - } catch (err) { - error = err - } - - await stopFfmpeg(command) - - if (shouldHaveError && !error) throw new Error('Ffmpeg did not have an error') - if (!shouldHaveError && error) throw error -} - -async function stopFfmpeg (command: FfmpegCommand) { - command.kill('SIGINT') - - await wait(500) -} - -async function waitUntilLivePublishedOnAllServers (servers: PeerTubeServer[], videoId: string) { - for (const server of servers) { - await server.live.waitUntilPublished({ videoId }) - } -} - -async function waitUntilLiveSavedOnAllServers (servers: PeerTubeServer[], videoId: string) { - for (const server of servers) { - await server.live.waitUntilSaved({ videoId }) - } -} - -async function checkLiveCleanupAfterSave (server: PeerTubeServer, videoUUID: string, resolutions: number[] = []) { - const basePath = server.servers.buildDirectory('streaming-playlists') - const hlsPath = join(basePath, 'hls', videoUUID) - - if (resolutions.length === 0) { - const result = await pathExists(hlsPath) - expect(result).to.be.false - - return - } - - const files = await readdir(hlsPath) - - // fragmented file and playlist per resolution + master playlist + segments sha256 json file - expect(files).to.have.lengthOf(resolutions.length * 2 + 2) - - for (const resolution of resolutions) { - const fragmentedFile = files.find(f => f.endsWith(`-${resolution}-fragmented.mp4`)) - expect(fragmentedFile).to.exist - - const playlistFile = files.find(f => f.endsWith(`${resolution}.m3u8`)) - expect(playlistFile).to.exist - } - - const masterPlaylistFile = files.find(f => f.endsWith('-master.m3u8')) - expect(masterPlaylistFile).to.exist - - const shaFile = files.find(f => f.endsWith('-segments-sha256.json')) - expect(shaFile).to.exist -} - -export { - sendRTMPStream, - waitFfmpegUntilError, - testFfmpegStreamError, - stopFfmpeg, - waitUntilLivePublishedOnAllServers, - waitUntilLiveSavedOnAllServers, - checkLiveCleanupAfterSave -} diff --git a/shared/extra-utils/videos/playlists-command.ts b/shared/extra-utils/videos/playlists-command.ts deleted file mode 100644 index ce23900d3..000000000 --- a/shared/extra-utils/videos/playlists-command.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { omit } from 'lodash' -import { pick } from '@shared/core-utils' -import { - BooleanBothQuery, - HttpStatusCode, - ResultList, - VideoExistInPlaylist, - VideoPlaylist, - VideoPlaylistCreate, - VideoPlaylistCreateResult, - VideoPlaylistElement, - VideoPlaylistElementCreate, - VideoPlaylistElementCreateResult, - VideoPlaylistElementUpdate, - VideoPlaylistReorder, - VideoPlaylistType, - VideoPlaylistUpdate -} from '@shared/models' -import { unwrapBody } from '../requests' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class PlaylistsCommand extends AbstractCommand { - - list (options: OverrideCommandOptions & { - start?: number - count?: number - sort?: string - }) { - const path = '/api/v1/video-playlists' - const query = pick(options, [ 'start', 'count', 'sort' ]) - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listByChannel (options: OverrideCommandOptions & { - handle: string - start?: number - count?: number - sort?: string - }) { - const path = '/api/v1/video-channels/' + options.handle + '/video-playlists' - const query = pick(options, [ 'start', 'count', 'sort' ]) - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listByAccount (options: OverrideCommandOptions & { - handle: string - start?: number - count?: number - sort?: string - search?: string - playlistType?: VideoPlaylistType - }) { - const path = '/api/v1/accounts/' + options.handle + '/video-playlists' - const query = pick(options, [ 'start', 'count', 'sort', 'search', 'playlistType' ]) - - return this.getRequestBody>({ - ...options, - - path, - query, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - get (options: OverrideCommandOptions & { - playlistId: number | string - }) { - const { playlistId } = options - const path = '/api/v1/video-playlists/' + playlistId - - return this.getRequestBody({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listVideos (options: OverrideCommandOptions & { - playlistId: number | string - start?: number - count?: number - query?: { nsfw?: BooleanBothQuery } - }) { - const path = '/api/v1/video-playlists/' + options.playlistId + '/videos' - const query = options.query ?? {} - - return this.getRequestBody>({ - ...options, - - path, - query: { - ...query, - start: options.start, - count: options.count - }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - delete (options: OverrideCommandOptions & { - playlistId: number | string - }) { - const path = '/api/v1/video-playlists/' + options.playlistId - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - async create (options: OverrideCommandOptions & { - attributes: VideoPlaylistCreate - }) { - const path = '/api/v1/video-playlists' - - const fields = omit(options.attributes, 'thumbnailfile') - - const attaches = options.attributes.thumbnailfile - ? { thumbnailfile: options.attributes.thumbnailfile } - : {} - - const body = await unwrapBody<{ videoPlaylist: VideoPlaylistCreateResult }>(this.postUploadRequest({ - ...options, - - path, - fields, - attaches, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - - return body.videoPlaylist - } - - update (options: OverrideCommandOptions & { - attributes: VideoPlaylistUpdate - playlistId: number | string - }) { - const path = '/api/v1/video-playlists/' + options.playlistId - - const fields = omit(options.attributes, 'thumbnailfile') - - const attaches = options.attributes.thumbnailfile - ? { thumbnailfile: options.attributes.thumbnailfile } - : {} - - return this.putUploadRequest({ - ...options, - - path, - fields, - attaches, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - async addElement (options: OverrideCommandOptions & { - playlistId: number | string - attributes: VideoPlaylistElementCreate | { videoId: string } - }) { - const attributes = { - ...options.attributes, - - videoId: await this.server.videos.getId({ ...options, uuid: options.attributes.videoId }) - } - - const path = '/api/v1/video-playlists/' + options.playlistId + '/videos' - - const body = await unwrapBody<{ videoPlaylistElement: VideoPlaylistElementCreateResult }>(this.postBodyRequest({ - ...options, - - path, - fields: attributes, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - - return body.videoPlaylistElement - } - - updateElement (options: OverrideCommandOptions & { - playlistId: number | string - elementId: number | string - attributes: VideoPlaylistElementUpdate - }) { - const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.elementId - - return this.putBodyRequest({ - ...options, - - path, - fields: options.attributes, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - removeElement (options: OverrideCommandOptions & { - playlistId: number | string - elementId: number - }) { - const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.elementId - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - reorderElements (options: OverrideCommandOptions & { - playlistId: number | string - attributes: VideoPlaylistReorder - }) { - const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/reorder' - - return this.postBodyRequest({ - ...options, - - path, - fields: options.attributes, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - getPrivacies (options: OverrideCommandOptions = {}) { - const path = '/api/v1/video-playlists/privacies' - - return this.getRequestBody<{ [ id: number ]: string }>({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - videosExist (options: OverrideCommandOptions & { - videoIds: number[] - }) { - const { videoIds } = options - const path = '/api/v1/users/me/video-playlists/videos-exist' - - return this.getRequestBody({ - ...options, - - path, - query: { videoIds }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/videos/playlists.ts b/shared/extra-utils/videos/playlists.ts deleted file mode 100644 index 3dde52bb9..000000000 --- a/shared/extra-utils/videos/playlists.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { expect } from 'chai' -import { readdir } from 'fs-extra' -import { join } from 'path' -import { root } from '../miscs' - -async function checkPlaylistFilesWereRemoved ( - playlistUUID: string, - internalServerNumber: number, - directories = [ 'thumbnails' ] -) { - const testDirectory = 'test' + internalServerNumber - - for (const directory of directories) { - const directoryPath = join(root(), testDirectory, directory) - - const files = await readdir(directoryPath) - for (const file of files) { - expect(file).to.not.contain(playlistUUID) - } - } -} - -export { - checkPlaylistFilesWereRemoved -} diff --git a/shared/extra-utils/videos/services-command.ts b/shared/extra-utils/videos/services-command.ts deleted file mode 100644 index 06760df42..000000000 --- a/shared/extra-utils/videos/services-command.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { HttpStatusCode } from '@shared/models' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class ServicesCommand extends AbstractCommand { - - getOEmbed (options: OverrideCommandOptions & { - oembedUrl: string - format?: string - maxHeight?: number - maxWidth?: number - }) { - const path = '/services/oembed' - const query = { - url: options.oembedUrl, - format: options.format, - maxheight: options.maxHeight, - maxwidth: options.maxWidth - } - - return this.getRequest({ - ...options, - - path, - query, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } -} diff --git a/shared/extra-utils/videos/streaming-playlists-command.ts b/shared/extra-utils/videos/streaming-playlists-command.ts deleted file mode 100644 index 5d40d35cb..000000000 --- a/shared/extra-utils/videos/streaming-playlists-command.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { HttpStatusCode } from '@shared/models' -import { unwrapBody, unwrapTextOrDecode, unwrapBodyOrDecodeToJSON } from '../requests' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export class StreamingPlaylistsCommand extends AbstractCommand { - - get (options: OverrideCommandOptions & { - url: string - }) { - return unwrapTextOrDecode(this.getRawRequest({ - ...options, - - url: options.url, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - } - - getSegment (options: OverrideCommandOptions & { - url: string - range?: string - }) { - return unwrapBody(this.getRawRequest({ - ...options, - - url: options.url, - range: options.range, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - } - - getSegmentSha256 (options: OverrideCommandOptions & { - url: string - }) { - return unwrapBodyOrDecodeToJSON<{ [ id: string ]: string }>(this.getRawRequest({ - ...options, - - url: options.url, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - } -} diff --git a/shared/extra-utils/videos/streaming-playlists.ts b/shared/extra-utils/videos/streaming-playlists.ts deleted file mode 100644 index 0451c0efe..000000000 --- a/shared/extra-utils/videos/streaming-playlists.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { expect } from 'chai' -import { basename } from 'path' -import { sha256 } from '@shared/core-utils/crypto' -import { removeFragmentedMP4Ext } from '@shared/core-utils' -import { HttpStatusCode, VideoStreamingPlaylist } from '@shared/models' -import { PeerTubeServer } from '../server' - -async function checkSegmentHash (options: { - server: PeerTubeServer - baseUrlPlaylist: string - baseUrlSegment: string - resolution: number - hlsPlaylist: VideoStreamingPlaylist -}) { - const { server, baseUrlPlaylist, baseUrlSegment, resolution, hlsPlaylist } = options - const command = server.streamingPlaylists - - const file = hlsPlaylist.files.find(f => f.resolution.id === resolution) - const videoName = basename(file.fileUrl) - - const playlist = await command.get({ url: `${baseUrlPlaylist}/${removeFragmentedMP4Ext(videoName)}.m3u8` }) - - const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist) - - const length = parseInt(matches[1], 10) - const offset = parseInt(matches[2], 10) - const range = `${offset}-${offset + length - 1}` - - const segmentBody = await command.getSegment({ - url: `${baseUrlSegment}/${videoName}`, - expectedStatus: HttpStatusCode.PARTIAL_CONTENT_206, - range: `bytes=${range}` - }) - - const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url }) - expect(sha256(segmentBody)).to.equal(shaBody[videoName][range]) -} - -async function checkLiveSegmentHash (options: { - server: PeerTubeServer - baseUrlSegment: string - videoUUID: string - segmentName: string - hlsPlaylist: VideoStreamingPlaylist -}) { - const { server, baseUrlSegment, videoUUID, segmentName, hlsPlaylist } = options - const command = server.streamingPlaylists - - const segmentBody = await command.getSegment({ url: `${baseUrlSegment}/${videoUUID}/${segmentName}` }) - const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url }) - - expect(sha256(segmentBody)).to.equal(shaBody[segmentName]) -} - -async function checkResolutionsInMasterPlaylist (options: { - server: PeerTubeServer - playlistUrl: string - resolutions: number[] -}) { - const { server, playlistUrl, resolutions } = options - - const masterPlaylist = await server.streamingPlaylists.get({ url: playlistUrl }) - - for (const resolution of resolutions) { - const reg = new RegExp( - '#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',(FRAME-RATE=\\d+,)?CODECS="avc1.64001f,mp4a.40.2"' - ) - - expect(masterPlaylist).to.match(reg) - } -} - -export { - checkSegmentHash, - checkLiveSegmentHash, - checkResolutionsInMasterPlaylist -} diff --git a/shared/extra-utils/videos/videos-command.ts b/shared/extra-utils/videos/videos-command.ts deleted file mode 100644 index 8ea828b40..000000000 --- a/shared/extra-utils/videos/videos-command.ts +++ /dev/null @@ -1,679 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ - -import { expect } from 'chai' -import { createReadStream, stat } from 'fs-extra' -import got, { Response as GotResponse } from 'got' -import { omit } from 'lodash' -import validator from 'validator' -import { buildUUID } from '@shared/core-utils/uuid' -import { pick } from '@shared/core-utils' -import { - HttpStatusCode, - ResultList, - UserVideoRateType, - Video, - VideoCreate, - VideoCreateResult, - VideoDetails, - VideoFileMetadata, - VideoPrivacy, - VideosCommonQuery, - VideoTranscodingCreate -} from '@shared/models' -import { buildAbsoluteFixturePath, wait } from '../miscs' -import { unwrapBody } from '../requests' -import { waitJobs } from '../server' -import { AbstractCommand, OverrideCommandOptions } from '../shared' - -export type VideoEdit = Partial> & { - fixture?: string - thumbnailfile?: string - previewfile?: string -} - -export class VideosCommand extends AbstractCommand { - getCategories (options: OverrideCommandOptions = {}) { - const path = '/api/v1/videos/categories' - - return this.getRequestBody<{ [id: number]: string }>({ - ...options, - path, - - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getLicences (options: OverrideCommandOptions = {}) { - const path = '/api/v1/videos/licences' - - return this.getRequestBody<{ [id: number]: string }>({ - ...options, - path, - - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getLanguages (options: OverrideCommandOptions = {}) { - const path = '/api/v1/videos/languages' - - return this.getRequestBody<{ [id: string]: string }>({ - ...options, - path, - - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getPrivacies (options: OverrideCommandOptions = {}) { - const path = '/api/v1/videos/privacies' - - return this.getRequestBody<{ [id in VideoPrivacy]: string }>({ - ...options, - path, - - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - // --------------------------------------------------------------------------- - - getDescription (options: OverrideCommandOptions & { - descriptionPath: string - }) { - return this.getRequestBody<{ description: string }>({ - ...options, - path: options.descriptionPath, - - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getFileMetadata (options: OverrideCommandOptions & { - url: string - }) { - return unwrapBody(this.getRawRequest({ - ...options, - - url: options.url, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - })) - } - - // --------------------------------------------------------------------------- - - view (options: OverrideCommandOptions & { - id: number | string - xForwardedFor?: string - }) { - const { id, xForwardedFor } = options - const path = '/api/v1/videos/' + id + '/views' - - return this.postBodyRequest({ - ...options, - - path, - xForwardedFor, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - rate (options: OverrideCommandOptions & { - id: number | string - rating: UserVideoRateType - }) { - const { id, rating } = options - const path = '/api/v1/videos/' + id + '/rate' - - return this.putBodyRequest({ - ...options, - - path, - fields: { rating }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - // --------------------------------------------------------------------------- - - get (options: OverrideCommandOptions & { - id: number | string - }) { - const path = '/api/v1/videos/' + options.id - - return this.getRequestBody({ - ...options, - - path, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - getWithToken (options: OverrideCommandOptions & { - id: number | string - }) { - return this.get({ - ...options, - - token: this.buildCommonRequestToken({ ...options, implicitToken: true }) - }) - } - - async getId (options: OverrideCommandOptions & { - uuid: number | string - }) { - const { uuid } = options - - if (validator.isUUID('' + uuid) === false) return uuid as number - - const { id } = await this.get({ ...options, id: uuid }) - - return id - } - - async listFiles (options: OverrideCommandOptions & { - id: number | string - }) { - const video = await this.get(options) - - const files = video.files || [] - const hlsFiles = video.streamingPlaylists[0]?.files || [] - - return files.concat(hlsFiles) - } - - // --------------------------------------------------------------------------- - - listMyVideos (options: OverrideCommandOptions & { - start?: number - count?: number - sort?: string - search?: string - isLive?: boolean - channelId?: number - } = {}) { - const path = '/api/v1/users/me/videos' - - return this.getRequestBody>({ - ...options, - - path, - query: pick(options, [ 'start', 'count', 'sort', 'search', 'isLive', 'channelId' ]), - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - // --------------------------------------------------------------------------- - - list (options: OverrideCommandOptions & VideosCommonQuery = {}) { - const path = '/api/v1/videos' - - const query = this.buildListQuery(options) - - return this.getRequestBody>({ - ...options, - - path, - query: { sort: 'name', ...query }, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listWithToken (options: OverrideCommandOptions & VideosCommonQuery = {}) { - return this.list({ - ...options, - - token: this.buildCommonRequestToken({ ...options, implicitToken: true }) - }) - } - - listByAccount (options: OverrideCommandOptions & VideosCommonQuery & { - handle: string - }) { - const { handle, search } = options - const path = '/api/v1/accounts/' + handle + '/videos' - - return this.getRequestBody>({ - ...options, - - path, - query: { search, ...this.buildListQuery(options) }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - listByChannel (options: OverrideCommandOptions & VideosCommonQuery & { - handle: string - }) { - const { handle } = options - const path = '/api/v1/video-channels/' + handle + '/videos' - - return this.getRequestBody>({ - ...options, - - path, - query: this.buildListQuery(options), - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - }) - } - - // --------------------------------------------------------------------------- - - async find (options: OverrideCommandOptions & { - name: string - }) { - const { data } = await this.list(options) - - return data.find(v => v.name === options.name) - } - - // --------------------------------------------------------------------------- - - update (options: OverrideCommandOptions & { - id: number | string - attributes?: VideoEdit - }) { - const { id, attributes = {} } = options - const path = '/api/v1/videos/' + id - - // Upload request - if (attributes.thumbnailfile || attributes.previewfile) { - const attaches: any = {} - if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile - if (attributes.previewfile) attaches.previewfile = attributes.previewfile - - return this.putUploadRequest({ - ...options, - - path, - fields: options.attributes, - attaches: { - thumbnailfile: attributes.thumbnailfile, - previewfile: attributes.previewfile - }, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - return this.putBodyRequest({ - ...options, - - path, - fields: options.attributes, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - remove (options: OverrideCommandOptions & { - id: number | string - }) { - const path = '/api/v1/videos/' + options.id - - return unwrapBody(this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - })) - } - - async removeAll () { - const { data } = await this.list() - - for (const v of data) { - await this.remove({ id: v.id }) - } - } - - // --------------------------------------------------------------------------- - - async upload (options: OverrideCommandOptions & { - attributes?: VideoEdit - mode?: 'legacy' | 'resumable' // default legacy - } = {}) { - const { mode = 'legacy' } = options - let defaultChannelId = 1 - - try { - const { videoChannels } = await this.server.users.getMyInfo({ token: options.token }) - defaultChannelId = videoChannels[0].id - } catch (e) { /* empty */ } - - // Override default attributes - const attributes = { - name: 'my super video', - category: 5, - licence: 4, - language: 'zh', - channelId: defaultChannelId, - nsfw: true, - waitTranscoding: false, - description: 'my super description', - support: 'my super support text', - tags: [ 'tag' ], - privacy: VideoPrivacy.PUBLIC, - commentsEnabled: true, - downloadEnabled: true, - fixture: 'video_short.webm', - - ...options.attributes - } - - const created = mode === 'legacy' - ? await this.buildLegacyUpload({ ...options, attributes }) - : await this.buildResumeUpload({ ...options, attributes }) - - // Wait torrent generation - const expectedStatus = this.buildExpectedStatus({ ...options, defaultExpectedStatus: HttpStatusCode.OK_200 }) - if (expectedStatus === HttpStatusCode.OK_200) { - let video: VideoDetails - - do { - video = await this.getWithToken({ ...options, id: created.uuid }) - - await wait(50) - } while (!video.files[0].torrentUrl) - } - - return created - } - - async buildLegacyUpload (options: OverrideCommandOptions & { - attributes: VideoEdit - }): Promise { - const path = '/api/v1/videos/upload' - - return unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({ - ...options, - - path, - fields: this.buildUploadFields(options.attributes), - attaches: this.buildUploadAttaches(options.attributes), - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.OK_200 - })).then(body => body.video || body as any) - } - - async buildResumeUpload (options: OverrideCommandOptions & { - attributes: VideoEdit - }): Promise { - const { attributes, expectedStatus } = options - - let size = 0 - let videoFilePath: string - let mimetype = 'video/mp4' - - if (attributes.fixture) { - videoFilePath = buildAbsoluteFixturePath(attributes.fixture) - size = (await stat(videoFilePath)).size - - if (videoFilePath.endsWith('.mkv')) { - mimetype = 'video/x-matroska' - } else if (videoFilePath.endsWith('.webm')) { - mimetype = 'video/webm' - } - } - - // Do not check status automatically, we'll check it manually - const initializeSessionRes = await this.prepareResumableUpload({ ...options, expectedStatus: null, attributes, size, mimetype }) - const initStatus = initializeSessionRes.status - - if (videoFilePath && initStatus === HttpStatusCode.CREATED_201) { - const locationHeader = initializeSessionRes.header['location'] - expect(locationHeader).to.not.be.undefined - - const pathUploadId = locationHeader.split('?')[1] - - const result = await this.sendResumableChunks({ ...options, pathUploadId, videoFilePath, size }) - - if (result.statusCode === HttpStatusCode.OK_200) { - await this.endResumableUpload({ ...options, expectedStatus: HttpStatusCode.NO_CONTENT_204, pathUploadId }) - } - - return result.body?.video || result.body as any - } - - const expectedInitStatus = expectedStatus === HttpStatusCode.OK_200 - ? HttpStatusCode.CREATED_201 - : expectedStatus - - expect(initStatus).to.equal(expectedInitStatus) - - return initializeSessionRes.body.video || initializeSessionRes.body - } - - async prepareResumableUpload (options: OverrideCommandOptions & { - attributes: VideoEdit - size: number - mimetype: string - - originalName?: string - lastModified?: number - }) { - const { attributes, originalName, lastModified, size, mimetype } = options - - const path = '/api/v1/videos/upload-resumable' - - return this.postUploadRequest({ - ...options, - - path, - headers: { - 'X-Upload-Content-Type': mimetype, - 'X-Upload-Content-Length': size.toString() - }, - fields: { - filename: attributes.fixture, - originalName, - lastModified, - - ...this.buildUploadFields(options.attributes) - }, - - // Fixture will be sent later - attaches: this.buildUploadAttaches(omit(options.attributes, 'fixture')), - implicitToken: true, - - defaultExpectedStatus: null - }) - } - - sendResumableChunks (options: OverrideCommandOptions & { - pathUploadId: string - videoFilePath: string - size: number - contentLength?: number - contentRangeBuilder?: (start: number, chunk: any) => string - }) { - const { pathUploadId, videoFilePath, size, contentLength, contentRangeBuilder, expectedStatus = HttpStatusCode.OK_200 } = options - - const path = '/api/v1/videos/upload-resumable' - let start = 0 - - const token = this.buildCommonRequestToken({ ...options, implicitToken: true }) - const url = this.server.url - - const readable = createReadStream(videoFilePath, { highWaterMark: 8 * 1024 }) - return new Promise>((resolve, reject) => { - readable.on('data', async function onData (chunk) { - readable.pause() - - const headers = { - 'Authorization': 'Bearer ' + token, - 'Content-Type': 'application/octet-stream', - 'Content-Range': contentRangeBuilder - ? contentRangeBuilder(start, chunk) - : `bytes ${start}-${start + chunk.length - 1}/${size}`, - 'Content-Length': contentLength ? contentLength + '' : chunk.length + '' - } - - const res = await got<{ video: VideoCreateResult }>({ - url, - method: 'put', - headers, - path: path + '?' + pathUploadId, - body: chunk, - responseType: 'json', - throwHttpErrors: false - }) - - start += chunk.length - - if (res.statusCode === expectedStatus) { - return resolve(res) - } - - if (res.statusCode !== HttpStatusCode.PERMANENT_REDIRECT_308) { - readable.off('data', onData) - return reject(new Error('Incorrect transient behaviour sending intermediary chunks')) - } - - readable.resume() - }) - }) - } - - endResumableUpload (options: OverrideCommandOptions & { - pathUploadId: string - }) { - return this.deleteRequest({ - ...options, - - path: '/api/v1/videos/upload-resumable', - rawQuery: options.pathUploadId, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - quickUpload (options: OverrideCommandOptions & { - name: string - nsfw?: boolean - privacy?: VideoPrivacy - fixture?: string - }) { - const attributes: VideoEdit = { name: options.name } - if (options.nsfw) attributes.nsfw = options.nsfw - if (options.privacy) attributes.privacy = options.privacy - if (options.fixture) attributes.fixture = options.fixture - - return this.upload({ ...options, attributes }) - } - - async randomUpload (options: OverrideCommandOptions & { - wait?: boolean // default true - additionalParams?: VideoEdit & { prefixName?: string } - } = {}) { - const { wait = true, additionalParams } = options - const prefixName = additionalParams?.prefixName || '' - const name = prefixName + buildUUID() - - const attributes = { name, ...additionalParams } - - const result = await this.upload({ ...options, attributes }) - - if (wait) await waitJobs([ this.server ]) - - return { ...result, name } - } - - // --------------------------------------------------------------------------- - - removeHLSFiles (options: OverrideCommandOptions & { - videoId: number | string - }) { - const path = '/api/v1/videos/' + options.videoId + '/hls' - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - removeWebTorrentFiles (options: OverrideCommandOptions & { - videoId: number | string - }) { - const path = '/api/v1/videos/' + options.videoId + '/webtorrent' - - return this.deleteRequest({ - ...options, - - path, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - runTranscoding (options: OverrideCommandOptions & { - videoId: number | string - transcodingType: 'hls' | 'webtorrent' - }) { - const path = '/api/v1/videos/' + options.videoId + '/transcoding' - - const fields: VideoTranscodingCreate = pick(options, [ 'transcodingType' ]) - - return this.postBodyRequest({ - ...options, - - path, - fields, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - - // --------------------------------------------------------------------------- - - private buildListQuery (options: VideosCommonQuery) { - return pick(options, [ - 'start', - 'count', - 'sort', - 'nsfw', - 'isLive', - 'categoryOneOf', - 'licenceOneOf', - 'languageOneOf', - 'tagsOneOf', - 'tagsAllOf', - 'isLocal', - 'include', - 'skipCount' - ]) - } - - private buildUploadFields (attributes: VideoEdit) { - return omit(attributes, [ 'fixture', 'thumbnailfile', 'previewfile' ]) - } - - private buildUploadAttaches (attributes: VideoEdit) { - const attaches: { [ name: string ]: string } = {} - - for (const key of [ 'thumbnailfile', 'previewfile' ]) { - if (attributes[key]) attaches[key] = buildAbsoluteFixturePath(attributes[key]) - } - - if (attributes.fixture) attaches.videofile = buildAbsoluteFixturePath(attributes.fixture) - - return attaches - } -} diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts deleted file mode 100644 index 2c3464aa8..000000000 --- a/shared/extra-utils/videos/videos.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ - -import { expect } from 'chai' -import { pathExists, readdir } from 'fs-extra' -import { basename, join } from 'path' -import { HttpStatusCode, VideoCaption, VideoDetails } from '@shared/models' -import { waitJobs } from '../server' -import { PeerTubeServer } from '../server/server' -import { VideoEdit } from './videos-command' - -async function checkVideoFilesWereRemoved (options: { - server: PeerTubeServer - video: VideoDetails - captions?: VideoCaption[] - onlyVideoFiles?: boolean // default false -}) { - const { video, server, captions = [], onlyVideoFiles = false } = options - - const webtorrentFiles = video.files || [] - const hlsFiles = video.streamingPlaylists[0]?.files || [] - - const thumbnailName = basename(video.thumbnailPath) - const previewName = basename(video.previewPath) - - const torrentNames = webtorrentFiles.concat(hlsFiles).map(f => basename(f.torrentUrl)) - - const captionNames = captions.map(c => basename(c.captionPath)) - - const webtorrentFilenames = webtorrentFiles.map(f => basename(f.fileUrl)) - const hlsFilenames = hlsFiles.map(f => basename(f.fileUrl)) - - let directories: { [ directory: string ]: string[] } = { - videos: webtorrentFilenames, - redundancy: webtorrentFilenames, - [join('playlists', 'hls')]: hlsFilenames, - [join('redundancy', 'hls')]: hlsFilenames - } - - if (onlyVideoFiles !== true) { - directories = { - ...directories, - - thumbnails: [ thumbnailName ], - previews: [ previewName ], - torrents: torrentNames, - captions: captionNames - } - } - - for (const directory of Object.keys(directories)) { - const directoryPath = server.servers.buildDirectory(directory) - - const directoryExists = await pathExists(directoryPath) - if (directoryExists === false) continue - - const existingFiles = await readdir(directoryPath) - for (const existingFile of existingFiles) { - for (const shouldNotExist of directories[directory]) { - expect(existingFile, `File ${existingFile} should not exist in ${directoryPath}`).to.not.contain(shouldNotExist) - } - } - } -} - -async function saveVideoInServers (servers: PeerTubeServer[], uuid: string) { - for (const server of servers) { - server.store.videoDetails = await server.videos.get({ id: uuid }) - } -} - -function checkUploadVideoParam ( - server: PeerTubeServer, - token: string, - attributes: Partial, - expectedStatus = HttpStatusCode.OK_200, - mode: 'legacy' | 'resumable' = 'legacy' -) { - return mode === 'legacy' - ? server.videos.buildLegacyUpload({ token, attributes, expectedStatus }) - : server.videos.buildResumeUpload({ token, attributes, expectedStatus }) -} - -// serverNumber starts from 1 -async function uploadRandomVideoOnServers ( - servers: PeerTubeServer[], - serverNumber: number, - additionalParams?: VideoEdit & { prefixName?: string } -) { - const server = servers.find(s => s.serverNumber === serverNumber) - const res = await server.videos.randomUpload({ wait: false, additionalParams }) - - await waitJobs(servers) - - return res -} - -// --------------------------------------------------------------------------- - -export { - checkUploadVideoParam, - uploadRandomVideoOnServers, - checkVideoFilesWereRemoved, - saveVideoInServers -} -- cgit v1.2.3