171 lines
5.5 KiB
TypeScript
171 lines
5.5 KiB
TypeScript
import { Command } from '@commander-js/extra-typings'
|
|
import { VideoCommentPolicy, VideoPrivacy, VideoPrivacyType } from '@peertube/peertube-models'
|
|
import { PeerTubeServer } from '@peertube/peertube-server-commands'
|
|
import { access, constants } from 'fs/promises'
|
|
import { isAbsolute } from 'path'
|
|
import { inspect } from 'util'
|
|
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?: number
|
|
licence?: number
|
|
language?: string
|
|
tags?: string[]
|
|
nsfw?: true
|
|
videoDescription?: string
|
|
privacy?: VideoPrivacyType
|
|
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 <url>', 'Server url')
|
|
.option('-U, --username <username>', 'Username')
|
|
.option('-p, --password <token>', 'Password')
|
|
.option('-b, --thumbnail <thumbnailPath>', 'Thumbnail path')
|
|
.option('--preview <previewPath>', 'Preview path')
|
|
.option('-f, --file <file>', 'Video absolute file path')
|
|
.option('-n, --video-name <name>', 'Video name')
|
|
.option('-c, --category <category_number>', 'Category number', parseInt)
|
|
.option('-l, --licence <licence_number>', 'Licence number', parseInt)
|
|
.option('-L, --language <language_code>', 'Language ISO 639 code (fr or en...)')
|
|
.option('-t, --tags <tags>', 'Video tags', listOptions)
|
|
.option('-N, --nsfw', 'Video is Not Safe For Work')
|
|
.option('-d, --video-description <description>', 'Video description')
|
|
.option('-P, --privacy <privacy_number>', 'Privacy', v => parseInt(v) as VideoPrivacyType)
|
|
.option('-C, --channel-name <channel_name>', 'Channel name')
|
|
.option('--no-comments-enabled', 'Disable video comments')
|
|
.option('-s, --support <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 <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) {
|
|
const defaultBooleanAttributes = {
|
|
nsfw: false,
|
|
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 {
|
|
booleanAttributes[key] = defaultBooleanAttributes[key]
|
|
}
|
|
}
|
|
|
|
const videoAttributes = {
|
|
name: options.videoName,
|
|
category: options.category || undefined,
|
|
licence: options.licence || undefined,
|
|
language: options.language || undefined,
|
|
privacy: options.privacy || VideoPrivacy.PUBLIC,
|
|
support: options.support || undefined,
|
|
description: options.videoDescription || undefined,
|
|
tags: options.tags || undefined,
|
|
|
|
commentsPolicy: options.noCommentsEnabled !== undefined
|
|
? options.noCommentsEnabled === true
|
|
? VideoCommentPolicy.DISABLED
|
|
: VideoCommentPolicy.ENABLED
|
|
: undefined,
|
|
|
|
...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
|
|
}
|