peertube-redundancy.ts 5.9 KB

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