ぼざクリタグ広場 https://hub.nizika.monster
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.
 
 
 
 
 

169 lines
4.7 KiB

  1. require 'open-uri'
  2. require 'set'
  3. require 'time'
  4. module Youtube
  5. class Sync
  6. def initialize client: ApiClient.new
  7. @client = client
  8. end
  9. def sync!
  10. video_ids = discover_video_ids
  11. return if video_ids.empty?
  12. video_ids.each_slice(50) do |ids|
  13. @client.videos(ids).fetch('items', []).each do |item|
  14. sync_video!(VideoItem.new(item))
  15. end
  16. end
  17. end
  18. private
  19. def discover_video_ids
  20. ids = Set.new
  21. query_terms.each do |q|
  22. response = @client.search_videos(q:, published_after: sync_since)
  23. response.fetch('items', []).each do |item|
  24. video_id = item.dig('id', 'videoId')
  25. ids << video_id if video_id.present?
  26. end
  27. end
  28. playlist_ids.each do |playlist_id|
  29. each_playlist_item(playlist_id) do |item|
  30. video_id = item.dig('contentDetails', 'videoId')
  31. video_id ||= item.dig('snippet', 'resourceId', 'videoId')
  32. ids << video_id if video_id.present?
  33. end
  34. end
  35. ids.to_a
  36. end
  37. def sync_video! video
  38. post = Post.where('url REGEXP ?', youtube_url_regexp(video.id)).first
  39. original_created_from = video.published_at.change(sec: 0)
  40. original_created_before = original_created_from + 1.minute
  41. post_created = false
  42. post_changed = false
  43. if post
  44. post.assign_attributes(title: video.title,
  45. original_created_from:,
  46. original_created_before:,
  47. thumbnail_base: video.thumbnail_url)
  48. post_changed = post.changed?
  49. post.save! if post_changed
  50. attach_thumbnail_if_needed!(post, video.thumbnail_url)
  51. else
  52. post_created = true
  53. post = Post.create!(
  54. title: video.title,
  55. url: video.url,
  56. thumbnail_base: video.thumbnail_url,
  57. uploaded_user_id: nil,
  58. original_created_from:,
  59. original_created_before:)
  60. attach_thumbnail_if_needed!(post, video.thumbnail_url)
  61. sync_post_tags!(post, [Tag.tagme.id, Tag.bot.id, Tag.youtube.id, Tag.video.id])
  62. end
  63. kept_tag_ids = post.tags.pluck(:id).to_set
  64. desired_tag_ids = kept_tag_ids.to_a
  65. deerjikist = Deerjikist.find_by(platform: :youtube, code: video.channel_id)
  66. if deerjikist
  67. desired_tag_ids.delete(Tag.no_deerjikist.id)
  68. desired_tag_ids << deerjikist.tag_id
  69. elsif post.tags.where(category: :deerjikist).none?
  70. desired_tag_ids << Tag.no_deerjikist.id
  71. end
  72. desired_tag_ids.uniq!
  73. sync_post_tags!(post, desired_tag_ids, current_tag_ids: kept_tag_ids)
  74. if post_created
  75. PostVersionRecorder.record!(post:, event_type: :create, created_by_user: nil)
  76. elsif post_changed || kept_tag_ids != desired_tag_ids.to_set
  77. PostVersionRecorder.ensure_snapshot!(post, created_by_user: nil)
  78. PostVersionRecorder.record!(post:, event_type: :update, created_by_user: nil)
  79. end
  80. end
  81. def sync_post_tags! post, desired_tag_ids, current_tag_ids: nil
  82. current_tag_ids ||= PostTag.kept.where(post_id: post.id).pluck(:tag_id).to_set
  83. desired_tag_ids = desired_tag_ids.compact.to_set
  84. to_add = desired_tag_ids - current_tag_ids
  85. to_remove = current_tag_ids - desired_tag_ids
  86. Tag.where(id: to_add.to_a).find_each do |tag|
  87. begin
  88. PostTag.create!(post:, tag:)
  89. rescue ActiveRecord::RecordNotUnique
  90. ;
  91. end
  92. end
  93. PostTag.where(post_id: post.id, tag_id: to_remove.to_a).kept.find_each do |pt|
  94. pt.discard_by!(nil)
  95. end
  96. end
  97. def attach_thumbnail_if_needed! post, thumbnail_url
  98. return if post.thumbnail.attached?
  99. return if thumbnail_url.blank?
  100. post.thumbnail.attach(
  101. io: URI.open(thumbnail_url),
  102. filename: File.basename(URI.parse(thumbnail_url).path),
  103. content_type: 'image/jpeg')
  104. post.resized_thumbnail!
  105. end
  106. def youtube_url_regexp id
  107. escaped = Regexp.escape(id)
  108. "(youtube\\.com/watch\\?v=#{ escaped }|youtu\\.be/#{ escaped })([^A-Za-z0-9_-]|$)"
  109. end
  110. def query_terms = ['ぼざろクリーチャーシリーズ', '伊地知ニジカ', '伊地知虹鹿']
  111. def playlist_ids
  112. ['PLrOch4zHkI5vu29b-f9umUQQ4tQkuWLPX',
  113. 'PLrOch4zHkI5vOK0RaytQq6PbucxQkkL0K',
  114. 'PLrOch4zHkI5tdwm9vSegiDQJOM-hgpcOC']
  115. end
  116. def sync_since = 14.days.ago
  117. def each_playlist_item playlist_id
  118. page_token = nil
  119. loop do
  120. response = @client.playlist_items(playlist_id:, page_token:)
  121. response.fetch('items', []).each do |item|
  122. yield item
  123. end
  124. page_token = response['nextPageToken']
  125. break if page_token.blank?
  126. end
  127. end
  128. end
  129. end