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 =
Threshold for title similarity.
0.6- DURATION_TOLERANCE_SECONDS =
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.
23 24 25 26 |
# File 'app/services/youtube/auto_link_service.rb', line 23 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.
21 22 23 |
# File 'app/services/youtube/auto_link_service.rb', line 21 def client @client end |
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
21 22 23 |
# File 'app/services/youtube/auto_link_service.rb', line 21 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.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'app/services/youtube/auto_link_service.rb', line 32 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.
81 82 83 84 85 86 |
# File 'app/services/youtube/auto_link_service.rb', line 81 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.
89 90 91 92 93 94 95 96 |
# File 'app/services/youtube/auto_link_service.rb', line 89 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? raise ArgumentError, 'That YouTube ID is already linked to another video' if Video.excluding(video).exists?(youtube_id: youtube_id) link!(video_id, youtube_id) end |
#search_for_video(video) ⇒ Array<Hash>
Search YouTube channel for videos matching a specific local video's title.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'app/services/youtube/auto_link_service.rb', line 56 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 |