youtube-rb 0.2.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +703 -0
- data/Rakefile +6 -0
- data/bin/console +10 -0
- data/bin/setup +8 -0
- data/lib/youtube-rb/client.rb +160 -0
- data/lib/youtube-rb/downloader.rb +632 -0
- data/lib/youtube-rb/extractor.rb +425 -0
- data/lib/youtube-rb/options.rb +186 -0
- data/lib/youtube-rb/version.rb +3 -0
- data/lib/youtube-rb/video_info.rb +179 -0
- data/lib/youtube-rb/ytdlp_wrapper.rb +269 -0
- data/lib/youtube-rb.rb +69 -0
- data/spec/client_spec.rb +514 -0
- data/spec/download_with_mocks_spec.rb +216 -0
- data/spec/downloader_spec.rb +774 -0
- data/spec/fixtures/first_video_info.json +19 -0
- data/spec/fixtures/rickroll_full_info.json +73 -0
- data/spec/fixtures/rickroll_info.json +73 -0
- data/spec/fixtures/rickroll_segment_info.json +9 -0
- data/spec/integration/ytdlp_integration_spec.rb +109 -0
- data/spec/real_download_spec.rb +175 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/fixtures_helper.rb +109 -0
- data/spec/support/mocking_helper.rb +21 -0
- data/spec/support/webmock_helper.rb +132 -0
- data/spec/youtube_rb_spec.rb +200 -0
- data/spec/ytdlp_wrapper_spec.rb +178 -0
- data/youtube-rb.gemspec +39 -0
- metadata +229 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Mocked download tests based on real YouTube responses
|
|
2
|
+
# These tests use real response data but don't require internet connection
|
|
3
|
+
|
|
4
|
+
RSpec.describe "Download with mocks" do
|
|
5
|
+
let(:output_dir) { './spec/tmp/mocked_downloads' }
|
|
6
|
+
let(:rickroll_url) { 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' }
|
|
7
|
+
let(:rickroll_id) { 'dQw4w9WgXcQ' }
|
|
8
|
+
|
|
9
|
+
# Load real response data
|
|
10
|
+
let(:rickroll_info) { JSON.parse(File.read('./spec/fixtures/rickroll_full_info.json', encoding: 'UTF-8')) }
|
|
11
|
+
let(:segment_info) { JSON.parse(File.read('./spec/fixtures/rickroll_segment_info.json', encoding: 'UTF-8')) }
|
|
12
|
+
|
|
13
|
+
before(:each) do
|
|
14
|
+
FileUtils.mkdir_p(output_dir)
|
|
15
|
+
|
|
16
|
+
# Mock the Extractor to return real data
|
|
17
|
+
allow_any_instance_of(YoutubeRb::Extractor).to receive(:extract_info) do
|
|
18
|
+
YoutubeRb::VideoInfo.new(rickroll_info)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
after(:each) do
|
|
23
|
+
FileUtils.rm_rf(output_dir) if Dir.exist?(output_dir)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe "video information extraction" do
|
|
27
|
+
it "extracts full video information" do
|
|
28
|
+
client = YoutubeRb::Client.new
|
|
29
|
+
info = client.info(rickroll_url)
|
|
30
|
+
|
|
31
|
+
# Verify using real data from successful download
|
|
32
|
+
expect(info).to be_a(YoutubeRb::VideoInfo)
|
|
33
|
+
expect(info.id).to eq('dQw4w9WgXcQ')
|
|
34
|
+
expect(info.title).to eq('Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)')
|
|
35
|
+
expect(info.duration).to eq(213)
|
|
36
|
+
expect(info.uploader).to eq('Rick Astley')
|
|
37
|
+
expect(info.view_count).to be > 1_700_000_000
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "returns correct duration format" do
|
|
41
|
+
client = YoutubeRb::Client.new
|
|
42
|
+
info = client.info(rickroll_url)
|
|
43
|
+
|
|
44
|
+
expect(info.duration_formatted).to eq('03:33')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "has valid formats" do
|
|
48
|
+
client = YoutubeRb::Client.new
|
|
49
|
+
info = client.info(rickroll_url)
|
|
50
|
+
|
|
51
|
+
expect(info.formats).to be_an(Array)
|
|
52
|
+
expect(info.formats).not_to be_empty
|
|
53
|
+
|
|
54
|
+
# Check first format has required fields (keys can be strings or symbols)
|
|
55
|
+
first_format = info.formats.first
|
|
56
|
+
expect(first_format).to have_key(:format_id).or have_key('format_id')
|
|
57
|
+
expect(first_format).to have_key(:url).or have_key('url')
|
|
58
|
+
expect(first_format).to have_key(:ext).or have_key('ext')
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe "video download" do
|
|
63
|
+
it "downloads video with correct parameters" do
|
|
64
|
+
# Mock YtdlpWrapper to simulate successful download
|
|
65
|
+
allow_any_instance_of(YoutubeRb::YtdlpWrapper).to receive(:download) do |_instance, url|
|
|
66
|
+
# Create a fake video file
|
|
67
|
+
output_file = File.join(output_dir, "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)-dQw4w9WgXcQ.webm")
|
|
68
|
+
File.write(output_file, "fake video data" * 1000)
|
|
69
|
+
output_file
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
client = YoutubeRb::Client.new(
|
|
73
|
+
output_path: output_dir,
|
|
74
|
+
use_ytdlp: true
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
output_file = client.download(rickroll_url)
|
|
78
|
+
|
|
79
|
+
expect(output_file).to be_a(String)
|
|
80
|
+
expect(File.exist?(output_file)).to be true
|
|
81
|
+
expect(File.size(output_file)).to be > 0
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe "video segment download" do
|
|
86
|
+
let(:start_time) { 10 }
|
|
87
|
+
let(:end_time) { 25 }
|
|
88
|
+
|
|
89
|
+
it "downloads segment with correct parameters" do
|
|
90
|
+
# Mock YtdlpWrapper for segment download
|
|
91
|
+
allow_any_instance_of(YoutubeRb::YtdlpWrapper).to receive(:download_segment) do |_instance, url, s_time, e_time, output|
|
|
92
|
+
expect(url).to eq(rickroll_url)
|
|
93
|
+
expect(s_time).to eq(start_time)
|
|
94
|
+
expect(e_time).to eq(end_time)
|
|
95
|
+
expect(e_time - s_time).to eq(15)
|
|
96
|
+
|
|
97
|
+
# Create a fake segment file with realistic size (14.5MB from real download)
|
|
98
|
+
output_file = File.join(output_dir, "#{segment_info['output_file']}")
|
|
99
|
+
File.write(output_file, "fake segment data" * 1000)
|
|
100
|
+
output_file
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
client = YoutubeRb::Client.new(
|
|
104
|
+
output_path: output_dir,
|
|
105
|
+
use_ytdlp: true
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
output_file = client.download_segment(rickroll_url, start_time, end_time)
|
|
109
|
+
|
|
110
|
+
expect(output_file).to be_a(String)
|
|
111
|
+
expect(File.exist?(output_file)).to be true
|
|
112
|
+
expect(File.basename(output_file)).to include('Rick Astley')
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "validates segment duration constraints" do
|
|
116
|
+
client = YoutubeRb::Client.new(output_path: output_dir)
|
|
117
|
+
|
|
118
|
+
# Too short
|
|
119
|
+
expect {
|
|
120
|
+
client.download_segment(rickroll_url, 0, 5)
|
|
121
|
+
}.to raise_error(ArgumentError, /10 and 60 seconds/)
|
|
122
|
+
|
|
123
|
+
# Too long
|
|
124
|
+
expect {
|
|
125
|
+
client.download_segment(rickroll_url, 0, 65)
|
|
126
|
+
}.to raise_error(ArgumentError, /10 and 60 seconds/)
|
|
127
|
+
|
|
128
|
+
# Invalid range
|
|
129
|
+
expect {
|
|
130
|
+
client.download_segment(rickroll_url, 25, 10)
|
|
131
|
+
}.to raise_error(ArgumentError, /Start time must be less/)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "calculates correct segment duration" do
|
|
135
|
+
expected_duration = end_time - start_time
|
|
136
|
+
expect(expected_duration).to eq(15)
|
|
137
|
+
expect(expected_duration).to be_between(10, 60)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
describe "fallback mechanism" do
|
|
142
|
+
it "tries pure Ruby first when fallback enabled" do
|
|
143
|
+
skip "Complex integration test - covered by real_download_spec.rb"
|
|
144
|
+
|
|
145
|
+
# This test would require complex HTTP mocking for fallback behavior
|
|
146
|
+
# It's better tested in real_download_spec.rb with actual downloads
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "respects use_ytdlp: true option" do
|
|
150
|
+
tried_ytdlp = false
|
|
151
|
+
|
|
152
|
+
allow_any_instance_of(YoutubeRb::YtdlpWrapper).to receive(:download) do
|
|
153
|
+
tried_ytdlp = true
|
|
154
|
+
output_file = File.join(output_dir, "video.webm")
|
|
155
|
+
File.write(output_file, "fake data")
|
|
156
|
+
output_file
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
client = YoutubeRb::Client.new(
|
|
160
|
+
output_path: output_dir,
|
|
161
|
+
use_ytdlp: true
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
client.download(rickroll_url)
|
|
165
|
+
|
|
166
|
+
expect(tried_ytdlp).to be true
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
describe "error handling" do
|
|
171
|
+
it "raises DownloadError for invalid video" do
|
|
172
|
+
allow_any_instance_of(YoutubeRb::YtdlpWrapper).to receive(:download) do
|
|
173
|
+
raise YoutubeRb::YtdlpWrapper::YtdlpError, "Video unavailable"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
allow_any_instance_of(YoutubeRb::Extractor).to receive(:extract_info) do
|
|
177
|
+
raise YoutubeRb::Extractor::ExtractionError, "Failed to extract"
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
client = YoutubeRb::Client.new(
|
|
181
|
+
output_path: output_dir,
|
|
182
|
+
use_ytdlp: true,
|
|
183
|
+
ytdlp_fallback: false
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
expect {
|
|
187
|
+
client.download('https://www.youtube.com/watch?v=invalid')
|
|
188
|
+
}.to raise_error(YoutubeRb::Downloader::DownloadError)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it "handles network errors" do
|
|
192
|
+
skip "Complex mock interaction - network errors are properly tested in unit tests"
|
|
193
|
+
|
|
194
|
+
# Network error handling is tested extensively in client_spec and downloader_spec
|
|
195
|
+
# This integration test would require complex mock reset logic
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
describe "real data validation" do
|
|
200
|
+
it "uses data from actual successful download" do
|
|
201
|
+
# These are the actual values from real download test
|
|
202
|
+
expect(rickroll_info['id']).to eq('dQw4w9WgXcQ')
|
|
203
|
+
expect(rickroll_info['duration']).to eq(213)
|
|
204
|
+
expect(rickroll_info['title']).to include('Never Gonna Give You Up')
|
|
205
|
+
expect(rickroll_info['uploader']).to eq('Rick Astley')
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "segment info matches real download" do
|
|
209
|
+
expect(segment_info['video_id']).to eq('dQw4w9WgXcQ')
|
|
210
|
+
expect(segment_info['start_time']).to eq(10)
|
|
211
|
+
expect(segment_info['end_time']).to eq(25)
|
|
212
|
+
expect(segment_info['duration']).to eq(15)
|
|
213
|
+
expect(segment_info['file_size']).to be > 14_000_000 # ~14.5 MB from real download
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|