Class: OllamaChat::Invidious
- Inherits:
-
Object
- Object
- OllamaChat::Invidious
- Defined in:
- lib/ollama_chat/invidious.rb
Constant Summary collapse
- YOUTUBE_URLS =
%r{ \A (?:https?://)? (?:www\.|m\.|music\.)? (?:youtube\.com/(?:watch\?v=|embed/|shorts/)|youtu\.be/) ([\w-]{11}) }x
Class Method Summary collapse
-
.fetch_transcript(tracks, chat) ⇒ String?
private
Selects the best caption track by language priority, fetches its timedtext XML from the YouTube baseUrl, and extracts the transcript text via Nokogiri.
-
.fetch_video_info(url, chat:) ⇒ StringIO?
Fetches video metadata (title, channel, description, duration) and, when available, a clean transcript via the Invidious companion player endpoint.
-
.format_details(details) ⇒ String
private
Formats videoDetails into a markdown header.
-
.format_duration(seconds) ⇒ String
private
Formats a duration in seconds as a human-readable string.
-
.pick_track(tracks, langs) ⇒ Hash?
private
Iterates the priority list of language regexes and returns the first track whose languageCode matches any pattern.
-
.strip_xml(xml) ⇒ String?
private
Extracts transcript text from YouTube timedtext XML using Nokogiri.
Class Method Details
.fetch_transcript(tracks, chat) ⇒ String? (private)
Selects the best caption track by language priority, fetches its timedtext XML from the YouTube baseUrl, and extracts the transcript text via Nokogiri.
90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/ollama_chat/invidious.rb', line 90 def fetch_transcript(tracks, chat) langs = OC::OLLAMA::CHAT::INVIDIOUS::CAPTION_LANGUAGES track = pick_track(tracks, langs) or return nil xml = nil chat.get_url( track['baseUrl'], remember: false ) { |tmp| xml = tmp.read } xml && strip_xml(xml) end |
.fetch_video_info(url, chat:) ⇒ StringIO?
Fetches video metadata (title, channel, description, duration) and, when available, a clean transcript via the Invidious companion player endpoint.
Steps:
1. POST /companion/youtubei/v1/player -> videoDetails +
captionTracks (single response)
2. Match languageCode against configured priority regexes
3. GET the matched track's baseUrl → timedtext XML
4. Extract <text> elements via Nokogiri → plain text
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/ollama_chat/invidious.rb', line 39 def fetch_video_info(url, chat:) invidious_url = OC::OLLAMA::CHAT::INVIDIOUS::URL? or return video_id = url[YOUTUBE_URLS, 1] or return base = invidious_url.to_s key = OC::OLLAMA::CHAT::INVIDIOUS::COMPANION_KEY # Step 1: POST /companion/youtubei/v1/player player_response = nil chat.request_url_response( :post, "#{base}/companion/youtubei/v1/player", body: JSON.dump('videoId' => video_id), headers: { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{key}", }, expects: 200, middlewares: OllamaChat::Utils::Fetcher.middlewares, ) do |response| player_response = JSON.parse(response.body) rescue JSON::ParserError => e chat.log(:error, e, data: { url: }) end return unless player_response details = player_response['videoDetails'] return unless details header = format_details(details) # Steps 2-4: pick track, fetch VTT, strip (best effort) tracks = player_response.dig( 'captions', 'playerCaptionsTracklistRenderer', 'captionTracks' ) transcript = fetch_transcript(tracks, chat) text = transcript ? header + transcript : header + "No captions available.\n" OllamaChat::Utils::Fetcher::ResponseMetadata.as_text(text) end |
.format_details(details) ⇒ String (private)
Formats videoDetails into a markdown header.
139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/ollama_chat/invidious.rb', line 139 def format_details(details) <<~HDR.chomp # #{details['title']} **Channel:** #{details['author']} **Duration:** #{format_duration(details['lengthSeconds'].to_i)} #{details['shortDescription'].to_s.strip} --- HDR end |
.format_duration(seconds) ⇒ String (private)
Formats a duration in seconds as a human-readable string.
157 158 159 |
# File 'lib/ollama_chat/invidious.rb', line 157 def format_duration(seconds) Tins::Duration.new(seconds).to_s end |
.pick_track(tracks, langs) ⇒ Hash? (private)
Iterates the priority list of language regexes and returns the first track whose languageCode matches any pattern.
108 109 110 111 112 113 114 115 116 117 |
# File 'lib/ollama_chat/invidious.rb', line 108 def pick_track(tracks, langs) return nil unless tracks&.any? langs.each do |pattern| track = tracks.find { |t| t['languageCode'] =~ pattern } return track if track end nil end |
.strip_xml(xml) ⇒ String? (private)
Extracts transcript text from YouTube timedtext XML using
Nokogiri. Word-level
125 126 127 128 129 130 131 132 133 |
# File 'lib/ollama_chat/invidious.rb', line 125 def strip_xml(xml) doc = Nokogiri::XML(xml) text = doc.xpath('//text') .map { |n| n.text.strip } .reject(&:blank?) .join(' ') .strip text.full? { Nokogiri::HTML.fragment(_1).text } end |