peertube-redundancy.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // eslint-disable @typescript-eslint/no-unnecessary-type-assertion
  2. import { registerTSPaths } from '../helpers/register-ts-paths'
  3. registerTSPaths()
  4. import * as program from 'commander'
  5. import { getAdminTokenOrDie, getServerCredentials } from './cli'
  6. import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'
  7. import { addVideoRedundancy, listVideoRedundancies, removeVideoRedundancy } from '@shared/extra-utils/server/redundancy'
  8. import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
  9. import validator from 'validator'
  10. import * as CliTable3 from 'cli-table3'
  11. import { URL } from 'url'
  12. import { uniq } from 'lodash'
  13. import bytes = require('bytes')
  14. program
  15. .name('plugins')
  16. .usage('[command] [options]')
  17. program
  18. .command('list-remote-redundancies')
  19. .description('List remote redundancies on your videos')
  20. .option('-u, --url <url>', 'Server url')
  21. .option('-U, --username <username>', 'Username')
  22. .option('-p, --password <token>', 'Password')
  23. .action(() => listRedundanciesCLI('my-videos'))
  24. program
  25. .command('list-my-redundancies')
  26. .description('List your redundancies of remote videos')
  27. .option('-u, --url <url>', 'Server url')
  28. .option('-U, --username <username>', 'Username')
  29. .option('-p, --password <token>', 'Password')
  30. .action(() => listRedundanciesCLI('remote-videos'))
  31. program
  32. .command('add')
  33. .description('Duplicate a video in your redundancy system')
  34. .option('-u, --url <url>', 'Server url')
  35. .option('-U, --username <username>', 'Username')
  36. .option('-p, --password <token>', 'Password')
  37. .option('-v, --video <videoId>', 'Video id to duplicate')
  38. .action((options) => addRedundancyCLI(options))
  39. program
  40. .command('remove')
  41. .description('Remove a video from your redundancies')
  42. .option('-u, --url <url>', 'Server url')
  43. .option('-U, --username <username>', 'Username')
  44. .option('-p, --password <token>', 'Password')
  45. .option('-v, --video <videoId>', 'Video id to remove from redundancies')
  46. .action((options) => removeRedundancyCLI(options))
  47. if (!process.argv.slice(2).length) {
  48. program.outputHelp()
  49. }
  50. program.parse(process.argv)
  51. // ----------------------------------------------------------------------------
  52. async function listRedundanciesCLI (target: VideoRedundanciesTarget) {
  53. const { url, username, password } = await getServerCredentials(program)
  54. const accessToken = await getAdminTokenOrDie(url, username, password)
  55. const redundancies = await listVideoRedundanciesData(url, accessToken, target)
  56. const table = new CliTable3({
  57. head: [ 'video id', 'video name', 'video url', 'files', 'playlists', 'by instances', 'total size' ]
  58. }) as any
  59. for (const redundancy of redundancies) {
  60. const webtorrentFiles = redundancy.redundancies.files
  61. const streamingPlaylists = redundancy.redundancies.streamingPlaylists
  62. let totalSize = ''
  63. if (target === 'remote-videos') {
  64. const tmp = webtorrentFiles.concat(streamingPlaylists)
  65. .reduce((a, b) => a + b.size, 0)
  66. totalSize = bytes(tmp)
  67. }
  68. const instances = uniq(
  69. webtorrentFiles.concat(streamingPlaylists)
  70. .map(r => r.fileUrl)
  71. .map(u => new URL(u).host)
  72. )
  73. table.push([
  74. redundancy.id.toString(),
  75. redundancy.name,
  76. redundancy.url,
  77. webtorrentFiles.length,
  78. streamingPlaylists.length,
  79. instances.join('\n'),
  80. totalSize
  81. ])
  82. }
  83. console.log(table.toString())
  84. process.exit(0)
  85. }
  86. async function addRedundancyCLI (options: { videoId: number }) {
  87. const { url, username, password } = await getServerCredentials(program)
  88. const accessToken = await getAdminTokenOrDie(url, username, password)
  89. if (!options['video'] || validator.isInt('' + options['video']) === false) {
  90. console.error('You need to specify the video id to duplicate and it should be a number.\n')
  91. program.outputHelp()
  92. process.exit(-1)
  93. }
  94. try {
  95. await addVideoRedundancy({
  96. url,
  97. accessToken,
  98. videoId: options['video']
  99. })
  100. console.log('Video will be duplicated by your instance!')
  101. process.exit(0)
  102. } catch (err) {
  103. if (err.message.includes(HttpStatusCode.CONFLICT_409)) {
  104. console.error('This video is already duplicated by your instance.')
  105. } else if (err.message.includes(HttpStatusCode.NOT_FOUND_404)) {
  106. console.error('This video id does not exist.')
  107. } else {
  108. console.error(err)
  109. }
  110. process.exit(-1)
  111. }
  112. }
  113. async function removeRedundancyCLI (options: { videoId: number }) {
  114. const { url, username, password } = await getServerCredentials(program)
  115. const accessToken = await getAdminTokenOrDie(url, username, password)
  116. if (!options['video'] || validator.isInt('' + options['video']) === false) {
  117. console.error('You need to specify the video id to remove from your redundancies.\n')
  118. program.outputHelp()
  119. process.exit(-1)
  120. }
  121. const videoId = parseInt(options['video'] + '', 10)
  122. let redundancies = await listVideoRedundanciesData(url, accessToken, 'my-videos')
  123. let videoRedundancy = redundancies.find(r => videoId === r.id)
  124. if (!videoRedundancy) {
  125. redundancies = await listVideoRedundanciesData(url, accessToken, 'remote-videos')
  126. videoRedundancy = redundancies.find(r => videoId === r.id)
  127. }
  128. if (!videoRedundancy) {
  129. console.error('Video redundancy not found.')
  130. process.exit(-1)
  131. }
  132. try {
  133. const ids = videoRedundancy.redundancies.files
  134. .concat(videoRedundancy.redundancies.streamingPlaylists)
  135. .map(r => r.id)
  136. for (const id of ids) {
  137. await removeVideoRedundancy({
  138. url,
  139. accessToken,
  140. redundancyId: id
  141. })
  142. }
  143. console.log('Video redundancy removed!')
  144. process.exit(0)
  145. } catch (err) {
  146. console.error(err)
  147. process.exit(-1)
  148. }
  149. }
  150. async function listVideoRedundanciesData (url: string, accessToken: string, target: VideoRedundanciesTarget) {
  151. const res = await listVideoRedundancies({
  152. url,
  153. accessToken,
  154. start: 0,
  155. count: 100,
  156. sort: 'name',
  157. target
  158. })
  159. return res.body.data as VideoRedundancy[]
  160. }