Yet Another Youtube Down Loader

⌈⌋ ⎇ branch:  yaydl


Check-in [57ab458f3b]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:yaydl 0.6.7: Now that we use the new YouTube API, the need for cipher decoding should be a thing of the past.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk | release-0.6.7
Files: files | file ages | folders
SHA3-256: 57ab458f3b9406a0fb8bb98fe1c3d341a0130fd2c7270c85932c351c7b77d34e
User & Date: Cthulhux 2021-06-21 11:00:50
Context
2021-10-02
14:57
yaydl 0.6.8: trying to circumvent new WatchMDH structures (yet incomplete) check-in: 198e368220 user: Cthulhux tags: trunk
2021-06-21
11:00
yaydl 0.6.7: Now that we use the new YouTube API, the need for cipher decoding should be a thing of the past. check-in: 57ab458f3b user: Cthulhux tags: trunk, release-0.6.7
2021-06-20
22:56
yaydl 0.6.6: Changed the YouTube API client to capture more videos. The last time today, I promise. check-in: 782afd82e0 user: Cthulhux tags: trunk, release-0.6.6
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Cargo.lock.

572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
dependencies = [
 "unicode-xid",
]

[[package]]
name = "qstring"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e"
dependencies = [
 "percent-encoding",
]

[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
 "proc-macro2",







<
<
<
<
<
<
<
<
<







572
573
574
575
576
577
578









579
580
581
582
583
584
585
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
dependencies = [
 "unicode-xid",
]










[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
 "proc-macro2",
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

[[package]]
name = "yaydl"
version = "0.6.6"
dependencies = [
 "anyhow",
 "cienli",
 "clap",
 "indicatif",
 "inventory",
 "qstring",
 "regex",
 "scraper",
 "serde_json",
 "ureq",
 "url",
 "urlencoding",
]







|






<







1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113

1114
1115
1116
1117
1118
1119
1120
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

[[package]]
name = "yaydl"
version = "0.6.7"
dependencies = [
 "anyhow",
 "cienli",
 "clap",
 "indicatif",
 "inventory",

 "regex",
 "scraper",
 "serde_json",
 "ureq",
 "url",
 "urlencoding",
]

Changes to Cargo.toml.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[package]
name = "yaydl"
description = "yet another youtube (and more) down loader"
version = "0.6.6"
authors = ["Cthulhux <git@tuxproject.de>"]
edition = "2018"
license = "CDDL-1.0"
repository = "https://code.rosaelefanten.org/yaydl"
categories = ["command-line-utilities"]
keywords = ["youtube", "downloading", "video"]

[dependencies]
anyhow = "1.0"
cienli = "0.1"
clap = "3.0.0-beta.2"
indicatif = "0.15"
inventory = "0.1"
qstring = "0.7"
regex = "1.4"
scraper = "0.12"
serde_json = "1.0"
ureq = { version = "2.1", features = ["json"] }
url = "2.2"
urlencoding = "1.1"

[profile.release]
lto = true



|













<









1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
[package]
name = "yaydl"
description = "yet another youtube (and more) down loader"
version = "0.6.7"
authors = ["Cthulhux <git@tuxproject.de>"]
edition = "2018"
license = "CDDL-1.0"
repository = "https://code.rosaelefanten.org/yaydl"
categories = ["command-line-utilities"]
keywords = ["youtube", "downloading", "video"]

[dependencies]
anyhow = "1.0"
cienli = "0.1"
clap = "3.0.0-beta.2"
indicatif = "0.15"
inventory = "0.1"

regex = "1.4"
scraper = "0.12"
serde_json = "1.0"
ureq = { version = "2.1", features = ["json"] }
url = "2.2"
urlencoding = "1.1"

[profile.release]
lto = true

Changes to src/handlers/youtube.rs.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

// Yet Another Youtube Down Loader
// - YouTube handler -

use crate::definitions::SiteDefinition;

use anyhow::Result;
use qstring::QString;
use regex::Regex;
use serde_json::{json, Value};
use urlencoding::decode;

static mut VIDEO_INFO: String = String::new();
static mut VIDEO_MIME: String = String::new();

unsafe fn get_video_info(id: &str) -> Result<Value> {
    if VIDEO_INFO.is_empty() {
        // We need to fetch the video information first.







<


<







15
16
17
18
19
20
21

22
23

24
25
26
27
28
29
30

// Yet Another Youtube Down Loader
// - YouTube handler -

use crate::definitions::SiteDefinition;

use anyhow::Result;

use regex::Regex;
use serde_json::{json, Value};


static mut VIDEO_INFO: String = String::new();
static mut VIDEO_MIME: String = String::new();

unsafe fn get_video_info(id: &str) -> Result<Value> {
    if VIDEO_INFO.is_empty() {
        // We need to fetch the video information first.
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
            let video_info_itags_adaptive =
                match video_info["streamingData"]["adaptiveFormats"].as_array() {
                    None => return Ok("".to_string()),
                    Some(itags) => itags,
                };

            let mut url_to_choose = "";
            let mut url_decoded: String;
            let mut cipher: QString;

            // Finding the least horrible combination of video and audio:
            let vq1 = "tiny";
            let vq2 = "small";
            let vq3 = "medium";
            let vq4 = "large";
            let vq5 = "hd720";







<
<







79
80
81
82
83
84
85


86
87
88
89
90
91
92
            let video_info_itags_adaptive =
                match video_info["streamingData"]["adaptiveFormats"].as_array() {
                    None => return Ok("".to_string()),
                    Some(itags) => itags,
                };

            let mut url_to_choose = "";



            // Finding the least horrible combination of video and audio:
            let vq1 = "tiny";
            let vq2 = "small";
            let vq3 = "medium";
            let vq4 = "large";
            let vq5 = "hd720";
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
                    && (!onlyaudio || itag["quality"] != json!(null))
                    && itag["audioQuality"] != json!(null)
                    && (onlyaudio && this_vq.is_empty()
                        || !onlyaudio && last_vq.is_empty() && !this_vq.is_empty())
                    || is_better_quality
                {
                    VIDEO_MIME = itag["mimeType"].to_string();
                    // Now here are two options: Either the URL is written directly in the itag
                    // or YouTube hides it in a "signatureCipher" which we need to decode first.
                    if itag["signatureCipher"] != json!(null) && itag["url"] == json!(null) {
                        // Sigh.
                        cipher = QString::from(itag["signatureCipher"].as_str().unwrap());
                        url_decoded = match decode(cipher.get("url").unwrap()) {
                            Ok(u) => u,
                            _ => unreachable!(),
                        };

                        // We might need to append a descrambled signature to the download URL.
                        // This is yet left to do.
                        url_to_choose = &url_decoded;
                    } else {
                        url_to_choose = itag["url"].as_str().unwrap();
                    }
                    last_vq = String::from(this_vq);
                    last_aq = String::from(this_aq);
                }
            }

            if url_to_choose.is_empty() {
                Err(anyhow::Error::msg(







<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|







133
134
135
136
137
138
139














140
141
142
143
144
145
146
147
148
                    && (!onlyaudio || itag["quality"] != json!(null))
                    && itag["audioQuality"] != json!(null)
                    && (onlyaudio && this_vq.is_empty()
                        || !onlyaudio && last_vq.is_empty() && !this_vq.is_empty())
                    || is_better_quality
                {
                    VIDEO_MIME = itag["mimeType"].to_string();














                    url_to_choose = itag["url"].as_str().unwrap();

                    last_vq = String::from(this_vq);
                    last_aq = String::from(this_aq);
                }
            }

            if url_to_choose.is_empty() {
                Err(anyhow::Error::msg(