From 3a4992633ee62d5edfbb484d9c6bcb3cf158489d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 31 Jul 2023 14:34:36 +0200 Subject: Migrate server to ESM Sorry for the very big commit that may lead to git log issues and merge conflicts, but it's a major step forward: * Server can be faster at startup because imports() are async and we can easily lazy import big modules * Angular doesn't seem to support ES import (with .js extension), so we had to correctly organize peertube into a monorepo: * Use yarn workspace feature * Use typescript reference projects for dependencies * Shared projects have been moved into "packages", each one is now a node module (with a dedicated package.json/tsconfig.json) * server/tools have been moved into apps/ and is now a dedicated app bundled and published on NPM so users don't have to build peertube cli tools manually * server/tests have been moved into packages/ so we don't compile them every time we want to run the server * Use isolatedModule option: * Had to move from const enum to const (https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums) * Had to explictely specify "type" imports when used in decorators * Prefer tsx (that uses esbuild under the hood) instead of ts-node to load typescript files (tests with mocha or scripts): * To reduce test complexity as esbuild doesn't support decorator metadata, we only test server files that do not import server models * We still build tests files into js files for a faster CI * Remove unmaintained peertube CLI import script * Removed some barrels to speed up execution (less imports) --- apps/peertube-cli/src/peertube-upload.ts | 167 +++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 apps/peertube-cli/src/peertube-upload.ts (limited to 'apps/peertube-cli/src/peertube-upload.ts') diff --git a/apps/peertube-cli/src/peertube-upload.ts b/apps/peertube-cli/src/peertube-upload.ts new file mode 100644 index 000000000..443f8ce1f --- /dev/null +++ b/apps/peertube-cli/src/peertube-upload.ts @@ -0,0 +1,167 @@ +import { access, constants } from 'fs/promises' +import { isAbsolute } from 'path' +import { inspect } from 'util' +import { Command } from '@commander-js/extra-typings' +import { VideoPrivacy } from '@peertube/peertube-models' +import { PeerTubeServer } from '@peertube/peertube-server-commands' +import { assignToken, buildServer, getServerCredentials, listOptions } from './shared/index.js' + +type UploadOptions = { + url?: string + username?: string + password?: string + thumbnail?: string + preview?: string + file?: string + videoName?: string + category?: string + licence?: string + language?: string + tags?: string + nsfw?: true + videoDescription?: string + privacy?: number + channelName?: string + noCommentsEnabled?: true + support?: string + noWaitTranscoding?: true + noDownloadEnabled?: true +} + +export function defineUploadProgram () { + const program = new Command('upload') + .description('Upload a video on a PeerTube instance') + .alias('up') + + program + .option('-u, --url ', 'Server url') + .option('-U, --username ', 'Username') + .option('-p, --password ', 'Password') + .option('-b, --thumbnail ', 'Thumbnail path') + .option('-v, --preview ', 'Preview path') + .option('-f, --file ', 'Video absolute file path') + .option('-n, --video-name ', 'Video name') + .option('-c, --category ', 'Category number') + .option('-l, --licence ', 'Licence number') + .option('-L, --language ', 'Language ISO 639 code (fr or en...)') + .option('-t, --tags ', 'Video tags', listOptions) + .option('-N, --nsfw', 'Video is Not Safe For Work') + .option('-d, --video-description ', 'Video description') + .option('-P, --privacy ', 'Privacy', parseInt) + .option('-C, --channel-name ', 'Channel name') + .option('--no-comments-enabled', 'Disable video comments') + .option('-s, --support ', 'Video support text') + .option('--no-wait-transcoding', 'Do not wait transcoding before publishing the video') + .option('--no-download-enabled', 'Disable video download') + .option('-v, --verbose ', 'Verbosity, from 0/\'error\' to 4/\'debug\'', 'info') + .action(async options => { + try { + const { url, username, password } = await getServerCredentials(options) + + if (!options.videoName || !options.file) { + if (!options.videoName) console.error('--video-name is required.') + if (!options.file) console.error('--file is required.') + + process.exit(-1) + } + + if (isAbsolute(options.file) === false) { + console.error('File path should be absolute.') + process.exit(-1) + } + + await run({ ...options, url, username, password }) + } catch (err) { + console.error('Cannot upload video: ' + err.message) + process.exit(-1) + } + }) + + return program +} + +// --------------------------------------------------------------------------- +// Private +// --------------------------------------------------------------------------- + +async function run (options: UploadOptions) { + const { url, username, password } = options + + const server = buildServer(url) + await assignToken(server, username, password) + + await access(options.file, constants.F_OK) + + console.log('Uploading %s video...', options.videoName) + + const baseAttributes = await buildVideoAttributesFromCommander(server, options) + + const attributes = { + ...baseAttributes, + + fixture: options.file, + thumbnailfile: options.thumbnail, + previewfile: options.preview + } + + try { + await server.videos.upload({ attributes }) + console.log(`Video ${options.videoName} uploaded.`) + process.exit(0) + } catch (err) { + const message = err.message || '' + if (message.includes('413')) { + console.error('Aborted: user quota is exceeded or video file is too big for this PeerTube instance.') + } else { + console.error(inspect(err)) + } + + process.exit(-1) + } +} + +async function buildVideoAttributesFromCommander (server: PeerTubeServer, options: UploadOptions, defaultAttributes: any = {}) { + const defaultBooleanAttributes = { + nsfw: false, + commentsEnabled: true, + downloadEnabled: true, + waitTranscoding: true + } + + const booleanAttributes: { [id in keyof typeof defaultBooleanAttributes]: boolean } | {} = {} + + for (const key of Object.keys(defaultBooleanAttributes)) { + if (options[key] !== undefined) { + booleanAttributes[key] = options[key] + } else if (defaultAttributes[key] !== undefined) { + booleanAttributes[key] = defaultAttributes[key] + } else { + booleanAttributes[key] = defaultBooleanAttributes[key] + } + } + + const videoAttributes = { + name: options.videoName || defaultAttributes.name, + category: options.category || defaultAttributes.category || undefined, + licence: options.licence || defaultAttributes.licence || undefined, + language: options.language || defaultAttributes.language || undefined, + privacy: options.privacy || defaultAttributes.privacy || VideoPrivacy.PUBLIC, + support: options.support || defaultAttributes.support || undefined, + description: options.videoDescription || defaultAttributes.description || undefined, + tags: options.tags || defaultAttributes.tags || undefined + } + + Object.assign(videoAttributes, booleanAttributes) + + if (options.channelName) { + const videoChannel = await server.channels.get({ channelName: options.channelName }) + + Object.assign(videoAttributes, { channelId: videoChannel.id }) + + if (!videoAttributes.support && videoChannel.support) { + Object.assign(videoAttributes, { support: videoChannel.support }) + } + } + + return videoAttributes +} -- cgit v1.2.3