sync-channel.ts 3.9 KB

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