youtube-transcript-rb 0.2.0 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -0
- data/.rubocop_todo.yml +166 -0
- data/README.md +9 -9
- data/lib/youtube-transcript-rb.rb +4 -0
- data/lib/youtube_rb/formatters.rb +263 -0
- data/lib/youtube_rb/transcript/api.rb +7 -11
- data/lib/youtube_rb/transcript/errors.rb +3 -3
- data/lib/youtube_rb/transcript/transcript.rb +2 -2
- data/lib/youtube_rb/transcript/transcript_list.rb +3 -3
- data/lib/youtube_rb/transcript/transcript_list_fetcher.rb +16 -19
- data/lib/youtube_rb/transcript.rb +0 -2
- data/lib/youtube_rb/{transcript/version.rb → version.rb} +1 -3
- data/sig/youtube_rb/transcript.rbs +0 -2
- data/spec/api_spec.rb +8 -8
- data/spec/errors_spec.rb +2 -2
- data/spec/formatters_spec.rb +34 -35
- data/spec/integration_spec.rb +14 -23
- data/spec/spec_helper.rb +51 -51
- data/spec/transcript_list_fetcher_spec.rb +11 -6
- data/spec/transcript_list_spec.rb +15 -18
- data/spec/transcript_spec.rb +9 -10
- metadata +8 -4
- data/lib/youtube_rb/transcript/formatters.rb +0 -267
data/spec/api_spec.rb
CHANGED
|
@@ -126,19 +126,19 @@ RSpec.describe YoutubeRb::Transcript::YouTubeTranscriptApi do
|
|
|
126
126
|
stub_request(:get, "https://www.youtube.com/api/timedtext?v=#{video_id}&lang=es")
|
|
127
127
|
.to_return(status: 200, body: sample_transcript_xml)
|
|
128
128
|
|
|
129
|
-
result = api.fetch(video_id, languages: [
|
|
129
|
+
result = api.fetch(video_id, languages: %w[es en])
|
|
130
130
|
expect(result.language_code).to eq("es")
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
it "falls back to next language if first not available" do
|
|
134
|
-
result = api.fetch(video_id, languages: [
|
|
134
|
+
result = api.fetch(video_id, languages: %w[ja en])
|
|
135
135
|
expect(result.language_code).to eq("en")
|
|
136
136
|
end
|
|
137
137
|
|
|
138
138
|
it "raises NoTranscriptFound when no language matches" do
|
|
139
|
-
expect
|
|
140
|
-
api.fetch(video_id, languages: [
|
|
141
|
-
|
|
139
|
+
expect do
|
|
140
|
+
api.fetch(video_id, languages: %w[ja ko zh])
|
|
141
|
+
end.to raise_error(YoutubeRb::Transcript::NoTranscriptFound)
|
|
142
142
|
end
|
|
143
143
|
|
|
144
144
|
context "with preserve_formatting option" do
|
|
@@ -233,7 +233,7 @@ RSpec.describe YoutubeRb::Transcript::YouTubeTranscriptApi do
|
|
|
233
233
|
end
|
|
234
234
|
|
|
235
235
|
describe "#fetch_all" do
|
|
236
|
-
let(:video_ids) { [
|
|
236
|
+
let(:video_ids) { %w[video1 video2 video3] }
|
|
237
237
|
|
|
238
238
|
before do
|
|
239
239
|
video_ids.each do |vid|
|
|
@@ -266,7 +266,7 @@ RSpec.describe YoutubeRb::Transcript::YouTubeTranscriptApi do
|
|
|
266
266
|
it "returns a hash of transcripts" do
|
|
267
267
|
results = api.fetch_all(video_ids)
|
|
268
268
|
expect(results).to be_a(Hash)
|
|
269
|
-
expect(results.keys).to
|
|
269
|
+
expect(results.keys).to match_array(video_ids)
|
|
270
270
|
end
|
|
271
271
|
|
|
272
272
|
it "fetches all video transcripts" do
|
|
@@ -279,7 +279,7 @@ RSpec.describe YoutubeRb::Transcript::YouTubeTranscriptApi do
|
|
|
279
279
|
|
|
280
280
|
it "respects language preference" do
|
|
281
281
|
results = api.fetch_all(video_ids, languages: ["en"])
|
|
282
|
-
results.
|
|
282
|
+
results.each_value do |transcript|
|
|
283
283
|
expect(transcript.language_code).to eq("en")
|
|
284
284
|
end
|
|
285
285
|
end
|
data/spec/errors_spec.rb
CHANGED
|
@@ -159,12 +159,12 @@ RSpec.describe YoutubeRb::Transcript do
|
|
|
159
159
|
|
|
160
160
|
describe YoutubeRb::Transcript::NoTranscriptFound do
|
|
161
161
|
let(:video_id) { "no_transcript" }
|
|
162
|
-
let(:requested_languages) { [
|
|
162
|
+
let(:requested_languages) { %w[ko ja] }
|
|
163
163
|
let(:transcript_data) { double("TranscriptList", to_s: "Available: en, es") }
|
|
164
164
|
let(:error) { described_class.new(video_id, requested_languages, transcript_data) }
|
|
165
165
|
|
|
166
166
|
it "stores requested_language_codes" do
|
|
167
|
-
expect(error.requested_language_codes).to eq([
|
|
167
|
+
expect(error.requested_language_codes).to eq(%w[ko ja])
|
|
168
168
|
end
|
|
169
169
|
|
|
170
170
|
it "stores transcript_data" do
|
data/spec/formatters_spec.rb
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
|
|
5
|
-
RSpec.describe YoutubeRb::
|
|
5
|
+
RSpec.describe YoutubeRb::Formatters do
|
|
6
6
|
# Helper to create a FetchedTranscript with snippets
|
|
7
|
-
def create_transcript(video_id: "test123", language: "English", language_code: "en", is_generated: false,
|
|
7
|
+
def create_transcript(video_id: "test123", language: "English", language_code: "en", is_generated: false,
|
|
8
|
+
snippets: nil)
|
|
8
9
|
snippets ||= [
|
|
9
10
|
YoutubeRb::Transcript::TranscriptSnippet.new(text: "Hello world", start: 0.0, duration: 2.5),
|
|
10
11
|
YoutubeRb::Transcript::TranscriptSnippet.new(text: "This is a test", start: 2.5, duration: 3.0),
|
|
@@ -24,7 +25,7 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
24
25
|
let(:transcript2) { create_transcript(video_id: "video2", language_code: "es", language: "Spanish") }
|
|
25
26
|
let(:transcripts) { [transcript, transcript2] }
|
|
26
27
|
|
|
27
|
-
describe YoutubeRb::
|
|
28
|
+
describe YoutubeRb::Formatters::Formatter do
|
|
28
29
|
let(:formatter) { described_class.new }
|
|
29
30
|
|
|
30
31
|
describe "#format_transcript" do
|
|
@@ -40,7 +41,7 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
|
|
43
|
-
describe YoutubeRb::
|
|
44
|
+
describe YoutubeRb::Formatters::JSONFormatter do
|
|
44
45
|
let(:formatter) { described_class.new }
|
|
45
46
|
|
|
46
47
|
describe "#format_transcript" do
|
|
@@ -88,7 +89,7 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
88
89
|
end
|
|
89
90
|
end
|
|
90
91
|
|
|
91
|
-
describe YoutubeRb::
|
|
92
|
+
describe YoutubeRb::Formatters::TextFormatter do
|
|
92
93
|
let(:formatter) { described_class.new }
|
|
93
94
|
|
|
94
95
|
describe "#format_transcript" do
|
|
@@ -118,7 +119,7 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
118
119
|
end
|
|
119
120
|
end
|
|
120
121
|
|
|
121
|
-
describe YoutubeRb::
|
|
122
|
+
describe YoutubeRb::Formatters::PrettyPrintFormatter do
|
|
122
123
|
let(:formatter) { described_class.new }
|
|
123
124
|
|
|
124
125
|
describe "#format_transcript" do
|
|
@@ -156,7 +157,7 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
156
157
|
end
|
|
157
158
|
end
|
|
158
159
|
|
|
159
|
-
describe YoutubeRb::
|
|
160
|
+
describe YoutubeRb::Formatters::SRTFormatter do
|
|
160
161
|
let(:formatter) { described_class.new }
|
|
161
162
|
|
|
162
163
|
describe "#format_transcript" do
|
|
@@ -227,7 +228,7 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
227
228
|
end
|
|
228
229
|
end
|
|
229
230
|
|
|
230
|
-
describe YoutubeRb::
|
|
231
|
+
describe YoutubeRb::Formatters::WebVTTFormatter do
|
|
231
232
|
let(:formatter) { described_class.new }
|
|
232
233
|
|
|
233
234
|
describe "#format_transcript" do
|
|
@@ -251,7 +252,7 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
251
252
|
lines = result.split("\n")
|
|
252
253
|
# Skip WEBVTT header
|
|
253
254
|
timestamp_lines = lines.select { |l| l.include?("-->") }
|
|
254
|
-
timestamp_lines.each_with_index do |line,
|
|
255
|
+
timestamp_lines.each_with_index do |line, _i|
|
|
255
256
|
prev_line = lines[lines.index(line) - 1]
|
|
256
257
|
# Previous line should be empty or WEBVTT, not a number
|
|
257
258
|
expect(prev_line).not_to match(/^\d+$/)
|
|
@@ -282,61 +283,59 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
282
283
|
end
|
|
283
284
|
end
|
|
284
285
|
|
|
285
|
-
describe YoutubeRb::
|
|
286
|
+
describe YoutubeRb::Formatters::FormatterLoader do
|
|
286
287
|
let(:loader) { described_class.new }
|
|
287
288
|
|
|
288
289
|
describe "#load" do
|
|
289
290
|
it "loads JSONFormatter for 'json'" do
|
|
290
291
|
formatter = loader.load("json")
|
|
291
|
-
expect(formatter).to be_a(YoutubeRb::
|
|
292
|
+
expect(formatter).to be_a(YoutubeRb::Formatters::JSONFormatter)
|
|
292
293
|
end
|
|
293
294
|
|
|
294
295
|
it "loads TextFormatter for 'text'" do
|
|
295
296
|
formatter = loader.load("text")
|
|
296
|
-
expect(formatter).to be_a(YoutubeRb::
|
|
297
|
+
expect(formatter).to be_a(YoutubeRb::Formatters::TextFormatter)
|
|
297
298
|
end
|
|
298
299
|
|
|
299
300
|
it "loads PrettyPrintFormatter for 'pretty'" do
|
|
300
301
|
formatter = loader.load("pretty")
|
|
301
|
-
expect(formatter).to be_a(YoutubeRb::
|
|
302
|
+
expect(formatter).to be_a(YoutubeRb::Formatters::PrettyPrintFormatter)
|
|
302
303
|
end
|
|
303
304
|
|
|
304
305
|
it "loads SRTFormatter for 'srt'" do
|
|
305
306
|
formatter = loader.load("srt")
|
|
306
|
-
expect(formatter).to be_a(YoutubeRb::
|
|
307
|
+
expect(formatter).to be_a(YoutubeRb::Formatters::SRTFormatter)
|
|
307
308
|
end
|
|
308
309
|
|
|
309
310
|
it "loads WebVTTFormatter for 'webvtt'" do
|
|
310
311
|
formatter = loader.load("webvtt")
|
|
311
|
-
expect(formatter).to be_a(YoutubeRb::
|
|
312
|
+
expect(formatter).to be_a(YoutubeRb::Formatters::WebVTTFormatter)
|
|
312
313
|
end
|
|
313
314
|
|
|
314
315
|
it "defaults to PrettyPrintFormatter" do
|
|
315
316
|
formatter = loader.load
|
|
316
|
-
expect(formatter).to be_a(YoutubeRb::
|
|
317
|
+
expect(formatter).to be_a(YoutubeRb::Formatters::PrettyPrintFormatter)
|
|
317
318
|
end
|
|
318
319
|
|
|
319
320
|
it "accepts symbol as formatter type" do
|
|
320
321
|
formatter = loader.load(:json)
|
|
321
|
-
expect(formatter).to be_a(YoutubeRb::
|
|
322
|
+
expect(formatter).to be_a(YoutubeRb::Formatters::JSONFormatter)
|
|
322
323
|
end
|
|
323
324
|
|
|
324
325
|
it "raises UnknownFormatterType for invalid type" do
|
|
325
326
|
expect { loader.load("invalid") }.to raise_error(
|
|
326
|
-
YoutubeRb::
|
|
327
|
+
YoutubeRb::Formatters::FormatterLoader::UnknownFormatterType
|
|
327
328
|
)
|
|
328
329
|
end
|
|
329
330
|
|
|
330
331
|
it "includes available formats in error message" do
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
expect(e.message).to include("pretty")
|
|
339
|
-
end
|
|
332
|
+
loader.load("invalid")
|
|
333
|
+
rescue YoutubeRb::Formatters::FormatterLoader::UnknownFormatterType => e
|
|
334
|
+
expect(e.message).to include("json")
|
|
335
|
+
expect(e.message).to include("text")
|
|
336
|
+
expect(e.message).to include("srt")
|
|
337
|
+
expect(e.message).to include("webvtt")
|
|
338
|
+
expect(e.message).to include("pretty")
|
|
340
339
|
end
|
|
341
340
|
end
|
|
342
341
|
|
|
@@ -352,7 +351,7 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
352
351
|
end
|
|
353
352
|
|
|
354
353
|
describe "integration tests" do
|
|
355
|
-
let(:loader) { YoutubeRb::
|
|
354
|
+
let(:loader) { YoutubeRb::Formatters::FormatterLoader.new }
|
|
356
355
|
|
|
357
356
|
it "can format transcript with each formatter type" do
|
|
358
357
|
%w[json text pretty srt webvtt].each do |type|
|
|
@@ -378,25 +377,25 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
378
377
|
let(:empty_transcript) { create_transcript(snippets: empty_snippets) }
|
|
379
378
|
|
|
380
379
|
it "JSONFormatter handles empty transcript" do
|
|
381
|
-
formatter = YoutubeRb::
|
|
380
|
+
formatter = YoutubeRb::Formatters::JSONFormatter.new
|
|
382
381
|
result = formatter.format_transcript(empty_transcript)
|
|
383
382
|
expect(JSON.parse(result)).to eq([])
|
|
384
383
|
end
|
|
385
384
|
|
|
386
385
|
it "TextFormatter handles empty transcript" do
|
|
387
|
-
formatter = YoutubeRb::
|
|
386
|
+
formatter = YoutubeRb::Formatters::TextFormatter.new
|
|
388
387
|
result = formatter.format_transcript(empty_transcript)
|
|
389
388
|
expect(result).to eq("")
|
|
390
389
|
end
|
|
391
390
|
|
|
392
391
|
it "SRTFormatter handles empty transcript" do
|
|
393
|
-
formatter = YoutubeRb::
|
|
392
|
+
formatter = YoutubeRb::Formatters::SRTFormatter.new
|
|
394
393
|
result = formatter.format_transcript(empty_transcript)
|
|
395
394
|
expect(result).to eq("\n")
|
|
396
395
|
end
|
|
397
396
|
|
|
398
397
|
it "WebVTTFormatter handles empty transcript" do
|
|
399
|
-
formatter = YoutubeRb::
|
|
398
|
+
formatter = YoutubeRb::Formatters::WebVTTFormatter.new
|
|
400
399
|
result = formatter.format_transcript(empty_transcript)
|
|
401
400
|
expect(result).to eq("WEBVTT\n\n\n")
|
|
402
401
|
end
|
|
@@ -413,7 +412,7 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
413
412
|
let(:special_transcript) { create_transcript(snippets: special_snippets) }
|
|
414
413
|
|
|
415
414
|
it "JSONFormatter escapes special characters" do
|
|
416
|
-
formatter = YoutubeRb::
|
|
415
|
+
formatter = YoutubeRb::Formatters::JSONFormatter.new
|
|
417
416
|
result = formatter.format_transcript(special_transcript)
|
|
418
417
|
parsed = JSON.parse(result)
|
|
419
418
|
expect(parsed[0]["text"]).to eq("Hello <b>world</b>")
|
|
@@ -421,14 +420,14 @@ RSpec.describe YoutubeRb::Transcript::Formatters do
|
|
|
421
420
|
end
|
|
422
421
|
|
|
423
422
|
it "TextFormatter preserves special characters" do
|
|
424
|
-
formatter = YoutubeRb::
|
|
423
|
+
formatter = YoutubeRb::Formatters::TextFormatter.new
|
|
425
424
|
result = formatter.format_transcript(special_transcript)
|
|
426
425
|
expect(result).to include("<b>world</b>")
|
|
427
426
|
expect(result).to include('"test"')
|
|
428
427
|
end
|
|
429
428
|
|
|
430
429
|
it "SRTFormatter preserves HTML tags in text" do
|
|
431
|
-
formatter = YoutubeRb::
|
|
430
|
+
formatter = YoutubeRb::Formatters::SRTFormatter.new
|
|
432
431
|
result = formatter.format_transcript(special_transcript)
|
|
433
432
|
expect(result).to include("<b>world</b>")
|
|
434
433
|
end
|
data/spec/integration_spec.rb
CHANGED
|
@@ -39,8 +39,6 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
39
39
|
expect(transcript_list.count).to be > 0
|
|
40
40
|
|
|
41
41
|
# Print available transcripts for debugging
|
|
42
|
-
puts "\nAvailable transcripts for video #{ted_talk_video_id}:"
|
|
43
|
-
puts transcript_list.to_s
|
|
44
42
|
end
|
|
45
43
|
|
|
46
44
|
it "returns a TranscriptList that is enumerable" do
|
|
@@ -67,9 +65,6 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
67
65
|
expect(first_snippet.text).to be_a(String)
|
|
68
66
|
expect(first_snippet.start).to be_a(Float)
|
|
69
67
|
expect(first_snippet.duration).to be_a(Float)
|
|
70
|
-
|
|
71
|
-
puts "\nFetched #{transcript.length} snippets"
|
|
72
|
-
puts "First snippet: #{first_snippet.text[0..50]}..."
|
|
73
68
|
end
|
|
74
69
|
|
|
75
70
|
it "fetches transcript with specific language" do
|
|
@@ -82,9 +77,9 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
82
77
|
|
|
83
78
|
it "falls back to alternative language if primary not available" do
|
|
84
79
|
# Request Japanese first, then English as fallback
|
|
85
|
-
transcript = api.fetch(ted_talk_video_id, languages: [
|
|
80
|
+
transcript = api.fetch(ted_talk_video_id, languages: %w[ja en])
|
|
86
81
|
|
|
87
|
-
expect([
|
|
82
|
+
expect(%w[ja en]).to include(transcript.language_code)
|
|
88
83
|
expect(transcript.snippets).not_to be_empty
|
|
89
84
|
end
|
|
90
85
|
|
|
@@ -92,7 +87,7 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
92
87
|
transcript = api.fetch(ted_talk_video_id, preserve_formatting: true)
|
|
93
88
|
|
|
94
89
|
expect(transcript).to be_a(YoutubeRb::Transcript::FetchedTranscript)
|
|
95
|
-
#
|
|
90
|
+
# NOTE: Not all videos have HTML formatting, so we just verify it doesn't break
|
|
96
91
|
end
|
|
97
92
|
end
|
|
98
93
|
|
|
@@ -111,9 +106,7 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
111
106
|
errors = []
|
|
112
107
|
|
|
113
108
|
results = api.fetch_all(video_ids, continue_on_error: true) do |video_id, result|
|
|
114
|
-
if result.is_a?(StandardError)
|
|
115
|
-
errors << { video_id: video_id, error: result }
|
|
116
|
-
end
|
|
109
|
+
errors << { video_id: video_id, error: result } if result.is_a?(StandardError)
|
|
117
110
|
end
|
|
118
111
|
|
|
119
112
|
expect(results).to have_key(ted_talk_video_id)
|
|
@@ -163,8 +156,6 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
163
156
|
expect(fetched).to be_a(YoutubeRb::Transcript::FetchedTranscript)
|
|
164
157
|
expect(fetched.language_code).to eq("es")
|
|
165
158
|
expect(fetched.snippets).not_to be_empty
|
|
166
|
-
|
|
167
|
-
puts "\nTranslated to Spanish: #{fetched.first.text[0..50]}..."
|
|
168
159
|
rescue YoutubeRb::Transcript::TranslationLanguageNotAvailable
|
|
169
160
|
skip "Spanish translation not available for this video"
|
|
170
161
|
rescue YoutubeRb::Transcript::IpBlocked
|
|
@@ -180,7 +171,7 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
180
171
|
let(:api) { YoutubeRb::Transcript::YouTubeTranscriptApi.new }
|
|
181
172
|
let(:transcript) { api.fetch(ted_talk_video_id) }
|
|
182
173
|
|
|
183
|
-
describe YoutubeRb::
|
|
174
|
+
describe YoutubeRb::Formatters::JSONFormatter do
|
|
184
175
|
it "formats real transcript as JSON" do
|
|
185
176
|
formatter = described_class.new
|
|
186
177
|
output = formatter.format_transcript(transcript)
|
|
@@ -192,7 +183,7 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
192
183
|
end
|
|
193
184
|
end
|
|
194
185
|
|
|
195
|
-
describe YoutubeRb::
|
|
186
|
+
describe YoutubeRb::Formatters::TextFormatter do
|
|
196
187
|
it "formats real transcript as plain text" do
|
|
197
188
|
formatter = described_class.new
|
|
198
189
|
output = formatter.format_transcript(transcript)
|
|
@@ -205,7 +196,7 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
205
196
|
end
|
|
206
197
|
end
|
|
207
198
|
|
|
208
|
-
describe YoutubeRb::
|
|
199
|
+
describe YoutubeRb::Formatters::SRTFormatter do
|
|
209
200
|
it "formats real transcript as SRT" do
|
|
210
201
|
formatter = described_class.new
|
|
211
202
|
output = formatter.format_transcript(transcript)
|
|
@@ -218,7 +209,7 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
218
209
|
end
|
|
219
210
|
end
|
|
220
211
|
|
|
221
|
-
describe YoutubeRb::
|
|
212
|
+
describe YoutubeRb::Formatters::WebVTTFormatter do
|
|
222
213
|
it "formats real transcript as WebVTT" do
|
|
223
214
|
formatter = described_class.new
|
|
224
215
|
output = formatter.format_transcript(transcript)
|
|
@@ -231,7 +222,7 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
231
222
|
end
|
|
232
223
|
end
|
|
233
224
|
|
|
234
|
-
describe YoutubeRb::
|
|
225
|
+
describe YoutubeRb::Formatters::PrettyPrintFormatter do
|
|
235
226
|
it "formats real transcript as pretty-printed output" do
|
|
236
227
|
formatter = described_class.new
|
|
237
228
|
output = formatter.format_transcript(transcript)
|
|
@@ -248,15 +239,15 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
248
239
|
let(:api) { YoutubeRb::Transcript::YouTubeTranscriptApi.new }
|
|
249
240
|
|
|
250
241
|
it "raises NoTranscriptFound for unavailable language" do
|
|
251
|
-
expect
|
|
242
|
+
expect do
|
|
252
243
|
api.fetch(ted_talk_video_id, languages: ["xx"]) # Invalid language code
|
|
253
|
-
|
|
244
|
+
end.to raise_error(YoutubeRb::Transcript::NoTranscriptFound)
|
|
254
245
|
end
|
|
255
246
|
|
|
256
247
|
it "raises appropriate error for invalid video ID" do
|
|
257
|
-
expect
|
|
248
|
+
expect do
|
|
258
249
|
api.fetch("this_is_not_a_valid_video_id_12345")
|
|
259
|
-
|
|
250
|
+
end.to raise_error(YoutubeRb::Transcript::CouldNotRetrieveTranscript)
|
|
260
251
|
end
|
|
261
252
|
|
|
262
253
|
it "raises TranscriptsDisabled for video without transcripts" do
|
|
@@ -275,7 +266,7 @@ RSpec.describe "Integration Tests", :integration do
|
|
|
275
266
|
expect(transcript).to respond_to(:map)
|
|
276
267
|
expect(transcript).to respond_to(:select)
|
|
277
268
|
expect(transcript).to respond_to(:first)
|
|
278
|
-
#
|
|
269
|
+
# NOTE: Enumerable doesn't provide #last by default, but we can use to_a.last
|
|
279
270
|
expect(transcript.to_a.last).to be_a(YoutubeRb::Transcript::TranscriptMetadataSnippet)
|
|
280
271
|
end
|
|
281
272
|
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# This file was generated by the `rspec --init` command. Conventionally, all
|
|
2
4
|
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
|
3
5
|
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
|
@@ -55,55 +57,53 @@ RSpec.configure do |config|
|
|
|
55
57
|
# triggering implicit auto-inclusion in groups with matching metadata.
|
|
56
58
|
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
57
59
|
|
|
58
|
-
# The settings below are suggested to provide a good initial experience
|
|
59
|
-
# with RSpec, but feel free to customize to your heart's content.
|
|
60
|
-
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
Kernel.srand config.seed
|
|
108
|
-
=end
|
|
60
|
+
# The settings below are suggested to provide a good initial experience
|
|
61
|
+
# with RSpec, but feel free to customize to your heart's content.
|
|
62
|
+
# # This allows you to limit a spec run to individual examples or groups
|
|
63
|
+
# # you care about by tagging them with `:focus` metadata. When nothing
|
|
64
|
+
# # is tagged with `:focus`, all examples get run. RSpec also provides
|
|
65
|
+
# # aliases for `it`, `describe`, and `context` that include `:focus`
|
|
66
|
+
# # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
|
67
|
+
# config.filter_run_when_matching :focus
|
|
68
|
+
#
|
|
69
|
+
# # Allows RSpec to persist some state between runs in order to support
|
|
70
|
+
# # the `--only-failures` and `--next-failure` CLI options. We recommend
|
|
71
|
+
# # you configure your source control system to ignore this file.
|
|
72
|
+
# config.example_status_persistence_file_path = "spec/examples.txt"
|
|
73
|
+
#
|
|
74
|
+
# # Limits the available syntax to the non-monkey patched syntax that is
|
|
75
|
+
# # recommended. For more details, see:
|
|
76
|
+
# # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/
|
|
77
|
+
# config.disable_monkey_patching!
|
|
78
|
+
#
|
|
79
|
+
# # This setting enables warnings. It's recommended, but in some cases may
|
|
80
|
+
# # be too noisy due to issues in dependencies.
|
|
81
|
+
# config.warnings = true
|
|
82
|
+
#
|
|
83
|
+
# # Many RSpec users commonly either run the entire suite or an individual
|
|
84
|
+
# # file, and it's useful to allow more verbose output when running an
|
|
85
|
+
# # individual spec file.
|
|
86
|
+
# if config.files_to_run.one?
|
|
87
|
+
# # Use the documentation formatter for detailed output,
|
|
88
|
+
# # unless a formatter has already been configured
|
|
89
|
+
# # (e.g. via a command-line flag).
|
|
90
|
+
# config.default_formatter = "doc"
|
|
91
|
+
# end
|
|
92
|
+
#
|
|
93
|
+
# # Print the 10 slowest examples and example groups at the
|
|
94
|
+
# # end of the spec run, to help surface which specs are running
|
|
95
|
+
# # particularly slow.
|
|
96
|
+
# config.profile_examples = 10
|
|
97
|
+
#
|
|
98
|
+
# # Run specs in random order to surface order dependencies. If you find an
|
|
99
|
+
# # order dependency and want to debug it, you can fix the order by providing
|
|
100
|
+
# # the seed, which is printed after each run.
|
|
101
|
+
# # --seed 1234
|
|
102
|
+
# config.order = :random
|
|
103
|
+
#
|
|
104
|
+
# # Seed global randomization in this process using the `--seed` CLI option.
|
|
105
|
+
# # Setting this allows you to use `--seed` to deterministically reproduce
|
|
106
|
+
# # test failures related to randomization by passing the same `--seed` value
|
|
107
|
+
# # as the one that triggered the failure.
|
|
108
|
+
# Kernel.srand config.seed
|
|
109
109
|
end
|
|
@@ -104,11 +104,11 @@ RSpec.describe YoutubeRb::Transcript::TranscriptListFetcher do
|
|
|
104
104
|
|
|
105
105
|
it "includes proper body in innertube request" do
|
|
106
106
|
fetcher.fetch(video_id)
|
|
107
|
-
expect(WebMock).to
|
|
108
|
-
.with
|
|
107
|
+
expect(WebMock).to(have_requested(:post, innertube_url)
|
|
108
|
+
.with do |req|
|
|
109
109
|
body = JSON.parse(req.body)
|
|
110
110
|
body["videoId"] == video_id && body["context"]["client"]["clientName"] == "ANDROID"
|
|
111
|
-
|
|
111
|
+
end)
|
|
112
112
|
end
|
|
113
113
|
end
|
|
114
114
|
|
|
@@ -461,8 +461,12 @@ RSpec.describe YoutubeRb::Transcript::TranscriptListFetcher do
|
|
|
461
461
|
|
|
462
462
|
stub_request(:post, innertube_url)
|
|
463
463
|
.to_return(
|
|
464
|
-
{ status: 200,
|
|
465
|
-
|
|
464
|
+
{ status: 200,
|
|
465
|
+
body: { "playabilityStatus" => { "status" => "LOGIN_REQUIRED",
|
|
466
|
+
"reason" => "Sign in to confirm you're not a bot" } }.to_json },
|
|
467
|
+
{ status: 200,
|
|
468
|
+
body: { "playabilityStatus" => { "status" => "LOGIN_REQUIRED",
|
|
469
|
+
"reason" => "Sign in to confirm you're not a bot" } }.to_json },
|
|
466
470
|
{ status: 200, body: sample_innertube_response.to_json }
|
|
467
471
|
)
|
|
468
472
|
end
|
|
@@ -480,7 +484,8 @@ RSpec.describe YoutubeRb::Transcript::TranscriptListFetcher do
|
|
|
480
484
|
.to_return(status: 200, body: sample_html)
|
|
481
485
|
|
|
482
486
|
stub_request(:post, innertube_url)
|
|
483
|
-
.to_return(status: 200, body: { "playabilityStatus" => { "status" => "LOGIN_REQUIRED",
|
|
487
|
+
.to_return(status: 200, body: { "playabilityStatus" => { "status" => "LOGIN_REQUIRED",
|
|
488
|
+
"reason" => "Sign in to confirm you're not a bot" } }.to_json)
|
|
484
489
|
end
|
|
485
490
|
|
|
486
491
|
it "raises RequestBlocked after exhausting retries" do
|