sync-channel.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import { logger } from '@server/helpers/logger.js'
  2. import { YoutubeDLWrapper } from '@server/helpers/youtube-dl/index.js'
  3. import { CONFIG } from '@server/initializers/config.js'
  4. import { buildYoutubeDLImport } from '@server/lib/video-pre-import.js'
  5. import { UserModel } from '@server/models/user/user.js'
  6. import { VideoImportModel } from '@server/models/video/video-import.js'
  7. import { MChannel, MChannelAccountDefault, MChannelSync } from '@server/types/models/index.js'
  8. import { VideoChannelSyncState, VideoPrivacy } from '@peertube/peertube-models'
  9. import { CreateJobArgument, JobQueue } from './job-queue/index.js'
  10. import { ServerConfigManager } from './server-config-manager.js'
  11. export async function synchronizeChannel (options: {
  12. channel: MChannelAccountDefault
  13. externalChannelUrl: string
  14. videosCountLimit: number
  15. channelSync?: MChannelSync
  16. onlyAfter?: Date
  17. }) {
  18. const { channel, externalChannelUrl, videosCountLimit, onlyAfter, channelSync } = options
  19. if (channelSync) {
  20. channelSync.state = VideoChannelSyncState.PROCESSING
  21. channelSync.lastSyncAt = new Date()
  22. await channelSync.save()
  23. }
  24. try {
  25. const user = await UserModel.loadByChannelActorId(channel.actorId)
  26. const youtubeDL = new YoutubeDLWrapper(
  27. externalChannelUrl,
  28. ServerConfigManager.Instance.getEnabledResolutions('vod'),
  29. CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION
  30. )
  31. const targetUrls = await youtubeDL.getInfoForListImport({ latestVideosCount: videosCountLimit })
  32. logger.info(
  33. 'Fetched %d candidate URLs for sync channel %s.',
  34. targetUrls.length, channel.Actor.preferredUsername, { targetUrls }
  35. )
  36. if (targetUrls.length === 0) {
  37. if (channelSync) {
  38. channelSync.state = VideoChannelSyncState.SYNCED
  39. await channelSync.save()
  40. }
  41. return
  42. }
  43. const children: CreateJobArgument[] = []
  44. for (const targetUrl of targetUrls) {
  45. if (await skipImport(channel, targetUrl, onlyAfter)) continue
  46. const { job } = await buildYoutubeDLImport({
  47. user,
  48. channel,
  49. targetUrl,
  50. channelSync,
  51. importDataOverride: {
  52. privacy: VideoPrivacy.PUBLIC
  53. }
  54. })
  55. children.push(job)
  56. }
  57. // Will update the channel sync status
  58. const parent: CreateJobArgument = {
  59. type: 'after-video-channel-import',
  60. payload: {
  61. channelSyncId: channelSync?.id
  62. }
  63. }
  64. await JobQueue.Instance.createJobWithChildren(parent, children)
  65. } catch (err) {
  66. logger.error(`Failed to import ${externalChannelUrl} in channel ${channel.name}`, { err })
  67. channelSync.state = VideoChannelSyncState.FAILED
  68. await channelSync.save()
  69. }
  70. }
  71. // ---------------------------------------------------------------------------
  72. async function skipImport (channel: MChannel, targetUrl: string, onlyAfter?: Date) {
  73. if (await VideoImportModel.urlAlreadyImported(channel.id, targetUrl)) {
  74. logger.debug('%s is already imported for channel %s, skipping video channel synchronization.', targetUrl, channel.name)
  75. return true
  76. }
  77. if (onlyAfter) {
  78. const youtubeDL = new YoutubeDLWrapper(
  79. targetUrl,
  80. ServerConfigManager.Instance.getEnabledResolutions('vod'),
  81. CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION
  82. )
  83. const videoInfo = await youtubeDL.getInfoForDownload()
  84. const onlyAfterWithoutTime = new Date(onlyAfter)
  85. onlyAfterWithoutTime.setHours(0, 0, 0, 0)
  86. if (videoInfo.originallyPublishedAtWithoutTime.getTime() < onlyAfterWithoutTime.getTime()) {
  87. return true
  88. }
  89. }
  90. return false
  91. }