mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-03 09:49:20 +02:00
Migrate eslint to v9
This commit is contained in:
parent
bad8ea2c2e
commit
034e1bf328
106 changed files with 2130 additions and 1445 deletions
151
.eslintrc.json
151
.eslintrc.json
|
@ -1,151 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "standard-with-typescript",
|
|
||||||
"root": true,
|
|
||||||
"rules": {
|
|
||||||
"eol-last": [
|
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"indent": "off",
|
|
||||||
"no-lone-blocks": "off",
|
|
||||||
"no-mixed-operators": "off",
|
|
||||||
"max-len": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"code": 140
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"array-bracket-spacing": [
|
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"quote-props": [
|
|
||||||
"error",
|
|
||||||
"consistent-as-needed"
|
|
||||||
],
|
|
||||||
"padded-blocks": "off",
|
|
||||||
"prefer-regex-literals": "off",
|
|
||||||
"no-async-promise-executor": "off",
|
|
||||||
"dot-notation": "off",
|
|
||||||
"promise/param-names": "off",
|
|
||||||
"import/first": "off",
|
|
||||||
"operator-linebreak": [
|
|
||||||
"error",
|
|
||||||
"after",
|
|
||||||
{
|
|
||||||
"overrides": {
|
|
||||||
"?": "before",
|
|
||||||
":": "before"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"quotes": "off",
|
|
||||||
|
|
||||||
"no-constant-binary-expression": "error",
|
|
||||||
|
|
||||||
"@typescript-eslint/indent": [
|
|
||||||
"error",
|
|
||||||
2,
|
|
||||||
{
|
|
||||||
"SwitchCase": 1,
|
|
||||||
"MemberExpression": "off",
|
|
||||||
// https://github.com/eslint/eslint/issues/15299
|
|
||||||
"ignoredNodes": ["PropertyDefinition", "TSTypeParameterInstantiation", "TSConditionalType *"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/consistent-type-assertions": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"assertionStyle": "as"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/array-type": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"default": "array"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/restrict-template-expressions": [
|
|
||||||
"off",
|
|
||||||
{
|
|
||||||
"allowNumber": "true"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-this-alias": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"allowDestructuring": true, // Allow `const { props, state } = this`; false by default
|
|
||||||
"allowedNames": ["self"] // Allow `const self = this`; `[]` by default
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
"@typescript-eslint/return-await": "off",
|
|
||||||
"@typescript-eslint/dot-notation": "off",
|
|
||||||
"@typescript-eslint/method-signature-style": "off",
|
|
||||||
"@typescript-eslint/no-base-to-string": "off",
|
|
||||||
"@typescript-eslint/quotes": [
|
|
||||||
"error",
|
|
||||||
"single",
|
|
||||||
{
|
|
||||||
"avoidEscape": true,
|
|
||||||
"allowTemplateLiterals": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"@typescript-eslint/explicit-function-return-type": "off",
|
|
||||||
"@typescript-eslint/promise-function-async": "off",
|
|
||||||
"@typescript-eslint/no-dynamic-delete": "off",
|
|
||||||
"@typescript-eslint/no-unnecessary-boolean-literal-compare": "off",
|
|
||||||
"@typescript-eslint/strict-boolean-expressions": "off",
|
|
||||||
"@typescript-eslint/consistent-type-definitions": "off",
|
|
||||||
"@typescript-eslint/no-misused-promises": "off",
|
|
||||||
"@typescript-eslint/no-namespace": "off",
|
|
||||||
"@typescript-eslint/no-empty-interface": "off",
|
|
||||||
"@typescript-eslint/no-extraneous-class": "off",
|
|
||||||
"@typescript-eslint/no-use-before-define": "off",
|
|
||||||
|
|
||||||
"require-await": "off",
|
|
||||||
"@typescript-eslint/require-await": "error",
|
|
||||||
|
|
||||||
// bugged but useful
|
|
||||||
"@typescript-eslint/restrict-plus-operands": "off",
|
|
||||||
|
|
||||||
// Requires strictNullChecks
|
|
||||||
"@typescript-eslint/prefer-nullish-coalescing": "off",
|
|
||||||
"@typescript-eslint/consistent-type-imports": "off",
|
|
||||||
"@typescript-eslint/consistent-indexed-object-style": "off",
|
|
||||||
"@typescript-eslint/no-confusing-void-expression": "off",
|
|
||||||
"@typescript-eslint/consistent-type-exports": "off",
|
|
||||||
"@typescript-eslint/key-spacing": "off",
|
|
||||||
|
|
||||||
"@typescript-eslint/no-unsafe-argument": "off",
|
|
||||||
|
|
||||||
"@typescript-eslint/ban-types": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"types": {
|
|
||||||
"{}": false,
|
|
||||||
"Function": false
|
|
||||||
},
|
|
||||||
"extendDefaults": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ignorePatterns": [
|
|
||||||
"node_modules",
|
|
||||||
"packages/tests/fixtures",
|
|
||||||
"apps/**/dist",
|
|
||||||
"packages/**/dist",
|
|
||||||
"server/dist",
|
|
||||||
"packages/types-generator/tests",
|
|
||||||
"*.js",
|
|
||||||
"/client",
|
|
||||||
"/dist"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"project": [
|
|
||||||
"./tsconfig.eslint.json"
|
|
||||||
],
|
|
||||||
"EXPERIMENTAL_useSourceOfProjectReferenceRedirect": true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,7 +16,6 @@ export function defineAuthProgram () {
|
||||||
.option('-p, --password <token>', 'Password')
|
.option('-p, --password <token>', 'Password')
|
||||||
.option('--default', 'add the entry as the new default')
|
.option('--default', 'add the entry as the new default')
|
||||||
.action(options => {
|
.action(options => {
|
||||||
/* eslint-disable no-import-assign */
|
|
||||||
prompt.override = options
|
prompt.override = options
|
||||||
prompt.start()
|
prompt.start()
|
||||||
prompt.get({
|
prompt.get({
|
||||||
|
@ -39,7 +38,6 @@ export function defineAuthProgram () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, async (_, result) => {
|
}, async (_, result) => {
|
||||||
|
|
||||||
// Check credentials
|
// Check credentials
|
||||||
try {
|
try {
|
||||||
// Strip out everything after the domain:port.
|
// Strip out everything after the domain:port.
|
||||||
|
@ -111,7 +109,9 @@ export function defineAuthProgram () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
program.addHelpText('after', '\n\n Examples:\n\n' +
|
program.addHelpText(
|
||||||
|
'after',
|
||||||
|
'\n\n Examples:\n\n' +
|
||||||
' $ peertube auth add -u https://peertube.cpy.re -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD"\n' +
|
' $ peertube auth add -u https://peertube.cpy.re -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD"\n' +
|
||||||
' $ peertube auth add -u https://peertube.cpy.re -U root\n' +
|
' $ peertube auth add -u https://peertube.cpy.re -U root\n' +
|
||||||
' $ peertube auth list\n' +
|
' $ peertube auth list\n' +
|
||||||
|
|
|
@ -127,7 +127,7 @@ async function buildVideoAttributesFromCommander (server: PeerTubeServer, option
|
||||||
waitTranscoding: true
|
waitTranscoding: true
|
||||||
}
|
}
|
||||||
|
|
||||||
const booleanAttributes: { [id in keyof typeof defaultBooleanAttributes]: boolean } | {} = {}
|
const booleanAttributes: { [id in keyof typeof defaultBooleanAttributes]: boolean } = {} as any
|
||||||
|
|
||||||
for (const key of Object.keys(defaultBooleanAttributes)) {
|
for (const key of Object.keys(defaultBooleanAttributes)) {
|
||||||
if (options[key] !== undefined) {
|
if (options[key] !== undefined) {
|
||||||
|
|
|
@ -72,8 +72,7 @@ function getRemoteObjectOrDie (
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
netrc: Netrc
|
netrc: Netrc
|
||||||
): { url: string, username: string, password: string } {
|
): { url: string, username: string, password: string } {
|
||||||
|
function exitIfNoOptions (optionNames: string[], errorPrefix = '') {
|
||||||
function exitIfNoOptions (optionNames: string[], errorPrefix: string = '') {
|
|
||||||
let exit = false
|
let exit = false
|
||||||
|
|
||||||
for (const key of optionNames) {
|
for (const key of optionNames) {
|
||||||
|
@ -184,11 +183,8 @@ export {
|
||||||
getRemoteObjectOrDie,
|
getRemoteObjectOrDie,
|
||||||
writeSettings,
|
writeSettings,
|
||||||
deleteSettings,
|
deleteSettings,
|
||||||
|
|
||||||
getServerCredentials,
|
getServerCredentials,
|
||||||
|
|
||||||
listOptions,
|
listOptions,
|
||||||
|
|
||||||
getAdminTokenOrDie,
|
getAdminTokenOrDie,
|
||||||
buildServer,
|
buildServer,
|
||||||
assignToken
|
assignToken
|
||||||
|
|
|
@ -1,181 +0,0 @@
|
||||||
{
|
|
||||||
"root": true,
|
|
||||||
"ignorePatterns": [
|
|
||||||
"projects/**/*",
|
|
||||||
"node_modules/",
|
|
||||||
"src/standalone/embed-player-api/dist"
|
|
||||||
],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": [
|
|
||||||
"*.ts"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"project": [
|
|
||||||
"tsconfig.eslint.json"
|
|
||||||
],
|
|
||||||
"EXPERIMENTAL_useSourceOfProjectReferenceRedirect": true,
|
|
||||||
"createDefaultProgram": false
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"../.eslintrc.json",
|
|
||||||
"plugin:@angular-eslint/recommended",
|
|
||||||
"plugin:@angular-eslint/template/process-inline-templates"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"jsdoc/newline-after-description": "off",
|
|
||||||
"jsdoc/check-alignment": "off",
|
|
||||||
"lines-between-class-members": "off",
|
|
||||||
"@typescript-eslint/lines-between-class-members": [ "off" ],
|
|
||||||
"arrow-body-style": "off",
|
|
||||||
"no-underscore-dangle": "off",
|
|
||||||
"n/no-callback-literal": "off",
|
|
||||||
"@angular-eslint/component-selector": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"type": [ "element", "attribute" ],
|
|
||||||
"prefix": "my",
|
|
||||||
"style": "kebab-case"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@angular-eslint/directive-selector": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"type": [ "element", "attribute" ],
|
|
||||||
"prefix": "my",
|
|
||||||
"style": "camelCase"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-this-alias": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"allowDestructuring": true,
|
|
||||||
"allowedNames": ["self", "player"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/prefer-readonly": "off",
|
|
||||||
"@angular-eslint/use-component-view-encapsulation": "error",
|
|
||||||
"prefer-arrow/prefer-arrow-functions": "off",
|
|
||||||
"@typescript-eslint/await-thenable": "error",
|
|
||||||
"@typescript-eslint/consistent-type-definitions": "off",
|
|
||||||
"@typescript-eslint/dot-notation": "off",
|
|
||||||
"@typescript-eslint/explicit-member-accessibility": [
|
|
||||||
"off",
|
|
||||||
{
|
|
||||||
"accessibility": "explicit"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/member-ordering": [
|
|
||||||
"off"
|
|
||||||
],
|
|
||||||
"@typescript-eslint/member-delimiter-style": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"multiline": {
|
|
||||||
"delimiter": "none",
|
|
||||||
"requireLast": true
|
|
||||||
},
|
|
||||||
"singleline": {
|
|
||||||
"delimiter": "comma",
|
|
||||||
"requireLast": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/prefer-for-of": "off",
|
|
||||||
"@typescript-eslint/no-empty-function": "error",
|
|
||||||
"@typescript-eslint/no-floating-promises": "off",
|
|
||||||
"@typescript-eslint/no-inferrable-types": "error",
|
|
||||||
"@typescript-eslint/no-shadow": [
|
|
||||||
"off",
|
|
||||||
{
|
|
||||||
"hoist": "all"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-unnecessary-qualifier": "error",
|
|
||||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
|
||||||
"@typescript-eslint/no-unused-expressions": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"allowTaggedTemplates": true,
|
|
||||||
"allowShortCircuit": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/quotes": [
|
|
||||||
"error",
|
|
||||||
"single",
|
|
||||||
{
|
|
||||||
"avoidEscape": true,
|
|
||||||
"allowTemplateLiterals": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/semi": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
],
|
|
||||||
"brace-style": [
|
|
||||||
"error",
|
|
||||||
"1tbs"
|
|
||||||
],
|
|
||||||
"comma-dangle": "error",
|
|
||||||
"curly": [
|
|
||||||
"error",
|
|
||||||
"multi-line"
|
|
||||||
],
|
|
||||||
"dot-notation": "off",
|
|
||||||
"no-useless-return": "off",
|
|
||||||
"indent": "off",
|
|
||||||
"no-bitwise": "off",
|
|
||||||
"no-console": "off",
|
|
||||||
"no-return-assign": "off",
|
|
||||||
"no-constant-condition": "error",
|
|
||||||
"no-control-regex": "error",
|
|
||||||
"no-duplicate-imports": "error",
|
|
||||||
"no-empty": "error",
|
|
||||||
"no-empty-function": [
|
|
||||||
"error",
|
|
||||||
{ "allow": [ "constructors" ] }
|
|
||||||
],
|
|
||||||
"no-invalid-regexp": "error",
|
|
||||||
"no-multiple-empty-lines": "error",
|
|
||||||
"no-redeclare": "error",
|
|
||||||
"no-regex-spaces": "error",
|
|
||||||
"no-return-await": "error",
|
|
||||||
"no-shadow": "off",
|
|
||||||
"no-unused-expressions": "error",
|
|
||||||
"semi": "error",
|
|
||||||
"space-before-function-paren": [
|
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"space-in-parens": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
],
|
|
||||||
"object-shorthand": [
|
|
||||||
"error",
|
|
||||||
"properties"
|
|
||||||
],
|
|
||||||
"quote-props": [
|
|
||||||
"error",
|
|
||||||
"consistent-as-needed"
|
|
||||||
],
|
|
||||||
"no-constant-binary-expression": "error",
|
|
||||||
"@typescript-eslint/unbound-method": [
|
|
||||||
"error",
|
|
||||||
{ "ignoreStatic": true }
|
|
||||||
],
|
|
||||||
"import/no-named-default": "off"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": [
|
|
||||||
"*.html"
|
|
||||||
],
|
|
||||||
"extends": [
|
|
||||||
"plugin:@angular-eslint/template/recommended",
|
|
||||||
"plugin:@angular-eslint/template/accessibility"
|
|
||||||
],
|
|
||||||
"rules": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -8,18 +8,18 @@ export class AdminRegistrationPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
async accept (username: string, moderationResponse: string) {
|
async accept (username: string, moderationResponse: string) {
|
||||||
const usernameEl = await $('*=' + username)
|
const usernameEl = $('*=' + username)
|
||||||
await usernameEl.waitForDisplayed()
|
await usernameEl.waitForDisplayed()
|
||||||
|
|
||||||
const tr = await findParentElement(usernameEl, async el => await el.getTagName() === 'tr')
|
const tr = await findParentElement(usernameEl, async el => await el.getTagName() === 'tr')
|
||||||
|
|
||||||
await tr.$('.action-cell .dropdown-root').click()
|
await tr.$('.action-cell .dropdown-root').click()
|
||||||
|
|
||||||
const accept = await $('span*=Accept this request')
|
const accept = $('span*=Accept this request')
|
||||||
await accept.waitForClickable()
|
await accept.waitForClickable()
|
||||||
await accept.click()
|
await accept.click()
|
||||||
|
|
||||||
const moderationResponseTextarea = await $('#moderationResponse')
|
const moderationResponseTextarea = $('#moderationResponse')
|
||||||
await moderationResponseTextarea.waitForDisplayed()
|
await moderationResponseTextarea.waitForDisplayed()
|
||||||
|
|
||||||
await moderationResponseTextarea.setValue(moderationResponse)
|
await moderationResponseTextarea.setValue(moderationResponse)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { getCheckbox } from '../utils'
|
||||||
|
|
||||||
export class AnonymousSettingsPage {
|
export class AnonymousSettingsPage {
|
||||||
async openSettings () {
|
async openSettings () {
|
||||||
const link = await $('my-header .settings-button')
|
const link = $('my-header .settings-button')
|
||||||
await link.waitForClickable()
|
await link.waitForClickable()
|
||||||
await link.click()
|
await link.click()
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ export class AnonymousSettingsPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
async closeSettings () {
|
async closeSettings () {
|
||||||
const closeModal = await $('.modal.show .modal-header > button')
|
const closeModal = $('.modal.show .modal-header > button')
|
||||||
await closeModal.waitForClickable()
|
await closeModal.waitForClickable()
|
||||||
await closeModal.click()
|
await closeModal.click()
|
||||||
|
|
||||||
|
|
|
@ -171,7 +171,7 @@ export class MyAccountPage {
|
||||||
await selectCustomSelect('videoChannelId', 'Main root channel')
|
await selectCustomSelect('videoChannelId', 'Main root channel')
|
||||||
await selectCustomSelect('privacy', privacy)
|
await selectCustomSelect('privacy', privacy)
|
||||||
|
|
||||||
const submit = await $('form input[type=submit]')
|
const submit = $('form input[type=submit]')
|
||||||
await submit.waitForClickable()
|
await submit.waitForClickable()
|
||||||
await submit.scrollIntoView()
|
await submit.scrollIntoView()
|
||||||
await submit.click()
|
await submit.click()
|
||||||
|
|
|
@ -22,9 +22,7 @@ export class PlayerPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
waitUntilPlayerWrapper () {
|
waitUntilPlayerWrapper () {
|
||||||
return browser.waitUntil(async () => {
|
return $('#placeholder-preview').waitForExist()
|
||||||
return !!(await $('#placeholder-preview'))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
waitUntilPlaying () {
|
waitUntilPlaying () {
|
||||||
|
@ -98,7 +96,7 @@ export class PlayerPage {
|
||||||
|
|
||||||
async fillEmbedVideoPassword (videoPassword: string) {
|
async fillEmbedVideoPassword (videoPassword: string) {
|
||||||
const videoPasswordInput = $('input#video-password-input')
|
const videoPasswordInput = $('input#video-password-input')
|
||||||
const confirmButton = await $('button#video-password-submit')
|
const confirmButton = $('button#video-password-submit')
|
||||||
|
|
||||||
await videoPasswordInput.clearValue()
|
await videoPasswordInput.clearValue()
|
||||||
await videoPasswordInput.setValue(videoPassword)
|
await videoPasswordInput.setValue(videoPassword)
|
||||||
|
|
|
@ -60,7 +60,7 @@ export class VideoListPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getVideosListName () {
|
async getVideosListName () {
|
||||||
const elems = await $$('.videos .video-miniature .video-name')
|
const elems = $$('.videos .video-miniature .video-name')
|
||||||
const texts = await elems.map(e => e.getText())
|
const texts = await elems.map(e => e.getText())
|
||||||
|
|
||||||
return texts.map(t => t.trim())
|
return texts.map(t => t.trim())
|
||||||
|
@ -93,7 +93,7 @@ export class VideoListPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getVideoMiniature (name: string) {
|
private async getVideoMiniature (name: string) {
|
||||||
const videoName = await $('.video-name=' + name)
|
const videoName = $('.video-name=' + name)
|
||||||
await videoName.waitForDisplayed()
|
await videoName.waitForDisplayed()
|
||||||
|
|
||||||
return findParentElement(videoName, async el => await el.getTagName() === 'my-video-miniature')
|
return findParentElement(videoName, async el => await el.getTagName() === 'my-video-miniature')
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { FIXTURE_URLS } from '../utils'
|
||||||
|
|
||||||
export class VideoPublishPage extends VideoManage {
|
export class VideoPublishPage extends VideoManage {
|
||||||
async navigateTo (tab?: 'Go live') {
|
async navigateTo (tab?: 'Go live') {
|
||||||
const publishButton = await $('.publish-button > a')
|
const publishButton = $('.publish-button > a')
|
||||||
|
|
||||||
await publishButton.waitForClickable()
|
await publishButton.waitForClickable()
|
||||||
await publishButton.click()
|
await publishButton.click()
|
||||||
|
@ -31,7 +31,7 @@ export class VideoPublishPage extends VideoManage {
|
||||||
|
|
||||||
await browser.pause(1000)
|
await browser.pause(1000)
|
||||||
|
|
||||||
const elem = await $(fileInputSelector)
|
const elem = $(fileInputSelector)
|
||||||
await elem.chooseFile(fileToUpload)
|
await elem.chooseFile(fileToUpload)
|
||||||
|
|
||||||
// Wait for the upload to finish
|
// Wait for the upload to finish
|
||||||
|
|
|
@ -108,12 +108,12 @@ export class VideoWatchPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fillVideoPassword (videoPassword: string) {
|
async fillVideoPassword (videoPassword: string) {
|
||||||
const videoPasswordInput = await $('input#confirmInput')
|
const videoPasswordInput = $('input#confirmInput')
|
||||||
await videoPasswordInput.waitForClickable()
|
await videoPasswordInput.waitForClickable()
|
||||||
await videoPasswordInput.clearValue()
|
await videoPasswordInput.clearValue()
|
||||||
await videoPasswordInput.setValue(videoPassword)
|
await videoPasswordInput.setValue(videoPassword)
|
||||||
|
|
||||||
const confirmButton = await $('input[value="Confirm"]')
|
const confirmButton = $('input[value="Confirm"]')
|
||||||
await confirmButton.waitForClickable()
|
await confirmButton.waitForClickable()
|
||||||
return confirmButton.click()
|
return confirmButton.click()
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ export class VideoWatchPage {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async like () {
|
async like () {
|
||||||
const likeButton = await $('.action-button-like')
|
const likeButton = $('.action-button-like')
|
||||||
const isActivated = (await likeButton.getAttribute('class')).includes('activated')
|
const isActivated = (await likeButton.getAttribute('class')).includes('activated')
|
||||||
|
|
||||||
let count: number
|
let count: number
|
||||||
|
@ -150,7 +150,7 @@ export class VideoWatchPage {
|
||||||
async clickOnManage () {
|
async clickOnManage () {
|
||||||
await this.clickOnMoreDropdownIcon()
|
await this.clickOnMoreDropdownIcon()
|
||||||
|
|
||||||
const items = await $$('.dropdown-menu.show .dropdown-item')
|
const items = $$('.dropdown-menu.show .dropdown-item')
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const content = await item.getText()
|
const content = await item.getText()
|
||||||
|
@ -205,36 +205,36 @@ export class VideoWatchPage {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async createThread (comment: string) {
|
async createThread (comment: string) {
|
||||||
const textarea = await $('my-video-comment-add textarea')
|
const textarea = $('my-video-comment-add textarea')
|
||||||
await textarea.waitForClickable()
|
await textarea.waitForClickable()
|
||||||
|
|
||||||
await textarea.setValue(comment)
|
await textarea.setValue(comment)
|
||||||
|
|
||||||
const confirmButton = await $('.comment-buttons .primary-button')
|
const confirmButton = $('.comment-buttons .primary-button')
|
||||||
await confirmButton.waitForClickable()
|
await confirmButton.waitForClickable()
|
||||||
await confirmButton.click()
|
await confirmButton.click()
|
||||||
|
|
||||||
const createdComment = await (await $('.comment-html p')).getText()
|
const createdComment = await $('.comment-html p').getText()
|
||||||
|
|
||||||
return expect(createdComment).toBe(comment)
|
return expect(createdComment).toBe(comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createReply (comment: string) {
|
async createReply (comment: string) {
|
||||||
const replyButton = await $('button.comment-action-reply')
|
const replyButton = $('button.comment-action-reply')
|
||||||
await replyButton.waitForClickable()
|
await replyButton.waitForClickable()
|
||||||
await replyButton.scrollIntoView({ block: 'center' })
|
await replyButton.scrollIntoView({ block: 'center' })
|
||||||
await replyButton.click()
|
await replyButton.click()
|
||||||
|
|
||||||
const textarea = await $('my-video-comment my-video-comment-add textarea')
|
const textarea = $('my-video-comment my-video-comment-add textarea')
|
||||||
await textarea.waitForClickable()
|
await textarea.waitForClickable()
|
||||||
await textarea.setValue(comment)
|
await textarea.setValue(comment)
|
||||||
|
|
||||||
const confirmButton = await $('my-video-comment .comment-buttons .primary-button')
|
const confirmButton = $('my-video-comment .comment-buttons .primary-button')
|
||||||
await confirmButton.waitForClickable()
|
await confirmButton.waitForClickable()
|
||||||
await replyButton.scrollIntoView({ block: 'center' })
|
await replyButton.scrollIntoView({ block: 'center' })
|
||||||
await confirmButton.click()
|
await confirmButton.click()
|
||||||
|
|
||||||
const createdComment = await $('.is-child .comment-html p')
|
const createdComment = $('.is-child .comment-html p')
|
||||||
await createdComment.waitForDisplayed()
|
await createdComment.waitForDisplayed()
|
||||||
|
|
||||||
return expect(await createdComment.getText()).toBe(comment)
|
return expect(await createdComment.getText()).toBe(comment)
|
||||||
|
|
|
@ -261,7 +261,7 @@ describe('NSFW', () => {
|
||||||
expect(await playerPage.getNSFWContentText()).toContain('This video contains sensitive content')
|
expect(await playerPage.getNSFWContentText()).toContain('This video contains sensitive content')
|
||||||
expect(await playerPage.hasPoster()).toBeTruthy()
|
expect(await playerPage.hasPoster()).toBeTruthy()
|
||||||
|
|
||||||
const moreButton = await playerPage.getMoreNSFWInfoButton()
|
const moreButton = playerPage.getMoreNSFWInfoButton()
|
||||||
expect(await moreButton.isDisplayed()).toBeTruthy()
|
expect(await moreButton.isDisplayed()).toBeTruthy()
|
||||||
|
|
||||||
await moreButton.click()
|
await moreButton.click()
|
||||||
|
@ -310,7 +310,7 @@ describe('NSFW', () => {
|
||||||
it('Should use a confirm modal when viewing the video and watch the video', async function () {
|
it('Should use a confirm modal when viewing the video and watch the video', async function () {
|
||||||
await go(videoUrl)
|
await go(videoUrl)
|
||||||
|
|
||||||
const confirmTitle = await videoWatchPage.getModalTitleEl()
|
const confirmTitle = videoWatchPage.getModalTitleEl()
|
||||||
await confirmTitle.waitForDisplayed()
|
await confirmTitle.waitForDisplayed()
|
||||||
expect(await confirmTitle.getText()).toContain('Mature or explicit content')
|
expect(await confirmTitle.getText()).toContain('Mature or explicit content')
|
||||||
|
|
||||||
|
|
201
client/eslint.config.mjs
Normal file
201
client/eslint.config.mjs
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||||
|
import love from 'eslint-config-love'
|
||||||
|
import stylistic from '@stylistic/eslint-plugin'
|
||||||
|
import angular from 'angular-eslint'
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
globalIgnores([
|
||||||
|
'**/node_modules/',
|
||||||
|
'**/dist',
|
||||||
|
'**/build',
|
||||||
|
'.angular'
|
||||||
|
]),
|
||||||
|
|
||||||
|
{
|
||||||
|
extends: [
|
||||||
|
love,
|
||||||
|
angular.configs.tsRecommended
|
||||||
|
],
|
||||||
|
|
||||||
|
processor: angular.processInlineTemplates,
|
||||||
|
|
||||||
|
plugins: {
|
||||||
|
'@stylistic': stylistic
|
||||||
|
},
|
||||||
|
|
||||||
|
files: [
|
||||||
|
'src/**/*.ts',
|
||||||
|
'e2e/**/*.ts'
|
||||||
|
],
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
'@angular-eslint/component-selector': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
'type': [ 'element', 'attribute' ],
|
||||||
|
'prefix': 'my',
|
||||||
|
'style': 'kebab-case'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'@angular-eslint/directive-selector': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
'type': [ 'element', 'attribute' ],
|
||||||
|
'prefix': 'my',
|
||||||
|
'style': 'camelCase'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'@angular-eslint/use-component-view-encapsulation': 'error',
|
||||||
|
|
||||||
|
'@typescript-eslint/prefer-readonly': 'off',
|
||||||
|
"n/no-callback-literal": "off",
|
||||||
|
|
||||||
|
'@stylistic/semi': [ 'error', 'never' ],
|
||||||
|
|
||||||
|
'eol-last': [ 'error', 'always' ],
|
||||||
|
'indent': 'off',
|
||||||
|
'no-lone-blocks': 'off',
|
||||||
|
'no-mixed-operators': 'off',
|
||||||
|
|
||||||
|
'max-len': [ 'error', {
|
||||||
|
code: 140
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'array-bracket-spacing': [ 'error', 'always' ],
|
||||||
|
'quote-props': [ 'error', 'consistent-as-needed' ],
|
||||||
|
'padded-blocks': 'off',
|
||||||
|
'no-async-promise-executor': 'off',
|
||||||
|
'dot-notation': 'off',
|
||||||
|
'promise/param-names': 'off',
|
||||||
|
'import/first': 'off',
|
||||||
|
|
||||||
|
'operator-linebreak': [ 'error', 'after', {
|
||||||
|
overrides: {
|
||||||
|
'?': 'before',
|
||||||
|
':': 'before'
|
||||||
|
}
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'@typescript-eslint/consistent-type-assertions': [ 'error', {
|
||||||
|
assertionStyle: 'as'
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'@typescript-eslint/array-type': [ 'error', {
|
||||||
|
default: 'array'
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'@typescript-eslint/restrict-template-expressions': [ 'off', {
|
||||||
|
allowNumber: 'true'
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'@typescript-eslint/no-this-alias': [ 'error', {
|
||||||
|
allowDestructuring: true,
|
||||||
|
allowedNames: [ 'self' ]
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'@typescript-eslint/return-await': 'off',
|
||||||
|
'@typescript-eslint/no-base-to-string': 'off',
|
||||||
|
'@typescript-eslint/quotes': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/promise-function-async': 'off',
|
||||||
|
'@typescript-eslint/no-dynamic-delete': 'off',
|
||||||
|
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off',
|
||||||
|
'@typescript-eslint/strict-boolean-expressions': 'off',
|
||||||
|
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||||
|
'@typescript-eslint/no-misused-promises': 'off',
|
||||||
|
'@typescript-eslint/no-namespace': 'off',
|
||||||
|
'@typescript-eslint/no-empty-interface': 'off',
|
||||||
|
'@typescript-eslint/no-extraneous-class': 'off',
|
||||||
|
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||||
|
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||||
|
'@typescript-eslint/restrict-plus-operands': 'off',
|
||||||
|
'@typescript-eslint/no-unnecessary-condition': 'off',
|
||||||
|
'@typescript-eslint/consistent-type-imports': 'off',
|
||||||
|
'no-implicit-globals': 'off',
|
||||||
|
'@typescript-eslint/no-confusing-void-expression': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'logical-assignment-operators': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-argument': 'off',
|
||||||
|
'@typescript-eslint/no-magic-numbers': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-call': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-type-assertion': 'off',
|
||||||
|
'@typescript-eslint/prefer-destructuring': 'off',
|
||||||
|
'promise/avoid-new': 'off',
|
||||||
|
'@typescript-eslint/class-methods-use-this': 'off',
|
||||||
|
'arrow-body-style': 'off',
|
||||||
|
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
|
||||||
|
'@typescript-eslint/consistent-type-exports': 'off',
|
||||||
|
'@typescript-eslint/init-declarations': 'off',
|
||||||
|
'no-console': 'off',
|
||||||
|
'@typescript-eslint/dot-notation': 'off',
|
||||||
|
'@typescript-eslint/method-signature-style': 'off',
|
||||||
|
'eslint-comments/require-description': 'off',
|
||||||
|
'max-lines': 'off',
|
||||||
|
'@typescript-eslint/no-misused-spread': 'off',
|
||||||
|
'consistent-this': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'prefer-regex-literals': 'off',
|
||||||
|
'@typescript-eslint/prefer-regexp-exec': 'off',
|
||||||
|
'@typescript-eslint/prefer-promise-reject-errors': 'off',
|
||||||
|
'@typescript-eslint/no-unnecessary-template-expression': 'off',
|
||||||
|
'@typescript-eslint/no-loop-func': 'off',
|
||||||
|
'@typescript-eslint/switch-exhaustiveness-check': 'off',
|
||||||
|
'@typescript-eslint/no-empty-object-type': 'off',
|
||||||
|
'@typescript-eslint/no-import-type-side-effects': 'off',
|
||||||
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
|
'no-useless-return': 'off',
|
||||||
|
'no-return-assign': 'off',
|
||||||
|
'@typescript-eslint/unbound-method': 'off',
|
||||||
|
'import/no-named-default': 'off',
|
||||||
|
|
||||||
|
"@typescript-eslint/no-deprecated": [ 'error', {
|
||||||
|
allow: [
|
||||||
|
{ from: 'package', package: 'video.js', name: 'options'}
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
|
||||||
|
// Can be interesting to enable
|
||||||
|
'@typescript-eslint/no-unsafe-return': 'off',
|
||||||
|
// Can be interesting to enable
|
||||||
|
'complexity': 'off',
|
||||||
|
// Interesting but has a bug with specific cases
|
||||||
|
'@typescript-eslint/no-unnecessary-type-parameters': 'off',
|
||||||
|
// TODO: enable
|
||||||
|
'@typescript-eslint/prefer-as-const': 'off',
|
||||||
|
// TODO: enable
|
||||||
|
'@typescript-eslint/max-params': 'off',
|
||||||
|
// TODO: enable
|
||||||
|
'@typescript-eslint/no-unsafe-function-type': 'off',
|
||||||
|
// TODO: enable
|
||||||
|
'@typescript-eslint/no-deprecated': 'off',
|
||||||
|
// TODO: enable
|
||||||
|
'@typescript-eslint/no-floating-promises': 'off',
|
||||||
|
|
||||||
|
// We use many nested callbacks in our tests
|
||||||
|
'max-nested-callbacks': 'off'
|
||||||
|
},
|
||||||
|
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
projectService: {
|
||||||
|
allowDefaultProject: [ 'src/standalone/build-tools/vite-utils.ts' ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
files: [ '**/*.html' ],
|
||||||
|
extends: [
|
||||||
|
angular.configs.templateRecommended,
|
||||||
|
angular.configs.templateAccessibility,
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
// TODO: enable
|
||||||
|
'@angular-eslint/template/button-has-type': 'off'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
|
@ -14,7 +14,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "npm run lint-ts && npm run lint-scss",
|
"lint": "npm run lint-ts && npm run lint-scss",
|
||||||
"lint-ts": "eslint --cache --ext .ts src/standalone/**/*.ts && npm run ng lint",
|
"lint-ts": "eslint",
|
||||||
"lint-scss": "stylelint 'src/**/*.scss'",
|
"lint-scss": "stylelint 'src/**/*.scss'",
|
||||||
"eslint": "eslint",
|
"eslint": "eslint",
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
|
@ -34,11 +34,6 @@
|
||||||
],
|
],
|
||||||
"typings": "*.d.ts",
|
"typings": "*.d.ts",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-eslint/builder": "^19.0.2",
|
|
||||||
"@angular-eslint/eslint-plugin": "^19.0.2",
|
|
||||||
"@angular-eslint/eslint-plugin-template": "^19.0.2",
|
|
||||||
"@angular-eslint/schematics": "^19.0.2",
|
|
||||||
"@angular-eslint/template-parser": "^19.0.2",
|
|
||||||
"@angular/animations": "^19.1.4",
|
"@angular/animations": "^19.1.4",
|
||||||
"@angular/build": "^19.1.5",
|
"@angular/build": "^19.1.5",
|
||||||
"@angular/cdk": "^19.1.2",
|
"@angular/cdk": "^19.1.2",
|
||||||
|
@ -73,8 +68,6 @@
|
||||||
"@types/sanitize-html": "2.11.0",
|
"@types/sanitize-html": "2.11.0",
|
||||||
"@types/sha.js": "^2.4.0",
|
"@types/sha.js": "^2.4.0",
|
||||||
"@types/video.js": "^7.3.40",
|
"@types/video.js": "^7.3.40",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
|
||||||
"@typescript-eslint/parser": "^7.0.2",
|
|
||||||
"@wdio/browserstack-service": "^9.12.7",
|
"@wdio/browserstack-service": "^9.12.7",
|
||||||
"@wdio/cli": "^9.12.7",
|
"@wdio/cli": "^9.12.7",
|
||||||
"@wdio/local-runner": "^9.12.7",
|
"@wdio/local-runner": "^9.12.7",
|
||||||
|
@ -90,10 +83,6 @@
|
||||||
"core-js": "^3.22.8",
|
"core-js": "^3.22.8",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"dompurify": "^3.1.6",
|
"dompurify": "^3.1.6",
|
||||||
"eslint": "^8.28.0",
|
|
||||||
"eslint-plugin-import": "2.29.1",
|
|
||||||
"eslint-plugin-jsdoc": "^48.1.0",
|
|
||||||
"eslint-plugin-prefer-arrow": "latest",
|
|
||||||
"expect-webdriverio": "^4.2.3",
|
"expect-webdriverio": "^4.2.3",
|
||||||
"hls.js": "~1.5.11",
|
"hls.js": "~1.5.11",
|
||||||
"intl-messageformat": "^10.1.0",
|
"intl-messageformat": "^10.1.0",
|
||||||
|
@ -126,5 +115,10 @@
|
||||||
"vite-plugin-node-polyfills": "^0.23.0",
|
"vite-plugin-node-polyfills": "^0.23.0",
|
||||||
"zone.js": "~0.15.0"
|
"zone.js": "~0.15.0"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"dependencies": {
|
||||||
|
"@stylistic/eslint-plugin": "^4.2.0",
|
||||||
|
"angular-eslint": "^19.3.0",
|
||||||
|
"eslint": "^9.26.0",
|
||||||
|
"eslint-config-love": "^119.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
<div *ngIf="formErrors.fromName" class="form-error" role="alert">{{ formErrors.fromName }}</div>
|
<div *ngIf="formErrors.fromName" class="form-error" role="alert">{{ formErrors.fromName }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<button>toto</button>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n for="fromEmail">Your email</label>
|
<label i18n for="fromEmail">Your email</label>
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -194,8 +194,8 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
|
||||||
.pipe(pairwise())
|
.pipe(pairwise())
|
||||||
.subscribe(([ oldValue, newValue ]) => {
|
.subscribe(([ oldValue, newValue ]) => {
|
||||||
if (oldValue === false && newValue === true) {
|
if (oldValue === false && newValue === true) {
|
||||||
/* eslint-disable max-len */
|
|
||||||
this.signupAlertMessage =
|
this.signupAlertMessage =
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
$localize`You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below.`
|
$localize`You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below.`
|
||||||
|
|
||||||
this.form().patchValue({
|
this.form().patchValue({
|
||||||
|
|
|
@ -122,7 +122,6 @@ export class EditVODTranscodingComponent implements OnInit, OnChanges {
|
||||||
if (newValue === false && hlsControl.value === false) {
|
if (newValue === false && hlsControl.value === false) {
|
||||||
hlsControl.setValue(true)
|
hlsControl.setValue(true)
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
this.notifier.info(
|
this.notifier.info(
|
||||||
$localize`Automatically enable HLS transcoding because at least 1 output format must be enabled when transcoding is enabled`,
|
$localize`Automatically enable HLS transcoding because at least 1 output format must be enabled when transcoding is enabled`,
|
||||||
'',
|
'',
|
||||||
|
|
|
@ -79,7 +79,6 @@ export class FollowersListComponent extends RestTable<ActorFollow> implements On
|
||||||
this.followService.acceptFollower(follows)
|
this.followService.acceptFollower(follows)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
const message = formatICU(
|
const message = formatICU(
|
||||||
$localize`Accepted {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`,
|
$localize`Accepted {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`,
|
||||||
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) }
|
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) }
|
||||||
|
@ -94,7 +93,6 @@ export class FollowersListComponent extends RestTable<ActorFollow> implements On
|
||||||
}
|
}
|
||||||
|
|
||||||
async rejectFollower (follows: ActorFollow[]) {
|
async rejectFollower (follows: ActorFollow[]) {
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
const message = formatICU(
|
const message = formatICU(
|
||||||
$localize`Do you really want to reject {count, plural, =1 {{followerName} follow request?} other {{count} follow requests?}}`,
|
$localize`Do you really want to reject {count, plural, =1 {{followerName} follow request?} other {{count} follow requests?}}`,
|
||||||
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) }
|
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) }
|
||||||
|
@ -106,7 +104,6 @@ export class FollowersListComponent extends RestTable<ActorFollow> implements On
|
||||||
this.followService.rejectFollower(follows)
|
this.followService.rejectFollower(follows)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
const message = formatICU(
|
const message = formatICU(
|
||||||
$localize`Rejected {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`,
|
$localize`Rejected {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`,
|
||||||
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) }
|
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) }
|
||||||
|
@ -126,7 +123,6 @@ export class FollowersListComponent extends RestTable<ActorFollow> implements On
|
||||||
let message = $localize`Deleted followers will be able to send again a follow request.`
|
let message = $localize`Deleted followers will be able to send again a follow request.`
|
||||||
message += '<br /><br />'
|
message += '<br /><br />'
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
message += formatICU(
|
message += formatICU(
|
||||||
$localize`Do you really want to delete {count, plural, =1 {{followerName} follow request?} other {{count} follow requests?}}`,
|
$localize`Do you really want to delete {count, plural, =1 {{followerName} follow request?} other {{count} follow requests?}}`,
|
||||||
icuParams
|
icuParams
|
||||||
|
@ -138,7 +134,6 @@ export class FollowersListComponent extends RestTable<ActorFollow> implements On
|
||||||
this.followService.removeFollower(follows)
|
this.followService.removeFollower(follows)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
const message = formatICU(
|
const message = formatICU(
|
||||||
$localize`Removed {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`,
|
$localize`Removed {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`,
|
||||||
icuParams
|
icuParams
|
||||||
|
|
|
@ -93,7 +93,6 @@ export class FollowingListComponent extends RestTable<ActorFollow> implements On
|
||||||
this.followService.unfollow(follows)
|
this.followService.unfollow(follows)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
const message = formatICU(
|
const message = formatICU(
|
||||||
$localize`You are not following {count, plural, =1 {{entryName} anymore.} other {these {count} entries anymore.}}`,
|
$localize`You are not following {count, plural, =1 {{entryName} anymore.} other {these {count} entries anymore.}}`,
|
||||||
icuParams
|
icuParams
|
||||||
|
|
|
@ -143,7 +143,6 @@ export class RegistrationListComponent extends RestTable<UserRegistration> imple
|
||||||
private async removeRegistrations (registrations: UserRegistration[]) {
|
private async removeRegistrations (registrations: UserRegistration[]) {
|
||||||
const icuParams = { count: registrations.length, username: registrations[0].username }
|
const icuParams = { count: registrations.length, username: registrations[0].username }
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
const message = formatICU(
|
const message = formatICU(
|
||||||
$localize`Do you really want to delete {count, plural, =1 {{username} registration request?} other {{count} registration requests?}}`,
|
$localize`Do you really want to delete {count, plural, =1 {{username} registration request?} other {{count} registration requests?}}`,
|
||||||
icuParams
|
icuParams
|
||||||
|
@ -155,7 +154,6 @@ export class RegistrationListComponent extends RestTable<UserRegistration> imple
|
||||||
this.adminRegistrationService.removeRegistrations(registrations)
|
this.adminRegistrationService.removeRegistrations(registrations)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
const message = formatICU(
|
const message = formatICU(
|
||||||
$localize`Removed {count, plural, =1 {{username} registration request} other {{count} registration requests}}`,
|
$localize`Removed {count, plural, =1 {{username} registration request} other {{count} registration requests}}`,
|
||||||
icuParams
|
icuParams
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { SelectOptionsItem } from '../../../../../types/select-options-item.mode
|
||||||
import { FormReactive } from '@app/shared/shared-forms/form-reactive'
|
import { FormReactive } from '@app/shared/shared-forms/form-reactive'
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
|
||||||
export abstract class UserEdit extends FormReactive implements OnInit {
|
export abstract class UserEdit extends FormReactive implements OnInit {
|
||||||
videoQuotaOptions: SelectOptionsItem[] = []
|
videoQuotaOptions: SelectOptionsItem[] = []
|
||||||
videoQuotaDailyOptions: SelectOptionsItem[] = []
|
videoQuotaDailyOptions: SelectOptionsItem[] = []
|
||||||
|
|
|
@ -372,13 +372,11 @@ export class VideoListComponent extends RestTable<Video> implements OnInit {
|
||||||
let message: string
|
let message: string
|
||||||
|
|
||||||
if (type === 'hls') {
|
if (type === 'hls') {
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
message = formatICU(
|
message = formatICU(
|
||||||
$localize`Are you sure you want to delete {count, plural, =1 {1 HLS streaming playlist} other {{count} HLS streaming playlists}}?`,
|
$localize`Are you sure you want to delete {count, plural, =1 {1 HLS streaming playlist} other {{count} HLS streaming playlists}}?`,
|
||||||
{ count: videos.length }
|
{ count: videos.length }
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
message = formatICU(
|
message = formatICU(
|
||||||
$localize`Are you sure you want to delete Web Video files of {count, plural, =1 {1 video} other {{count} videos}}?`,
|
$localize`Are you sure you want to delete Web Video files of {count, plural, =1 {1 video} other {{count} videos}}?`,
|
||||||
{ count: videos.length }
|
{ count: videos.length }
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { Component, ViewEncapsulation } from '@angular/core'
|
import { Component, ViewEncapsulation } from '@angular/core'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allows to lazy load global player styles in the watch component
|
* Allows to lazy load global player styles in the watch component
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-player-styles',
|
selector: 'my-player-styles',
|
||||||
template: '',
|
template: '',
|
||||||
styleUrls: [ './player-styles.component.scss' ],
|
styleUrls: [ './player-styles.component.scss' ],
|
||||||
/* eslint-disable @angular-eslint/use-component-view-encapsulation */
|
// eslint-disable-next-line @angular-eslint/use-component-view-encapsulation
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
standalone: true
|
standalone: true
|
||||||
})
|
})
|
||||||
|
|
|
@ -179,7 +179,6 @@ export class VideoPublishComponent implements OnInit, CanComponentDeactivate {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async buildUploadMessages () {
|
private async buildUploadMessages () {
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
const noQuota =
|
const noQuota =
|
||||||
$localize`Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.`
|
$localize`Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.`
|
||||||
const autoBlock =
|
const autoBlock =
|
||||||
|
@ -188,7 +187,6 @@ export class VideoPublishComponent implements OnInit, CanComponentDeactivate {
|
||||||
const quotaLeftDaily =
|
const quotaLeftDaily =
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
$localize`Your daily video quota is insufficient. If you want to add more videos, you must wait for 24 hours or an admin must increase your daily quota.`
|
$localize`Your daily video quota is insufficient. If you want to add more videos, you must wait for 24 hours or an admin must increase your daily quota.`
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
const quotaLeft = $localize`Your video quota is insufficient. If you want to add more videos, an admin must increase your quota.`
|
const quotaLeft = $localize`Your video quota is insufficient. If you want to add more videos, an admin must increase your quota.`
|
||||||
|
|
||||||
const uploadMessages = {
|
const uploadMessages = {
|
||||||
|
|
|
@ -74,7 +74,7 @@ type Form = {
|
||||||
previewfile: FormControl<Blob>
|
previewfile: FormControl<Blob>
|
||||||
support: FormControl<string>
|
support: FormControl<string>
|
||||||
schedulePublicationAt: FormControl<Date>
|
schedulePublicationAt: FormControl<Date>
|
||||||
pluginData: FormGroup<any>
|
pluginData: FormGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
|
@ -372,7 +372,6 @@ export class VideoManageController implements OnDestroy {
|
||||||
|
|
||||||
let blockedWarning = ''
|
let blockedWarning = ''
|
||||||
if (willBeBlocked) {
|
if (willBeBlocked) {
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
blockedWarning = ' ' +
|
blockedWarning = ' ' +
|
||||||
$localize`Your video will also be automatically blocked since video publication requires manual validation by moderators.`
|
$localize`Your video will also be automatically blocked since video publication requires manual validation by moderators.`
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,7 +256,6 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
// Inject JS
|
// Inject JS
|
||||||
if (this.serverConfig.instance.customizations.javascript) {
|
if (this.serverConfig.instance.customizations.javascript) {
|
||||||
try {
|
try {
|
||||||
/* eslint-disable no-eval */
|
|
||||||
window.eval(this.serverConfig.instance.customizations.javascript)
|
window.eval(this.serverConfig.instance.customizations.javascript)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Cannot eval custom JavaScript.', err)
|
logger.error('Cannot eval custom JavaScript.', err)
|
||||||
|
|
|
@ -36,7 +36,7 @@ export class Hotkey {
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public combo: string | string[],
|
public combo: string | string[],
|
||||||
public callback: (event: KeyboardEvent, combo: string) => any | boolean,
|
public callback: (event: KeyboardEvent, combo: string) => any,
|
||||||
public description?: string | Function
|
public description?: string | Function
|
||||||
) {
|
) {
|
||||||
this.combo = arrayify(combo)
|
this.combo = arrayify(combo)
|
||||||
|
|
|
@ -40,7 +40,6 @@ export class ActorBannerEditComponent implements OnInit {
|
||||||
this.maxBannerSize = config.banner.file.size.max
|
this.maxBannerSize = config.banner.file.size.max
|
||||||
this.bannerExtensions = config.banner.file.extensions.join(', ')
|
this.bannerExtensions = config.banner.file.extensions.join(', ')
|
||||||
|
|
||||||
/* eslint-disable max-len */
|
|
||||||
this.bannerFormat = $localize`ratio 6/1, recommended size: 1920x317, max size: ${
|
this.bannerFormat = $localize`ratio 6/1, recommended size: 1920x317, max size: ${
|
||||||
getBytes(this.maxBannerSize)
|
getBytes(this.maxBannerSize)
|
||||||
}, extensions: ${this.bannerExtensions}`
|
}, extensions: ${this.bannerExtensions}`
|
||||||
|
|
|
@ -39,6 +39,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
|
||||||
readonly placeholder = input($localize`Filter...`)
|
readonly placeholder = input($localize`Filter...`)
|
||||||
readonly inputId = input('table-filter')
|
readonly inputId = input('table-filter')
|
||||||
|
|
||||||
|
// eslint-disable-next-line @angular-eslint/no-output-native
|
||||||
readonly search = output<string>()
|
readonly search = output<string>()
|
||||||
|
|
||||||
searchValue: string
|
searchValue: string
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { SortMeta } from 'primeng/api'
|
|
||||||
import { Directive, OnInit, inject } from '@angular/core'
|
import { Directive, OnInit, inject } from '@angular/core'
|
||||||
import { Notifier, RestPagination, RestTable } from '@app/core'
|
import { Notifier, RestPagination, RestTable } from '@app/core'
|
||||||
|
import { SortMeta } from 'primeng/api'
|
||||||
import { AccountBlock } from './account-block.model'
|
import { AccountBlock } from './account-block.model'
|
||||||
import { BlocklistComponentType, BlocklistService } from './blocklist.service'
|
import { BlocklistComponentType, BlocklistService } from './blocklist.service'
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
|
||||||
export class GenericAccountBlocklistComponent extends RestTable implements OnInit {
|
export class GenericAccountBlocklistComponent extends RestTable implements OnInit {
|
||||||
private notifier = inject(Notifier)
|
private notifier = inject(Notifier)
|
||||||
private blocklistService = inject(BlocklistService)
|
private blocklistService = inject(BlocklistService)
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { SortMeta } from 'primeng/api'
|
|
||||||
import { Directive, OnInit, inject, viewChild } from '@angular/core'
|
import { Directive, OnInit, inject, viewChild } from '@angular/core'
|
||||||
import { Notifier, RestPagination, RestTable } from '@app/core'
|
import { Notifier, RestPagination, RestTable } from '@app/core'
|
||||||
import { BatchDomainsModalComponent } from '@app/shared/shared-moderation/batch-domains-modal.component'
|
import { BatchDomainsModalComponent } from '@app/shared/shared-moderation/batch-domains-modal.component'
|
||||||
import { ServerBlock } from '@peertube/peertube-models'
|
import { ServerBlock } from '@peertube/peertube-models'
|
||||||
|
import { SortMeta } from 'primeng/api'
|
||||||
import { BlocklistComponentType, BlocklistService } from './blocklist.service'
|
import { BlocklistComponentType, BlocklistService } from './blocklist.service'
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
|
||||||
export class GenericServerBlocklistComponent extends RestTable implements OnInit {
|
export class GenericServerBlocklistComponent extends RestTable implements OnInit {
|
||||||
protected notifier = inject(Notifier)
|
protected notifier = inject(Notifier)
|
||||||
protected blocklistService = inject(BlocklistService)
|
protected blocklistService = inject(BlocklistService)
|
||||||
|
|
|
@ -398,9 +398,7 @@ export class VideosListComponent implements OnInit, OnDestroy {
|
||||||
for (const video of this.videos) {
|
for (const video of this.videos) {
|
||||||
const publishedDate = video.publishedAt
|
const publishedDate = video.publishedAt
|
||||||
|
|
||||||
for (let i = 0; i < periods.length; i++) {
|
for (const period of periods) {
|
||||||
const period = periods[i]
|
|
||||||
|
|
||||||
if (currentGroupedDate <= period.value && period.validator(publishedDate)) {
|
if (currentGroupedDate <= period.value && period.validator(publishedDate)) {
|
||||||
if (currentGroupedDate !== period.value) {
|
if (currentGroupedDate !== period.value) {
|
||||||
if (period.value !== GroupDate.OLDER) onlyOlderPeriod = false
|
if (period.value !== GroupDate.OLDER) onlyOlderPeriod = false
|
||||||
|
|
|
@ -6,7 +6,7 @@ const dictionary: { max: number, type: string }[] = [
|
||||||
{ max: 1.125899906842624e15, type: 'TB' }
|
{ max: 1.125899906842624e15, type: 'TB' }
|
||||||
]
|
]
|
||||||
|
|
||||||
function getBytes (value: number, precision?: number | undefined): string | number {
|
function getBytes (value: number, precision?: number): string | number {
|
||||||
const format = dictionary.find(d => value < d.max) || dictionary[dictionary.length - 1]
|
const format = dictionary.find(d => value < d.max) || dictionary[dictionary.length - 1]
|
||||||
const calc = value / (format.max / 1024)
|
const calc = value / (format.max / 1024)
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,6 @@ class PluginsManager {
|
||||||
|
|
||||||
async runHook<T> (hookName: ClientHookName, resultArg?: T | Promise<T>, params?: any) {
|
async runHook<T> (hookName: ClientHookName, resultArg?: T | Promise<T>, params?: any) {
|
||||||
if (!this.hooks[hookName]) {
|
if (!this.hooks[hookName]) {
|
||||||
// eslint-disable-next-line no-return-await
|
|
||||||
return await resultArg
|
return await resultArg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ export class ThemeManager {
|
||||||
loadThemeStyle (name: string) {
|
loadThemeStyle (name: string) {
|
||||||
const links = document.getElementsByTagName('link')
|
const links = document.getElementsByTagName('link')
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
||||||
for (let i = 0; i < links.length; i++) {
|
for (let i = 0; i < links.length; i++) {
|
||||||
const link = links[i]
|
const link = links[i]
|
||||||
if (link.getAttribute('rel').includes('style') && link.getAttribute('title')) {
|
if (link.getAttribute('rel').includes('style') && link.getAttribute('title')) {
|
||||||
|
|
|
@ -57,7 +57,6 @@ class ContextMenuPlugin extends Plugin {
|
||||||
|
|
||||||
menu.on('dispose', () => {
|
menu.on('dispose', () => {
|
||||||
for (const event of [ 'click', 'tap' ]) {
|
for (const event of [ 'click', 'tap' ]) {
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
||||||
videojs.off(documentEl as Element, event, menu.dispose)
|
videojs.off(documentEl as Element, event, menu.dispose)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +83,6 @@ class ContextMenuPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const event of [ 'click', 'tap' ]) {
|
for (const event of [ 'click', 'tap' ]) {
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
||||||
videojs.on(documentEl as Element, event, menu.dispose)
|
videojs.on(documentEl as Element, event, menu.dispose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,15 @@ class ContextMenu extends Menu {
|
||||||
|
|
||||||
// Each menu component has its own `dispose` method that can be
|
// Each menu component has its own `dispose` method that can be
|
||||||
// safely bound and unbound to events while maintaining its context.
|
// safely bound and unbound to events while maintaining its context.
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
||||||
this.dispose = videojs.bind(this, this.dispose)
|
this.dispose = videojs.bind(this, this.dispose)
|
||||||
|
|
||||||
for (const c of options.content()) {
|
for (const c of options.content()) {
|
||||||
this.addItem(new ContextMenuItem(player, {
|
this.addItem(
|
||||||
|
new ContextMenuItem(player, {
|
||||||
label: c.label,
|
label: c.label,
|
||||||
listener: videojs.bind(player, c.listener)
|
listener: videojs.bind(player, c.listener)
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ const ClickableComponent = videojs.getComponent('ClickableComponent')
|
||||||
export class ProgressBarMarkerComponent extends ClickableComponent {
|
export class ProgressBarMarkerComponent extends ClickableComponent {
|
||||||
declare options_: ProgressBarMarkerComponentOptions & videojs.ComponentOptions
|
declare options_: ProgressBarMarkerComponentOptions & videojs.ComponentOptions
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
|
||||||
constructor (player: videojs.Player, options?: ProgressBarMarkerComponentOptions & videojs.ComponentOptions) {
|
constructor (player: videojs.Player, options?: ProgressBarMarkerComponentOptions & videojs.ComponentOptions) {
|
||||||
super(player, options)
|
super(player, options)
|
||||||
|
|
||||||
|
|
|
@ -240,11 +240,11 @@ class PeerTubeHotkeysPlugin extends Plugin {
|
||||||
const isNonLatin = key.charCodeAt(0) >= capitalHetaCode
|
const isNonLatin = key.charCodeAt(0) >= capitalHetaCode
|
||||||
|
|
||||||
if (isNonLatin) {
|
if (isNonLatin) {
|
||||||
if (code.indexOf('Key') === 0 && code.length === 4) { // i.e. 'KeyW'
|
if (code.startsWith('Key') && code.length === 4) { // i.e. 'KeyW'
|
||||||
return code.charAt(3)
|
return code.charAt(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (code.indexOf('Digit') === 0 && code.length === 6) { // i.e. 'Digit7'
|
if (code.startsWith('Digit') && code.length === 6) { // i.e. 'Digit7'
|
||||||
return code.charAt(5)
|
return code.charAt(5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
export * from './peertube-dock-component'
|
|
||||||
export * from './peertube-dock-plugin'
|
|
|
@ -11,7 +11,7 @@ import { omit } from '@peertube/peertube-core-utils'
|
||||||
const HlsWithP2P = HlsJsP2PEngine.injectMixin(Hlsjs)
|
const HlsWithP2P = HlsJsP2PEngine.injectMixin(Hlsjs)
|
||||||
|
|
||||||
type ErrorCounts = {
|
type ErrorCounts = {
|
||||||
[ type: string ]: number
|
[type: string]: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -35,10 +35,8 @@ const registerSourceHandler = function (vjs: typeof videojs) {
|
||||||
|
|
||||||
if (alreadyRegistered) return
|
if (alreadyRegistered) return
|
||||||
|
|
||||||
alreadyRegistered = true;
|
alreadyRegistered = true // FIXME: typings
|
||||||
|
;(html5 as any).registerSourceHandler({
|
||||||
// FIXME: typings
|
|
||||||
(html5 as any).registerSourceHandler({
|
|
||||||
canHandleSource: function (source: videojs.Tech.SourceObject) {
|
canHandleSource: function (source: videojs.Tech.SourceObject) {
|
||||||
const hlsTypeRE = /^application\/x-mpegURL|application\/vnd\.apple\.mpegurl$/i
|
const hlsTypeRE = /^application\/x-mpegURL|application\/vnd\.apple\.mpegurl$/i
|
||||||
const hlsExtRE = /\.m3u8/i
|
const hlsExtRE = /\.m3u8/i
|
||||||
|
@ -58,10 +56,8 @@ const registerSourceHandler = function (vjs: typeof videojs) {
|
||||||
|
|
||||||
return tech.hlsProvider
|
return tech.hlsProvider
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0) // FIXME: typings
|
||||||
|
;(vjs as any).Html5Hlsjs = Html5Hlsjs
|
||||||
// FIXME: typings
|
|
||||||
(vjs as any).Html5Hlsjs = Html5Hlsjs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -71,7 +67,6 @@ const registerSourceHandler = function (vjs: typeof videojs) {
|
||||||
const Plugin = videojs.getPlugin('plugin')
|
const Plugin = videojs.getPlugin('plugin')
|
||||||
|
|
||||||
class HLSJSConfigHandler extends Plugin {
|
class HLSJSConfigHandler extends Plugin {
|
||||||
|
|
||||||
constructor (player: videojs.Player, options: HlsjsConfigHandlerOptions) {
|
constructor (player: videojs.Player, options: HlsjsConfigHandlerOptions) {
|
||||||
super(player, options)
|
super(player, options)
|
||||||
|
|
||||||
|
@ -132,7 +127,7 @@ export class Html5Hlsjs {
|
||||||
|
|
||||||
private liveEnded = false
|
private liveEnded = false
|
||||||
|
|
||||||
private handlers: { [ id in 'play' | 'error' ]: EventListener } = {
|
private handlers: { [id in 'play' | 'error']: EventListener } = {
|
||||||
play: null,
|
play: null,
|
||||||
error: null
|
error: null
|
||||||
}
|
}
|
||||||
|
@ -143,8 +138,8 @@ export class Html5Hlsjs {
|
||||||
this.vjs = vjs
|
this.vjs = vjs
|
||||||
this.source = source
|
this.source = source
|
||||||
|
|
||||||
this.tech = tech;
|
this.tech = tech
|
||||||
(this.tech as any).name_ = 'Hlsjs'
|
;(this.tech as any).name_ = 'Hlsjs'
|
||||||
|
|
||||||
this.videoElement = tech.el() as HTMLVideoElement
|
this.videoElement = tech.el() as HTMLVideoElement
|
||||||
this.player = vjs((tech.options_ as any).playerId)
|
this.player = vjs((tech.options_ as any).playerId)
|
||||||
|
@ -222,7 +217,7 @@ export class Html5Hlsjs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleUnrecovarableError (error: any) {
|
private _handleUnrecoverableError (error: any) {
|
||||||
if (this.hls.levels.filter(l => l.id > -1).length > 1) {
|
if (this.hls.levels.filter(l => l.id > -1).length > 1) {
|
||||||
this._removeQuality(this.hls.loadLevel)
|
this._removeQuality(this.hls.loadLevel)
|
||||||
return
|
return
|
||||||
|
@ -255,7 +250,7 @@ export class Html5Hlsjs {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] > 2) {
|
if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] > 2) {
|
||||||
this._handleUnrecovarableError(error)
|
this._handleUnrecoverableError(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,9 +259,8 @@ export class Html5Hlsjs {
|
||||||
|
|
||||||
// We may have errors if the live ended because of a fast-restream in the same permanent live
|
// We may have errors if the live ended because of a fast-restream in the same permanent live
|
||||||
if (this.liveEnded) {
|
if (this.liveEnded) {
|
||||||
logger.info('Forcing end of live stream after a network error');
|
logger.info('Forcing end of live stream after a network error')
|
||||||
|
;(this.player as any)?.handleTechEnded_()
|
||||||
(this.player as any)?.handleTechEnded_()
|
|
||||||
this.hls?.stopLoad()
|
this.hls?.stopLoad()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -286,7 +280,7 @@ export class Html5Hlsjs {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this._handleUnrecovarableError(error)
|
this._handleUnrecoverableError(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onError (_event: any, data: ErrorData) {
|
private _onError (_event: any, data: ErrorData) {
|
||||||
|
@ -307,11 +301,14 @@ export class Html5Hlsjs {
|
||||||
if (data.type === Hlsjs.ErrorTypes.NETWORK_ERROR) {
|
if (data.type === Hlsjs.ErrorTypes.NETWORK_ERROR) {
|
||||||
error.code = 2
|
error.code = 2
|
||||||
this._handleNetworkError(error)
|
this._handleNetworkError(error)
|
||||||
} else if (data.fatal && data.type === Hlsjs.ErrorTypes.MEDIA_ERROR && data.details !== 'manifestIncompatibleCodecsError') {
|
} else if (
|
||||||
|
data.fatal && data.type === Hlsjs.ErrorTypes.MEDIA_ERROR &&
|
||||||
|
data.details !== Hlsjs.ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR
|
||||||
|
) {
|
||||||
error.code = 3
|
error.code = 3
|
||||||
this._handleMediaError(error)
|
this._handleMediaError(error)
|
||||||
} else if (data.fatal) {
|
} else if (data.fatal) {
|
||||||
this._handleUnrecovarableError(error)
|
this._handleUnrecoverableError(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,9 +144,9 @@ class SettingsButton extends Button {
|
||||||
}
|
}
|
||||||
|
|
||||||
showDialog () {
|
showDialog () {
|
||||||
this.player().peertube().onMenuOpened();
|
this.player().peertube().onMenuOpened()
|
||||||
|
|
||||||
(this.menu.el() as HTMLElement).style.opacity = '1'
|
;(this.menu.el() as HTMLElement).style.opacity = '1'
|
||||||
|
|
||||||
this.dialog.show()
|
this.dialog.show()
|
||||||
this.el().setAttribute('aria-expanded', 'true')
|
this.el().setAttribute('aria-expanded', 'true')
|
||||||
|
@ -162,8 +162,8 @@ class SettingsButton extends Button {
|
||||||
this.dialog.hide()
|
this.dialog.hide()
|
||||||
this.el().setAttribute('aria-expanded', 'false')
|
this.el().setAttribute('aria-expanded', 'false')
|
||||||
|
|
||||||
this.setDialogSize(this.getComponentSize(this.menu));
|
this.setDialogSize(this.getComponentSize(this.menu))
|
||||||
(this.menu.el() as HTMLElement).style.opacity = '1'
|
;(this.menu.el() as HTMLElement).style.opacity = '1'
|
||||||
this.resetChildren()
|
this.resetChildren()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +248,6 @@ class SettingsButton extends Button {
|
||||||
|
|
||||||
// Hide children to avoid sub menus stacking on top of each other
|
// Hide children to avoid sub menus stacking on top of each other
|
||||||
// or having multiple menus open
|
// or having multiple menus open
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
||||||
settingsMenuItem.on('click', videojs.bind(this, this.hideChildren))
|
settingsMenuItem.on('click', videojs.bind(this, this.hideChildren))
|
||||||
|
|
||||||
// Whether to add or remove selected class on the settings sub menu element
|
// Whether to add or remove selected class on the settings sub menu element
|
||||||
|
@ -273,7 +272,6 @@ class SettingsButton extends Button {
|
||||||
isInIframe () {
|
isInIframe () {
|
||||||
return window.self !== window.top
|
return window.self !== window.top
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.registerComponent('SettingsButton', SettingsButton)
|
Component.registerComponent('SettingsButton', SettingsButton)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"extends": "../../../tsconfig.json",
|
"extends": "../../../tsconfig.json",
|
||||||
"include": [
|
"include": [
|
||||||
"./index.ts"
|
"./src/**/*.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
/* eslint-disable @typescript-eslint/array-type */
|
|
||||||
import { FormArray, FormControl, FormGroup } from '@angular/forms'
|
|
||||||
|
|
||||||
type Unbox<T> = T extends Array<infer V> ? V : T
|
|
||||||
|
|
||||||
export type ModelFormGroup<T> = FormGroup<
|
|
||||||
{ [K in keyof T]: T[K] extends Array<any> ? FormArray<FormControl<Unbox<T[K]>>> : FormControl<T[K]> }
|
|
||||||
>
|
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"include": [
|
|
||||||
// adjust "includes" to what makes sense for you and your project
|
|
||||||
"src/**/*.ts",
|
|
||||||
"e2e/**/*.ts"
|
|
||||||
],
|
|
||||||
"references": [
|
|
||||||
{ "path": "../packages/core-utils" },
|
|
||||||
{ "path": "../packages/models" },
|
|
||||||
{ "path": "../packages/typescript-utils" }
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -51,11 +51,9 @@
|
||||||
{ "path": "../packages/models" },
|
{ "path": "../packages/models" },
|
||||||
{ "path": "../packages/typescript-utils" }
|
{ "path": "../packages/typescript-utils" }
|
||||||
],
|
],
|
||||||
"files": [
|
|
||||||
"src/polyfills.ts"
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"src/polyfills.ts",
|
"src/polyfills.ts",
|
||||||
|
"src/environments/*.ts",
|
||||||
"src/main*.ts",
|
"src/main*.ts",
|
||||||
"src/**/*.d.ts",
|
"src/**/*.d.ts",
|
||||||
"src/app/**/*.ts",
|
"src/app/**/*.ts",
|
||||||
|
|
1218
client/yarn.lock
1218
client/yarn.lock
File diff suppressed because it is too large
Load diff
161
eslint.config.mjs
Normal file
161
eslint.config.mjs
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||||
|
import love from 'eslint-config-love'
|
||||||
|
import stylistic from '@stylistic/eslint-plugin'
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
globalIgnores([
|
||||||
|
'**/node_modules/',
|
||||||
|
'node_modules',
|
||||||
|
'packages/tests/fixtures',
|
||||||
|
'apps/**/dist',
|
||||||
|
'packages/**/dist',
|
||||||
|
'server/dist',
|
||||||
|
'packages/types-generator',
|
||||||
|
'*.js',
|
||||||
|
'client',
|
||||||
|
'dist'
|
||||||
|
]),
|
||||||
|
|
||||||
|
{
|
||||||
|
extends: [ love ],
|
||||||
|
|
||||||
|
plugins: {
|
||||||
|
'@stylistic': stylistic
|
||||||
|
},
|
||||||
|
|
||||||
|
files: [
|
||||||
|
'server/**/*.ts',
|
||||||
|
'scripts/**/*.ts',
|
||||||
|
'packages/**/*.ts',
|
||||||
|
'apps/**/*.ts'
|
||||||
|
],
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
'@stylistic/semi': [ 'error', 'never' ],
|
||||||
|
|
||||||
|
'eol-last': [ 'error', 'always' ],
|
||||||
|
'indent': 'off',
|
||||||
|
'no-lone-blocks': 'off',
|
||||||
|
'no-mixed-operators': 'off',
|
||||||
|
|
||||||
|
'max-len': [ 'error', {
|
||||||
|
code: 140
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'array-bracket-spacing': [ 'error', 'always' ],
|
||||||
|
'quote-props': [ 'error', 'consistent-as-needed' ],
|
||||||
|
'padded-blocks': 'off',
|
||||||
|
'no-async-promise-executor': 'off',
|
||||||
|
'dot-notation': 'off',
|
||||||
|
'promise/param-names': 'off',
|
||||||
|
'import/first': 'off',
|
||||||
|
|
||||||
|
'operator-linebreak': [ 'error', 'after', {
|
||||||
|
overrides: {
|
||||||
|
'?': 'before',
|
||||||
|
':': 'before'
|
||||||
|
}
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'@typescript-eslint/consistent-type-assertions': [ 'error', {
|
||||||
|
assertionStyle: 'as'
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'@typescript-eslint/array-type': [ 'error', {
|
||||||
|
default: 'array'
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'@typescript-eslint/restrict-template-expressions': [ 'off', {
|
||||||
|
allowNumber: 'true'
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'@typescript-eslint/no-this-alias': [ 'error', {
|
||||||
|
allowDestructuring: true,
|
||||||
|
allowedNames: [ 'self' ]
|
||||||
|
} ],
|
||||||
|
|
||||||
|
'@typescript-eslint/return-await': 'off',
|
||||||
|
'@typescript-eslint/no-base-to-string': 'off',
|
||||||
|
'@typescript-eslint/quotes': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/promise-function-async': 'off',
|
||||||
|
'@typescript-eslint/no-dynamic-delete': 'off',
|
||||||
|
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off',
|
||||||
|
'@typescript-eslint/strict-boolean-expressions': 'off',
|
||||||
|
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||||
|
'@typescript-eslint/no-misused-promises': 'off',
|
||||||
|
'@typescript-eslint/no-namespace': 'off',
|
||||||
|
'@typescript-eslint/no-empty-interface': 'off',
|
||||||
|
'@typescript-eslint/no-extraneous-class': 'off',
|
||||||
|
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||||
|
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||||
|
'@typescript-eslint/restrict-plus-operands': 'off',
|
||||||
|
'@typescript-eslint/no-unnecessary-condition': 'off',
|
||||||
|
'@typescript-eslint/consistent-type-imports': 'off',
|
||||||
|
'no-implicit-globals': 'off',
|
||||||
|
'@typescript-eslint/no-confusing-void-expression': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'logical-assignment-operators': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-argument': 'off',
|
||||||
|
'@typescript-eslint/no-magic-numbers': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-call': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-type-assertion': 'off',
|
||||||
|
'@typescript-eslint/prefer-destructuring': 'off',
|
||||||
|
'promise/avoid-new': 'off',
|
||||||
|
'@typescript-eslint/class-methods-use-this': 'off',
|
||||||
|
'arrow-body-style': 'off',
|
||||||
|
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
|
||||||
|
'@typescript-eslint/consistent-type-exports': 'off',
|
||||||
|
'@typescript-eslint/init-declarations': 'off',
|
||||||
|
'no-console': 'off',
|
||||||
|
'@typescript-eslint/dot-notation': 'off',
|
||||||
|
'@typescript-eslint/method-signature-style': 'off',
|
||||||
|
'eslint-comments/require-description': 'off',
|
||||||
|
'max-lines': 'off',
|
||||||
|
'@typescript-eslint/no-misused-spread': 'off',
|
||||||
|
'consistent-this': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'prefer-regex-literals': 'off',
|
||||||
|
'@typescript-eslint/prefer-regexp-exec': 'off',
|
||||||
|
'@typescript-eslint/prefer-promise-reject-errors': 'off',
|
||||||
|
'@typescript-eslint/no-unnecessary-template-expression': 'off',
|
||||||
|
'@typescript-eslint/no-loop-func': 'off',
|
||||||
|
'@typescript-eslint/switch-exhaustiveness-check': 'off',
|
||||||
|
'@typescript-eslint/no-empty-object-type': 'off',
|
||||||
|
'@typescript-eslint/no-import-type-side-effects': 'off',
|
||||||
|
'@typescript-eslint/unbound-method': 'off',
|
||||||
|
|
||||||
|
"require-await": "off",
|
||||||
|
"@typescript-eslint/require-await": "error",
|
||||||
|
|
||||||
|
// Can be interesting to enable
|
||||||
|
'@typescript-eslint/no-unsafe-return': 'off',
|
||||||
|
// Can be interesting to enable
|
||||||
|
'complexity': 'off',
|
||||||
|
// Interesting but has a bug with specific cases
|
||||||
|
'@typescript-eslint/no-unnecessary-type-parameters': 'off',
|
||||||
|
// TODO: enable
|
||||||
|
'@typescript-eslint/prefer-as-const': 'off',
|
||||||
|
// TODO: enable
|
||||||
|
'@typescript-eslint/max-params': 'off',
|
||||||
|
// TODO: enable
|
||||||
|
'@typescript-eslint/no-unsafe-function-type': 'off',
|
||||||
|
|
||||||
|
// We use many nested callbacks in our tests
|
||||||
|
'max-nested-callbacks': 'off'
|
||||||
|
},
|
||||||
|
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
linterOptions: {
|
||||||
|
reportUnusedDisableDirectives: 'off'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
|
@ -198,6 +198,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@peertube/maildev": "^1.2.0",
|
"@peertube/maildev": "^1.2.0",
|
||||||
"@peertube/resolve-tspaths": "^0.8.14",
|
"@peertube/resolve-tspaths": "^0.8.14",
|
||||||
|
"@stylistic/eslint-plugin": "^4.2.0",
|
||||||
"@types/archiver": "^6.0.2",
|
"@types/archiver": "^6.0.2",
|
||||||
"@types/bcrypt": "^5.0.0",
|
"@types/bcrypt": "^5.0.0",
|
||||||
"@types/bencode": "^2.0.0",
|
"@types/bencode": "^2.0.0",
|
||||||
|
@ -230,7 +231,6 @@
|
||||||
"@types/webtorrent": "^0.109.0",
|
"@types/webtorrent": "^0.109.0",
|
||||||
"@types/ws": "^8.2.0",
|
"@types/ws": "^8.2.0",
|
||||||
"@types/yauzl": "^2.10.3",
|
"@types/yauzl": "^2.10.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
|
||||||
"autocannon": "^8.0.0",
|
"autocannon": "^8.0.0",
|
||||||
"chai": "^5.1.0",
|
"chai": "^5.1.0",
|
||||||
"chai-json-schema": "^1.5.0",
|
"chai-json-schema": "^1.5.0",
|
||||||
|
@ -238,11 +238,8 @@
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.1.2",
|
||||||
"depcheck": "^1.4.2",
|
"depcheck": "^1.4.2",
|
||||||
"esbuild": "^0.24.2",
|
"esbuild": "^0.24.2",
|
||||||
"eslint": "8.56.0",
|
"eslint": "^9.26.0",
|
||||||
"eslint-config-standard-with-typescript": "43.0.1",
|
"eslint-config-love": "^119.0.0",
|
||||||
"eslint-plugin-import": "^2.20.1",
|
|
||||||
"eslint-plugin-n": "^16.0.0",
|
|
||||||
"eslint-plugin-promise": "^6.0.0",
|
|
||||||
"fast-xml-parser": "^4.0.0-beta.8",
|
"fast-xml-parser": "^4.0.0-beta.8",
|
||||||
"jpeg-js": "^0.4.4",
|
"jpeg-js": "^0.4.4",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
|
|
|
@ -79,7 +79,7 @@ const I18N_LOCALE_ALIAS = {
|
||||||
'zh': 'zh-Hans-CN'
|
'zh': 'zh-Hans-CN'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const POSSIBLE_LOCALES = (Object.keys(I18N_LOCALES) as string[]).concat(Object.keys(I18N_LOCALE_ALIAS))
|
export const POSSIBLE_LOCALES = Object.keys(I18N_LOCALES).concat(Object.keys(I18N_LOCALE_ALIAS))
|
||||||
|
|
||||||
export function getDefaultLocale () {
|
export function getDefaultLocale () {
|
||||||
return 'en-US'
|
return 'en-US'
|
||||||
|
@ -89,7 +89,7 @@ export function isDefaultLocale (locale: string) {
|
||||||
return getCompleteLocale(locale) === getCompleteLocale(getDefaultLocale())
|
return getCompleteLocale(locale) === getCompleteLocale(getDefaultLocale())
|
||||||
}
|
}
|
||||||
|
|
||||||
export function peertubeTranslate (str: string, translations?: { [ id: string ]: string }) {
|
export function peertubeTranslate (str: string, translations?: { [id: string]: string }) {
|
||||||
if (!translations?.[str]) return str
|
if (!translations?.[str]) return str
|
||||||
|
|
||||||
return translations[str]
|
return translations[str]
|
||||||
|
|
|
@ -3,9 +3,7 @@ import { VideoResolution } from '@peertube/peertube-models'
|
||||||
import ffmpeg, { FfprobeData } from 'fluent-ffmpeg'
|
import ffmpeg, { FfprobeData } from 'fluent-ffmpeg'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Helpers to run ffprobe and extract data from the JSON output
|
* Helpers to run ffprobe and extract data from the JSON output
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function ffprobePromise (path: string) {
|
function ffprobePromise (path: string) {
|
||||||
|
@ -23,9 +21,45 @@ function ffprobePromise (path: string) {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const imageCodecs = new Set([
|
const imageCodecs = new Set([
|
||||||
'ansi', 'apng', 'bintext', 'bmp', 'brender_pix', 'dpx', 'exr', 'fits', 'gem', 'gif', 'jpeg2000', 'jpgls', 'mjpeg', 'mjpegb', 'msp2',
|
'ansi',
|
||||||
'pam', 'pbm', 'pcx', 'pfm', 'pgm', 'pgmyuv', 'pgx', 'photocd', 'pictor', 'png', 'ppm', 'psd', 'sgi', 'sunrast', 'svg', 'targa', 'tiff',
|
'apng',
|
||||||
'txd', 'webp', 'xbin', 'xbm', 'xface', 'xpm', 'xwd'
|
'bintext',
|
||||||
|
'bmp',
|
||||||
|
'brender_pix',
|
||||||
|
'dpx',
|
||||||
|
'exr',
|
||||||
|
'fits',
|
||||||
|
'gem',
|
||||||
|
'gif',
|
||||||
|
'jpeg2000',
|
||||||
|
'jpgls',
|
||||||
|
'mjpeg',
|
||||||
|
'mjpegb',
|
||||||
|
'msp2',
|
||||||
|
'pam',
|
||||||
|
'pbm',
|
||||||
|
'pcx',
|
||||||
|
'pfm',
|
||||||
|
'pgm',
|
||||||
|
'pgmyuv',
|
||||||
|
'pgx',
|
||||||
|
'photocd',
|
||||||
|
'pictor',
|
||||||
|
'png',
|
||||||
|
'ppm',
|
||||||
|
'psd',
|
||||||
|
'sgi',
|
||||||
|
'sunrast',
|
||||||
|
'svg',
|
||||||
|
'targa',
|
||||||
|
'tiff',
|
||||||
|
'txd',
|
||||||
|
'webp',
|
||||||
|
'xbin',
|
||||||
|
'xbm',
|
||||||
|
'xface',
|
||||||
|
'xpm',
|
||||||
|
'xwd'
|
||||||
])
|
])
|
||||||
|
|
||||||
async function isAudioFile (path: string, existingProbe?: FfprobeData) {
|
async function isAudioFile (path: string, existingProbe?: FfprobeData) {
|
||||||
|
@ -61,7 +95,7 @@ async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) {
|
||||||
return { absolutePath: data.format.filename }
|
return { absolutePath: data.format.filename }
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMaxAudioBitrate (type: 'aac' | 'mp3' | string, bitrate: number) {
|
function getMaxAudioBitrate (type: string, bitrate: number) {
|
||||||
const maxKBitrate = 384
|
const maxKBitrate = 384
|
||||||
const kToBits = (kbits: number) => kbits * 1000
|
const kToBits = (kbits: number) => kbits * 1000
|
||||||
|
|
||||||
|
@ -209,16 +243,13 @@ export {
|
||||||
ffprobePromise,
|
ffprobePromise,
|
||||||
getAudioStream,
|
getAudioStream,
|
||||||
getChaptersFromContainer,
|
getChaptersFromContainer,
|
||||||
|
|
||||||
getMaxAudioBitrate,
|
getMaxAudioBitrate,
|
||||||
|
|
||||||
getVideoStream,
|
getVideoStream,
|
||||||
getVideoStreamBitrate,
|
getVideoStreamBitrate,
|
||||||
getVideoStreamDimensionsInfo,
|
getVideoStreamDimensionsInfo,
|
||||||
getVideoStreamDuration,
|
getVideoStreamDuration,
|
||||||
getVideoStreamFPS,
|
getVideoStreamFPS,
|
||||||
hasAudioStream,
|
hasAudioStream,
|
||||||
|
|
||||||
hasVideoStream,
|
hasVideoStream,
|
||||||
isAudioFile
|
isAudioFile
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export function addDefaultEncoderGlobalParams (command: FfmpegCommand) {
|
||||||
|
|
||||||
export function addDefaultEncoderParams (options: {
|
export function addDefaultEncoderParams (options: {
|
||||||
command: FfmpegCommand
|
command: FfmpegCommand
|
||||||
encoder: 'libx264' | string
|
encoder: string
|
||||||
fps: number
|
fps: number
|
||||||
|
|
||||||
streamNum?: number
|
streamNum?: number
|
||||||
|
|
|
@ -2,6 +2,6 @@ import { ServerHookName } from './server-hook.model.js'
|
||||||
|
|
||||||
export interface RegisterServerHookOptions {
|
export interface RegisterServerHookOptions {
|
||||||
target: ServerHookName
|
target: ServerHookName
|
||||||
handler: Function
|
handler: () => any
|
||||||
priority?: number
|
priority?: number
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,5 +17,4 @@ export type VideoSortField =
|
||||||
// trending sorts
|
// trending sorts
|
||||||
'trending' | '-trending' |
|
'trending' | '-trending' |
|
||||||
'hot' | '-hot' |
|
'hot' | '-hot' |
|
||||||
'best' | '-best' |
|
|
||||||
'best' | '-best'
|
'best' | '-best'
|
||||||
|
|
|
@ -122,7 +122,7 @@ export function makeUploadRequest (
|
||||||
method?: 'POST' | 'PUT'
|
method?: 'POST' | 'PUT'
|
||||||
|
|
||||||
fields: { [fieldName: string]: any }
|
fields: { [fieldName: string]: any }
|
||||||
attaches?: { [attachName: string]: any | any[] }
|
attaches?: { [attachName: string]: any }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
let req = options.method === 'PUT'
|
let req = options.method === 'PUT'
|
||||||
|
|
|
@ -5,12 +5,14 @@ export function setDefaultVideoChannel (servers: PeerTubeServer[]) {
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
servers.map(s => {
|
servers.map(s => {
|
||||||
return s.users.getMyInfo()
|
return s.users.getMyInfo()
|
||||||
.then(user => { s.store.channel = user.videoChannels[0] })
|
.then(user => {
|
||||||
|
s.store.channel = user.videoChannels[0]
|
||||||
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setDefaultChannelAvatar (serversArg: PeerTubeServer | PeerTubeServer[], channelName: string = 'root_channel') {
|
export async function setDefaultChannelAvatar (serversArg: PeerTubeServer | PeerTubeServer[], channelName = 'root_channel') {
|
||||||
const servers = arrayify(serversArg)
|
const servers = arrayify(serversArg)
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
/* eslint-disable @typescript-eslint/indent */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||||
|
|
||||||
import { pick } from '@peertube/peertube-core-utils'
|
import { pick } from '@peertube/peertube-core-utils'
|
||||||
|
|
|
@ -170,7 +170,6 @@ export async function checkResolutionsInMasterPlaylist (options: {
|
||||||
|
|
||||||
if (splittedAudio && hasAudio && hasVideo) {
|
if (splittedAudio && hasAudio && hasVideo) {
|
||||||
expect(masterPlaylist).to.match(
|
expect(masterPlaylist).to.match(
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
new RegExp(
|
new RegExp(
|
||||||
`#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="(group_Audio|audio)",NAME="(Audio|audio_0)"(,AUTOSELECT=YES)?,DEFAULT=YES,URI="[^.]*0.m3u8"`
|
`#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="(group_Audio|audio)",NAME="(Audio|audio_0)"(,AUTOSELECT=YES)?,DEFAULT=YES,URI="[^.]*0.m3u8"`
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,6 +5,7 @@ export async function checkTrackerInfohash (serverUrl: string, infohash: string)
|
||||||
const path = '/tracker/announce'
|
const path = '/tracker/announce'
|
||||||
|
|
||||||
// From bittorrent-tracker
|
// From bittorrent-tracker
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
||||||
const infohashBinary = escape(Buffer.from(infohash, 'hex').toString('binary')).replace(/[@*/+]/g, function (char) {
|
const infohashBinary = escape(Buffer.from(infohash, 'hex').toString('binary')).replace(/[@*/+]/g, function (char) {
|
||||||
return '%' + char.charCodeAt(0).toString(16).toUpperCase()
|
return '%' + char.charCodeAt(0).toString(16).toUpperCase()
|
||||||
})
|
})
|
||||||
|
|
|
@ -39,7 +39,7 @@ export class TranscriberFactory {
|
||||||
getEngineByName (engineName: string) {
|
getEngineByName (engineName: string) {
|
||||||
const engine = this.engines.find(({ name }) => name === engineName)
|
const engine = this.engines.find(({ name }) => name === engineName)
|
||||||
if (!engine) {
|
if (!engine) {
|
||||||
throw new Error(`Unknow engine ${engineName}`)
|
throw new Error(`Unknown engine ${engineName}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return engine
|
return engine
|
||||||
|
|
|
@ -30,7 +30,8 @@ export class TranscriptFile {
|
||||||
const guessableFormats = [ 'txt', 'vtt', 'srt' ]
|
const guessableFormats = [ 'txt', 'vtt', 'srt' ]
|
||||||
assert(
|
assert(
|
||||||
guessableFormats.includes(format),
|
guessableFormats.includes(format),
|
||||||
`Couldn't guess transcript format from extension "${format}". Valid formats are: ${guessableFormats.join(', ')}."`)
|
`Couldn't guess transcript format from extension "${format}". Valid formats are: ${guessableFormats.join(', ')}."`
|
||||||
|
)
|
||||||
|
|
||||||
return new TranscriptFile({ path, language, format: format as TranscriptFormat })
|
return new TranscriptFile({ path, language, format: format as TranscriptFormat })
|
||||||
}
|
}
|
||||||
|
@ -49,7 +50,7 @@ export class TranscriptFile {
|
||||||
return new TranscriptFile({ path, language, format })
|
return new TranscriptFile({ path, language, format })
|
||||||
}
|
}
|
||||||
|
|
||||||
async equals (transcript: TranscriptFile, caseSensitive: boolean = true) {
|
async equals (transcript: TranscriptFile, caseSensitive = true) {
|
||||||
if (this.language !== transcript.language) {
|
if (this.language !== transcript.language) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,7 @@ elif [ "$1" = "external-plugins" ]; then
|
||||||
runJSTest "$1" 1 $externalPluginsFiles
|
runJSTest "$1" 1 $externalPluginsFiles
|
||||||
MOCHA_PARALLEL=true runJSTest "$1" $((2*$speedFactor)) $peertubeRunnerFiles
|
MOCHA_PARALLEL=true runJSTest "$1" $((2*$speedFactor)) $peertubeRunnerFiles
|
||||||
elif [ "$1" = "lint" ]; then
|
elif [ "$1" = "lint" ]; then
|
||||||
npm run eslint -- --ext .ts "server/**/*.ts" "scripts/**/*.ts" "packages/**/*.ts" "apps/**/*.ts"
|
npm run eslint
|
||||||
|
|
||||||
npm run swagger-cli -- validate support/doc/api/openapi.yaml
|
npm run swagger-cli -- validate support/doc/api/openapi.yaml
|
||||||
|
|
||||||
|
|
|
@ -16,28 +16,32 @@ const lTags = loggerTagsFactory('api', 'runner')
|
||||||
|
|
||||||
const runnerJobFilesRouter = express.Router()
|
const runnerJobFilesRouter = express.Router()
|
||||||
|
|
||||||
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/max-quality/audio',
|
runnerJobFilesRouter.post(
|
||||||
|
'/jobs/:jobUUID/files/videos/:videoId/max-quality/audio',
|
||||||
apiRateLimiter,
|
apiRateLimiter,
|
||||||
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
||||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||||
asyncMiddleware(getMaxQualitySeparatedAudioFile)
|
asyncMiddleware(getMaxQualitySeparatedAudioFile)
|
||||||
)
|
)
|
||||||
|
|
||||||
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/max-quality',
|
runnerJobFilesRouter.post(
|
||||||
|
'/jobs/:jobUUID/files/videos/:videoId/max-quality',
|
||||||
apiRateLimiter,
|
apiRateLimiter,
|
||||||
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
||||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||||
asyncMiddleware(getMaxQualityVideoFile)
|
asyncMiddleware(getMaxQualityVideoFile)
|
||||||
)
|
)
|
||||||
|
|
||||||
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/previews/max-quality',
|
runnerJobFilesRouter.post(
|
||||||
|
'/jobs/:jobUUID/files/videos/:videoId/previews/max-quality',
|
||||||
apiRateLimiter,
|
apiRateLimiter,
|
||||||
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
||||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||||
getMaxQualityVideoPreview
|
getMaxQualityVideoPreview
|
||||||
)
|
)
|
||||||
|
|
||||||
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/studio/task-files/:filename',
|
runnerJobFilesRouter.post(
|
||||||
|
'/jobs/:jobUUID/files/videos/:videoId/studio/task-files/:filename',
|
||||||
apiRateLimiter,
|
apiRateLimiter,
|
||||||
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
||||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||||
|
@ -59,7 +63,10 @@ async function getMaxQualitySeparatedAudioFile (req: express.Request, res: expre
|
||||||
const video = res.locals.videoAll
|
const video = res.locals.videoAll
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
'Get max quality separated audio file of video %s of job %s for runner %s', video.uuid, runnerJob.uuid, runner.name,
|
'Get max quality separated audio file of video %s of job %s for runner %s',
|
||||||
|
video.uuid,
|
||||||
|
runnerJob.uuid,
|
||||||
|
runner.name,
|
||||||
lTags(runner.name, runnerJob.id, runnerJob.type)
|
lTags(runner.name, runnerJob.id, runnerJob.type)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -74,7 +81,10 @@ async function getMaxQualityVideoFile (req: express.Request, res: express.Respon
|
||||||
const video = res.locals.videoAll
|
const video = res.locals.videoAll
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
'Get max quality file of video %s of job %s for runner %s', video.uuid, runnerJob.uuid, runner.name,
|
'Get max quality file of video %s of job %s for runner %s',
|
||||||
|
video.uuid,
|
||||||
|
runnerJob.uuid,
|
||||||
|
runner.name,
|
||||||
lTags(runner.name, runnerJob.id, runnerJob.type)
|
lTags(runner.name, runnerJob.id, runnerJob.type)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -124,7 +134,10 @@ function getMaxQualityVideoPreview (req: express.Request, res: express.Response)
|
||||||
const video = res.locals.videoAll
|
const video = res.locals.videoAll
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
'Get max quality preview file of video %s of job %s for runner %s', video.uuid, runnerJob.uuid, runner.name,
|
'Get max quality preview file of video %s of job %s for runner %s',
|
||||||
|
video.uuid,
|
||||||
|
runnerJob.uuid,
|
||||||
|
runner.name,
|
||||||
lTags(runner.name, runnerJob.id, runnerJob.type)
|
lTags(runner.name, runnerJob.id, runnerJob.type)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -140,7 +153,11 @@ function getVideoStudioTaskFile (req: express.Request, res: express.Response) {
|
||||||
const filename = req.params.filename
|
const filename = req.params.filename
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
'Get video studio task file %s of video %s of job %s for runner %s', filename, video.uuid, runnerJob.uuid, runner.name,
|
'Get video studio task file %s of video %s of job %s for runner %s',
|
||||||
|
filename,
|
||||||
|
video.uuid,
|
||||||
|
runnerJob.uuid,
|
||||||
|
runner.name,
|
||||||
lTags(runner.name, runnerJob.id, runnerJob.type)
|
lTags(runner.name, runnerJob.id, runnerJob.type)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ async function replaceVideoSourceResumable (req: express.Request, res: express.R
|
||||||
async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVideoFile) {
|
async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVideoFile) {
|
||||||
const jobs: (CreateJobArgument & CreateJobOptions)[] = [
|
const jobs: (CreateJobArgument & CreateJobOptions)[] = [
|
||||||
{
|
{
|
||||||
type: 'manage-video-torrent' as 'manage-video-torrent',
|
type: 'manage-video-torrent' as const,
|
||||||
payload: {
|
payload: {
|
||||||
videoId: video.id,
|
videoId: video.id,
|
||||||
videoFileId: videoFile.id,
|
videoFileId: videoFile.id,
|
||||||
|
@ -175,7 +175,7 @@ async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVide
|
||||||
buildStoryboardJobIfNeeded({ video, federate: false }),
|
buildStoryboardJobIfNeeded({ video, federate: false }),
|
||||||
|
|
||||||
{
|
{
|
||||||
type: 'federate-video' as 'federate-video',
|
type: 'federate-video' as const,
|
||||||
payload: {
|
payload: {
|
||||||
videoUUID: video.uuid,
|
videoUUID: video.uuid,
|
||||||
isNewVideoForFederation: false
|
isNewVideoForFederation: false
|
||||||
|
@ -189,7 +189,7 @@ async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVide
|
||||||
|
|
||||||
if (video.state === VideoState.TO_TRANSCODE) {
|
if (video.state === VideoState.TO_TRANSCODE) {
|
||||||
jobs.push({
|
jobs.push({
|
||||||
type: 'transcoding-job-builder' as 'transcoding-job-builder',
|
type: 'transcoding-job-builder' as const,
|
||||||
payload: {
|
payload: {
|
||||||
videoUUID: video.uuid,
|
videoUUID: video.uuid,
|
||||||
optimizeJob: {
|
optimizeJob: {
|
||||||
|
|
|
@ -73,9 +73,7 @@ lazyStaticRouter.use(
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
lazyStaticRouter,
|
lazyStaticRouter
|
||||||
getPreview,
|
|
||||||
getVideoCaption
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -33,9 +33,9 @@ async function getSitemap (req: express.Request, res: express.Response) {
|
||||||
const sitemapStream = new SitemapStream({
|
const sitemapStream = new SitemapStream({
|
||||||
hostname: WEBSERVER.URL,
|
hostname: WEBSERVER.URL,
|
||||||
errorHandler: (err: Error, level: ErrorLevel) => {
|
errorHandler: (err: Error, level: ErrorLevel) => {
|
||||||
if (level === 'warn') {
|
if (level === ErrorLevel.WARN) {
|
||||||
logger.warn('Warning in sitemap generation.', { err })
|
logger.warn('Warning in sitemap generation.', { err })
|
||||||
} else if (level === 'throw') {
|
} else if (level === ErrorLevel.THROW) {
|
||||||
logger.error('Error in sitemap generation.', { err })
|
logger.error('Error in sitemap generation.', { err })
|
||||||
|
|
||||||
throw err
|
throw err
|
||||||
|
|
|
@ -208,7 +208,6 @@ function parseSemVersion (s: string) {
|
||||||
function execShell (command: string, options?: ExecOptions) {
|
function execShell (command: string, options?: ExecOptions) {
|
||||||
return new Promise<{ err?: Error, stdout: string, stderr: string }>((res, rej) => {
|
return new Promise<{ err?: Error, stdout: string, stderr: string }>((res, rej) => {
|
||||||
exec(command, options, (err, stdout, stderr) => {
|
exec(command, options, (err, stdout, stderr) => {
|
||||||
// eslint-disable-next-line prefer-promise-reject-errors
|
|
||||||
if (err) return rej({ err, stdout, stderr })
|
if (err) return rej({ err, stdout, stderr })
|
||||||
|
|
||||||
return res({ stdout, stderr })
|
return res({ stdout, stderr })
|
||||||
|
@ -274,25 +273,17 @@ const pipelinePromise = promisify(pipeline)
|
||||||
export {
|
export {
|
||||||
objectConverter,
|
objectConverter,
|
||||||
mapToJSON,
|
mapToJSON,
|
||||||
|
|
||||||
sanitizeUrl,
|
sanitizeUrl,
|
||||||
sanitizeHost,
|
sanitizeHost,
|
||||||
|
|
||||||
execShell,
|
execShell,
|
||||||
|
|
||||||
pageToStartAndCount,
|
pageToStartAndCount,
|
||||||
peertubeTruncate,
|
peertubeTruncate,
|
||||||
|
|
||||||
scryptPromise,
|
scryptPromise,
|
||||||
|
|
||||||
randomBytesPromise,
|
randomBytesPromise,
|
||||||
|
|
||||||
generateRSAKeyPairPromise,
|
generateRSAKeyPairPromise,
|
||||||
generateED25519KeyPairPromise,
|
generateED25519KeyPairPromise,
|
||||||
|
|
||||||
execPromise2,
|
execPromise2,
|
||||||
execPromise,
|
execPromise,
|
||||||
pipelinePromise,
|
pipelinePromise,
|
||||||
|
|
||||||
parseSemVersion
|
parseSemVersion
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,10 +55,9 @@ const STATIC_CACHE = {
|
||||||
|
|
||||||
const localCache = new Map<string, any>()
|
const localCache = new Map<string, any>()
|
||||||
|
|
||||||
const nodeDocumentLoader = (jsonld as any).documentLoaders.node();
|
const nodeDocumentLoader = (jsonld as any).documentLoaders.node()
|
||||||
|
|
||||||
/* eslint-disable no-import-assign */
|
;(jsonld as any).documentLoader = async (url: string) => {
|
||||||
(jsonld as any).documentLoader = async (url: string) => {
|
|
||||||
if (url in STATIC_CACHE) {
|
if (url in STATIC_CACHE) {
|
||||||
return {
|
return {
|
||||||
contextUrl: null,
|
contextUrl: null,
|
||||||
|
|
|
@ -6,7 +6,7 @@ function isWebfingerLocalResourceValid (value: string) {
|
||||||
if (!exists(value)) return false
|
if (!exists(value)) return false
|
||||||
if (value.startsWith('acct:') === false) return false
|
if (value.startsWith('acct:') === false) return false
|
||||||
|
|
||||||
const actorWithHost = value.substr(5)
|
const actorWithHost = value.substring(5)
|
||||||
const actorParts = actorWithHost.split('@')
|
const actorParts = actorWithHost.split('@')
|
||||||
if (actorParts.length !== 2) return false
|
if (actorParts.length !== 2) return false
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import { Module } from 'module'
|
import { Module } from 'module'
|
||||||
import { extname } from 'path'
|
import { extname } from 'path'
|
||||||
|
|
||||||
function decachePlugin (require: NodeRequire, libraryPath: string) {
|
function decachePlugin (require: NodeJS.Require, libraryPath: string) {
|
||||||
const moduleName = find(require, libraryPath)
|
const moduleName = find(require, libraryPath)
|
||||||
|
|
||||||
if (!moduleName) return
|
if (!moduleName) return
|
||||||
|
@ -16,7 +16,7 @@ function decachePlugin (require: NodeRequire, libraryPath: string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function decacheModule (require: NodeRequire, name: string) {
|
function decacheModule (require: NodeJS.Require, name: string) {
|
||||||
const moduleName = find(require, name)
|
const moduleName = find(require, name)
|
||||||
|
|
||||||
if (!moduleName) return
|
if (!moduleName) return
|
||||||
|
@ -37,7 +37,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function find (require: NodeRequire, moduleName: string) {
|
function find (require: NodeJS.Require, moduleName: string) {
|
||||||
try {
|
try {
|
||||||
return require.resolve(moduleName)
|
return require.resolve(moduleName)
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -45,14 +45,14 @@ function find (require: NodeRequire, moduleName: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchCache (require: NodeRequire, moduleName: string, callback: (current: NodeModule) => void) {
|
function searchCache (require: NodeJS.Require, moduleName: string, callback: (current: NodeJS.Module) => void) {
|
||||||
const resolvedModule = require.resolve(moduleName)
|
const resolvedModule = require.resolve(moduleName)
|
||||||
let mod: NodeModule
|
let mod: NodeJS.Module
|
||||||
const visited = {}
|
const visited = {}
|
||||||
|
|
||||||
if (resolvedModule && ((mod = require.cache[resolvedModule]) !== undefined)) {
|
if (resolvedModule && ((mod = require.cache[resolvedModule]) !== undefined)) {
|
||||||
// Recursively go over the results
|
// Recursively go over the results
|
||||||
(function run (current) {
|
;(function run (current) {
|
||||||
visited[current.id] = true
|
visited[current.id] = true
|
||||||
|
|
||||||
current.children.forEach(function (child) {
|
current.children.forEach(function (child) {
|
||||||
|
@ -66,10 +66,10 @@ function searchCache (require: NodeRequire, moduleName: string, callback: (curre
|
||||||
callback(current)
|
callback(current)
|
||||||
})(mod)
|
})(mod)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function removeCachedPath (pluginPath: string) {
|
function removeCachedPath (pluginPath: string) {
|
||||||
const pathCache = (Module as any)._pathCache as { [ id: string ]: string[] }
|
const pathCache = (Module as any)._pathCache as { [id: string]: string[] }
|
||||||
|
|
||||||
Object.keys(pathCache).forEach(function (cacheKey) {
|
Object.keys(pathCache).forEach(function (cacheKey) {
|
||||||
if (cacheKey.includes(pluginPath)) {
|
if (cacheKey.includes(pluginPath)) {
|
||||||
|
|
|
@ -58,8 +58,7 @@ export const unsafeSSRFGot = got.extend({
|
||||||
|
|
||||||
const bodyLimit = bodyKBLimit * 1000
|
const bodyLimit = bodyKBLimit * 1000
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
void promiseOrStream.on('downloadProgress', progress => {
|
||||||
promiseOrStream.on('downloadProgress', progress => {
|
|
||||||
if (progress.transferred > bodyLimit && progress.percent !== 1) {
|
if (progress.transferred > bodyLimit && progress.percent !== 1) {
|
||||||
const message = `Exceeded the download limit of ${bodyLimit} B`
|
const message = `Exceeded the download limit of ${bodyLimit} B`
|
||||||
logger.warn(message, lTags())
|
logger.warn(message, lTags())
|
||||||
|
|
|
@ -20,10 +20,10 @@ class StreamReplacer extends Transform {
|
||||||
// readable side of the transform stream
|
// readable side of the transform stream
|
||||||
while ((index = this.pendingChunk.indexOf('\n')) !== -1) {
|
while ((index = this.pendingChunk.indexOf('\n')) !== -1) {
|
||||||
// The `end` parameter is non-inclusive, so increase it to include the newline we found
|
// The `end` parameter is non-inclusive, so increase it to include the newline we found
|
||||||
const line = this.pendingChunk.slice(0, ++index)
|
const line = this.pendingChunk.subarray(0, ++index)
|
||||||
|
|
||||||
// `start` is inclusive, but we are already one char ahead of the newline -> all good
|
// `start` is inclusive, but we are already one char ahead of the newline -> all good
|
||||||
this.pendingChunk = this.pendingChunk.slice(index)
|
this.pendingChunk = this.pendingChunk.subarray(index)
|
||||||
|
|
||||||
// We have a single line here! Prepend the string we want
|
// We have a single line here! Prepend the string we want
|
||||||
this.push(this.doReplace(line))
|
this.push(this.doReplace(line))
|
||||||
|
|
|
@ -47,7 +47,7 @@ export async function unzip (options: {
|
||||||
const entryPath = join(destination, entry.fileName)
|
const entryPath = join(destination, entry.fileName)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (/\/$/.test(entry.fileName)) {
|
if (entry.fileName.endsWith('/')) {
|
||||||
await ensureDir(entryPath)
|
await ensureDir(entryPath)
|
||||||
logger.debug(`Creating directory from zip ${entryPath}`, lTags())
|
logger.debug(`Creating directory from zip ${entryPath}`, lTags())
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ const lTags = loggerTagsFactory('youtube-dl')
|
||||||
const youtubeDLBinaryPath = join(CONFIG.STORAGE.BIN_DIR, CONFIG.IMPORT.VIDEOS.HTTP.YOUTUBE_DL_RELEASE.NAME)
|
const youtubeDLBinaryPath = join(CONFIG.STORAGE.BIN_DIR, CONFIG.IMPORT.VIDEOS.HTTP.YOUTUBE_DL_RELEASE.NAME)
|
||||||
|
|
||||||
export class YoutubeDLCLI {
|
export class YoutubeDLCLI {
|
||||||
|
|
||||||
static async safeGet () {
|
static async safeGet () {
|
||||||
if (!await pathExists(youtubeDLBinaryPath)) {
|
if (!await pathExists(youtubeDLBinaryPath)) {
|
||||||
await ensureDir(dirname(youtubeDLBinaryPath))
|
await ensureDir(dirname(youtubeDLBinaryPath))
|
||||||
|
@ -86,7 +85,7 @@ export class YoutubeDLCLI {
|
||||||
* case #3 is the resolution-degraded equivalent of #1, and already a pretty safe fallback
|
* case #3 is the resolution-degraded equivalent of #1, and already a pretty safe fallback
|
||||||
*
|
*
|
||||||
* in any case we avoid AV1, see https://github.com/Chocobozzz/PeerTube/issues/3499
|
* in any case we avoid AV1, see https://github.com/Chocobozzz/PeerTube/issues/3499
|
||||||
**/
|
*/
|
||||||
|
|
||||||
let result: string[] = []
|
let result: string[] = []
|
||||||
|
|
||||||
|
@ -111,7 +110,6 @@ export class YoutubeDLCLI {
|
||||||
}
|
}
|
||||||
|
|
||||||
private constructor () {
|
private constructor () {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
download (options: {
|
download (options: {
|
||||||
|
@ -199,7 +197,7 @@ export class YoutubeDLCLI {
|
||||||
for (let i = 0, len = data.length; i < len; i++) {
|
for (let i = 0, len = data.length; i < len; i++) {
|
||||||
const line = data[i]
|
const line = data[i]
|
||||||
|
|
||||||
if (line.indexOf(skipString) === 0) {
|
if (line.startsWith(skipString)) {
|
||||||
files.push(line.slice(skipString.length))
|
files.push(line.slice(skipString.length))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ class ClientHtml {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendHTML (html: string, res: express.Response, localizedHTML: boolean = false) {
|
function sendHTML (html: string, res: express.Response, localizedHTML = false) {
|
||||||
res.set('Content-Type', 'text/html; charset=UTF-8')
|
res.set('Content-Type', 'text/html; charset=UTF-8')
|
||||||
res.set('Cache-Control', 'max-age=0, no-cache, must-revalidate')
|
res.set('Cache-Control', 'max-age=0, no-cache, must-revalidate')
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,6 @@ export class TagsHtml {
|
||||||
|
|
||||||
// OEmbed
|
// OEmbed
|
||||||
for (const oembedLinkTag of oembedLinkTags) {
|
for (const oembedLinkTag of oembedLinkTags) {
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
tagsStr += `<link rel="alternate" type="${oembedLinkTag.type}" href="${oembedLinkTag.href}" title="${
|
tagsStr += `<link rel="alternate" type="${oembedLinkTag.type}" href="${oembedLinkTag.href}" title="${
|
||||||
escapeAttribute(oembedLinkTag.escapedTitle)
|
escapeAttribute(oembedLinkTag.escapedTitle)
|
||||||
}" />`
|
}" />`
|
||||||
|
|
|
@ -13,18 +13,21 @@ export interface PeerTubeInternalEvents {
|
||||||
'chapters-updated': (options: { video: MVideoImmutable }) => void
|
'chapters-updated': (options: { video: MVideoImmutable }) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||||
declare interface InternalEventEmitter {
|
declare interface InternalEventEmitter {
|
||||||
on<U extends keyof PeerTubeInternalEvents>(
|
on<U extends keyof PeerTubeInternalEvents>(
|
||||||
event: U, listener: PeerTubeInternalEvents[U]
|
event: U,
|
||||||
|
listener: PeerTubeInternalEvents[U]
|
||||||
): this
|
): this
|
||||||
|
|
||||||
emit<U extends keyof PeerTubeInternalEvents>(
|
emit<U extends keyof PeerTubeInternalEvents>(
|
||||||
event: U, ...args: Parameters<PeerTubeInternalEvents[U]>
|
event: U,
|
||||||
|
...args: Parameters<PeerTubeInternalEvents[U]>
|
||||||
): boolean
|
): boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||||
class InternalEventEmitter extends EventEmitter {
|
class InternalEventEmitter extends EventEmitter {
|
||||||
|
|
||||||
private static instance: InternalEventEmitter
|
private static instance: InternalEventEmitter
|
||||||
|
|
||||||
static get Instance () {
|
static get Instance () {
|
||||||
|
|
|
@ -77,34 +77,34 @@ import { processVideoTranscription } from './handlers/video-transcription.js'
|
||||||
import { processVideosViewsStats } from './handlers/video-views-stats.js'
|
import { processVideosViewsStats } from './handlers/video-views-stats.js'
|
||||||
|
|
||||||
export type CreateJobArgument =
|
export type CreateJobArgument =
|
||||||
{ type: 'activitypub-http-broadcast', payload: ActivitypubHttpBroadcastPayload } |
|
| { type: 'activitypub-http-broadcast', payload: ActivitypubHttpBroadcastPayload }
|
||||||
{ type: 'activitypub-http-broadcast-parallel', payload: ActivitypubHttpBroadcastPayload } |
|
| { type: 'activitypub-http-broadcast-parallel', payload: ActivitypubHttpBroadcastPayload }
|
||||||
{ type: 'activitypub-http-unicast', payload: ActivitypubHttpUnicastPayload } |
|
| { type: 'activitypub-http-unicast', payload: ActivitypubHttpUnicastPayload }
|
||||||
{ type: 'activitypub-http-fetcher', payload: ActivitypubHttpFetcherPayload } |
|
| { type: 'activitypub-http-fetcher', payload: ActivitypubHttpFetcherPayload }
|
||||||
{ type: 'activitypub-cleaner', payload: {} } |
|
| { type: 'activitypub-cleaner', payload: {} }
|
||||||
{ type: 'activitypub-follow', payload: ActivitypubFollowPayload } |
|
| { type: 'activitypub-follow', payload: ActivitypubFollowPayload }
|
||||||
{ type: 'video-file-import', payload: VideoFileImportPayload } |
|
| { type: 'video-file-import', payload: VideoFileImportPayload }
|
||||||
{ type: 'video-transcoding', payload: VideoTranscodingPayload } |
|
| { type: 'video-transcoding', payload: VideoTranscodingPayload }
|
||||||
{ type: 'email', payload: EmailPayload } |
|
| { type: 'email', payload: EmailPayload }
|
||||||
{ type: 'transcoding-job-builder', payload: TranscodingJobBuilderPayload } |
|
| { type: 'transcoding-job-builder', payload: TranscodingJobBuilderPayload }
|
||||||
{ type: 'video-import', payload: VideoImportPayload } |
|
| { type: 'video-import', payload: VideoImportPayload }
|
||||||
{ type: 'activitypub-refresher', payload: RefreshPayload } |
|
| { type: 'activitypub-refresher', payload: RefreshPayload }
|
||||||
{ type: 'videos-views-stats', payload: {} } |
|
| { type: 'videos-views-stats', payload: {} }
|
||||||
{ type: 'video-live-ending', payload: VideoLiveEndingPayload } |
|
| { type: 'video-live-ending', payload: VideoLiveEndingPayload }
|
||||||
{ type: 'actor-keys', payload: ActorKeysPayload } |
|
| { type: 'actor-keys', payload: ActorKeysPayload }
|
||||||
{ type: 'video-redundancy', payload: VideoRedundancyPayload } |
|
| { type: 'video-redundancy', payload: VideoRedundancyPayload }
|
||||||
{ type: 'video-studio-edition', payload: VideoStudioEditionPayload } |
|
| { type: 'video-studio-edition', payload: VideoStudioEditionPayload }
|
||||||
{ type: 'manage-video-torrent', payload: ManageVideoTorrentPayload } |
|
| { type: 'manage-video-torrent', payload: ManageVideoTorrentPayload }
|
||||||
{ type: 'move-to-object-storage', payload: MoveStoragePayload } |
|
| { type: 'move-to-object-storage', payload: MoveStoragePayload }
|
||||||
{ type: 'move-to-file-system', payload: MoveStoragePayload } |
|
| { type: 'move-to-file-system', payload: MoveStoragePayload }
|
||||||
{ type: 'video-channel-import', payload: VideoChannelImportPayload } |
|
| { type: 'video-channel-import', payload: VideoChannelImportPayload }
|
||||||
{ type: 'after-video-channel-import', payload: AfterVideoChannelImportPayload } |
|
| { type: 'after-video-channel-import', payload: AfterVideoChannelImportPayload }
|
||||||
{ type: 'notify', payload: NotifyPayload } |
|
| { type: 'notify', payload: NotifyPayload }
|
||||||
{ type: 'federate-video', payload: FederateVideoPayload } |
|
| { type: 'federate-video', payload: FederateVideoPayload }
|
||||||
{ type: 'create-user-export', payload: CreateUserExportPayload } |
|
| { type: 'create-user-export', payload: CreateUserExportPayload }
|
||||||
{ type: 'generate-video-storyboard', payload: GenerateStoryboardPayload } |
|
| { type: 'generate-video-storyboard', payload: GenerateStoryboardPayload }
|
||||||
{ type: 'import-user-archive', payload: ImportUserArchivePayload } |
|
| { type: 'import-user-archive', payload: ImportUserArchivePayload }
|
||||||
{ type: 'video-transcription', payload: VideoTranscriptionPayload }
|
| { type: 'video-transcription', payload: VideoTranscriptionPayload }
|
||||||
|
|
||||||
export type CreateJobOptions = {
|
export type CreateJobOptions = {
|
||||||
delay?: number
|
delay?: number
|
||||||
|
@ -182,7 +182,6 @@ const jobTypes: JobType[] = [
|
||||||
const silentFailure = new Set<JobType>([ 'activitypub-http-unicast' ])
|
const silentFailure = new Set<JobType>([ 'activitypub-http-unicast' ])
|
||||||
|
|
||||||
class JobQueue {
|
class JobQueue {
|
||||||
|
|
||||||
private static instance: JobQueue
|
private static instance: JobQueue
|
||||||
|
|
||||||
private workers: { [id in JobType]?: Worker } = {}
|
private workers: { [id in JobType]?: Worker } = {}
|
||||||
|
@ -214,7 +213,9 @@ class JobQueue {
|
||||||
connection: Redis.getRedisClientOptions('FlowProducer'),
|
connection: Redis.getRedisClientOptions('FlowProducer'),
|
||||||
prefix: this.jobRedisPrefix
|
prefix: this.jobRedisPrefix
|
||||||
})
|
})
|
||||||
this.flowProducer.on('error', err => { logger.error('Error in flow producer', { err }) })
|
this.flowProducer.on('error', err => {
|
||||||
|
logger.error('Error in flow producer', { err })
|
||||||
|
})
|
||||||
|
|
||||||
this.addRepeatableJobs()
|
this.addRepeatableJobs()
|
||||||
}
|
}
|
||||||
|
@ -237,7 +238,7 @@ class JobQueue {
|
||||||
return timeoutPromise(p, timeout)
|
return timeoutPromise(p, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
const processor = async (jobArg: Job<any>) => {
|
const processor = async (jobArg: Job) => {
|
||||||
const job = await Hooks.wrapObject(jobArg, 'filter:job-queue.process.params', { type: handlerName })
|
const job = await Hooks.wrapObject(jobArg, 'filter:job-queue.process.params', { type: handlerName })
|
||||||
|
|
||||||
return Hooks.wrapPromiseFun(handler, job, 'filter:job-queue.process.result')
|
return Hooks.wrapPromiseFun(handler, job, 'filter:job-queue.process.result')
|
||||||
|
@ -258,7 +259,9 @@ class JobQueue {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
worker.on('error', err => { logger.error('Error in job worker %s.', handlerName, { err }) })
|
worker.on('error', err => {
|
||||||
|
logger.error('Error in job worker %s.', handlerName, { err })
|
||||||
|
})
|
||||||
|
|
||||||
this.workers[handlerName] = worker
|
this.workers[handlerName] = worker
|
||||||
}
|
}
|
||||||
|
@ -270,7 +273,9 @@ class JobQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
const queue = new Queue(handlerName, queueOptions)
|
const queue = new Queue(handlerName, queueOptions)
|
||||||
queue.on('error', err => { logger.error('Error in job queue %s.', handlerName, { err }) })
|
queue.on('error', err => {
|
||||||
|
logger.error('Error in job queue %s.', handlerName, { err })
|
||||||
|
})
|
||||||
|
|
||||||
this.queues[handlerName] = queue
|
this.queues[handlerName] = queue
|
||||||
|
|
||||||
|
@ -286,7 +291,9 @@ class JobQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
const queueEvents = new QueueEvents(handlerName, queueEventsOptions)
|
const queueEvents = new QueueEvents(handlerName, queueEventsOptions)
|
||||||
queueEvents.on('error', err => { logger.error('Error in job queue events %s.', handlerName, { err }) })
|
queueEvents.on('error', err => {
|
||||||
|
logger.error('Error in job queue events %s.', handlerName, { err })
|
||||||
|
})
|
||||||
|
|
||||||
this.queueEvents[handlerName] = queueEvents
|
this.queueEvents[handlerName] = queueEvents
|
||||||
}
|
}
|
||||||
|
@ -557,5 +564,6 @@ class JobQueue {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
JobQueue, jobTypes
|
JobQueue,
|
||||||
|
jobTypes
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ interface MuxingSessionEvents {
|
||||||
'after-cleanup': (options: { videoUUID: string }) => void
|
'after-cleanup': (options: { videoUUID: string }) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||||
declare interface MuxingSession {
|
declare interface MuxingSession {
|
||||||
on<U extends keyof MuxingSessionEvents>(
|
on<U extends keyof MuxingSessionEvents>(
|
||||||
event: U,
|
event: U,
|
||||||
|
@ -61,7 +62,8 @@ declare interface MuxingSession {
|
||||||
): boolean
|
): boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class MuxingSession extends EventEmitter {
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||||
|
class MuxingSession extends EventEmitter implements MuxingSession {
|
||||||
private transcodingWrapper: AbstractTranscodingWrapper
|
private transcodingWrapper: AbstractTranscodingWrapper
|
||||||
|
|
||||||
private readonly context: any
|
private readonly context: any
|
||||||
|
|
|
@ -10,13 +10,16 @@ interface TranscodingWrapperEvents {
|
||||||
'error': (options: { err: Error }) => void
|
'error': (options: { err: Error }) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||||
declare interface AbstractTranscodingWrapper {
|
declare interface AbstractTranscodingWrapper {
|
||||||
on<U extends keyof TranscodingWrapperEvents>(
|
on<U extends keyof TranscodingWrapperEvents>(
|
||||||
event: U, listener: TranscodingWrapperEvents[U]
|
event: U,
|
||||||
|
listener: TranscodingWrapperEvents[U]
|
||||||
): this
|
): this
|
||||||
|
|
||||||
emit<U extends keyof TranscodingWrapperEvents>(
|
emit<U extends keyof TranscodingWrapperEvents>(
|
||||||
event: U, ...args: Parameters<TranscodingWrapperEvents[U]>
|
event: U,
|
||||||
|
...args: Parameters<TranscodingWrapperEvents[U]>
|
||||||
): boolean
|
): boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +51,7 @@ interface AbstractTranscodingWrapperOptions {
|
||||||
outDirectory: string
|
outDirectory: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||||
abstract class AbstractTranscodingWrapper extends EventEmitter {
|
abstract class AbstractTranscodingWrapper extends EventEmitter {
|
||||||
protected readonly videoLive: MVideoLiveVideo
|
protected readonly videoLive: MVideoLiveVideo
|
||||||
|
|
||||||
|
@ -111,6 +115,5 @@ abstract class AbstractTranscodingWrapper extends EventEmitter {
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type AbstractTranscodingWrapperOptions,
|
type AbstractTranscodingWrapperOptions,
|
||||||
|
|
||||||
AbstractTranscodingWrapper
|
AbstractTranscodingWrapper
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,15 +200,12 @@ function createAccountAbuse (options: {
|
||||||
|
|
||||||
export {
|
export {
|
||||||
isLocalLiveVideoAccepted,
|
isLocalLiveVideoAccepted,
|
||||||
|
|
||||||
isLocalVideoFileAccepted,
|
isLocalVideoFileAccepted,
|
||||||
isLocalVideoThreadAccepted,
|
isLocalVideoThreadAccepted,
|
||||||
isRemoteVideoCommentAccepted,
|
isRemoteVideoCommentAccepted,
|
||||||
isLocalVideoCommentReplyAccepted,
|
isLocalVideoCommentReplyAccepted,
|
||||||
isPreImportVideoAccepted,
|
isPreImportVideoAccepted,
|
||||||
isPostImportVideoAccepted,
|
isPostImportVideoAccepted,
|
||||||
|
|
||||||
createAbuse,
|
|
||||||
createVideoAbuse,
|
createVideoAbuse,
|
||||||
createVideoCommentAbuse,
|
createVideoCommentAbuse,
|
||||||
createAccountAbuse
|
createAccountAbuse
|
||||||
|
|
|
@ -273,25 +273,18 @@ async function getObjectStorageFileSize (options: {
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type BucketInfo,
|
type BucketInfo,
|
||||||
|
|
||||||
buildKey,
|
buildKey,
|
||||||
|
|
||||||
storeObject,
|
storeObject,
|
||||||
storeContent,
|
storeContent,
|
||||||
storeStream,
|
storeStream,
|
||||||
|
|
||||||
removeObject,
|
removeObject,
|
||||||
removeObjectByFullKey,
|
removeObjectByFullKey,
|
||||||
removePrefix,
|
removePrefix,
|
||||||
|
|
||||||
makeAvailable,
|
makeAvailable,
|
||||||
|
|
||||||
updateObjectACL,
|
updateObjectACL,
|
||||||
updatePrefixACL,
|
updatePrefixACL,
|
||||||
|
|
||||||
listKeysOfPrefix,
|
listKeysOfPrefix,
|
||||||
createObjectReadStream,
|
createObjectReadStream,
|
||||||
|
|
||||||
getObjectStorageFileSize
|
getObjectStorageFileSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,11 +337,15 @@ async function uploadToStorage (options: {
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'Completed %s%s in bucket %s',
|
'Completed %s%s in bucket %s',
|
||||||
bucketInfo.PREFIX, objectStorageKey, bucketInfo.BUCKET_NAME, { ...lTags(), responseMetadata: response.$metadata }
|
bucketInfo.PREFIX,
|
||||||
|
objectStorageKey,
|
||||||
|
bucketInfo.BUCKET_NAME,
|
||||||
|
{ ...lTags(), responseMetadata: response.$metadata }
|
||||||
)
|
)
|
||||||
|
|
||||||
return getInternalUrl(bucketInfo, objectStorageKey)
|
return getInternalUrl(bucketInfo, objectStorageKey)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
||||||
throw parseS3Error(err)
|
throw parseS3Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,8 +87,7 @@ function handleObjectStorageFailure (res: express.Response, err: Error) {
|
||||||
|
|
||||||
return res.fail({
|
return res.fail({
|
||||||
status: HttpStatusCode.INTERNAL_SERVER_ERROR_500,
|
status: HttpStatusCode.INTERNAL_SERVER_ERROR_500,
|
||||||
message: err.message,
|
message: err.message
|
||||||
type: err.name
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import { logger } from '@server/helpers/logger.js'
|
||||||
// Try to keep consistency with their metric name/description so it's easier to process (grafana dashboard template etc)
|
// Try to keep consistency with their metric name/description so it's easier to process (grafana dashboard template etc)
|
||||||
|
|
||||||
export class NodeJSObserversBuilder {
|
export class NodeJSObserversBuilder {
|
||||||
|
|
||||||
constructor (private readonly meter: Meter) {
|
constructor (private readonly meter: Meter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +61,6 @@ export class NodeJSObserversBuilder {
|
||||||
observableResult.observe(cpuTotal, (userUsageMicros + systemUsageMicros) / 1e6)
|
observableResult.observe(cpuTotal, (userUsageMicros + systemUsageMicros) / 1e6)
|
||||||
observableResult.observe(cpuUser, userUsageMicros / 1e6)
|
observableResult.observe(cpuUser, userUsageMicros / 1e6)
|
||||||
observableResult.observe(cpuSystem, systemUsageMicros / 1e6)
|
observableResult.observe(cpuSystem, systemUsageMicros / 1e6)
|
||||||
|
|
||||||
}, [ cpuTotal, cpuUser, cpuSystem ])
|
}, [ cpuTotal, cpuUser, cpuSystem ])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +117,7 @@ export class NodeJSObserversBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildEventLoopLagObserver () {
|
private buildEventLoopLagObserver () {
|
||||||
const reportEventloopLag = (start: [ number, number ], observableResult: ObservableResult, res: () => void) => {
|
const reportEventloopLag = (start: [number, number], observableResult: ObservableResult, res: () => void) => {
|
||||||
const delta = process.hrtime(start)
|
const delta = process.hrtime(start)
|
||||||
const nanosec = delta[0] * 1e9 + delta[1]
|
const nanosec = delta[0] * 1e9 + delta[1]
|
||||||
const seconds = nanosec / 1e9
|
const seconds = nanosec / 1e9
|
||||||
|
@ -180,6 +178,7 @@ export class NodeJSObserversBuilder {
|
||||||
|
|
||||||
const data = {}
|
const data = {}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
||||||
for (let i = 0; i < resources.length; i++) {
|
for (let i = 0; i < resources.length; i++) {
|
||||||
const resource = resources[i]
|
const resource = resources[i]
|
||||||
|
|
||||||
|
|
|
@ -68,8 +68,13 @@ async function registerOpentelemetryTracing () {
|
||||||
}, DiagLogLevel.INFO)
|
}, DiagLogLevel.INFO)
|
||||||
|
|
||||||
const tracerProvider = new NodeTracerProvider.default.NodeTracerProvider({
|
const tracerProvider = new NodeTracerProvider.default.NodeTracerProvider({
|
||||||
|
spanProcessors: [
|
||||||
|
new BatchSpanProcessor.default.BatchSpanProcessor(
|
||||||
|
new JaegerExporter({ endpoint: CONFIG.OPEN_TELEMETRY.TRACING.JAEGER_EXPORTER.ENDPOINT })
|
||||||
|
)
|
||||||
|
],
|
||||||
resource: new Resource.default.Resource({
|
resource: new Resource.default.Resource({
|
||||||
[SemanticResourceAttributes.default.SemanticResourceAttributes.SERVICE_NAME]: 'peertube'
|
[SemanticResourceAttributes.default.ATTR_SERVICE_NAME]: 'peertube'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -92,16 +97,10 @@ async function registerOpentelemetryTracing () {
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
tracerProvider.addSpanProcessor(
|
|
||||||
new BatchSpanProcessor.default.BatchSpanProcessor(
|
|
||||||
new JaegerExporter({ endpoint: CONFIG.OPEN_TELEMETRY.TRACING.JAEGER_EXPORTER.ENDPOINT })
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
tracerProvider.register()
|
tracerProvider.register()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function wrapWithSpanAndContext <T> (spanName: string, cb: () => Promise<T>) {
|
async function wrapWithSpanAndContext<T> (spanName: string, cb: () => Promise<T>) {
|
||||||
const { context, trace } = await import('@opentelemetry/api')
|
const { context, trace } = await import('@opentelemetry/api')
|
||||||
|
|
||||||
if (CONFIG.OPEN_TELEMETRY.TRACING.ENABLED !== true) {
|
if (CONFIG.OPEN_TELEMETRY.TRACING.ENABLED !== true) {
|
||||||
|
@ -135,6 +134,5 @@ class TrackerMock {
|
||||||
|
|
||||||
class SpanMock {
|
class SpanMock {
|
||||||
end () {
|
end () {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,9 @@ import { authenticateRunnerSocket, authenticateSocket } from '../middlewares/ind
|
||||||
import { isDevInstance } from '@peertube/peertube-node-utils'
|
import { isDevInstance } from '@peertube/peertube-node-utils'
|
||||||
|
|
||||||
class PeerTubeSocket {
|
class PeerTubeSocket {
|
||||||
|
|
||||||
private static instance: PeerTubeSocket
|
private static instance: PeerTubeSocket
|
||||||
|
|
||||||
private userNotificationSockets: { [ userId: number ]: Socket[] } = {}
|
private userNotificationSockets: { [userId: number]: Socket[] } = {}
|
||||||
private liveVideosNamespace: Namespace
|
private liveVideosNamespace: Namespace
|
||||||
private readonly runnerSockets = new Set<Socket>()
|
private readonly runnerSockets = new Set<Socket>()
|
||||||
|
|
||||||
|
@ -51,16 +50,14 @@ class PeerTubeSocket {
|
||||||
const videoId = params.videoId + ''
|
const videoId = params.videoId + ''
|
||||||
if (!isIdValid(videoId)) return
|
if (!isIdValid(videoId)) return
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
void socket.join(videoId)
|
||||||
socket.join(videoId)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('unsubscribe', params => {
|
socket.on('unsubscribe', params => {
|
||||||
const videoId = params.videoId + ''
|
const videoId = params.videoId + ''
|
||||||
if (!isIdValid(videoId)) return
|
if (!isIdValid(videoId)) return
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
void socket.leave(videoId)
|
||||||
socket.leave(videoId)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -54,13 +54,13 @@ export interface RegisteredPlugin {
|
||||||
|
|
||||||
// Only if this is a plugin
|
// Only if this is a plugin
|
||||||
registerHelpers?: RegisterHelpers
|
registerHelpers?: RegisterHelpers
|
||||||
unregister?: Function
|
unregister?: () => any
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HookInformationValue {
|
export interface HookInformationValue {
|
||||||
npmName: string
|
npmName: string
|
||||||
pluginName: string
|
pluginName: string
|
||||||
handler: Function
|
handler: () => any
|
||||||
priority: number
|
priority: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,6 @@ type PluginLocalesTranslations = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PluginManager implements ServerHook {
|
export class PluginManager implements ServerHook {
|
||||||
|
|
||||||
private static instance: PluginManager
|
private static instance: PluginManager
|
||||||
|
|
||||||
private registeredPlugins: { [name: string]: RegisteredPlugin } = {}
|
private registeredPlugins: { [name: string]: RegisteredPlugin } = {}
|
||||||
|
@ -267,7 +266,9 @@ export class PluginManager implements ServerHook {
|
||||||
hookType,
|
hookType,
|
||||||
result,
|
result,
|
||||||
params,
|
params,
|
||||||
onError: err => { logger.error('Cannot run hook %s of plugin %s.', hookName, hook.pluginName, { err }) }
|
onError: err => {
|
||||||
|
logger.error('Cannot run hook %s of plugin %s.', hookName, hook.pluginName, { err })
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,9 +359,8 @@ export class PluginManager implements ServerHook {
|
||||||
|
|
||||||
const packageJSON = await this.getPackageJSON(pluginName, pluginType)
|
const packageJSON = await this.getPackageJSON(pluginName, pluginType)
|
||||||
|
|
||||||
this.sanitizeAndCheckPackageJSONOrThrow(packageJSON, pluginType);
|
this.sanitizeAndCheckPackageJSONOrThrow(packageJSON, pluginType)
|
||||||
|
;[ plugin ] = await PluginModel.upsert({
|
||||||
[ plugin ] = await PluginModel.upsert({
|
|
||||||
name: pluginName,
|
name: pluginName,
|
||||||
description: packageJSON.description,
|
description: packageJSON.description,
|
||||||
homepage: packageJSON.homepage,
|
homepage: packageJSON.homepage,
|
||||||
|
|
|
@ -60,7 +60,7 @@ async function execYarn (command: string) {
|
||||||
} catch (result) {
|
} catch (result) {
|
||||||
logger.error('Cannot exec yarn.', { command, err: result.err, stderr: result.stderr })
|
logger.error('Cannot exec yarn.', { command, err: result.err, stderr: result.stderr })
|
||||||
|
|
||||||
throw result.err
|
throw result.err as Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,12 +39,14 @@ export function safeUploadXCleanup (file: FileQuery) {
|
||||||
.catch(err => logger.error('Cannot delete the file %s', file.name, { err }))
|
.catch(err => logger.error('Cannot delete the file %s', file.name, { err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildUploadXFile <T extends UploadXMetadata> (reqBody: T) {
|
export function buildUploadXFile<T extends UploadXMetadata> (reqBody: T) {
|
||||||
return {
|
return {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-spread
|
||||||
...reqBody,
|
...reqBody,
|
||||||
|
|
||||||
path: getResumableUploadPath(reqBody.name),
|
path: getResumableUploadPath(reqBody.name),
|
||||||
filename: reqBody.metadata.filename
|
filename: reqBody.metadata.filename,
|
||||||
|
originalname: reqBody.originalName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,21 +72,26 @@ export function setupUploadResumableRoutes (options: {
|
||||||
uploadDeleteMiddlewares = []
|
uploadDeleteMiddlewares = []
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
router.post(routePath,
|
router.post(
|
||||||
|
routePath,
|
||||||
authenticate,
|
authenticate,
|
||||||
...uploadInitBeforeMiddlewares,
|
...uploadInitBeforeMiddlewares,
|
||||||
resumableInitValidator,
|
resumableInitValidator,
|
||||||
...uploadInitAfterMiddlewares,
|
...uploadInitAfterMiddlewares,
|
||||||
(req, res) => uploadx.upload(req, res) // Prevent next() call, explicitly tell to uploadx it's the end
|
// Prevent next() call, explicitly tell to uploadx it's the end
|
||||||
|
(req, res) => uploadx.upload(req, res)
|
||||||
)
|
)
|
||||||
|
|
||||||
router.delete(routePath,
|
router.delete(
|
||||||
|
routePath,
|
||||||
authenticate,
|
authenticate,
|
||||||
...uploadDeleteMiddlewares,
|
...uploadDeleteMiddlewares,
|
||||||
(req, res) => uploadx.upload(req, res) // Prevent next() call, explicitly tell to uploadx it's the end
|
// Prevent next() call, explicitly tell to uploadx it's the end
|
||||||
|
(req, res) => uploadx.upload(req, res)
|
||||||
)
|
)
|
||||||
|
|
||||||
router.put(routePath,
|
router.put(
|
||||||
|
routePath,
|
||||||
authenticate,
|
authenticate,
|
||||||
uploadx.upload, // uploadx doesn't next() before the file upload completes
|
uploadx.upload, // uploadx doesn't next() before the file upload completes
|
||||||
...uploadedMiddlewares,
|
...uploadedMiddlewares,
|
||||||
|
|
|
@ -93,9 +93,7 @@ export class VideosExporter extends AbstractUserExporter<VideoExportJSON> {
|
||||||
|
|
||||||
const live = video.isLive
|
const live = video.isLive
|
||||||
? await VideoLiveModel.loadByVideoIdWithSettings(videoId)
|
? await VideoLiveModel.loadByVideoIdWithSettings(videoId)
|
||||||
: undefined
|
: undefined // We already have captions, so we can set it to the video object
|
||||||
|
|
||||||
// We already have captions, so we can set it to the video object
|
|
||||||
;(video as any).VideoCaptions = captions
|
;(video as any).VideoCaptions = captions
|
||||||
// Then fetch more attributes for AP serialization
|
// Then fetch more attributes for AP serialization
|
||||||
const videoAP = await video.lightAPToFullAP(undefined)
|
const videoAP = await video.lightAPToFullAP(undefined)
|
||||||
|
|
|
@ -5,10 +5,9 @@ import { AbstractUserImporter } from './abstract-user-importer.js'
|
||||||
|
|
||||||
type SanitizedObject = AutoTagPoliciesJSON['reviewComments']
|
type SanitizedObject = AutoTagPoliciesJSON['reviewComments']
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
export class ReviewCommentsTagPoliciesImporter
|
export class ReviewCommentsTagPoliciesImporter
|
||||||
extends AbstractUserImporter <AutoTagPoliciesJSON, AutoTagPoliciesJSON['reviewComments'] & { archiveFiles?: never }, SanitizedObject> {
|
extends AbstractUserImporter<AutoTagPoliciesJSON, AutoTagPoliciesJSON['reviewComments'] & { archiveFiles?: never }, SanitizedObject>
|
||||||
|
{
|
||||||
protected getImportObjects (json: AutoTagPoliciesJSON) {
|
protected getImportObjects (json: AutoTagPoliciesJSON) {
|
||||||
if (!json.reviewComments) return []
|
if (!json.reviewComments) return []
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,20 @@ import { pick } from '@peertube/peertube-core-utils'
|
||||||
|
|
||||||
const lTags = loggerTagsFactory('user-import')
|
const lTags = loggerTagsFactory('user-import')
|
||||||
|
|
||||||
type SanitizedObject = Pick<UserSettingsExportJSON, 'nsfwPolicy' | 'autoPlayVideo' | 'autoPlayNextVideo' | 'autoPlayNextVideo' |
|
type SanitizedObject = Pick<
|
||||||
'autoPlayNextVideoPlaylist' | 'p2pEnabled' | 'videosHistoryEnabled' | 'videoLanguages' | 'theme' | 'notificationSettings'>
|
UserSettingsExportJSON,
|
||||||
|
| 'nsfwPolicy'
|
||||||
export class UserSettingsImporter extends AbstractUserImporter <UserSettingsExportJSON, UserSettingsExportJSON, SanitizedObject> {
|
| 'autoPlayVideo'
|
||||||
|
| 'autoPlayNextVideo'
|
||||||
|
| 'autoPlayNextVideoPlaylist'
|
||||||
|
| 'p2pEnabled'
|
||||||
|
| 'videosHistoryEnabled'
|
||||||
|
| 'videoLanguages'
|
||||||
|
| 'theme'
|
||||||
|
| 'notificationSettings'
|
||||||
|
>
|
||||||
|
|
||||||
|
export class UserSettingsImporter extends AbstractUserImporter<UserSettingsExportJSON, UserSettingsExportJSON, SanitizedObject> {
|
||||||
protected getImportObjects (json: UserSettingsExportJSON) {
|
protected getImportObjects (json: UserSettingsExportJSON) {
|
||||||
return [ json ]
|
return [ json ]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import { VideoPlaylistPrivacy, VideoPlaylistType, VideoPlaylistsExportJSON } from '@peertube/peertube-models'
|
import { VideoPlaylistPrivacy, VideoPlaylistType, VideoPlaylistsExportJSON } from '@peertube/peertube-models'
|
||||||
import { logger, loggerTagsFactory } from '@server/helpers/logger.js'
|
import { logger, loggerTagsFactory } from '@server/helpers/logger.js'
|
||||||
import { buildUUID } from '@peertube/peertube-node-utils'
|
import { buildUUID } from '@peertube/peertube-node-utils'
|
||||||
import {
|
import { MChannelBannerAccountDefault, MVideoPlaylistFull, MVideoPlaylistThumbnail } from '@server/types/models/index.js'
|
||||||
MChannelBannerAccountDefault, MVideoPlaylistFull,
|
|
||||||
MVideoPlaylistThumbnail
|
|
||||||
} from '@server/types/models/index.js'
|
|
||||||
import { getLocalVideoPlaylistActivityPubUrl, getLocalVideoPlaylistElementActivityPubUrl } from '@server/lib/activitypub/url.js'
|
import { getLocalVideoPlaylistActivityPubUrl, getLocalVideoPlaylistElementActivityPubUrl } from '@server/lib/activitypub/url.js'
|
||||||
import { VideoChannelModel } from '@server/models/video/video-channel.js'
|
import { VideoChannelModel } from '@server/models/video/video-channel.js'
|
||||||
import { VideoPlaylistModel } from '@server/models/video/video-playlist.js'
|
import { VideoPlaylistModel } from '@server/models/video/video-playlist.js'
|
||||||
|
@ -33,11 +30,9 @@ import { generateThumbnailForPlaylist } from '@server/lib/video-playlist.js'
|
||||||
const lTags = loggerTagsFactory('user-import')
|
const lTags = loggerTagsFactory('user-import')
|
||||||
|
|
||||||
type ImportObject = VideoPlaylistsExportJSON['videoPlaylists'][0]
|
type ImportObject = VideoPlaylistsExportJSON['videoPlaylists'][0]
|
||||||
type SanitizedObject = Pick<ImportObject, 'type' | 'displayName' | 'privacy' | 'elements' | 'description' | 'elements' | 'channel' |
|
type SanitizedObject = Pick<ImportObject, 'type' | 'displayName' | 'privacy' | 'elements' | 'description' | 'channel' | 'archiveFiles'>
|
||||||
'archiveFiles'>
|
|
||||||
|
|
||||||
export class VideoPlaylistsImporter extends AbstractUserImporter <VideoPlaylistsExportJSON, ImportObject, SanitizedObject> {
|
|
||||||
|
|
||||||
|
export class VideoPlaylistsImporter extends AbstractUserImporter<VideoPlaylistsExportJSON, ImportObject, SanitizedObject> {
|
||||||
protected getImportObjects (json: VideoPlaylistsExportJSON) {
|
protected getImportObjects (json: VideoPlaylistsExportJSON) {
|
||||||
return json.videoPlaylists
|
return json.videoPlaylists
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,51 +107,51 @@ export class UserImporter {
|
||||||
// Keep consistency in import order (don't import videos before channels for example)
|
// Keep consistency in import order (don't import videos before channels for example)
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: 'account' as 'account',
|
name: 'account' as const,
|
||||||
importer: new AccountImporter(this.buildImporterOptions(user, 'account.json'))
|
importer: new AccountImporter(this.buildImporterOptions(user, 'account.json'))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'userSettings' as 'userSettings',
|
name: 'userSettings' as const,
|
||||||
importer: new UserSettingsImporter(this.buildImporterOptions(user, 'user-settings.json'))
|
importer: new UserSettingsImporter(this.buildImporterOptions(user, 'user-settings.json'))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'channels' as 'channels',
|
name: 'channels' as const,
|
||||||
importer: new ChannelsImporter(this.buildImporterOptions(user, 'channels.json'))
|
importer: new ChannelsImporter(this.buildImporterOptions(user, 'channels.json'))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'blocklist' as 'blocklist',
|
name: 'blocklist' as const,
|
||||||
importer: new BlocklistImporter(this.buildImporterOptions(user, 'blocklist.json'))
|
importer: new BlocklistImporter(this.buildImporterOptions(user, 'blocklist.json'))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'following' as 'following',
|
name: 'following' as const,
|
||||||
importer: new FollowingImporter(this.buildImporterOptions(user, 'following.json'))
|
importer: new FollowingImporter(this.buildImporterOptions(user, 'following.json'))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'videos' as 'videos',
|
name: 'videos' as const,
|
||||||
importer: new VideosImporter(this.buildImporterOptions(user, 'videos.json'))
|
importer: new VideosImporter(this.buildImporterOptions(user, 'videos.json'))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'likes' as 'likes',
|
name: 'likes' as const,
|
||||||
importer: new LikesImporter(this.buildImporterOptions(user, 'likes.json'))
|
importer: new LikesImporter(this.buildImporterOptions(user, 'likes.json'))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'dislikes' as 'dislikes',
|
name: 'dislikes' as const,
|
||||||
importer: new DislikesImporter(this.buildImporterOptions(user, 'dislikes.json'))
|
importer: new DislikesImporter(this.buildImporterOptions(user, 'dislikes.json'))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'videoPlaylists' as 'videoPlaylists',
|
name: 'videoPlaylists' as const,
|
||||||
importer: new VideoPlaylistsImporter(this.buildImporterOptions(user, 'video-playlists.json'))
|
importer: new VideoPlaylistsImporter(this.buildImporterOptions(user, 'video-playlists.json'))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'userVideoHistory' as 'userVideoHistory',
|
name: 'userVideoHistory' as const,
|
||||||
importer: new UserVideoHistoryImporter(this.buildImporterOptions(user, 'video-history.json'))
|
importer: new UserVideoHistoryImporter(this.buildImporterOptions(user, 'video-history.json'))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'watchedWordsLists' as 'watchedWordsLists',
|
name: 'watchedWordsLists' as const,
|
||||||
importer: new WatchedWordsListsImporter(this.buildImporterOptions(user, 'watched-words-lists.json'))
|
importer: new WatchedWordsListsImporter(this.buildImporterOptions(user, 'watched-words-lists.json'))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'commentAutoTagPolicies' as 'commentAutoTagPolicies',
|
name: 'commentAutoTagPolicies' as const,
|
||||||
importer: new ReviewCommentsTagPoliciesImporter(this.buildImporterOptions(user, 'automatic-tag-policies.json'))
|
importer: new ReviewCommentsTagPoliciesImporter(this.buildImporterOptions(user, 'automatic-tag-policies.json'))
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -134,7 +134,7 @@ export function buildSortDirectionAndField (value: string) {
|
||||||
let field: string
|
let field: string
|
||||||
let direction: 'ASC' | 'DESC'
|
let direction: 'ASC' | 'DESC'
|
||||||
|
|
||||||
if (value.substring(0, 1) === '-') {
|
if (value.startsWith('-')) {
|
||||||
direction = 'DESC'
|
direction = 'DESC'
|
||||||
field = value.substring(1)
|
field = value.substring(1)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { VideoCaptionModel } from '../video/video-caption.js'
|
||||||
import { VideoCommentModel } from '../video/video-comment.js'
|
import { VideoCommentModel } from '../video/video-comment.js'
|
||||||
import { VideoImportModel } from '../video/video-import.js'
|
import { VideoImportModel } from '../video/video-import.js'
|
||||||
import { VideoModel } from '../video/video.js'
|
import { VideoModel } from '../video/video.js'
|
||||||
import { UserNotificationListQueryBuilder } from './sql/user-notitication-list-query-builder.js'
|
import { UserNotificationListQueryBuilder } from './sql/user-notification-list-query-builder.js'
|
||||||
import { UserRegistrationModel } from './user-registration.js'
|
import { UserRegistrationModel } from './user-registration.js'
|
||||||
import { UserModel } from './user.js'
|
import { UserModel } from './user.js'
|
||||||
import { ActorImageModel } from '../actor/actor-image.js'
|
import { ActorImageModel } from '../actor/actor-image.js'
|
||||||
|
|
8
server/core/types/express.d.ts
vendored
8
server/core/types/express.d.ts
vendored
|
@ -1,4 +1,4 @@
|
||||||
import { HttpMethodType, PeerTubeProblemDocumentData, ServerLogLevel, VideoCreate } from '@peertube/peertube-models'
|
import { HttpMethodType, PeerTubeProblemDocumentData, ServerErrorCodeType, ServerLogLevel, VideoCreate } from '@peertube/peertube-models'
|
||||||
import { RegisterServerAuthExternalOptions } from '@server/types/index.js'
|
import { RegisterServerAuthExternalOptions } from '@server/types/index.js'
|
||||||
import {
|
import {
|
||||||
MAbuseMessage,
|
MAbuseMessage,
|
||||||
|
@ -77,8 +77,8 @@ declare module 'express' {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Upload file with a duration added by our middleware
|
// Upload file with a duration added by our middleware
|
||||||
export type VideoLegacyUploadFile = Pick<Express.Multer.File, 'path' | 'filename' | 'size', 'originalname'> & {
|
export type VideoLegacyUploadFile = Pick<Express.Multer.File, 'path' | 'filename' | 'size' | 'originalname'> & {
|
||||||
duration: number
|
duration?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// Our custom UploadXFile object using our custom metadata
|
// Our custom UploadXFile object using our custom metadata
|
||||||
|
@ -106,7 +106,7 @@ declare module 'express' {
|
||||||
|
|
||||||
title?: string
|
title?: string
|
||||||
status?: number
|
status?: number
|
||||||
type?: ServerErrorCode | string
|
type?: ServerErrorCodeType
|
||||||
instance?: string
|
instance?: string
|
||||||
|
|
||||||
data?: PeerTubeProblemDocumentData
|
data?: PeerTubeProblemDocumentData
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue