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.
@@ -0,0 +1,19 @@
1
+ {
2
+ "id": "jNQXAC9IVRw",
3
+ "title": null,
4
+ "fulltitle": null,
5
+ "description": null,
6
+ "uploader": null,
7
+ "uploader_id": null,
8
+ "duration": null,
9
+ "duration_formatted": null,
10
+ "view_count": null,
11
+ "like_count": null,
12
+ "upload_date": null,
13
+ "thumbnail": "https://i.ytimg.com/vi/jNQXAC9IVRw/maxresdefault.jpg",
14
+ "formats": [],
15
+ "subtitles": {},
16
+ "url": null,
17
+ "ext": "mp4",
18
+ "webpage_url": "https://www.youtube.com/watch?v=jNQXAC9IVRw"
19
+ }
@@ -0,0 +1,73 @@
1
+ {
2
+ "id": "dQw4w9WgXcQ",
3
+ "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
4
+ "fulltitle": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
5
+ "description": "The official video for “Never Gonna Give You Up” by Rick Astley. \n\nNever: The Autobiography 📚 OUT NOW! \nFollow this link to get your copy and listen to Rick’s ‘Never’ playlist ❤️ #RickAstleyNever\nhttps://linktr.ee/rickastleynever\n\n“Never Gonna Give You Up” was a global smash on its release in July 1987, topping the charts in 25 countries including Rick’s native UK and the US Billboard Hot 100. It also won the Brit Award for Best single in 1988. Stock Aitken and Waterman wrote and produced the track which was the lead-off single and lead track from Rick’s debut LP “Whenever You Need Somebody”. The album was itself a UK number one and would go on to sell over 15 million copies worldwide.\n\nThe legendary video was directed by Simon West – who later went on to make Hollywood blockbusters such as Con Air, Lara Croft – Tomb Raider and The Expendables 2. The video passed the 1bn YouTube views milestone on 28 July 2021.\n\nSubscribe to the official Rick Astley YouTube channel: https://RickAstley.lnk.to/YTSubID\n\nFollow Rick Astley:\nFacebook: https://RickAstley.lnk.to/FBFollowID \nTwitter: https://RickAstley.lnk.to/TwitterID \nInstagram: https://RickAstley.lnk.to/InstagramID \nWebsite: https://RickAstley.lnk.to/storeID \nTikTok: https://RickAstley.lnk.to/TikTokID\n\nListen to Rick Astley:\nSpotify: https://RickAstley.lnk.to/SpotifyID \nApple Music: https://RickAstley.lnk.to/AppleMusicID \nAmazon Music: https://RickAstley.lnk.to/AmazonMusicID \nDeezer: https://RickAstley.lnk.to/DeezerID \n\nLyrics:\nWe’re no strangers to love\nYou know the rules and so do I\nA full commitment’s what I’m thinking of\nYou wouldn’t get this from any other guy\n\nI just wanna tell you how I’m feeling\nGotta make you understand\n\nNever gonna give you up\nNever gonna let you down\nNever gonna run around and desert you\nNever gonna make you cry\nNever gonna say goodbye\nNever gonna tell a lie and hurt you\n\nWe’ve known each other for so long\nYour heart’s been aching but you’re too shy to say it\nInside we both know what’s been going on\nWe know the game and we’re gonna play it\n\nAnd if you ask me how I’m feeling\nDon’t tell me you’re too blind to see\n\nNever gonna give you up\nNever gonna let you down\nNever gonna run around and desert you\nNever gonna make you cry\nNever gonna say goodbye\nNever gonna tell a lie and hurt you\n\n#RickAstley #NeverGonnaGiveYouUp #WheneverYouNeedSomebody #OfficialMusicVideo",
6
+ "uploader": "Rick Astley",
7
+ "uploader_id": "UCuAXFkgsw1L7xaCfnd5JJOw",
8
+ "duration": 213,
9
+ "duration_formatted": "03:33",
10
+ "view_count": 1731876521,
11
+ "like_count": null,
12
+ "upload_date": "20091024",
13
+ "thumbnail": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
14
+ "formats": [
15
+ {
16
+ "format_id": "18",
17
+ "format_note": "360p",
18
+ "ext": "mp4",
19
+ "url": "https://rr5---sn-ajnv45-5i.googlevideo.com/videoplayback?expire=1768351095&ei=F5Fmae9e0YOL2g-auvwo&ip=84.17.55.134&id=o-AL--MBsb8cncxZtEDEp8FPcv0T2vVhknp-zVZR5x2uTL&itag=18&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&cps=588&met=1768329495%2C&mh=7c&mm=31%2C29&mn=sn-ajnv45-5i%2Csn-f5f7lne6&ms=au%2Crdu&mv=m&mvi=5&pl=24&rms=au%2Cau&initcwndbps=1267500&bui=AW-iu_r-dXF932QmSV3_zIf4ceyjWpVHA1XLlMbdSfRWXU5baLnjwJq8Hn2pgx9yEDWlq5VXGFv0gfvA&spc=q5xjPGGEKSkdbJQ35FbYATFOOB_n7_qxs7YZRYTs0z44nAInBkTy72V3UGB1e7mRnaReXGLJCQs&vprv=1&svpuc=1&mime=video%2Fmp4&ns=AUNPvmN3_pFD8szzAPSR7cwR&rqh=1&cnr=14&ratebypass=yes&dur=213.089&lmt=1766960953317159&mt=1768328985&fvip=2&fexp=51355912%2C51552689%2C51565115%2C51565681%2C51580968&c=WEB&sefc=1&txp=5538534&n=D6cTu_N-RL9qS7LeDmV&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cspc%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Ccnr%2Cratebypass%2Cdur%2Clmt&lsparams=cps%2Cmet%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Crms%2Cinitcwndbps&lsig=APaTxxMwRAIgVrZKRrq9rz36V084NOTnRem5u8hkG7SSopqggSxramECIDZD8DxOPzYb5TJ3Ip-PbHZxnNRHoGfDgB0Krvq12Bat",
20
+ "width": 640,
21
+ "height": 360,
22
+ "fps": 25,
23
+ "vcodec": "avc1.42001E",
24
+ "acodec": "none",
25
+ "tbr": 444,
26
+ "abr": null,
27
+ "vbr": null,
28
+ "filesize": null,
29
+ "quality": "medium",
30
+ "protocol": null
31
+ }
32
+ ],
33
+ "subtitles": {
34
+ "en": [
35
+ {
36
+ "ext": "vtt",
37
+ "url": "https://www.youtube.com/api/timedtext?v=dQw4w9WgXcQ&ei=F5Fmae9e0YOL2g-auvwo&caps=asr&opi=112496729&exp=xpe&xoaf=5&xowf=1&hl=pl&ip=0.0.0.0&ipbits=0&expire=1768354695&sparams=ip,ipbits,expire,v,ei,caps,opi,exp,xoaf&signature=5450FAA6D26B1BE8B7E391DC234701C3F175D213.B1A4BFD7E80FB0F99C5F9514356DA1494449C4AB&key=yt8&kind=asr&lang=en",
38
+ "name": "angielski (wygenerowane automatycznie)"
39
+ }
40
+ ],
41
+ "es-419": [
42
+ {
43
+ "ext": "vtt",
44
+ "url": "https://www.youtube.com/api/timedtext?v=dQw4w9WgXcQ&ei=F5Fmae9e0YOL2g-auvwo&caps=asr&opi=112496729&exp=xpe&xoaf=5&xowf=1&hl=pl&ip=0.0.0.0&ipbits=0&expire=1768354695&sparams=ip,ipbits,expire,v,ei,caps,opi,exp,xoaf&signature=5450FAA6D26B1BE8B7E391DC234701C3F175D213.B1A4BFD7E80FB0F99C5F9514356DA1494449C4AB&key=yt8&lang=es-419",
45
+ "name": "hiszpański (Ameryka Łacińska)"
46
+ }
47
+ ],
48
+ "ja": [
49
+ {
50
+ "ext": "vtt",
51
+ "url": "https://www.youtube.com/api/timedtext?v=dQw4w9WgXcQ&ei=F5Fmae9e0YOL2g-auvwo&caps=asr&opi=112496729&exp=xpe&xoaf=5&xowf=1&hl=pl&ip=0.0.0.0&ipbits=0&expire=1768354695&sparams=ip,ipbits,expire,v,ei,caps,opi,exp,xoaf&signature=5450FAA6D26B1BE8B7E391DC234701C3F175D213.B1A4BFD7E80FB0F99C5F9514356DA1494449C4AB&key=yt8&lang=ja",
52
+ "name": "japoński"
53
+ }
54
+ ],
55
+ "de-DE": [
56
+ {
57
+ "ext": "vtt",
58
+ "url": "https://www.youtube.com/api/timedtext?v=dQw4w9WgXcQ&ei=F5Fmae9e0YOL2g-auvwo&caps=asr&opi=112496729&exp=xpe&xoaf=5&xowf=1&hl=pl&ip=0.0.0.0&ipbits=0&expire=1768354695&sparams=ip,ipbits,expire,v,ei,caps,opi,exp,xoaf&signature=5450FAA6D26B1BE8B7E391DC234701C3F175D213.B1A4BFD7E80FB0F99C5F9514356DA1494449C4AB&key=yt8&lang=de-DE",
59
+ "name": "niemiecki (Niemcy)"
60
+ }
61
+ ],
62
+ "pt-BR": [
63
+ {
64
+ "ext": "vtt",
65
+ "url": "https://www.youtube.com/api/timedtext?v=dQw4w9WgXcQ&ei=F5Fmae9e0YOL2g-auvwo&caps=asr&opi=112496729&exp=xpe&xoaf=5&xowf=1&hl=pl&ip=0.0.0.0&ipbits=0&expire=1768354695&sparams=ip,ipbits,expire,v,ei,caps,opi,exp,xoaf&signature=5450FAA6D26B1BE8B7E391DC234701C3F175D213.B1A4BFD7E80FB0F99C5F9514356DA1494449C4AB&key=yt8&lang=pt-BR",
66
+ "name": "portugalski (Brazylia)"
67
+ }
68
+ ]
69
+ },
70
+ "url": null,
71
+ "ext": "mp4",
72
+ "webpage_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
73
+ }
@@ -0,0 +1,73 @@
1
+ {
2
+ "id": "dQw4w9WgXcQ",
3
+ "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
4
+ "fulltitle": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
5
+ "description": "The official video for “Never Gonna Give You Up” by Rick Astley. \n\nNever: The Autobiography 📚 OUT NOW! \nFollow this link to get your copy and listen to Rick’s ‘Never’ playlist ❤️ #RickAstleyNever\nhttps://linktr.ee/rickastleynever\n\n“Never Gonna Give You Up” was a global smash on its release in July 1987, topping the charts in 25 countries including Rick’s native UK and the US Billboard Hot 100. It also won the Brit Award for Best single in 1988. Stock Aitken and Waterman wrote and produced the track which was the lead-off single and lead track from Rick’s debut LP “Whenever You Need Somebody”. The album was itself a UK number one and would go on to sell over 15 million copies worldwide.\n\nThe legendary video was directed by Simon West – who later went on to make Hollywood blockbusters such as Con Air, Lara Croft – Tomb Raider and The Expendables 2. The video passed the 1bn YouTube views milestone on 28 July 2021.\n\nSubscribe to the official Rick Astley YouTube channel: https://RickAstley.lnk.to/YTSubID\n\nFollow Rick Astley:\nFacebook: https://RickAstley.lnk.to/FBFollowID \nTwitter: https://RickAstley.lnk.to/TwitterID \nInstagram: https://RickAstley.lnk.to/InstagramID \nWebsite: https://RickAstley.lnk.to/storeID \nTikTok: https://RickAstley.lnk.to/TikTokID\n\nListen to Rick Astley:\nSpotify: https://RickAstley.lnk.to/SpotifyID \nApple Music: https://RickAstley.lnk.to/AppleMusicID \nAmazon Music: https://RickAstley.lnk.to/AmazonMusicID \nDeezer: https://RickAstley.lnk.to/DeezerID \n\nLyrics:\nWe’re no strangers to love\nYou know the rules and so do I\nA full commitment’s what I’m thinking of\nYou wouldn’t get this from any other guy\n\nI just wanna tell you how I’m feeling\nGotta make you understand\n\nNever gonna give you up\nNever gonna let you down\nNever gonna run around and desert you\nNever gonna make you cry\nNever gonna say goodbye\nNever gonna tell a lie and hurt you\n\nWe’ve known each other for so long\nYour heart’s been aching but you’re too shy to say it\nInside we both know what’s been going on\nWe know the game and we’re gonna play it\n\nAnd if you ask me how I’m feeling\nDon’t tell me you’re too blind to see\n\nNever gonna give you up\nNever gonna let you down\nNever gonna run around and desert you\nNever gonna make you cry\nNever gonna say goodbye\nNever gonna tell a lie and hurt you\n\n#RickAstley #NeverGonnaGiveYouUp #WheneverYouNeedSomebody #OfficialMusicVideo",
6
+ "uploader": "Rick Astley",
7
+ "uploader_id": "UCuAXFkgsw1L7xaCfnd5JJOw",
8
+ "duration": 213,
9
+ "duration_formatted": "03:33",
10
+ "view_count": 1731843872,
11
+ "like_count": null,
12
+ "upload_date": "20091024",
13
+ "thumbnail": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
14
+ "formats": [
15
+ {
16
+ "format_id": "18",
17
+ "format_note": "360p",
18
+ "ext": "mp4",
19
+ "url": "https://rr2---sn-f5fz7n7s.googlevideo.com/videoplayback?expire=1768343011&ei=g3FmaeR5y8yL2g_7l-eICg&ip=84.17.55.134&id=o-AE_TGtR6vcKbP3dr7x-UwzmksgxC5mDvndWL4yKnJcGX&itag=18&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&cps=580&met=1768321411%2C&mh=7c&mm=31%2C29&mn=sn-f5fz7n7s%2Csn-f5f7lne6&ms=au%2Crdu&mv=m&mvi=2&pl=24&rms=au%2Cau&initcwndbps=1967500&bui=AW-iu_r1vGJg5psIN1fOQmXjtOJDTyl_Qs9ozb7L9IvAaC8imT4-6cML3VrA2Q1ckNlfcejY5ep9U6B6&spc=q5xjPBGNQjcm0cdbotZ4tn3XKssBgI1Dq_exNgLyK_KWOFpwdb8_gMXQRA-PchA94s4uqWzaIgE&vprv=1&svpuc=1&mime=video%2Fmp4&ns=Y-oEpOukfySV7nbk3a7i1rsR&rqh=1&cnr=14&ratebypass=yes&dur=213.089&lmt=1766960953317159&mt=1768321065&fvip=2&fexp=51355912%2C51552689%2C51565116%2C51565681%2C51580968&c=WEB&sefc=1&txp=5538534&n=tRZoC9ArNiWHnSdmOBs&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cspc%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Ccnr%2Cratebypass%2Cdur%2Clmt&lsparams=cps%2Cmet%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Crms%2Cinitcwndbps&lsig=APaTxxMwRQIhAORAeBYJJ1XQIXAMTMbRQW5tf70o3VlzCYAPTD-1BjujAiB7g_MBsDJjLYZ16RHf7bcMT1A1JkzkRMDIsRtHMfDkLg%3D%3D",
20
+ "width": 640,
21
+ "height": 360,
22
+ "fps": 25,
23
+ "vcodec": "avc1.42001E",
24
+ "acodec": "none",
25
+ "tbr": 444,
26
+ "abr": null,
27
+ "vbr": null,
28
+ "filesize": null,
29
+ "quality": "medium",
30
+ "protocol": null
31
+ }
32
+ ],
33
+ "subtitles": {
34
+ "en": [
35
+ {
36
+ "ext": "vtt",
37
+ "url": "https://www.youtube.com/api/timedtext?v=dQw4w9WgXcQ&ei=g3FmaeR5y8yL2g_7l-eICg&caps=asr&opi=112496729&exp=xpe&xoaf=5&xowf=1&hl=pl&ip=0.0.0.0&ipbits=0&expire=1768346611&sparams=ip,ipbits,expire,v,ei,caps,opi,exp,xoaf&signature=90D718CF0CA0629C15CB69C577AA8A9892F1224B.78A7E051DF5A825124E320FFCA6557CAD9A00BB6&key=yt8&kind=asr&lang=en",
38
+ "name": "angielski (wygenerowane automatycznie)"
39
+ }
40
+ ],
41
+ "es-419": [
42
+ {
43
+ "ext": "vtt",
44
+ "url": "https://www.youtube.com/api/timedtext?v=dQw4w9WgXcQ&ei=g3FmaeR5y8yL2g_7l-eICg&caps=asr&opi=112496729&exp=xpe&xoaf=5&xowf=1&hl=pl&ip=0.0.0.0&ipbits=0&expire=1768346611&sparams=ip,ipbits,expire,v,ei,caps,opi,exp,xoaf&signature=90D718CF0CA0629C15CB69C577AA8A9892F1224B.78A7E051DF5A825124E320FFCA6557CAD9A00BB6&key=yt8&lang=es-419",
45
+ "name": "hiszpański (Ameryka Łacińska)"
46
+ }
47
+ ],
48
+ "ja": [
49
+ {
50
+ "ext": "vtt",
51
+ "url": "https://www.youtube.com/api/timedtext?v=dQw4w9WgXcQ&ei=g3FmaeR5y8yL2g_7l-eICg&caps=asr&opi=112496729&exp=xpe&xoaf=5&xowf=1&hl=pl&ip=0.0.0.0&ipbits=0&expire=1768346611&sparams=ip,ipbits,expire,v,ei,caps,opi,exp,xoaf&signature=90D718CF0CA0629C15CB69C577AA8A9892F1224B.78A7E051DF5A825124E320FFCA6557CAD9A00BB6&key=yt8&lang=ja",
52
+ "name": "japoński"
53
+ }
54
+ ],
55
+ "de-DE": [
56
+ {
57
+ "ext": "vtt",
58
+ "url": "https://www.youtube.com/api/timedtext?v=dQw4w9WgXcQ&ei=g3FmaeR5y8yL2g_7l-eICg&caps=asr&opi=112496729&exp=xpe&xoaf=5&xowf=1&hl=pl&ip=0.0.0.0&ipbits=0&expire=1768346611&sparams=ip,ipbits,expire,v,ei,caps,opi,exp,xoaf&signature=90D718CF0CA0629C15CB69C577AA8A9892F1224B.78A7E051DF5A825124E320FFCA6557CAD9A00BB6&key=yt8&lang=de-DE",
59
+ "name": "niemiecki (Niemcy)"
60
+ }
61
+ ],
62
+ "pt-BR": [
63
+ {
64
+ "ext": "vtt",
65
+ "url": "https://www.youtube.com/api/timedtext?v=dQw4w9WgXcQ&ei=g3FmaeR5y8yL2g_7l-eICg&caps=asr&opi=112496729&exp=xpe&xoaf=5&xowf=1&hl=pl&ip=0.0.0.0&ipbits=0&expire=1768346611&sparams=ip,ipbits,expire,v,ei,caps,opi,exp,xoaf&signature=90D718CF0CA0629C15CB69C577AA8A9892F1224B.78A7E051DF5A825124E320FFCA6557CAD9A00BB6&key=yt8&lang=pt-BR",
66
+ "name": "portugalski (Brazylia)"
67
+ }
68
+ ]
69
+ },
70
+ "url": null,
71
+ "ext": "mp4",
72
+ "webpage_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
73
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
3
+ "video_id": "dQw4w9WgXcQ",
4
+ "start_time": 10,
5
+ "end_time": 25,
6
+ "duration": 15,
7
+ "file_size": 19311959,
8
+ "output_file": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)-dQw4w9WgXcQ.webm"
9
+ }
@@ -0,0 +1,109 @@
1
+ # Integration tests for yt-dlp backend
2
+ # These tests require yt-dlp to be installed and an internet connection
3
+ # Run with: bundle exec rspec spec/integration --tag integration
4
+
5
+ RSpec.describe "YoutubeRb + yt-dlp Integration", :integration do
6
+ # Test URL - Rick Astley (public domain, always available)
7
+ let(:test_url) { 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' }
8
+ let(:test_video_id) { 'dQw4w9WgXcQ' }
9
+
10
+ before(:all) do
11
+ @client = YoutubeRb::Client.new
12
+ @deps = @client.check_dependencies
13
+
14
+ unless @deps[:ytdlp]
15
+ skip "yt-dlp is not installed. Install with: pip install -U yt-dlp"
16
+ end
17
+ end
18
+
19
+ describe "dependency check" do
20
+ it "detects yt-dlp installation" do
21
+ expect(@deps[:ytdlp]).to be true
22
+ expect(@deps[:ytdlp_version]).to be_a(String)
23
+ expect(@deps[:ytdlp_version]).not_to eq('not installed')
24
+ end
25
+
26
+ it "reports yt-dlp version" do
27
+ expect(@deps[:ytdlp_version]).to match(/\d+\.\d+\.\d+/)
28
+ end
29
+ end
30
+
31
+ describe "video information extraction" do
32
+ it "extracts video info using default client" do
33
+ info = YoutubeRb.info(test_url)
34
+
35
+ expect(info).to be_a(YoutubeRb::VideoInfo)
36
+ expect(info.id).to eq(test_video_id)
37
+ expect(info.title).to include('Rick Astley')
38
+ expect(info.duration).to be > 0
39
+ expect(info.uploader).not_to be_nil
40
+ end
41
+
42
+ it "extracts info with yt-dlp wrapper directly" do
43
+ wrapper = YoutubeRb::YtdlpWrapper.new
44
+ info = wrapper.extract_info(test_url)
45
+
46
+ expect(info).to be_a(Hash)
47
+ expect(info['id']).to eq(test_video_id)
48
+ expect(info['title']).to be_a(String)
49
+ expect(info['duration']).to be_a(Integer)
50
+ end
51
+ end
52
+
53
+ describe "backend selection" do
54
+ it "creates client with yt-dlp backend enabled" do
55
+ client = YoutubeRb::Client.new(use_ytdlp: true, verbose: false)
56
+ expect(client.options.use_ytdlp).to be true
57
+ end
58
+
59
+ it "creates client with fallback mode" do
60
+ client = YoutubeRb::Client.new(ytdlp_fallback: true, verbose: false)
61
+ expect(client.options.ytdlp_fallback).to be true
62
+ end
63
+
64
+ it "creates client with verbose mode" do
65
+ client = YoutubeRb::Client.new(verbose: true)
66
+ expect(client.options.verbose).to be true
67
+ end
68
+ end
69
+
70
+ describe "YtdlpWrapper availability" do
71
+ it "reports availability correctly" do
72
+ expect(YoutubeRb::YtdlpWrapper.available?).to be true
73
+ end
74
+
75
+ it "returns version string" do
76
+ version = YoutubeRb::YtdlpWrapper.version
77
+ expect(version).to be_a(String)
78
+ expect(version).not_to be_empty
79
+ expect(version).not_to eq('not installed')
80
+ end
81
+ end
82
+
83
+ describe "download with yt-dlp (slow)", :slow do
84
+ let(:output_dir) { './spec/tmp/downloads' }
85
+
86
+ before(:each) do
87
+ FileUtils.mkdir_p(output_dir)
88
+ end
89
+
90
+ after(:each) do
91
+ FileUtils.rm_rf(output_dir) if Dir.exist?(output_dir)
92
+ end
93
+
94
+ it "downloads video segment using yt-dlp backend" do
95
+ client = YoutubeRb::Client.new(
96
+ output_path: output_dir,
97
+ use_ytdlp: true,
98
+ verbose: false
99
+ )
100
+
101
+ # Download just the first 10 seconds to speed up test
102
+ output_file = client.download_segment(test_url, 0, 10)
103
+
104
+ expect(output_file).to be_a(String)
105
+ expect(File.exist?(output_file)).to be true
106
+ expect(File.size(output_file)).to be > 0
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,175 @@
1
+ # Real download tests - require internet connection and yt-dlp
2
+ # Run with: bundle exec rspec spec/real_download_spec.rb --tag real_download
3
+ # These tests actually download videos from YouTube
4
+
5
+ RSpec.describe "Real video downloads", :real_download do
6
+ let(:output_dir) { './spec/tmp/real_downloads' }
7
+
8
+ before(:all) do
9
+ # Allow real HTTP connections for these tests
10
+ WebMock.allow_net_connect!
11
+
12
+ # Check if yt-dlp is available
13
+ unless YoutubeRb::YtdlpWrapper.available?
14
+ skip "yt-dlp is not installed. Install with: pip install -U yt-dlp"
15
+ end
16
+ end
17
+
18
+ after(:all) do
19
+ # Restore WebMock behavior
20
+ WebMock.disable_net_connect!(allow_localhost: true)
21
+ end
22
+
23
+ before(:each) do
24
+ FileUtils.mkdir_p(output_dir)
25
+ end
26
+
27
+ after(:each) do
28
+ # Clean up downloaded files
29
+ FileUtils.rm_rf(output_dir) if Dir.exist?(output_dir)
30
+ end
31
+
32
+ describe "Rick Astley - Never Gonna Give You Up" do
33
+ # Rick Astley video works without cookies
34
+ let(:rickroll_url) { 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' }
35
+ let(:rickroll_id) { 'dQw4w9WgXcQ' }
36
+
37
+ it "extracts full video information" do
38
+ puts "\n → Testing video info extraction for Rick Astley"
39
+
40
+ info = YoutubeRb.info(rickroll_url)
41
+
42
+ expect(info).to be_a(YoutubeRb::VideoInfo)
43
+ expect(info.id).to eq(rickroll_id)
44
+ expect(info.title).to include('Never Gonna Give You Up')
45
+ expect(info.duration).to eq(213)
46
+ expect(info.uploader).to eq('Rick Astley')
47
+
48
+ puts " ✓ Title: #{info.title}"
49
+ puts " ✓ Duration: #{info.duration} seconds"
50
+ puts " ✓ Uploader: #{info.uploader}"
51
+
52
+ # Save the response for mocking later
53
+ File.write('./spec/fixtures/rickroll_full_info.json', JSON.pretty_generate(info.to_h))
54
+ puts " ✓ Saved response to spec/fixtures/rickroll_full_info.json for mocking"
55
+ end
56
+
57
+ it "downloads 15-second segment from 10 to 25 seconds" do
58
+ puts "\n → Testing segment download: Rick Astley (10-25 sec)"
59
+ puts " → This will download ~14MB of video segment..."
60
+
61
+ start_time = 10
62
+ end_time = 25
63
+
64
+ client = YoutubeRb::Client.new(
65
+ output_path: output_dir,
66
+ use_ytdlp: true,
67
+ verbose: true
68
+ )
69
+
70
+ output_file = client.download_segment(rickroll_url, start_time, end_time)
71
+
72
+ # Verify download
73
+ expect(output_file).to be_a(String)
74
+ expect(File.exist?(output_file)).to be true
75
+ expect(File.size(output_file)).to be > 50_000 # At least 50KB for 15 sec
76
+
77
+ # Verify segment duration using ffprobe if available
78
+ if system('which ffprobe > /dev/null 2>&1')
79
+ duration_cmd = "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 '#{output_file}'"
80
+ duration = `#{duration_cmd}`.strip.to_f
81
+
82
+ # Duration should be approximately 15 seconds (allow generous variance)
83
+ # NOTE: Without re-encoding (--force-keyframes-at-cuts), yt-dlp cuts at keyframes only,
84
+ # which can result in segments being a few seconds longer than requested.
85
+ # This is acceptable for performance reasons - cutting at keyframes is 10x faster.
86
+ # Expected: 15 seconds, but may be 20-30 seconds due to keyframe positions.
87
+ expect(duration).to be_between(10, 30)
88
+ puts " ✓ Segment duration: #{duration.round(1)} seconds (requested: 15s)"
89
+ end
90
+
91
+ puts " ✓ Downloaded to: #{output_file}"
92
+ puts " ✓ File size: #{(File.size(output_file) / 1024.0).round(2)} KB"
93
+
94
+ # Save info about what we downloaded for mocking
95
+ segment_info = {
96
+ url: rickroll_url,
97
+ video_id: rickroll_id,
98
+ start_time: start_time,
99
+ end_time: end_time,
100
+ duration: end_time - start_time,
101
+ file_size: File.size(output_file),
102
+ output_file: File.basename(output_file)
103
+ }
104
+ File.write('./spec/fixtures/rickroll_segment_info.json', JSON.pretty_generate(segment_info))
105
+ puts " ✓ Saved segment info to spec/fixtures/rickroll_segment_info.json"
106
+ end
107
+
108
+ it "downloads full video using yt-dlp backend", :very_slow do
109
+ puts "\n → Testing full video download: Rick Astley (dQw4w9WgXcQ)"
110
+ puts " → This will download ~240MB of video data..."
111
+ puts " → This may take a few minutes depending on your connection..."
112
+
113
+ client = YoutubeRb::Client.new(
114
+ output_path: output_dir,
115
+ use_ytdlp: true,
116
+ verbose: true
117
+ )
118
+
119
+ output_file = client.download(rickroll_url)
120
+
121
+ # Verify download
122
+ expect(output_file).to be_a(String)
123
+ expect(File.exist?(output_file)).to be true
124
+ expect(File.size(output_file)).to be > 10_000_000 # At least 10MB
125
+
126
+ puts " ✓ Downloaded to: #{output_file}"
127
+ puts " ✓ File size: #{(File.size(output_file) / 1024.0 / 1024.0).round(2)} MB"
128
+ end
129
+ end
130
+
131
+ describe "fallback mechanism" do
132
+ let(:test_url) { 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' }
133
+
134
+ it "falls back to yt-dlp when pure Ruby fails", :very_slow do
135
+ puts "\n → Testing automatic fallback to yt-dlp"
136
+ puts " → This may download ~240MB of video data..."
137
+
138
+ client = YoutubeRb::Client.new(
139
+ output_path: output_dir,
140
+ use_ytdlp: false, # Start with pure Ruby
141
+ ytdlp_fallback: true, # Enable fallback
142
+ verbose: true
143
+ )
144
+
145
+ # This should try pure Ruby first, then fall back to yt-dlp
146
+ output_file = client.download(test_url)
147
+
148
+ expect(output_file).to be_a(String)
149
+ expect(File.exist?(output_file)).to be true
150
+ expect(File.size(output_file)).to be > 10_000_000 # At least 10MB
151
+
152
+ puts " ✓ Fallback worked, file downloaded"
153
+ puts " ✓ File size: #{(File.size(output_file) / 1024.0 / 1024.0).round(2)} MB"
154
+ end
155
+ end
156
+
157
+ describe "error handling" do
158
+ it "raises error for invalid video ID" do
159
+ puts "\n → Testing error handling for invalid video"
160
+
161
+ client = YoutubeRb::Client.new(
162
+ output_path: output_dir,
163
+ use_ytdlp: true
164
+ )
165
+
166
+ invalid_url = 'https://www.youtube.com/watch?v=invalidvideoid'
167
+
168
+ expect {
169
+ client.download(invalid_url)
170
+ }.to raise_error(YoutubeRb::Downloader::DownloadError)
171
+
172
+ puts " ✓ Correctly raised error for invalid video"
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,31 @@
1
+ require "youtube-rb"
2
+ require "webmock/rspec"
3
+ require "tmpdir"
4
+ require "fileutils"
5
+
6
+ # Allow connections to stubbed URLs only
7
+ WebMock.disable_net_connect!(allow_localhost: false)
8
+
9
+ RSpec.configure do |config|
10
+ # Enable flags like --only-failures and --next-failure
11
+ config.example_status_persistence_file_path = ".rspec_status"
12
+
13
+ # Disable RSpec exposing methods globally on `Module` and `main`
14
+ config.disable_monkey_patching!
15
+
16
+ config.expect_with :rspec do |c|
17
+ c.syntax = :expect
18
+ end
19
+
20
+ # Load support files
21
+ Dir[File.join(__dir__, "support", "**", "*.rb")].sort.each { |f| require f }
22
+
23
+ # Clean up test downloads before each test
24
+ config.before(:each) do
25
+ @test_output_dir = Dir.mktmpdir('youtube-rb-test')
26
+ end
27
+
28
+ config.after(:each) do
29
+ FileUtils.rm_rf(@test_output_dir) if @test_output_dir && Dir.exist?(@test_output_dir)
30
+ end
31
+ end
@@ -0,0 +1,109 @@
1
+ module FixturesHelper
2
+ def fixture_path(filename)
3
+ File.join(__dir__, '..', 'fixtures', filename)
4
+ end
5
+
6
+ def load_fixture(filename)
7
+ File.read(fixture_path(filename))
8
+ end
9
+
10
+ def load_json_fixture(filename)
11
+ JSON.parse(load_fixture(filename))
12
+ end
13
+
14
+ # Real data from rickroll video
15
+ def rickroll_video_info
16
+ @rickroll_video_info ||= load_json_fixture('rickroll_info.json')
17
+ end
18
+
19
+ # Sample video data for testing
20
+ def sample_video_data
21
+ {
22
+ 'id' => 'test123abc',
23
+ 'title' => 'Test Video Title',
24
+ 'fulltitle' => 'Test Video Title',
25
+ 'description' => 'This is a test video description',
26
+ 'uploader' => 'Test Channel',
27
+ 'uploader_id' => 'UC1234567890',
28
+ 'duration' => 180,
29
+ 'view_count' => 1000,
30
+ 'like_count' => 50,
31
+ 'upload_date' => '20240101',
32
+ 'thumbnail' => 'https://example.com/thumbnail.jpg',
33
+ 'ext' => 'mp4',
34
+ 'webpage_url' => 'https://www.youtube.com/watch?v=test123abc',
35
+ 'formats' => [
36
+ {
37
+ 'format_id' => '18',
38
+ 'ext' => 'mp4',
39
+ 'height' => 360,
40
+ 'url' => 'https://example.com/video.mp4',
41
+ 'vcodec' => 'avc1.42001E',
42
+ 'acodec' => 'mp4a.40.2',
43
+ 'quality' => 'medium',
44
+ 'tbr' => 500
45
+ },
46
+ {
47
+ 'format_id' => '22',
48
+ 'ext' => 'mp4',
49
+ 'height' => 720,
50
+ 'url' => 'https://example.com/video-720p.mp4',
51
+ 'vcodec' => 'avc1.64001F',
52
+ 'acodec' => 'mp4a.40.2',
53
+ 'quality' => 'hd720',
54
+ 'tbr' => 2000
55
+ }
56
+ ],
57
+ 'subtitles' => {
58
+ 'en' => [
59
+ {
60
+ 'ext' => 'vtt',
61
+ 'url' => 'https://example.com/subtitles-en.vtt',
62
+ 'name' => 'English'
63
+ }
64
+ ],
65
+ 'es' => [
66
+ {
67
+ 'ext' => 'vtt',
68
+ 'url' => 'https://example.com/subtitles-es.vtt',
69
+ 'name' => 'Spanish'
70
+ }
71
+ ]
72
+ }
73
+ }
74
+ end
75
+
76
+ # Sample subtitle content (VTT format)
77
+ def sample_subtitle_vtt
78
+ <<~VTT
79
+ WEBVTT
80
+
81
+ 00:00:00.000 --> 00:00:05.000
82
+ Welcome to this test video
83
+
84
+ 00:00:05.000 --> 00:00:10.000
85
+ This is a sample subtitle
86
+
87
+ 00:00:10.000 --> 00:00:15.000
88
+ Testing subtitle trimming
89
+
90
+ 00:00:15.000 --> 00:00:20.000
91
+ End of subtitle test
92
+ VTT
93
+ end
94
+
95
+ # Sample video binary data (minimal MP4 header)
96
+ def sample_video_binary
97
+ # Minimal valid MP4 file (just ftyp box)
98
+ [
99
+ 0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70,
100
+ 0x69, 0x73, 0x6F, 0x6D, 0x00, 0x00, 0x02, 0x00,
101
+ 0x69, 0x73, 0x6F, 0x6D, 0x69, 0x73, 0x6F, 0x32,
102
+ 0x61, 0x76, 0x63, 0x31, 0x6D, 0x70, 0x34, 0x31
103
+ ].pack('C*')
104
+ end
105
+ end
106
+
107
+ RSpec.configure do |config|
108
+ config.include FixturesHelper
109
+ end
@@ -0,0 +1,21 @@
1
+ module MockingHelper
2
+ # Mock the Extractor to return video info without making HTTP requests
3
+ def mock_extractor(video_data)
4
+ video_info = YoutubeRb::VideoInfo.new(video_data)
5
+
6
+ allow_any_instance_of(YoutubeRb::Extractor).to receive(:extract_info).and_return(video_info)
7
+ allow_any_instance_of(YoutubeRb::Extractor).to receive(:extract_formats).and_return(video_data['formats'] || [])
8
+ allow_any_instance_of(YoutubeRb::Extractor).to receive(:extract_subtitles).and_return(video_data['subtitles'] || {})
9
+
10
+ video_info
11
+ end
12
+
13
+ # Mock extractor to raise error
14
+ def mock_extractor_error(error_class = YoutubeRb::Extractor::ExtractionError, message = 'Test error')
15
+ allow_any_instance_of(YoutubeRb::Extractor).to receive(:extract_info).and_raise(error_class, message)
16
+ end
17
+ end
18
+
19
+ RSpec.configure do |config|
20
+ config.include MockingHelper
21
+ end