Migrate eslint to v9
This commit is contained in:
parent
bad8ea2c2e
commit
034e1bf328
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('--default', 'add the entry as the new default')
|
||||
.action(options => {
|
||||
/* eslint-disable no-import-assign */
|
||||
prompt.override = options
|
||||
prompt.start()
|
||||
prompt.get({
|
||||
@ -39,7 +38,6 @@ export function defineAuthProgram () {
|
||||
}
|
||||
}
|
||||
}, async (_, result) => {
|
||||
|
||||
// Check credentials
|
||||
try {
|
||||
// Strip out everything after the domain:port.
|
||||
@ -111,11 +109,13 @@ export function defineAuthProgram () {
|
||||
}
|
||||
})
|
||||
|
||||
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 root\n' +
|
||||
' $ peertube auth list\n' +
|
||||
' $ peertube auth del https://peertube.cpy.re\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 root\n' +
|
||||
' $ peertube auth list\n' +
|
||||
' $ peertube auth del https://peertube.cpy.re\n'
|
||||
)
|
||||
|
||||
return program
|
||||
|
@ -127,7 +127,7 @@ async function buildVideoAttributesFromCommander (server: PeerTubeServer, option
|
||||
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)) {
|
||||
if (options[key] !== undefined) {
|
||||
|
@ -72,8 +72,7 @@ function getRemoteObjectOrDie (
|
||||
settings: Settings,
|
||||
netrc: Netrc
|
||||
): { url: string, username: string, password: string } {
|
||||
|
||||
function exitIfNoOptions (optionNames: string[], errorPrefix: string = '') {
|
||||
function exitIfNoOptions (optionNames: string[], errorPrefix = '') {
|
||||
let exit = false
|
||||
|
||||
for (const key of optionNames) {
|
||||
@ -126,9 +125,9 @@ function listOptions (val: string) {
|
||||
|
||||
function getServerCredentials (options: CommonProgramOptions) {
|
||||
return Promise.all([ getSettings(), getNetrc() ])
|
||||
.then(([ settings, netrc ]) => {
|
||||
return getRemoteObjectOrDie(options, settings, netrc)
|
||||
})
|
||||
.then(([ settings, netrc ]) => {
|
||||
return getRemoteObjectOrDie(options, settings, netrc)
|
||||
})
|
||||
}
|
||||
|
||||
function buildServer (url: string) {
|
||||
@ -184,11 +183,8 @@ export {
|
||||
getRemoteObjectOrDie,
|
||||
writeSettings,
|
||||
deleteSettings,
|
||||
|
||||
getServerCredentials,
|
||||
|
||||
listOptions,
|
||||
|
||||
getAdminTokenOrDie,
|
||||
buildServer,
|
||||
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) {
|
||||
const usernameEl = await $('*=' + username)
|
||||
const usernameEl = $('*=' + username)
|
||||
await usernameEl.waitForDisplayed()
|
||||
|
||||
const tr = await findParentElement(usernameEl, async el => await el.getTagName() === 'tr')
|
||||
|
||||
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.click()
|
||||
|
||||
const moderationResponseTextarea = await $('#moderationResponse')
|
||||
const moderationResponseTextarea = $('#moderationResponse')
|
||||
await moderationResponseTextarea.waitForDisplayed()
|
||||
|
||||
await moderationResponseTextarea.setValue(moderationResponse)
|
||||
|
@ -3,7 +3,7 @@ import { getCheckbox } from '../utils'
|
||||
|
||||
export class AnonymousSettingsPage {
|
||||
async openSettings () {
|
||||
const link = await $('my-header .settings-button')
|
||||
const link = $('my-header .settings-button')
|
||||
await link.waitForClickable()
|
||||
await link.click()
|
||||
|
||||
@ -11,7 +11,7 @@ export class AnonymousSettingsPage {
|
||||
}
|
||||
|
||||
async closeSettings () {
|
||||
const closeModal = await $('.modal.show .modal-header > button')
|
||||
const closeModal = $('.modal.show .modal-header > button')
|
||||
await closeModal.waitForClickable()
|
||||
await closeModal.click()
|
||||
|
||||
|
@ -171,7 +171,7 @@ export class MyAccountPage {
|
||||
await selectCustomSelect('videoChannelId', 'Main root channel')
|
||||
await selectCustomSelect('privacy', privacy)
|
||||
|
||||
const submit = await $('form input[type=submit]')
|
||||
const submit = $('form input[type=submit]')
|
||||
await submit.waitForClickable()
|
||||
await submit.scrollIntoView()
|
||||
await submit.click()
|
||||
|
@ -22,9 +22,7 @@ export class PlayerPage {
|
||||
}
|
||||
|
||||
waitUntilPlayerWrapper () {
|
||||
return browser.waitUntil(async () => {
|
||||
return !!(await $('#placeholder-preview'))
|
||||
})
|
||||
return $('#placeholder-preview').waitForExist()
|
||||
}
|
||||
|
||||
waitUntilPlaying () {
|
||||
@ -98,7 +96,7 @@ export class PlayerPage {
|
||||
|
||||
async fillEmbedVideoPassword (videoPassword: string) {
|
||||
const videoPasswordInput = $('input#video-password-input')
|
||||
const confirmButton = await $('button#video-password-submit')
|
||||
const confirmButton = $('button#video-password-submit')
|
||||
|
||||
await videoPasswordInput.clearValue()
|
||||
await videoPasswordInput.setValue(videoPassword)
|
||||
|
@ -60,7 +60,7 @@ export class VideoListPage {
|
||||
}
|
||||
|
||||
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())
|
||||
|
||||
return texts.map(t => t.trim())
|
||||
@ -93,7 +93,7 @@ export class VideoListPage {
|
||||
}
|
||||
|
||||
private async getVideoMiniature (name: string) {
|
||||
const videoName = await $('.video-name=' + name)
|
||||
const videoName = $('.video-name=' + name)
|
||||
await videoName.waitForDisplayed()
|
||||
|
||||
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 {
|
||||
async navigateTo (tab?: 'Go live') {
|
||||
const publishButton = await $('.publish-button > a')
|
||||
const publishButton = $('.publish-button > a')
|
||||
|
||||
await publishButton.waitForClickable()
|
||||
await publishButton.click()
|
||||
@ -31,7 +31,7 @@ export class VideoPublishPage extends VideoManage {
|
||||
|
||||
await browser.pause(1000)
|
||||
|
||||
const elem = await $(fileInputSelector)
|
||||
const elem = $(fileInputSelector)
|
||||
await elem.chooseFile(fileToUpload)
|
||||
|
||||
// Wait for the upload to finish
|
||||
|
@ -108,12 +108,12 @@ export class VideoWatchPage {
|
||||
}
|
||||
|
||||
async fillVideoPassword (videoPassword: string) {
|
||||
const videoPasswordInput = await $('input#confirmInput')
|
||||
const videoPasswordInput = $('input#confirmInput')
|
||||
await videoPasswordInput.waitForClickable()
|
||||
await videoPasswordInput.clearValue()
|
||||
await videoPasswordInput.setValue(videoPassword)
|
||||
|
||||
const confirmButton = await $('input[value="Confirm"]')
|
||||
const confirmButton = $('input[value="Confirm"]')
|
||||
await confirmButton.waitForClickable()
|
||||
return confirmButton.click()
|
||||
}
|
||||
@ -123,7 +123,7 @@ export class VideoWatchPage {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async like () {
|
||||
const likeButton = await $('.action-button-like')
|
||||
const likeButton = $('.action-button-like')
|
||||
const isActivated = (await likeButton.getAttribute('class')).includes('activated')
|
||||
|
||||
let count: number
|
||||
@ -150,7 +150,7 @@ export class VideoWatchPage {
|
||||
async clickOnManage () {
|
||||
await this.clickOnMoreDropdownIcon()
|
||||
|
||||
const items = await $$('.dropdown-menu.show .dropdown-item')
|
||||
const items = $$('.dropdown-menu.show .dropdown-item')
|
||||
|
||||
for (const item of items) {
|
||||
const content = await item.getText()
|
||||
@ -205,36 +205,36 @@ export class VideoWatchPage {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async createThread (comment: string) {
|
||||
const textarea = await $('my-video-comment-add textarea')
|
||||
const textarea = $('my-video-comment-add textarea')
|
||||
await textarea.waitForClickable()
|
||||
|
||||
await textarea.setValue(comment)
|
||||
|
||||
const confirmButton = await $('.comment-buttons .primary-button')
|
||||
const confirmButton = $('.comment-buttons .primary-button')
|
||||
await confirmButton.waitForClickable()
|
||||
await confirmButton.click()
|
||||
|
||||
const createdComment = await (await $('.comment-html p')).getText()
|
||||
const createdComment = await $('.comment-html p').getText()
|
||||
|
||||
return expect(createdComment).toBe(comment)
|
||||
}
|
||||
|
||||
async createReply (comment: string) {
|
||||
const replyButton = await $('button.comment-action-reply')
|
||||
const replyButton = $('button.comment-action-reply')
|
||||
await replyButton.waitForClickable()
|
||||
await replyButton.scrollIntoView({ block: 'center' })
|
||||
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.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 replyButton.scrollIntoView({ block: 'center' })
|
||||
await confirmButton.click()
|
||||
|
||||
const createdComment = await $('.is-child .comment-html p')
|
||||
const createdComment = $('.is-child .comment-html p')
|
||||
await createdComment.waitForDisplayed()
|
||||
|
||||
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.hasPoster()).toBeTruthy()
|
||||
|
||||
const moreButton = await playerPage.getMoreNSFWInfoButton()
|
||||
const moreButton = playerPage.getMoreNSFWInfoButton()
|
||||
expect(await moreButton.isDisplayed()).toBeTruthy()
|
||||
|
||||
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 () {
|
||||
await go(videoUrl)
|
||||
|
||||
const confirmTitle = await videoWatchPage.getModalTitleEl()
|
||||
const confirmTitle = videoWatchPage.getModalTitleEl()
|
||||
await confirmTitle.waitForDisplayed()
|
||||
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": {
|
||||
"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'",
|
||||
"eslint": "eslint",
|
||||
"ng": "ng",
|
||||
@ -34,11 +34,6 @@
|
||||
],
|
||||
"typings": "*.d.ts",
|
||||
"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/build": "^19.1.5",
|
||||
"@angular/cdk": "^19.1.2",
|
||||
@ -73,8 +68,6 @@
|
||||
"@types/sanitize-html": "2.11.0",
|
||||
"@types/sha.js": "^2.4.0",
|
||||
"@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/cli": "^9.12.7",
|
||||
"@wdio/local-runner": "^9.12.7",
|
||||
@ -90,10 +83,6 @@
|
||||
"core-js": "^3.22.8",
|
||||
"debug": "^4.3.1",
|
||||
"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",
|
||||
"hls.js": "~1.5.11",
|
||||
"intl-messageformat": "^10.1.0",
|
||||
@ -126,5 +115,10 @@
|
||||
"vite-plugin-node-polyfills": "^0.23.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>
|
||||
|
||||
<button>toto</button>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="fromEmail">Your email</label>
|
||||
<input
|
||||
|
@ -194,8 +194,8 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
|
||||
.pipe(pairwise())
|
||||
.subscribe(([ oldValue, newValue ]) => {
|
||||
if (oldValue === false && newValue === true) {
|
||||
/* eslint-disable max-len */
|
||||
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.`
|
||||
|
||||
this.form().patchValue({
|
||||
|
@ -122,7 +122,6 @@ export class EditVODTranscodingComponent implements OnInit, OnChanges {
|
||||
if (newValue === false && hlsControl.value === false) {
|
||||
hlsControl.setValue(true)
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
this.notifier.info(
|
||||
$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)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
// eslint-disable-next-line max-len
|
||||
const message = formatICU(
|
||||
$localize`Accepted {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`,
|
||||
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) }
|
||||
@ -94,7 +93,6 @@ export class FollowersListComponent extends RestTable<ActorFollow> implements On
|
||||
}
|
||||
|
||||
async rejectFollower (follows: ActorFollow[]) {
|
||||
// eslint-disable-next-line max-len
|
||||
const message = formatICU(
|
||||
$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]) }
|
||||
@ -106,7 +104,6 @@ export class FollowersListComponent extends RestTable<ActorFollow> implements On
|
||||
this.followService.rejectFollower(follows)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
// eslint-disable-next-line max-len
|
||||
const message = formatICU(
|
||||
$localize`Rejected {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`,
|
||||
{ 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.`
|
||||
message += '<br /><br />'
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
message += formatICU(
|
||||
$localize`Do you really want to delete {count, plural, =1 {{followerName} follow request?} other {{count} follow requests?}}`,
|
||||
icuParams
|
||||
@ -138,7 +134,6 @@ export class FollowersListComponent extends RestTable<ActorFollow> implements On
|
||||
this.followService.removeFollower(follows)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
// eslint-disable-next-line max-len
|
||||
const message = formatICU(
|
||||
$localize`Removed {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`,
|
||||
icuParams
|
||||
|
@ -93,7 +93,6 @@ export class FollowingListComponent extends RestTable<ActorFollow> implements On
|
||||
this.followService.unfollow(follows)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
// eslint-disable-next-line max-len
|
||||
const message = formatICU(
|
||||
$localize`You are not following {count, plural, =1 {{entryName} anymore.} other {these {count} entries anymore.}}`,
|
||||
icuParams
|
||||
|
@ -143,7 +143,6 @@ export class RegistrationListComponent extends RestTable<UserRegistration> imple
|
||||
private async removeRegistrations (registrations: UserRegistration[]) {
|
||||
const icuParams = { count: registrations.length, username: registrations[0].username }
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
const message = formatICU(
|
||||
$localize`Do you really want to delete {count, plural, =1 {{username} registration request?} other {{count} registration requests?}}`,
|
||||
icuParams
|
||||
@ -155,7 +154,6 @@ export class RegistrationListComponent extends RestTable<UserRegistration> imple
|
||||
this.adminRegistrationService.removeRegistrations(registrations)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
// eslint-disable-next-line max-len
|
||||
const message = formatICU(
|
||||
$localize`Removed {count, plural, =1 {{username} registration request} other {{count} registration requests}}`,
|
||||
icuParams
|
||||
|
@ -7,7 +7,6 @@ import { SelectOptionsItem } from '../../../../../types/select-options-item.mode
|
||||
import { FormReactive } from '@app/shared/shared-forms/form-reactive'
|
||||
|
||||
@Directive()
|
||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||
export abstract class UserEdit extends FormReactive implements OnInit {
|
||||
videoQuotaOptions: SelectOptionsItem[] = []
|
||||
videoQuotaDailyOptions: SelectOptionsItem[] = []
|
||||
@ -53,7 +52,7 @@ export abstract class UserEdit extends FormReactive implements OnInit {
|
||||
.subscribe(translations => {
|
||||
if (authUser.role.id === UserRole.ADMINISTRATOR) {
|
||||
this.roles = Object.entries(USER_ROLE_LABELS)
|
||||
.map(([ key, value ]) => ({ value: key.toString(), label: peertubeTranslate(value, translations) }))
|
||||
.map(([ key, value ]) => ({ value: key.toString(), label: peertubeTranslate(value, translations) }))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -372,13 +372,11 @@ export class VideoListComponent extends RestTable<Video> implements OnInit {
|
||||
let message: string
|
||||
|
||||
if (type === 'hls') {
|
||||
// eslint-disable-next-line max-len
|
||||
message = formatICU(
|
||||
$localize`Are you sure you want to delete {count, plural, =1 {1 HLS streaming playlist} other {{count} HLS streaming playlists}}?`,
|
||||
{ count: videos.length }
|
||||
)
|
||||
} else {
|
||||
// eslint-disable-next-line max-len
|
||||
message = formatICU(
|
||||
$localize`Are you sure you want to delete Web Video files of {count, plural, =1 {1 video} other {{count} videos}}?`,
|
||||
{ count: videos.length }
|
||||
|
@ -1,14 +1,14 @@
|
||||
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({
|
||||
selector: 'my-player-styles',
|
||||
template: '',
|
||||
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,
|
||||
standalone: true
|
||||
})
|
||||
|
@ -179,7 +179,6 @@ export class VideoPublishComponent implements OnInit, CanComponentDeactivate {
|
||||
}
|
||||
|
||||
private async buildUploadMessages () {
|
||||
// eslint-disable-next-line max-len
|
||||
const noQuota =
|
||||
$localize`Sorry, the upload feature is disabled for your account. If you want to add videos, an admin must unlock your quota.`
|
||||
const autoBlock =
|
||||
@ -188,7 +187,6 @@ export class VideoPublishComponent implements OnInit, CanComponentDeactivate {
|
||||
const quotaLeftDaily =
|
||||
// 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.`
|
||||
// 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 uploadMessages = {
|
||||
|
@ -74,7 +74,7 @@ type Form = {
|
||||
previewfile: FormControl<Blob>
|
||||
support: FormControl<string>
|
||||
schedulePublicationAt: FormControl<Date>
|
||||
pluginData: FormGroup<any>
|
||||
pluginData: FormGroup
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -372,7 +372,6 @@ export class VideoManageController implements OnDestroy {
|
||||
|
||||
let blockedWarning = ''
|
||||
if (willBeBlocked) {
|
||||
// eslint-disable-next-line max-len
|
||||
blockedWarning = ' ' +
|
||||
$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
|
||||
if (this.serverConfig.instance.customizations.javascript) {
|
||||
try {
|
||||
/* eslint-disable no-eval */
|
||||
window.eval(this.serverConfig.instance.customizations.javascript)
|
||||
} catch (err) {
|
||||
logger.error('Cannot eval custom JavaScript.', err)
|
||||
|
@ -36,7 +36,7 @@ export class Hotkey {
|
||||
|
||||
constructor (
|
||||
public combo: string | string[],
|
||||
public callback: (event: KeyboardEvent, combo: string) => any | boolean,
|
||||
public callback: (event: KeyboardEvent, combo: string) => any,
|
||||
public description?: string | Function
|
||||
) {
|
||||
this.combo = arrayify(combo)
|
||||
|
@ -40,7 +40,6 @@ export class ActorBannerEditComponent implements OnInit {
|
||||
this.maxBannerSize = config.banner.file.size.max
|
||||
this.bannerExtensions = config.banner.file.extensions.join(', ')
|
||||
|
||||
/* eslint-disable max-len */
|
||||
this.bannerFormat = $localize`ratio 6/1, recommended size: 1920x317, max size: ${
|
||||
getBytes(this.maxBannerSize)
|
||||
}, extensions: ${this.bannerExtensions}`
|
||||
|
@ -39,6 +39,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
|
||||
readonly placeholder = input($localize`Filter...`)
|
||||
readonly inputId = input('table-filter')
|
||||
|
||||
// eslint-disable-next-line @angular-eslint/no-output-native
|
||||
readonly search = output<string>()
|
||||
|
||||
searchValue: string
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { SortMeta } from 'primeng/api'
|
||||
import { Directive, OnInit, inject } from '@angular/core'
|
||||
import { Notifier, RestPagination, RestTable } from '@app/core'
|
||||
import { SortMeta } from 'primeng/api'
|
||||
import { AccountBlock } from './account-block.model'
|
||||
import { BlocklistComponentType, BlocklistService } from './blocklist.service'
|
||||
|
||||
@Directive()
|
||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||
export class GenericAccountBlocklistComponent extends RestTable implements OnInit {
|
||||
private notifier = inject(Notifier)
|
||||
private blocklistService = inject(BlocklistService)
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { SortMeta } from 'primeng/api'
|
||||
import { Directive, OnInit, inject, viewChild } from '@angular/core'
|
||||
import { Notifier, RestPagination, RestTable } from '@app/core'
|
||||
import { BatchDomainsModalComponent } from '@app/shared/shared-moderation/batch-domains-modal.component'
|
||||
import { ServerBlock } from '@peertube/peertube-models'
|
||||
import { SortMeta } from 'primeng/api'
|
||||
import { BlocklistComponentType, BlocklistService } from './blocklist.service'
|
||||
|
||||
@Directive()
|
||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||
export class GenericServerBlocklistComponent extends RestTable implements OnInit {
|
||||
protected notifier = inject(Notifier)
|
||||
protected blocklistService = inject(BlocklistService)
|
||||
|
@ -398,9 +398,7 @@ export class VideosListComponent implements OnInit, OnDestroy {
|
||||
for (const video of this.videos) {
|
||||
const publishedDate = video.publishedAt
|
||||
|
||||
for (let i = 0; i < periods.length; i++) {
|
||||
const period = periods[i]
|
||||
|
||||
for (const period of periods) {
|
||||
if (currentGroupedDate <= period.value && period.validator(publishedDate)) {
|
||||
if (currentGroupedDate !== period.value) {
|
||||
if (period.value !== GroupDate.OLDER) onlyOlderPeriod = false
|
||||
|
@ -6,7 +6,7 @@ const dictionary: { max: number, type: string }[] = [
|
||||
{ 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 calc = value / (format.max / 1024)
|
||||
|
||||
|
@ -121,7 +121,6 @@ class PluginsManager {
|
||||
|
||||
async runHook<T> (hookName: ClientHookName, resultArg?: T | Promise<T>, params?: any) {
|
||||
if (!this.hooks[hookName]) {
|
||||
// eslint-disable-next-line no-return-await
|
||||
return await resultArg
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ export class ThemeManager {
|
||||
loadThemeStyle (name: string) {
|
||||
const links = document.getElementsByTagName('link')
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
const link = links[i]
|
||||
if (link.getAttribute('rel').includes('style') && link.getAttribute('title')) {
|
||||
|
@ -57,7 +57,6 @@ class ContextMenuPlugin extends Plugin {
|
||||
|
||||
menu.on('dispose', () => {
|
||||
for (const event of [ 'click', 'tap' ]) {
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
videojs.off(documentEl as Element, event, menu.dispose)
|
||||
}
|
||||
|
||||
@ -84,7 +83,6 @@ class ContextMenuPlugin extends Plugin {
|
||||
}
|
||||
|
||||
for (const event of [ 'click', 'tap' ]) {
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
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
|
||||
// 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)
|
||||
|
||||
for (const c of options.content()) {
|
||||
this.addItem(new ContextMenuItem(player, {
|
||||
label: c.label,
|
||||
listener: videojs.bind(player, c.listener)
|
||||
}))
|
||||
this.addItem(
|
||||
new ContextMenuItem(player, {
|
||||
label: c.label,
|
||||
listener: videojs.bind(player, c.listener)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@ const ClickableComponent = videojs.getComponent('ClickableComponent')
|
||||
export class ProgressBarMarkerComponent extends ClickableComponent {
|
||||
declare options_: ProgressBarMarkerComponentOptions & videojs.ComponentOptions
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
||||
constructor (player: videojs.Player, options?: ProgressBarMarkerComponentOptions & videojs.ComponentOptions) {
|
||||
super(player, options)
|
||||
|
||||
|
@ -240,11 +240,11 @@ class PeerTubeHotkeysPlugin extends Plugin {
|
||||
const isNonLatin = key.charCodeAt(0) >= capitalHetaCode
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
type ErrorCounts = {
|
||||
[ type: string ]: number
|
||||
[type: string]: number
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -35,10 +35,8 @@ const registerSourceHandler = function (vjs: typeof videojs) {
|
||||
|
||||
if (alreadyRegistered) return
|
||||
|
||||
alreadyRegistered = true;
|
||||
|
||||
// FIXME: typings
|
||||
(html5 as any).registerSourceHandler({
|
||||
alreadyRegistered = true // FIXME: typings
|
||||
;(html5 as any).registerSourceHandler({
|
||||
canHandleSource: function (source: videojs.Tech.SourceObject) {
|
||||
const hlsTypeRE = /^application\/x-mpegURL|application\/vnd\.apple\.mpegurl$/i
|
||||
const hlsExtRE = /\.m3u8/i
|
||||
@ -58,10 +56,8 @@ const registerSourceHandler = function (vjs: typeof videojs) {
|
||||
|
||||
return tech.hlsProvider
|
||||
}
|
||||
}, 0);
|
||||
|
||||
// FIXME: typings
|
||||
(vjs as any).Html5Hlsjs = Html5Hlsjs
|
||||
}, 0) // FIXME: typings
|
||||
;(vjs as any).Html5Hlsjs = Html5Hlsjs
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -71,7 +67,6 @@ const registerSourceHandler = function (vjs: typeof videojs) {
|
||||
const Plugin = videojs.getPlugin('plugin')
|
||||
|
||||
class HLSJSConfigHandler extends Plugin {
|
||||
|
||||
constructor (player: videojs.Player, options: HlsjsConfigHandlerOptions) {
|
||||
super(player, options)
|
||||
|
||||
@ -132,7 +127,7 @@ export class Html5Hlsjs {
|
||||
|
||||
private liveEnded = false
|
||||
|
||||
private handlers: { [ id in 'play' | 'error' ]: EventListener } = {
|
||||
private handlers: { [id in 'play' | 'error']: EventListener } = {
|
||||
play: null,
|
||||
error: null
|
||||
}
|
||||
@ -143,8 +138,8 @@ export class Html5Hlsjs {
|
||||
this.vjs = vjs
|
||||
this.source = source
|
||||
|
||||
this.tech = tech;
|
||||
(this.tech as any).name_ = 'Hlsjs'
|
||||
this.tech = tech
|
||||
;(this.tech as any).name_ = 'Hlsjs'
|
||||
|
||||
this.videoElement = tech.el() as HTMLVideoElement
|
||||
this.player = vjs((tech.options_ as any).playerId)
|
||||
@ -162,7 +157,7 @@ export class Html5Hlsjs {
|
||||
break
|
||||
case mediaError.MEDIA_ERR_DECODE:
|
||||
errorTxt = 'The video playback was aborted due to a corruption problem or because the video used features ' +
|
||||
'your browser did not support'
|
||||
'your browser did not support'
|
||||
this._handleMediaError(mediaError)
|
||||
break
|
||||
case mediaError.MEDIA_ERR_NETWORK:
|
||||
@ -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) {
|
||||
this._removeQuality(this.hls.loadLevel)
|
||||
return
|
||||
@ -255,7 +250,7 @@ export class Html5Hlsjs {
|
||||
}
|
||||
|
||||
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
|
||||
if (this.liveEnded) {
|
||||
logger.info('Forcing end of live stream after a network error');
|
||||
|
||||
(this.player as any)?.handleTechEnded_()
|
||||
logger.info('Forcing end of live stream after a network error')
|
||||
;(this.player as any)?.handleTechEnded_()
|
||||
this.hls?.stopLoad()
|
||||
|
||||
return
|
||||
@ -286,7 +280,7 @@ export class Html5Hlsjs {
|
||||
return
|
||||
}
|
||||
|
||||
this._handleUnrecovarableError(error)
|
||||
this._handleUnrecoverableError(error)
|
||||
}
|
||||
|
||||
private _onError (_event: any, data: ErrorData) {
|
||||
@ -307,11 +301,14 @@ export class Html5Hlsjs {
|
||||
if (data.type === Hlsjs.ErrorTypes.NETWORK_ERROR) {
|
||||
error.code = 2
|
||||
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
|
||||
this._handleMediaError(error)
|
||||
} else if (data.fatal) {
|
||||
this._handleUnrecovarableError(error)
|
||||
this._handleUnrecoverableError(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,9 +144,9 @@ class SettingsButton extends Button {
|
||||
}
|
||||
|
||||
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.el().setAttribute('aria-expanded', 'true')
|
||||
@ -162,8 +162,8 @@ class SettingsButton extends Button {
|
||||
this.dialog.hide()
|
||||
this.el().setAttribute('aria-expanded', 'false')
|
||||
|
||||
this.setDialogSize(this.getComponentSize(this.menu));
|
||||
(this.menu.el() as HTMLElement).style.opacity = '1'
|
||||
this.setDialogSize(this.getComponentSize(this.menu))
|
||||
;(this.menu.el() as HTMLElement).style.opacity = '1'
|
||||
this.resetChildren()
|
||||
}
|
||||
|
||||
@ -248,7 +248,6 @@ class SettingsButton extends Button {
|
||||
|
||||
// Hide children to avoid sub menus stacking on top of each other
|
||||
// or having multiple menus open
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
settingsMenuItem.on('click', videojs.bind(this, this.hideChildren))
|
||||
|
||||
// Whether to add or remove selected class on the settings sub menu element
|
||||
@ -273,7 +272,6 @@ class SettingsButton extends Button {
|
||||
isInIframe () {
|
||||
return window.self !== window.top
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component.registerComponent('SettingsButton', SettingsButton)
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"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/typescript-utils" }
|
||||
],
|
||||
"files": [
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/polyfills.ts",
|
||||
"src/environments/*.ts",
|
||||
"src/main*.ts",
|
||||
"src/**/*.d.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": {
|
||||
"@peertube/maildev": "^1.2.0",
|
||||
"@peertube/resolve-tspaths": "^0.8.14",
|
||||
"@stylistic/eslint-plugin": "^4.2.0",
|
||||
"@types/archiver": "^6.0.2",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/bencode": "^2.0.0",
|
||||
@ -230,7 +231,6 @@
|
||||
"@types/webtorrent": "^0.109.0",
|
||||
"@types/ws": "^8.2.0",
|
||||
"@types/yauzl": "^2.10.3",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
||||
"autocannon": "^8.0.0",
|
||||
"chai": "^5.1.0",
|
||||
"chai-json-schema": "^1.5.0",
|
||||
@ -238,11 +238,8 @@
|
||||
"concurrently": "^9.1.2",
|
||||
"depcheck": "^1.4.2",
|
||||
"esbuild": "^0.24.2",
|
||||
"eslint": "8.56.0",
|
||||
"eslint-config-standard-with-typescript": "43.0.1",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"eslint-plugin-n": "^16.0.0",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint": "^9.26.0",
|
||||
"eslint-config-love": "^119.0.0",
|
||||
"fast-xml-parser": "^4.0.0-beta.8",
|
||||
"jpeg-js": "^0.4.4",
|
||||
"jszip": "^3.10.1",
|
||||
|
@ -79,7 +79,7 @@ const I18N_LOCALE_ALIAS = {
|
||||
'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 () {
|
||||
return 'en-US'
|
||||
@ -89,7 +89,7 @@ export function isDefaultLocale (locale: string) {
|
||||
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
|
||||
|
||||
return translations[str]
|
||||
|
@ -3,9 +3,7 @@ import { VideoResolution } from '@peertube/peertube-models'
|
||||
import ffmpeg, { FfprobeData } from 'fluent-ffmpeg'
|
||||
|
||||
/**
|
||||
*
|
||||
* Helpers to run ffprobe and extract data from the JSON output
|
||||
*
|
||||
*/
|
||||
|
||||
function ffprobePromise (path: string) {
|
||||
@ -23,9 +21,45 @@ function ffprobePromise (path: string) {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const imageCodecs = new Set([
|
||||
'ansi', 'apng', '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'
|
||||
'ansi',
|
||||
'apng',
|
||||
'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) {
|
||||
@ -61,7 +95,7 @@ async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) {
|
||||
return { absolutePath: data.format.filename }
|
||||
}
|
||||
|
||||
function getMaxAudioBitrate (type: 'aac' | 'mp3' | string, bitrate: number) {
|
||||
function getMaxAudioBitrate (type: string, bitrate: number) {
|
||||
const maxKBitrate = 384
|
||||
const kToBits = (kbits: number) => kbits * 1000
|
||||
|
||||
@ -209,16 +243,13 @@ export {
|
||||
ffprobePromise,
|
||||
getAudioStream,
|
||||
getChaptersFromContainer,
|
||||
|
||||
getMaxAudioBitrate,
|
||||
|
||||
getVideoStream,
|
||||
getVideoStreamBitrate,
|
||||
getVideoStreamDimensionsInfo,
|
||||
getVideoStreamDuration,
|
||||
getVideoStreamFPS,
|
||||
hasAudioStream,
|
||||
|
||||
hasVideoStream,
|
||||
isAudioFile
|
||||
}
|
||||
|
@ -5,15 +5,15 @@ import { buildStreamSuffix } from '../ffmpeg-utils.js'
|
||||
export function addDefaultEncoderGlobalParams (command: FfmpegCommand) {
|
||||
// avoid issues when transcoding some files: https://trac.ffmpeg.org/ticket/6375
|
||||
command.outputOption('-max_muxing_queue_size 1024')
|
||||
// strip all metadata
|
||||
.outputOption('-map_metadata -1')
|
||||
// allows import of source material with incompatible pixel formats (e.g. MJPEG video)
|
||||
.outputOption('-pix_fmt yuv420p')
|
||||
// strip all metadata
|
||||
.outputOption('-map_metadata -1')
|
||||
// allows import of source material with incompatible pixel formats (e.g. MJPEG video)
|
||||
.outputOption('-pix_fmt yuv420p')
|
||||
}
|
||||
|
||||
export function addDefaultEncoderParams (options: {
|
||||
command: FfmpegCommand
|
||||
encoder: 'libx264' | string
|
||||
encoder: string
|
||||
fps: number
|
||||
|
||||
streamNum?: number
|
||||
|
@ -2,6 +2,6 @@ import { ServerHookName } from './server-hook.model.js'
|
||||
|
||||
export interface RegisterServerHookOptions {
|
||||
target: ServerHookName
|
||||
handler: Function
|
||||
handler: () => any
|
||||
priority?: number
|
||||
}
|
||||
|
@ -17,5 +17,4 @@ export type VideoSortField =
|
||||
// trending sorts
|
||||
'trending' | '-trending' |
|
||||
'hot' | '-hot' |
|
||||
'best' | '-best' |
|
||||
'best' | '-best'
|
||||
|
@ -122,7 +122,7 @@ export function makeUploadRequest (
|
||||
method?: 'POST' | 'PUT'
|
||||
|
||||
fields: { [fieldName: string]: any }
|
||||
attaches?: { [attachName: string]: any | any[] }
|
||||
attaches?: { [attachName: string]: any }
|
||||
}
|
||||
) {
|
||||
let req = options.method === 'PUT'
|
||||
|
@ -5,12 +5,14 @@ export function setDefaultVideoChannel (servers: PeerTubeServer[]) {
|
||||
return Promise.all(
|
||||
servers.map(s => {
|
||||
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)
|
||||
|
||||
return Promise.all(
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||
|
||||
import { pick } from '@peertube/peertube-core-utils'
|
||||
|
@ -170,7 +170,6 @@ export async function checkResolutionsInMasterPlaylist (options: {
|
||||
|
||||
if (splittedAudio && hasAudio && hasVideo) {
|
||||
expect(masterPlaylist).to.match(
|
||||
// eslint-disable-next-line max-len
|
||||
new RegExp(
|
||||
`#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'
|
||||
|
||||
// From bittorrent-tracker
|
||||
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
||||
const infohashBinary = escape(Buffer.from(infohash, 'hex').toString('binary')).replace(/[@*/+]/g, function (char) {
|
||||
return '%' + char.charCodeAt(0).toString(16).toUpperCase()
|
||||
})
|
||||
|
@ -39,7 +39,7 @@ export class TranscriberFactory {
|
||||
getEngineByName (engineName: string) {
|
||||
const engine = this.engines.find(({ name }) => name === engineName)
|
||||
if (!engine) {
|
||||
throw new Error(`Unknow engine ${engineName}`)
|
||||
throw new Error(`Unknown engine ${engineName}`)
|
||||
}
|
||||
|
||||
return engine
|
||||
|
@ -30,7 +30,8 @@ export class TranscriptFile {
|
||||
const guessableFormats = [ 'txt', 'vtt', 'srt' ]
|
||||
assert(
|
||||
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 })
|
||||
}
|
||||
@ -49,7 +50,7 @@ export class TranscriptFile {
|
||||
return new TranscriptFile({ path, language, format })
|
||||
}
|
||||
|
||||
async equals (transcript: TranscriptFile, caseSensitive: boolean = true) {
|
||||
async equals (transcript: TranscriptFile, caseSensitive = true) {
|
||||
if (this.language !== transcript.language) {
|
||||
return false
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ elif [ "$1" = "external-plugins" ]; then
|
||||
runJSTest "$1" 1 $externalPluginsFiles
|
||||
MOCHA_PARALLEL=true runJSTest "$1" $((2*$speedFactor)) $peertubeRunnerFiles
|
||||
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
|
||||
|
||||
|
@ -16,28 +16,32 @@ const lTags = loggerTagsFactory('api', 'runner')
|
||||
|
||||
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,
|
||||
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||
asyncMiddleware(getMaxQualitySeparatedAudioFile)
|
||||
)
|
||||
|
||||
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/max-quality',
|
||||
runnerJobFilesRouter.post(
|
||||
'/jobs/:jobUUID/files/videos/:videoId/max-quality',
|
||||
apiRateLimiter,
|
||||
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||
asyncMiddleware(getMaxQualityVideoFile)
|
||||
)
|
||||
|
||||
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/previews/max-quality',
|
||||
runnerJobFilesRouter.post(
|
||||
'/jobs/:jobUUID/files/videos/:videoId/previews/max-quality',
|
||||
apiRateLimiter,
|
||||
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||
getMaxQualityVideoPreview
|
||||
)
|
||||
|
||||
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/studio/task-files/:filename',
|
||||
runnerJobFilesRouter.post(
|
||||
'/jobs/:jobUUID/files/videos/:videoId/studio/task-files/:filename',
|
||||
apiRateLimiter,
|
||||
asyncMiddleware(jobOfRunnerGetValidatorFactory([ RunnerJobState.PROCESSING ])),
|
||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||
@ -59,7 +63,10 @@ async function getMaxQualitySeparatedAudioFile (req: express.Request, res: expre
|
||||
const video = res.locals.videoAll
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
@ -74,7 +81,10 @@ async function getMaxQualityVideoFile (req: express.Request, res: express.Respon
|
||||
const video = res.locals.videoAll
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
@ -124,7 +134,10 @@ function getMaxQualityVideoPreview (req: express.Request, res: express.Response)
|
||||
const video = res.locals.videoAll
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
@ -140,7 +153,11 @@ function getVideoStudioTaskFile (req: express.Request, res: express.Response) {
|
||||
const filename = req.params.filename
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
|
@ -164,7 +164,7 @@ async function replaceVideoSourceResumable (req: express.Request, res: express.R
|
||||
async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVideoFile) {
|
||||
const jobs: (CreateJobArgument & CreateJobOptions)[] = [
|
||||
{
|
||||
type: 'manage-video-torrent' as 'manage-video-torrent',
|
||||
type: 'manage-video-torrent' as const,
|
||||
payload: {
|
||||
videoId: video.id,
|
||||
videoFileId: videoFile.id,
|
||||
@ -175,7 +175,7 @@ async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVide
|
||||
buildStoryboardJobIfNeeded({ video, federate: false }),
|
||||
|
||||
{
|
||||
type: 'federate-video' as 'federate-video',
|
||||
type: 'federate-video' as const,
|
||||
payload: {
|
||||
videoUUID: video.uuid,
|
||||
isNewVideoForFederation: false
|
||||
@ -189,7 +189,7 @@ async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVide
|
||||
|
||||
if (video.state === VideoState.TO_TRANSCODE) {
|
||||
jobs.push({
|
||||
type: 'transcoding-job-builder' as 'transcoding-job-builder',
|
||||
type: 'transcoding-job-builder' as const,
|
||||
payload: {
|
||||
videoUUID: video.uuid,
|
||||
optimizeJob: {
|
||||
|
@ -73,9 +73,7 @@ lazyStaticRouter.use(
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
lazyStaticRouter,
|
||||
getPreview,
|
||||
getVideoCaption
|
||||
lazyStaticRouter
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@ -33,9 +33,9 @@ async function getSitemap (req: express.Request, res: express.Response) {
|
||||
const sitemapStream = new SitemapStream({
|
||||
hostname: WEBSERVER.URL,
|
||||
errorHandler: (err: Error, level: ErrorLevel) => {
|
||||
if (level === 'warn') {
|
||||
if (level === ErrorLevel.WARN) {
|
||||
logger.warn('Warning in sitemap generation.', { err })
|
||||
} else if (level === 'throw') {
|
||||
} else if (level === ErrorLevel.THROW) {
|
||||
logger.error('Error in sitemap generation.', { err })
|
||||
|
||||
throw err
|
||||
|
@ -208,7 +208,6 @@ function parseSemVersion (s: string) {
|
||||
function execShell (command: string, options?: ExecOptions) {
|
||||
return new Promise<{ err?: Error, stdout: string, stderr: string }>((res, rej) => {
|
||||
exec(command, options, (err, stdout, stderr) => {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
if (err) return rej({ err, stdout, stderr })
|
||||
|
||||
return res({ stdout, stderr })
|
||||
@ -274,25 +273,17 @@ const pipelinePromise = promisify(pipeline)
|
||||
export {
|
||||
objectConverter,
|
||||
mapToJSON,
|
||||
|
||||
sanitizeUrl,
|
||||
sanitizeHost,
|
||||
|
||||
execShell,
|
||||
|
||||
pageToStartAndCount,
|
||||
peertubeTruncate,
|
||||
|
||||
scryptPromise,
|
||||
|
||||
randomBytesPromise,
|
||||
|
||||
generateRSAKeyPairPromise,
|
||||
generateED25519KeyPairPromise,
|
||||
|
||||
execPromise2,
|
||||
execPromise,
|
||||
pipelinePromise,
|
||||
|
||||
parseSemVersion
|
||||
}
|
||||
|
@ -55,10 +55,9 @@ const STATIC_CACHE = {
|
||||
|
||||
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) {
|
||||
return {
|
||||
contextUrl: null,
|
||||
|
@ -6,7 +6,7 @@ function isWebfingerLocalResourceValid (value: string) {
|
||||
if (!exists(value)) return false
|
||||
if (value.startsWith('acct:') === false) return false
|
||||
|
||||
const actorWithHost = value.substr(5)
|
||||
const actorWithHost = value.substring(5)
|
||||
const actorParts = actorWithHost.split('@')
|
||||
if (actorParts.length !== 2) return false
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
import { Module } from 'module'
|
||||
import { extname } from 'path'
|
||||
|
||||
function decachePlugin (require: NodeRequire, libraryPath: string) {
|
||||
function decachePlugin (require: NodeJS.Require, libraryPath: string) {
|
||||
const moduleName = find(require, libraryPath)
|
||||
|
||||
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)
|
||||
|
||||
if (!moduleName) return
|
||||
@ -37,7 +37,7 @@ export {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function find (require: NodeRequire, moduleName: string) {
|
||||
function find (require: NodeJS.Require, moduleName: string) {
|
||||
try {
|
||||
return require.resolve(moduleName)
|
||||
} 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)
|
||||
let mod: NodeModule
|
||||
let mod: NodeJS.Module
|
||||
const visited = {}
|
||||
|
||||
if (resolvedModule && ((mod = require.cache[resolvedModule]) !== undefined)) {
|
||||
// Recursively go over the results
|
||||
(function run (current) {
|
||||
;(function run (current) {
|
||||
visited[current.id] = true
|
||||
|
||||
current.children.forEach(function (child) {
|
||||
@ -66,10 +66,10 @@ function searchCache (require: NodeRequire, moduleName: string, callback: (curre
|
||||
callback(current)
|
||||
})(mod)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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) {
|
||||
if (cacheKey.includes(pluginPath)) {
|
||||
|
@ -58,8 +58,7 @@ export const unsafeSSRFGot = got.extend({
|
||||
|
||||
const bodyLimit = bodyKBLimit * 1000
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||
promiseOrStream.on('downloadProgress', progress => {
|
||||
void promiseOrStream.on('downloadProgress', progress => {
|
||||
if (progress.transferred > bodyLimit && progress.percent !== 1) {
|
||||
const message = `Exceeded the download limit of ${bodyLimit} B`
|
||||
logger.warn(message, lTags())
|
||||
|
@ -20,10 +20,10 @@ class StreamReplacer extends Transform {
|
||||
// readable side of the transform stream
|
||||
while ((index = this.pendingChunk.indexOf('\n')) !== -1) {
|
||||
// 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
|
||||
this.pendingChunk = this.pendingChunk.slice(index)
|
||||
this.pendingChunk = this.pendingChunk.subarray(index)
|
||||
|
||||
// We have a single line here! Prepend the string we want
|
||||
this.push(this.doReplace(line))
|
||||
|
@ -47,7 +47,7 @@ export async function unzip (options: {
|
||||
const entryPath = join(destination, entry.fileName)
|
||||
|
||||
try {
|
||||
if (/\/$/.test(entry.fileName)) {
|
||||
if (entry.fileName.endsWith('/')) {
|
||||
await ensureDir(entryPath)
|
||||
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)
|
||||
|
||||
export class YoutubeDLCLI {
|
||||
|
||||
static async safeGet () {
|
||||
if (!await pathExists(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
|
||||
*
|
||||
* in any case we avoid AV1, see https://github.com/Chocobozzz/PeerTube/issues/3499
|
||||
**/
|
||||
*/
|
||||
|
||||
let result: string[] = []
|
||||
|
||||
@ -111,7 +110,6 @@ export class YoutubeDLCLI {
|
||||
}
|
||||
|
||||
private constructor () {
|
||||
|
||||
}
|
||||
|
||||
download (options: {
|
||||
@ -199,7 +197,7 @@ export class YoutubeDLCLI {
|
||||
for (let i = 0, len = data.length; i < len; i++) {
|
||||
const line = data[i]
|
||||
|
||||
if (line.indexOf(skipString) === 0) {
|
||||
if (line.startsWith(skipString)) {
|
||||
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('Cache-Control', 'max-age=0, no-cache, must-revalidate')
|
||||
|
||||
|
@ -117,7 +117,6 @@ export class TagsHtml {
|
||||
|
||||
// OEmbed
|
||||
for (const oembedLinkTag of oembedLinkTags) {
|
||||
// eslint-disable-next-line max-len
|
||||
tagsStr += `<link rel="alternate" type="${oembedLinkTag.type}" href="${oembedLinkTag.href}" title="${
|
||||
escapeAttribute(oembedLinkTag.escapedTitle)
|
||||
}" />`
|
||||
|
@ -13,18 +13,21 @@ export interface PeerTubeInternalEvents {
|
||||
'chapters-updated': (options: { video: MVideoImmutable }) => void
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||
declare interface InternalEventEmitter {
|
||||
on<U extends keyof PeerTubeInternalEvents>(
|
||||
event: U, listener: PeerTubeInternalEvents[U]
|
||||
event: U,
|
||||
listener: PeerTubeInternalEvents[U]
|
||||
): this
|
||||
|
||||
emit<U extends keyof PeerTubeInternalEvents>(
|
||||
event: U, ...args: Parameters<PeerTubeInternalEvents[U]>
|
||||
event: U,
|
||||
...args: Parameters<PeerTubeInternalEvents[U]>
|
||||
): boolean
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||
class InternalEventEmitter extends EventEmitter {
|
||||
|
||||
private static instance: InternalEventEmitter
|
||||
|
||||
static get Instance () {
|
||||
|
@ -77,34 +77,34 @@ import { processVideoTranscription } from './handlers/video-transcription.js'
|
||||
import { processVideosViewsStats } from './handlers/video-views-stats.js'
|
||||
|
||||
export type CreateJobArgument =
|
||||
{ type: 'activitypub-http-broadcast', payload: ActivitypubHttpBroadcastPayload } |
|
||||
{ type: 'activitypub-http-broadcast-parallel', payload: ActivitypubHttpBroadcastPayload } |
|
||||
{ type: 'activitypub-http-unicast', payload: ActivitypubHttpUnicastPayload } |
|
||||
{ type: 'activitypub-http-fetcher', payload: ActivitypubHttpFetcherPayload } |
|
||||
{ type: 'activitypub-cleaner', payload: {} } |
|
||||
{ type: 'activitypub-follow', payload: ActivitypubFollowPayload } |
|
||||
{ type: 'video-file-import', payload: VideoFileImportPayload } |
|
||||
{ type: 'video-transcoding', payload: VideoTranscodingPayload } |
|
||||
{ type: 'email', payload: EmailPayload } |
|
||||
{ type: 'transcoding-job-builder', payload: TranscodingJobBuilderPayload } |
|
||||
{ type: 'video-import', payload: VideoImportPayload } |
|
||||
{ type: 'activitypub-refresher', payload: RefreshPayload } |
|
||||
{ type: 'videos-views-stats', payload: {} } |
|
||||
{ type: 'video-live-ending', payload: VideoLiveEndingPayload } |
|
||||
{ type: 'actor-keys', payload: ActorKeysPayload } |
|
||||
{ type: 'video-redundancy', payload: VideoRedundancyPayload } |
|
||||
{ type: 'video-studio-edition', payload: VideoStudioEditionPayload } |
|
||||
{ type: 'manage-video-torrent', payload: ManageVideoTorrentPayload } |
|
||||
{ type: 'move-to-object-storage', payload: MoveStoragePayload } |
|
||||
{ type: 'move-to-file-system', payload: MoveStoragePayload } |
|
||||
{ type: 'video-channel-import', payload: VideoChannelImportPayload } |
|
||||
{ type: 'after-video-channel-import', payload: AfterVideoChannelImportPayload } |
|
||||
{ type: 'notify', payload: NotifyPayload } |
|
||||
{ type: 'federate-video', payload: FederateVideoPayload } |
|
||||
{ type: 'create-user-export', payload: CreateUserExportPayload } |
|
||||
{ type: 'generate-video-storyboard', payload: GenerateStoryboardPayload } |
|
||||
{ type: 'import-user-archive', payload: ImportUserArchivePayload } |
|
||||
{ type: 'video-transcription', payload: VideoTranscriptionPayload }
|
||||
| { type: 'activitypub-http-broadcast', payload: ActivitypubHttpBroadcastPayload }
|
||||
| { type: 'activitypub-http-broadcast-parallel', payload: ActivitypubHttpBroadcastPayload }
|
||||
| { type: 'activitypub-http-unicast', payload: ActivitypubHttpUnicastPayload }
|
||||
| { type: 'activitypub-http-fetcher', payload: ActivitypubHttpFetcherPayload }
|
||||
| { type: 'activitypub-cleaner', payload: {} }
|
||||
| { type: 'activitypub-follow', payload: ActivitypubFollowPayload }
|
||||
| { type: 'video-file-import', payload: VideoFileImportPayload }
|
||||
| { type: 'video-transcoding', payload: VideoTranscodingPayload }
|
||||
| { type: 'email', payload: EmailPayload }
|
||||
| { type: 'transcoding-job-builder', payload: TranscodingJobBuilderPayload }
|
||||
| { type: 'video-import', payload: VideoImportPayload }
|
||||
| { type: 'activitypub-refresher', payload: RefreshPayload }
|
||||
| { type: 'videos-views-stats', payload: {} }
|
||||
| { type: 'video-live-ending', payload: VideoLiveEndingPayload }
|
||||
| { type: 'actor-keys', payload: ActorKeysPayload }
|
||||
| { type: 'video-redundancy', payload: VideoRedundancyPayload }
|
||||
| { type: 'video-studio-edition', payload: VideoStudioEditionPayload }
|
||||
| { type: 'manage-video-torrent', payload: ManageVideoTorrentPayload }
|
||||
| { type: 'move-to-object-storage', payload: MoveStoragePayload }
|
||||
| { type: 'move-to-file-system', payload: MoveStoragePayload }
|
||||
| { type: 'video-channel-import', payload: VideoChannelImportPayload }
|
||||
| { type: 'after-video-channel-import', payload: AfterVideoChannelImportPayload }
|
||||
| { type: 'notify', payload: NotifyPayload }
|
||||
| { type: 'federate-video', payload: FederateVideoPayload }
|
||||
| { type: 'create-user-export', payload: CreateUserExportPayload }
|
||||
| { type: 'generate-video-storyboard', payload: GenerateStoryboardPayload }
|
||||
| { type: 'import-user-archive', payload: ImportUserArchivePayload }
|
||||
| { type: 'video-transcription', payload: VideoTranscriptionPayload }
|
||||
|
||||
export type CreateJobOptions = {
|
||||
delay?: number
|
||||
@ -182,7 +182,6 @@ const jobTypes: JobType[] = [
|
||||
const silentFailure = new Set<JobType>([ 'activitypub-http-unicast' ])
|
||||
|
||||
class JobQueue {
|
||||
|
||||
private static instance: JobQueue
|
||||
|
||||
private workers: { [id in JobType]?: Worker } = {}
|
||||
@ -214,7 +213,9 @@ class JobQueue {
|
||||
connection: Redis.getRedisClientOptions('FlowProducer'),
|
||||
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()
|
||||
}
|
||||
@ -237,7 +238,7 @@ class JobQueue {
|
||||
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 })
|
||||
|
||||
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
|
||||
}
|
||||
@ -270,7 +273,9 @@ class JobQueue {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -286,7 +291,9 @@ class JobQueue {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@ -345,7 +352,7 @@ class JobQueue {
|
||||
|
||||
createJobAsync (options: CreateJobArgument & CreateJobOptions): void {
|
||||
this.createJob(options)
|
||||
.catch(err => logger.error('Cannot create job.', { err, options }))
|
||||
.catch(err => logger.error('Cannot create job.', { err, options }))
|
||||
}
|
||||
|
||||
createJob (options: CreateJobArgument & CreateJobOptions | undefined) {
|
||||
@ -557,5 +564,6 @@ class JobQueue {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
JobQueue, jobTypes
|
||||
JobQueue,
|
||||
jobTypes
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ interface MuxingSessionEvents {
|
||||
'after-cleanup': (options: { videoUUID: string }) => void
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||
declare interface MuxingSession {
|
||||
on<U extends keyof MuxingSessionEvents>(
|
||||
event: U,
|
||||
@ -61,7 +62,8 @@ declare interface MuxingSession {
|
||||
): 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 readonly context: any
|
||||
|
@ -10,13 +10,16 @@ interface TranscodingWrapperEvents {
|
||||
'error': (options: { err: Error }) => void
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||
declare interface AbstractTranscodingWrapper {
|
||||
on<U extends keyof TranscodingWrapperEvents>(
|
||||
event: U, listener: TranscodingWrapperEvents[U]
|
||||
event: U,
|
||||
listener: TranscodingWrapperEvents[U]
|
||||
): this
|
||||
|
||||
emit<U extends keyof TranscodingWrapperEvents>(
|
||||
event: U, ...args: Parameters<TranscodingWrapperEvents[U]>
|
||||
event: U,
|
||||
...args: Parameters<TranscodingWrapperEvents[U]>
|
||||
): boolean
|
||||
}
|
||||
|
||||
@ -48,6 +51,7 @@ interface AbstractTranscodingWrapperOptions {
|
||||
outDirectory: string
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||
abstract class AbstractTranscodingWrapper extends EventEmitter {
|
||||
protected readonly videoLive: MVideoLiveVideo
|
||||
|
||||
@ -111,6 +115,5 @@ abstract class AbstractTranscodingWrapper extends EventEmitter {
|
||||
|
||||
export {
|
||||
type AbstractTranscodingWrapperOptions,
|
||||
|
||||
AbstractTranscodingWrapper
|
||||
}
|
||||
|
@ -200,15 +200,12 @@ function createAccountAbuse (options: {
|
||||
|
||||
export {
|
||||
isLocalLiveVideoAccepted,
|
||||
|
||||
isLocalVideoFileAccepted,
|
||||
isLocalVideoThreadAccepted,
|
||||
isRemoteVideoCommentAccepted,
|
||||
isLocalVideoCommentReplyAccepted,
|
||||
isPreImportVideoAccepted,
|
||||
isPostImportVideoAccepted,
|
||||
|
||||
createAbuse,
|
||||
createVideoAbuse,
|
||||
createVideoCommentAbuse,
|
||||
createAccountAbuse
|
||||
|
@ -273,25 +273,18 @@ async function getObjectStorageFileSize (options: {
|
||||
|
||||
export {
|
||||
type BucketInfo,
|
||||
|
||||
buildKey,
|
||||
|
||||
storeObject,
|
||||
storeContent,
|
||||
storeStream,
|
||||
|
||||
removeObject,
|
||||
removeObjectByFullKey,
|
||||
removePrefix,
|
||||
|
||||
makeAvailable,
|
||||
|
||||
updateObjectACL,
|
||||
updatePrefixACL,
|
||||
|
||||
listKeysOfPrefix,
|
||||
createObjectReadStream,
|
||||
|
||||
getObjectStorageFileSize
|
||||
}
|
||||
|
||||
@ -344,11 +337,15 @@ async function uploadToStorage (options: {
|
||||
|
||||
logger.debug(
|
||||
'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)
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
||||
throw parseS3Error(err)
|
||||
}
|
||||
}
|
||||
|
@ -87,8 +87,7 @@ function handleObjectStorageFailure (res: express.Response, err: Error) {
|
||||
|
||||
return res.fail({
|
||||
status: HttpStatusCode.INTERNAL_SERVER_ERROR_500,
|
||||
message: err.message,
|
||||
type: err.name
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
export class NodeJSObserversBuilder {
|
||||
|
||||
constructor (private readonly meter: Meter) {
|
||||
}
|
||||
|
||||
@ -62,7 +61,6 @@ export class NodeJSObserversBuilder {
|
||||
observableResult.observe(cpuTotal, (userUsageMicros + systemUsageMicros) / 1e6)
|
||||
observableResult.observe(cpuUser, userUsageMicros / 1e6)
|
||||
observableResult.observe(cpuSystem, systemUsageMicros / 1e6)
|
||||
|
||||
}, [ cpuTotal, cpuUser, cpuSystem ])
|
||||
}
|
||||
|
||||
@ -119,7 +117,7 @@ export class NodeJSObserversBuilder {
|
||||
}
|
||||
|
||||
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 nanosec = delta[0] * 1e9 + delta[1]
|
||||
const seconds = nanosec / 1e9
|
||||
@ -180,6 +178,7 @@ export class NodeJSObserversBuilder {
|
||||
|
||||
const data = {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
||||
for (let i = 0; i < resources.length; i++) {
|
||||
const resource = resources[i]
|
||||
|
||||
|
@ -68,8 +68,13 @@ async function registerOpentelemetryTracing () {
|
||||
}, DiagLogLevel.INFO)
|
||||
|
||||
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({
|
||||
[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()
|
||||
}
|
||||
|
||||
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')
|
||||
|
||||
if (CONFIG.OPEN_TELEMETRY.TRACING.ENABLED !== true) {
|
||||
@ -135,6 +134,5 @@ class TrackerMock {
|
||||
|
||||
class SpanMock {
|
||||
end () {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,9 @@ import { authenticateRunnerSocket, authenticateSocket } from '../middlewares/ind
|
||||
import { isDevInstance } from '@peertube/peertube-node-utils'
|
||||
|
||||
class PeerTubeSocket {
|
||||
|
||||
private static instance: PeerTubeSocket
|
||||
|
||||
private userNotificationSockets: { [ userId: number ]: Socket[] } = {}
|
||||
private userNotificationSockets: { [userId: number]: Socket[] } = {}
|
||||
private liveVideosNamespace: Namespace
|
||||
private readonly runnerSockets = new Set<Socket>()
|
||||
|
||||
@ -51,16 +50,14 @@ class PeerTubeSocket {
|
||||
const videoId = params.videoId + ''
|
||||
if (!isIdValid(videoId)) return
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||
socket.join(videoId)
|
||||
void socket.join(videoId)
|
||||
})
|
||||
|
||||
socket.on('unsubscribe', params => {
|
||||
const videoId = params.videoId + ''
|
||||
if (!isIdValid(videoId)) return
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||
socket.leave(videoId)
|
||||
void socket.leave(videoId)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -54,13 +54,13 @@ export interface RegisteredPlugin {
|
||||
|
||||
// Only if this is a plugin
|
||||
registerHelpers?: RegisterHelpers
|
||||
unregister?: Function
|
||||
unregister?: () => any
|
||||
}
|
||||
|
||||
export interface HookInformationValue {
|
||||
npmName: string
|
||||
pluginName: string
|
||||
handler: Function
|
||||
handler: () => any
|
||||
priority: number
|
||||
}
|
||||
|
||||
@ -69,7 +69,6 @@ type PluginLocalesTranslations = {
|
||||
}
|
||||
|
||||
export class PluginManager implements ServerHook {
|
||||
|
||||
private static instance: PluginManager
|
||||
|
||||
private registeredPlugins: { [name: string]: RegisteredPlugin } = {}
|
||||
@ -267,7 +266,9 @@ export class PluginManager implements ServerHook {
|
||||
hookType,
|
||||
result,
|
||||
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)
|
||||
|
||||
this.sanitizeAndCheckPackageJSONOrThrow(packageJSON, pluginType);
|
||||
|
||||
[ plugin ] = await PluginModel.upsert({
|
||||
this.sanitizeAndCheckPackageJSONOrThrow(packageJSON, pluginType)
|
||||
;[ plugin ] = await PluginModel.upsert({
|
||||
name: pluginName,
|
||||
description: packageJSON.description,
|
||||
homepage: packageJSON.homepage,
|
||||
@ -662,7 +662,7 @@ export class PluginManager implements ServerHook {
|
||||
const { result: packageJSONValid, badFields } = isPackageJSONValid(packageJSON, pluginType)
|
||||
if (!packageJSONValid) {
|
||||
const formattedFields = badFields.map(f => `"${f}"`)
|
||||
.join(', ')
|
||||
.join(', ')
|
||||
|
||||
throw new Error(`PackageJSON is invalid (invalid fields: ${formattedFields}).`)
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ async function execYarn (command: string) {
|
||||
} catch (result) {
|
||||
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 }))
|
||||
}
|
||||
|
||||
export function buildUploadXFile <T extends UploadXMetadata> (reqBody: T) {
|
||||
export function buildUploadXFile<T extends UploadXMetadata> (reqBody: T) {
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-spread
|
||||
...reqBody,
|
||||
|
||||
path: getResumableUploadPath(reqBody.name),
|
||||
filename: reqBody.metadata.filename
|
||||
filename: reqBody.metadata.filename,
|
||||
originalname: reqBody.originalName
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,21 +72,26 @@ export function setupUploadResumableRoutes (options: {
|
||||
uploadDeleteMiddlewares = []
|
||||
} = options
|
||||
|
||||
router.post(routePath,
|
||||
router.post(
|
||||
routePath,
|
||||
authenticate,
|
||||
...uploadInitBeforeMiddlewares,
|
||||
resumableInitValidator,
|
||||
...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,
|
||||
...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,
|
||||
uploadx.upload, // uploadx doesn't next() before the file upload completes
|
||||
...uploadedMiddlewares,
|
||||
|
@ -93,9 +93,7 @@ export class VideosExporter extends AbstractUserExporter<VideoExportJSON> {
|
||||
|
||||
const live = video.isLive
|
||||
? await VideoLiveModel.loadByVideoIdWithSettings(videoId)
|
||||
: undefined
|
||||
|
||||
// We already have captions, so we can set it to the video object
|
||||
: undefined // We already have captions, so we can set it to the video object
|
||||
;(video as any).VideoCaptions = captions
|
||||
// Then fetch more attributes for AP serialization
|
||||
const videoAP = await video.lightAPToFullAP(undefined)
|
||||
|
@ -5,10 +5,9 @@ import { AbstractUserImporter } from './abstract-user-importer.js'
|
||||
|
||||
type SanitizedObject = AutoTagPoliciesJSON['reviewComments']
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
export class ReviewCommentsTagPoliciesImporter
|
||||
extends AbstractUserImporter <AutoTagPoliciesJSON, AutoTagPoliciesJSON['reviewComments'] & { archiveFiles?: never }, SanitizedObject> {
|
||||
|
||||
extends AbstractUserImporter<AutoTagPoliciesJSON, AutoTagPoliciesJSON['reviewComments'] & { archiveFiles?: never }, SanitizedObject>
|
||||
{
|
||||
protected getImportObjects (json: AutoTagPoliciesJSON) {
|
||||
if (!json.reviewComments) return []
|
||||
|
||||
|
@ -20,11 +20,20 @@ import { pick } from '@peertube/peertube-core-utils'
|
||||
|
||||
const lTags = loggerTagsFactory('user-import')
|
||||
|
||||
type SanitizedObject = Pick<UserSettingsExportJSON, 'nsfwPolicy' | 'autoPlayVideo' | 'autoPlayNextVideo' | 'autoPlayNextVideo' |
|
||||
'autoPlayNextVideoPlaylist' | 'p2pEnabled' | 'videosHistoryEnabled' | 'videoLanguages' | 'theme' | 'notificationSettings'>
|
||||
|
||||
export class UserSettingsImporter extends AbstractUserImporter <UserSettingsExportJSON, UserSettingsExportJSON, SanitizedObject> {
|
||||
type SanitizedObject = Pick<
|
||||
UserSettingsExportJSON,
|
||||
| 'nsfwPolicy'
|
||||
| 'autoPlayVideo'
|
||||
| 'autoPlayNextVideo'
|
||||
| 'autoPlayNextVideoPlaylist'
|
||||
| 'p2pEnabled'
|
||||
| 'videosHistoryEnabled'
|
||||
| 'videoLanguages'
|
||||
| 'theme'
|
||||
| 'notificationSettings'
|
||||
>
|
||||
|
||||
export class UserSettingsImporter extends AbstractUserImporter<UserSettingsExportJSON, UserSettingsExportJSON, SanitizedObject> {
|
||||
protected getImportObjects (json: UserSettingsExportJSON) {
|
||||
return [ json ]
|
||||
}
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { VideoPlaylistPrivacy, VideoPlaylistType, VideoPlaylistsExportJSON } from '@peertube/peertube-models'
|
||||
import { logger, loggerTagsFactory } from '@server/helpers/logger.js'
|
||||
import { buildUUID } from '@peertube/peertube-node-utils'
|
||||
import {
|
||||
MChannelBannerAccountDefault, MVideoPlaylistFull,
|
||||
MVideoPlaylistThumbnail
|
||||
} from '@server/types/models/index.js'
|
||||
import { MChannelBannerAccountDefault, MVideoPlaylistFull, MVideoPlaylistThumbnail } from '@server/types/models/index.js'
|
||||
import { getLocalVideoPlaylistActivityPubUrl, getLocalVideoPlaylistElementActivityPubUrl } from '@server/lib/activitypub/url.js'
|
||||
import { VideoChannelModel } from '@server/models/video/video-channel.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')
|
||||
|
||||
type ImportObject = VideoPlaylistsExportJSON['videoPlaylists'][0]
|
||||
type SanitizedObject = Pick<ImportObject, 'type' | 'displayName' | 'privacy' | 'elements' | 'description' | 'elements' | 'channel' |
|
||||
'archiveFiles'>
|
||||
|
||||
export class VideoPlaylistsImporter extends AbstractUserImporter <VideoPlaylistsExportJSON, ImportObject, SanitizedObject> {
|
||||
type SanitizedObject = Pick<ImportObject, 'type' | 'displayName' | 'privacy' | 'elements' | 'description' | 'channel' | 'archiveFiles'>
|
||||
|
||||
export class VideoPlaylistsImporter extends AbstractUserImporter<VideoPlaylistsExportJSON, ImportObject, SanitizedObject> {
|
||||
protected getImportObjects (json: VideoPlaylistsExportJSON) {
|
||||
return json.videoPlaylists
|
||||
}
|
||||
|
@ -107,51 +107,51 @@ export class UserImporter {
|
||||
// Keep consistency in import order (don't import videos before channels for example)
|
||||
return [
|
||||
{
|
||||
name: 'account' as 'account',
|
||||
name: 'account' as const,
|
||||
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'))
|
||||
},
|
||||
{
|
||||
name: 'channels' as 'channels',
|
||||
name: 'channels' as const,
|
||||
importer: new ChannelsImporter(this.buildImporterOptions(user, 'channels.json'))
|
||||
},
|
||||
{
|
||||
name: 'blocklist' as 'blocklist',
|
||||
name: 'blocklist' as const,
|
||||
importer: new BlocklistImporter(this.buildImporterOptions(user, 'blocklist.json'))
|
||||
},
|
||||
{
|
||||
name: 'following' as 'following',
|
||||
name: 'following' as const,
|
||||
importer: new FollowingImporter(this.buildImporterOptions(user, 'following.json'))
|
||||
},
|
||||
{
|
||||
name: 'videos' as 'videos',
|
||||
name: 'videos' as const,
|
||||
importer: new VideosImporter(this.buildImporterOptions(user, 'videos.json'))
|
||||
},
|
||||
{
|
||||
name: 'likes' as 'likes',
|
||||
name: 'likes' as const,
|
||||
importer: new LikesImporter(this.buildImporterOptions(user, 'likes.json'))
|
||||
},
|
||||
{
|
||||
name: 'dislikes' as 'dislikes',
|
||||
name: 'dislikes' as const,
|
||||
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'))
|
||||
},
|
||||
{
|
||||
name: 'userVideoHistory' as 'userVideoHistory',
|
||||
name: 'userVideoHistory' as const,
|
||||
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'))
|
||||
},
|
||||
{
|
||||
name: 'commentAutoTagPolicies' as 'commentAutoTagPolicies',
|
||||
name: 'commentAutoTagPolicies' as const,
|
||||
importer: new ReviewCommentsTagPoliciesImporter(this.buildImporterOptions(user, 'automatic-tag-policies.json'))
|
||||
}
|
||||
]
|
||||
|
@ -134,7 +134,7 @@ export function buildSortDirectionAndField (value: string) {
|
||||
let field: string
|
||||
let direction: 'ASC' | 'DESC'
|
||||
|
||||
if (value.substring(0, 1) === '-') {
|
||||
if (value.startsWith('-')) {
|
||||
direction = 'DESC'
|
||||
field = value.substring(1)
|
||||
} else {
|
||||
|
@ -18,7 +18,7 @@ import { VideoCaptionModel } from '../video/video-caption.js'
|
||||
import { VideoCommentModel } from '../video/video-comment.js'
|
||||
import { VideoImportModel } from '../video/video-import.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 { UserModel } from './user.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 {
|
||||
MAbuseMessage,
|
||||
@ -77,8 +77,8 @@ declare module 'express' {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Upload file with a duration added by our middleware
|
||||
export type VideoLegacyUploadFile = Pick<Express.Multer.File, 'path' | 'filename' | 'size', 'originalname'> & {
|
||||
duration: number
|
||||
export type VideoLegacyUploadFile = Pick<Express.Multer.File, 'path' | 'filename' | 'size' | 'originalname'> & {
|
||||
duration?: number
|
||||
}
|
||||
|
||||
// Our custom UploadXFile object using our custom metadata
|
||||
@ -106,7 +106,7 @@ declare module 'express' {
|
||||
|
||||
title?: string
|
||||
status?: number
|
||||
type?: ServerErrorCode | string
|
||||
type?: ServerErrorCodeType
|
||||
instance?: string
|
||||
|
||||
data?: PeerTubeProblemDocumentData
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user