Class: YouTube::AutoLinkService
- Inherits:
-
Object
- Object
- YouTube::AutoLinkService
- Defined in:
- app/services/youtube/auto_link_service.rb
Overview
Discovers YouTube channel videos and matches them to local Video records
that don't have a youtube_id yet, using title similarity and duration.
Usage:
service = YouTube::AutoLinkService.new
matches = service.discover_matches
=> [{ youtube_id:, youtube_title:, youtube_duration:, youtube_thumbnail:,
local_video:, confidence:, title_similarity: }, ...]
service.link!(video_id, youtube_id)
Constant Summary collapse
- TITLE_SIMILARITY_THRESHOLD =
0.6- DURATION_TOLERANCE_SECONDS =
5
Instance Attribute Summary collapse
-
#client ⇒ Object
readonly
Returns the value of attribute client.
-
#logger ⇒ Object
readonly
Returns the value of attribute logger.
Instance Method Summary collapse
-
#discover_matches(local_scope: nil) ⇒ Array<Hash>
Fetch all channel videos from YouTube and match against unlinked local videos.
-
#initialize(client: nil) ⇒ AutoLinkService
constructor
A new instance of AutoLinkService.
-
#link!(video_id, youtube_id) ⇒ Object
Link a local video to a YouTube video ID and trigger metadata sync.
-
#safe_link!(video_id, youtube_id) ⇒ Object
Like #link! but raises if the local row is already linked or the YouTube ID is taken elsewhere.
-
#search_for_video(video) ⇒ Array<Hash>
Search YouTube channel for videos matching a specific local video's title.
Constructor Details
#initialize(client: nil) ⇒ AutoLinkService
Returns a new instance of AutoLinkService.
21 22 23 24 |
# File 'app/services/youtube/auto_link_service.rb', line 21 def initialize(client: nil) @client = client || YouTube::ApiClient.new @logger = Rails.logger end |
Instance Attribute Details
#client ⇒ Object (readonly)
Returns the value of attribute client.
19 20 21 |
# File 'app/services/youtube/auto_link_service.rb', line 19 def client @client end |
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
19 20 21 |
# File 'app/services/youtube/auto_link_service.rb', line 19 def logger @logger end |
Instance Method Details
#discover_matches(local_scope: nil) ⇒ Array<Hash>
Fetch all channel videos from YouTube and match against unlinked local videos.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'app/services/youtube/auto_link_service.rb', line 30 def discover_matches(local_scope: nil) channel = @client.my_channel return [] unless channel youtube_videos = fetch_all_channel_videos(channel.id) return [] if youtube_videos.empty? existing_youtube_ids = Video.where.not(youtube_id: [nil, '']).pluck(:youtube_id).to_set unlinked_yt_videos = youtube_videos.reject { |v| existing_youtube_ids.include?(v[:youtube_id]) } return [] if unlinked_yt_videos.empty? scope = local_scope || Video.all local_candidates = scope .where('youtube_id IS NULL OR youtube_id = ?', '') .pluck(:id, :title, :duration_in_seconds) .map { |id, title, dur| { id: id, title: title.to_s, duration: dur } } return [] if local_candidates.empty? match_videos(unlinked_yt_videos, local_candidates) end |
#link!(video_id, youtube_id) ⇒ Object
Link a local video to a YouTube video ID and trigger metadata sync.
79 80 81 82 83 84 |
# File 'app/services/youtube/auto_link_service.rb', line 79 def link!(video_id, youtube_id) video = Video.find(video_id) video.update!(youtube_id: youtube_id) YouTubeSyncWorker.perform_async(video_id) video end |
#safe_link!(video_id, youtube_id) ⇒ Object
Like #link! but raises if the local row is already linked or the YouTube ID is taken elsewhere.
87 88 89 90 91 92 93 94 95 96 |
# File 'app/services/youtube/auto_link_service.rb', line 87 def safe_link!(video_id, youtube_id) video = Video.find(video_id) raise ArgumentError, 'Video already has a YouTube ID' if video.youtube_id.present? if Video.where.not(id: video.id).exists?(youtube_id: youtube_id) raise ArgumentError, 'That YouTube ID is already linked to another video' end link!(video_id, youtube_id) end |
#search_for_video(video) ⇒ Array<Hash>
Search YouTube channel for videos matching a specific local video's title.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'app/services/youtube/auto_link_service.rb', line 54 def search_for_video(video) return [] if video.title.blank? channel = @client.my_channel return [] unless channel results = @client.list_channel_videos(channel_id: channel.id, max_results: 20) return [] if results.items.blank? yt_ids = results.items.map { |item| item.id.video_id } yt_details = fetch_video_details(yt_ids) existing_youtube_ids = Video.where.not(youtube_id: [nil, '']).pluck(:youtube_id).to_set yt_details .reject { |v| existing_youtube_ids.include?(v[:youtube_id]) } .map { |yt| yt.merge(title_similarity: title_similarity(video.title, yt[:youtube_title])) } .select { |yt| yt[:title_similarity] > 0.3 } .sort_by { |yt| -yt[:title_similarity] } .first(10) end |