ニジカ投稿局 https://tv.nizika.tv
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

171 lines
5.5 KiB

  1. import { Command } from '@commander-js/extra-typings'
  2. import { VideoCommentPolicy, VideoPrivacy, VideoPrivacyType } from '@peertube/peertube-models'
  3. import { PeerTubeServer } from '@peertube/peertube-server-commands'
  4. import { access, constants } from 'fs/promises'
  5. import { isAbsolute } from 'path'
  6. import { inspect } from 'util'
  7. import { assignToken, buildServer, getServerCredentials, listOptions } from './shared/index.js'
  8. type UploadOptions = {
  9. url?: string
  10. username?: string
  11. password?: string
  12. thumbnail?: string
  13. preview?: string
  14. file?: string
  15. videoName?: string
  16. category?: number
  17. licence?: number
  18. language?: string
  19. tags?: string[]
  20. nsfw?: true
  21. videoDescription?: string
  22. privacy?: VideoPrivacyType
  23. channelName?: string
  24. noCommentsEnabled?: true
  25. support?: string
  26. noWaitTranscoding?: true
  27. noDownloadEnabled?: true
  28. }
  29. export function defineUploadProgram () {
  30. const program = new Command('upload')
  31. .description('Upload a video on a PeerTube instance')
  32. .alias('up')
  33. program
  34. .option('-u, --url <url>', 'Server url')
  35. .option('-U, --username <username>', 'Username')
  36. .option('-p, --password <token>', 'Password')
  37. .option('-b, --thumbnail <thumbnailPath>', 'Thumbnail path')
  38. .option('--preview <previewPath>', 'Preview path')
  39. .option('-f, --file <file>', 'Video absolute file path')
  40. .option('-n, --video-name <name>', 'Video name')
  41. .option('-c, --category <category_number>', 'Category number', parseInt)
  42. .option('-l, --licence <licence_number>', 'Licence number', parseInt)
  43. .option('-L, --language <language_code>', 'Language ISO 639 code (fr or en...)')
  44. .option('-t, --tags <tags>', 'Video tags', listOptions)
  45. .option('-N, --nsfw', 'Video is Not Safe For Work')
  46. .option('-d, --video-description <description>', 'Video description')
  47. .option('-P, --privacy <privacy_number>', 'Privacy', v => parseInt(v) as VideoPrivacyType)
  48. .option('-C, --channel-name <channel_name>', 'Channel name')
  49. .option('--no-comments-enabled', 'Disable video comments')
  50. .option('-s, --support <support>', 'Video support text')
  51. .option('--no-wait-transcoding', 'Do not wait transcoding before publishing the video')
  52. .option('--no-download-enabled', 'Disable video download')
  53. .option('-v, --verbose <verbose>', 'Verbosity, from 0/\'error\' to 4/\'debug\'', 'info')
  54. .action(async options => {
  55. try {
  56. const { url, username, password } = await getServerCredentials(options)
  57. if (!options.videoName || !options.file) {
  58. if (!options.videoName) console.error('--video-name is required.')
  59. if (!options.file) console.error('--file is required.')
  60. process.exit(-1)
  61. }
  62. if (isAbsolute(options.file) === false) {
  63. console.error('File path should be absolute.')
  64. process.exit(-1)
  65. }
  66. await run({ ...options, url, username, password })
  67. } catch (err) {
  68. console.error('Cannot upload video: ' + err.message)
  69. process.exit(-1)
  70. }
  71. })
  72. return program
  73. }
  74. // ---------------------------------------------------------------------------
  75. // Private
  76. // ---------------------------------------------------------------------------
  77. async function run (options: UploadOptions) {
  78. const { url, username, password } = options
  79. const server = buildServer(url)
  80. await assignToken(server, username, password)
  81. await access(options.file, constants.F_OK)
  82. console.log('Uploading %s video...', options.videoName)
  83. const baseAttributes = await buildVideoAttributesFromCommander(server, options)
  84. const attributes = {
  85. ...baseAttributes,
  86. fixture: options.file,
  87. thumbnailfile: options.thumbnail,
  88. previewfile: options.preview
  89. }
  90. try {
  91. await server.videos.upload({ attributes })
  92. console.log(`Video ${options.videoName} uploaded.`)
  93. process.exit(0)
  94. } catch (err) {
  95. const message = err.message || ''
  96. if (message.includes('413')) {
  97. console.error('Aborted: user quota is exceeded or video file is too big for this PeerTube instance.')
  98. } else {
  99. console.error(inspect(err))
  100. }
  101. process.exit(-1)
  102. }
  103. }
  104. async function buildVideoAttributesFromCommander (server: PeerTubeServer, options: UploadOptions) {
  105. const defaultBooleanAttributes = {
  106. nsfw: false,
  107. downloadEnabled: true,
  108. waitTranscoding: true
  109. }
  110. const booleanAttributes: { [id in keyof typeof defaultBooleanAttributes]: boolean } | {} = {}
  111. for (const key of Object.keys(defaultBooleanAttributes)) {
  112. if (options[key] !== undefined) {
  113. booleanAttributes[key] = options[key]
  114. } else {
  115. booleanAttributes[key] = defaultBooleanAttributes[key]
  116. }
  117. }
  118. const videoAttributes = {
  119. name: options.videoName,
  120. category: options.category || undefined,
  121. licence: options.licence || undefined,
  122. language: options.language || undefined,
  123. privacy: options.privacy || VideoPrivacy.PUBLIC,
  124. support: options.support || undefined,
  125. description: options.videoDescription || undefined,
  126. tags: options.tags || undefined,
  127. commentsPolicy: options.noCommentsEnabled !== undefined
  128. ? options.noCommentsEnabled === true
  129. ? VideoCommentPolicy.DISABLED
  130. : VideoCommentPolicy.ENABLED
  131. : undefined,
  132. ...booleanAttributes
  133. }
  134. if (options.channelName) {
  135. const videoChannel = await server.channels.get({ channelName: options.channelName })
  136. Object.assign(videoAttributes, { channelId: videoChannel.id })
  137. if (!videoAttributes.support && videoChannel.support) {
  138. Object.assign(videoAttributes, { support: videoChannel.support })
  139. }
  140. }
  141. return videoAttributes
  142. }