From d4e2b0454c1bb62fdc9416b130fb83c3356d1a6c Mon Sep 17 00:00:00 2001 From: blueJpg <2238288979@qq.com> Date: Sun, 12 May 2024 02:28:12 +0800 Subject: [PATCH] [+] add youtube downloader --- rust/Cargo.lock | 613 +++++++++++++++++++++++++++++++------ rust/Cargo.toml | 15 +- rust/Makefile | 12 + rust/rust-toolchain | 1 + rust/src/api/db.rs | 4 +- rust/src/api/mod.rs | 2 + rust/src/api/video_info.rs | 16 + rust/src/api/youtube.rs | 327 ++++++++++++++++++++ rust/src/frb_generated.rs | 551 ++++++++++++++++++++++++++++++++- 9 files changed, 1442 insertions(+), 99 deletions(-) create mode 100644 rust/Makefile create mode 100644 rust/rust-toolchain create mode 100644 rust/src/api/video_info.rs create mode 100644 rust/src/api/youtube.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index dcb1462..f0b6c7e 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -95,6 +95,19 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "async-compression" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c90a406b4495d129f00461241616194cb8a032c8d1c53c657f0961d5f8e0498" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + [[package]] name = "atoi" version = "2.0.0" @@ -228,6 +241,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets 0.52.5", ] @@ -248,6 +262,40 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6" +dependencies = [ + "cookie", + "idna 0.3.0", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -288,6 +336,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-queue" version = "0.3.11" @@ -313,6 +370,76 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core 0.10.2", + "darling_macro 0.10.2", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.9.3", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core 0.10.2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn 1.0.109", +] + [[package]] name = "dart-sys-fork" version = "4.1.1" @@ -367,6 +494,39 @@ dependencies = [ "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.10.7" @@ -461,6 +621,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flume" version = "0.11.0" @@ -667,6 +837,25 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.4" @@ -678,7 +867,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 1.1.0", "indexmap", "slab", "tokio", @@ -753,6 +942,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -764,6 +964,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.0" @@ -771,7 +982,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.1.0", ] [[package]] @@ -782,8 +993,8 @@ checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -793,12 +1004,42 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.3.1" @@ -808,9 +1049,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.4", + "http 1.1.0", + "http-body 1.0.0", "httparse", "itoa", "pin-project-lite", @@ -820,20 +1061,16 @@ dependencies = [ ] [[package]] -name = "hyper-rustls" -version = "0.26.0" +name = "hyper-tls" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "futures-util", - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", + "bytes", + "hyper 0.14.28", + "native-tls", "tokio", - "tokio-rustls", - "tower-service", + "tokio-native-tls", ] [[package]] @@ -844,7 +1081,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper", + "hyper 1.3.1", "hyper-util", "native-tls", "tokio", @@ -861,9 +1098,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.3.1", "pin-project-lite", "socket2", "tokio", @@ -895,6 +1132,22 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.5.0" @@ -1010,6 +1263,18 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "log-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a42526bb432bcd1b43571d5f163984effa25409a29f1a3242a54d0577d55bcf" +dependencies = [ + "darling 0.10.2", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "md-5" version = "0.10.6" @@ -1103,6 +1368,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.46" @@ -1337,6 +1608,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1352,6 +1629,22 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" +dependencies = [ + "idna 0.3.0", + "psl-types", +] + [[package]] name = "quote" version = "1.0.33" @@ -1440,23 +1733,23 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.12.4" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.22.1", + "async-compression", + "base64 0.21.7", "bytes", + "cookie", + "cookie_store", "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", + "hyper-tls 0.5.0", "ipnet", "js-sys", "log", @@ -1465,9 +1758,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", - "rustls-pemfile", - "rustls-pki-types", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", @@ -1475,30 +1766,58 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls", "tokio-socks", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", - "webpki-roots", - "winreg", + "winreg 0.50.0", ] [[package]] -name = "ring" -version = "0.17.8" +name = "reqwest" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin 0.9.8", - "untrusted", - "windows-sys 0.52.0", + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.4", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", + "hyper-tls 0.6.0", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.1.2", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-socks", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.52.0", ] [[package]] @@ -1533,7 +1852,9 @@ dependencies = [ "lazy_static", "log", "oslog 0.2.0", - "reqwest", + "regex", + "reqwest 0.12.4", + "rustube", "serde", "sqlx", "tokio", @@ -1545,6 +1866,15 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.28" @@ -1559,17 +1889,12 @@ dependencies = [ ] [[package]] -name = "rustls" -version = "0.22.4" +name = "rustls-pemfile" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "log", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", + "base64 0.21.7", ] [[package]] @@ -1584,19 +1909,36 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f344d206c5e1b010eec27349b815a4805f70a778895959d70b74b9b529b30a" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] -name = "rustls-webpki" -version = "0.102.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" +name = "rustube" +version = "0.6.0" +source = "git+https://github.com/Heng30/rustube.git#8eda9a23b7463ee48b69d0a72d25951b8087a8d3" dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", + "bytes", + "cfg-if", + "chrono", + "derivative", + "derive_more", + "futures", + "log", + "log-derive", + "mime", + "once_cell", + "regex", + "reqwest 0.11.27", + "rustc_version", + "serde", + "serde_json", + "serde_qs", + "serde_with", + "thiserror", + "tokio", + "tokio-stream", + "url", ] [[package]] @@ -1643,6 +1985,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.193" @@ -1674,6 +2022,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_qs" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1686,6 +2045,29 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_json", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sha1" version = "0.10.6" @@ -1992,6 +2374,18 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.5.0" @@ -2098,6 +2492,37 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2115,9 +2540,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -2153,17 +2578,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" -dependencies = [ - "rustls", - "rustls-pki-types", - "tokio", -] - [[package]] name = "tokio-socks" version = "0.5.1" @@ -2305,12 +2719,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - [[package]] name = "url" version = "2.5.0" @@ -2318,8 +2726,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", + "serde", ] [[package]] @@ -2428,22 +2837,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] -name = "web-sys" -version = "0.3.66" +name = "wasm-streams" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ + "futures-util", "js-sys", "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] -name = "webpki-roots" -version = "0.26.1" +name = "web-sys" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ - "rustls-pki-types", + "js-sys", + "wasm-bindgen", ] [[package]] @@ -2613,6 +3026,16 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "winreg" version = "0.52.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 38ba0a1..e5e6617 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -9,16 +9,22 @@ crate-type = ["cdylib", "staticlib"] [dependencies] log = "0.4" anyhow = "1.0" +regex = "1.10" chrono = "0.4" lazy_static = "1.4" flutter_rust_bridge = "=2.0.0-dev.33" -tokio = { version = "1", features = ["full"] } +tokio = { version = "1.37", features = ["full"] } serde = { version = "1.0", features = ["serde_derive"] } [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] env_logger = "0.10" reqwest = { version = "0.12", features = ["json", "socks"] } sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite"] } +rustube = { git = "https://github.com/Heng30/rustube.git", features = [ + "default", + "socks", + "callback", +] } [target.'cfg(target_os = "ios")'.dependencies] oslog = "0.2.0" @@ -29,8 +35,13 @@ android_logger = "0.13" # mobile platform [target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] reqwest = { version = "0.12", features = [ - "rustls-tls", "native-tls-vendored", "json", "socks", ] } +rustube = { git = "https://github.com/Heng30/rustube.git", features = [ + "default", + "native-tls-vendored", + "socks", + "callback", +] } diff --git a/rust/Makefile b/rust/Makefile new file mode 100644 index 0000000..4ae0a7d --- /dev/null +++ b/rust/Makefile @@ -0,0 +1,12 @@ +#!/bin/bash + +run-evn=RUST_LOG=error,warn,info,debug,sqlx=off,reqwest=off,html2text=off + +all: + cargo build + +build-android: + cargo apk build --lib + +run: + $(run-evn) cargo run --bin downloader diff --git a/rust/rust-toolchain b/rust/rust-toolchain new file mode 100644 index 0000000..2bf5ad0 --- /dev/null +++ b/rust/rust-toolchain @@ -0,0 +1 @@ +stable diff --git a/rust/src/api/db.rs b/rust/src/api/db.rs index b678c2e..d685a9e 100644 --- a/rust/src/api/db.rs +++ b/rust/src/api/db.rs @@ -1,8 +1,10 @@ use anyhow::Result; -use serde::{Deserialize, Serialize}; use std::collections::HashMap; use tokio::{fs, sync::Mutex}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use serde::{Deserialize, Serialize}; + #[cfg(not(any(target_os = "android", target_os = "ios")))] use sqlx::{ migrate::MigrateDatabase, diff --git a/rust/src/api/mod.rs b/rust/src/api/mod.rs index 3fd4a07..f14e592 100644 --- a/rust/src/api/mod.rs +++ b/rust/src/api/mod.rs @@ -1,5 +1,7 @@ pub mod log; pub mod db; +pub mod video_info; +pub mod youtube; #[flutter_rust_bridge::frb(init)] pub fn init_app() { diff --git a/rust/src/api/video_info.rs b/rust/src/api/video_info.rs new file mode 100644 index 0000000..91d08c1 --- /dev/null +++ b/rust/src/api/video_info.rs @@ -0,0 +1,16 @@ +#[derive(Debug, Clone)] +pub struct Info { + pub title: String, + pub author: String, + pub video_id: String, + pub short_description: String, + pub view_count: u64, + pub length_seconds: u64, +} + +#[derive(Debug, Clone, Default)] +pub struct ProgerssData { + pub current_size: u64, + pub total_size: Option, +} + diff --git a/rust/src/api/youtube.rs b/rust/src/api/youtube.rs new file mode 100644 index 0000000..09ff36e --- /dev/null +++ b/rust/src/api/youtube.rs @@ -0,0 +1,327 @@ +use super::video_info::{Info, ProgerssData}; +use crate::frb_generated::StreamSink; +use anyhow::Result; +use regex::Regex; +use rustube::{ + fetcher, reqwest, tokio::sync::mpsc, url::Url, Callback, CallbackArguments, Id, VideoFetcher, +}; +use std::collections::HashSet; + +async fn client(proxy_url: Option) -> Result { + let client = match proxy_url { + Some(url) => { + let cookie_jar = fetcher::recommended_cookies(); + let headers = fetcher::recommended_headers(); + + reqwest::Client::builder() + .default_headers(headers) + .cookie_provider(std::sync::Arc::new(cookie_jar)) + .proxy(reqwest::Proxy::all(url)?) + .build()? + } + None => reqwest::Client::new(), + }; + + Ok(client) +} + +pub async fn fetch_ids( + keyword: String, + max_id_count: usize, + proxy_url: Option, +) -> Result> { + let client = client(proxy_url).await?; + let mut url = Url::parse("https://www.youtube.com/results")?; + url.query_pairs_mut().append_pair("search_query", &keyword); + let url = url.to_string(); + + let html = client.get(url).send().await?.text().await?; + + let mut ids = HashSet::new(); + let re = Regex::new(r#""videoId":"([^"]*)""#)?; + for cap in re.captures_iter(&html) { + if ids.len() >= max_id_count { + break; + } + + if let Some(value) = cap.get(1) { + log::debug!("Found: {}", value.as_str()); + ids.insert(value.as_str().to_string()); + } + } + + return Ok(ids.into_iter().collect()); +} + +pub async fn video_info(url: String, proxy_url: Option) -> Result { + let id = Id::from_raw(&url)?; + video_info_by_id(id.to_string(), proxy_url).await +} + +pub async fn video_info_by_id(id: String, proxy_url: Option) -> Result { + let id = Id::from_str(&id)?; + let client = client(proxy_url).await?; + + let descrambler = VideoFetcher::from_id_with_client(id.into_owned(), client) + .fetch() + .await?; + + let raw_info = descrambler.video_info(); + + Ok(Info { + title: raw_info.player_response.video_details.title.to_string(), + author: raw_info.player_response.video_details.author.to_string(), + video_id: raw_info.player_response.video_details.video_id.to_string(), + short_description: raw_info + .player_response + .video_details + .short_description + .to_string(), + view_count: raw_info.player_response.video_details.view_count, + length_seconds: raw_info.player_response.video_details.length_seconds, + }) +} + +pub async fn download_video( + url: String, + download_path: String, + proxy_url: Option, +) -> Result<()> { + let id = Id::from_raw(&url)?; + download_video_by_id(id.to_string(), download_path, proxy_url).await +} + +pub async fn download_video_by_id( + id: String, + download_path: String, + proxy_url: Option, +) -> Result<()> { + let id = Id::from_str(&id)?; + let client = client(proxy_url).await?; + + let video = VideoFetcher::from_id_with_client(id.into_owned(), client) + .fetch() + .await? + .descramble()?; + + match video.best_quality() { + Some(stream) => stream.download_to(download_path).await?, + None => match video.worst_video() { + None => anyhow::bail!("no video"), + Some(stream) => stream.download_to(download_path).await?, + }, + }; + + Ok(()) +} + +pub async fn download_video_by_id_with_callback( + sink: StreamSink, + id: String, + download_path: String, + proxy_url: Option, +) -> Result<()> { + let (tx, mut rx) = mpsc::channel(1024); + + tokio::spawn(async move { + match inner_download_video_by_id_with_callback(id, download_path, proxy_url, tx).await { + Err(e) => log::warn!("{e:?}"), + _ => (), + } + }); + + while let Some(item) = rx.recv().await { + if let Err(e) = sink.add(ProgerssData { + current_size: item.current_chunk as u64, + total_size: item.content_length, + }) { + log::warn!("sink add error: {e:?}"); + } + } + Ok(()) +} + +async fn inner_download_video_by_id_with_callback( + id: String, + download_path: String, + proxy_url: Option, + tx: mpsc::Sender, +) -> Result<()> { + let id = Id::from_str(&id)?; + let client = client(proxy_url).await?; + + let video = VideoFetcher::from_id_with_client(id.into_owned(), client) + .fetch() + .await? + .descramble()?; + + let cb = Callback::new().connect_on_progress_sender(tx, true); + match video.best_quality() { + Some(stream) => stream.download_to_with_callback(download_path, cb).await?, + None => match video.worst_video() { + None => anyhow::bail!("no video"), + Some(stream) => stream.download_to(download_path).await?, + }, + }; + + Ok(()) +} + +pub async fn download_audio( + url: String, + download_path: String, + proxy_url: Option, +) -> Result<()> { + let id = Id::from_raw(&url)?; + download_audio_by_id(id.to_string(), download_path, proxy_url).await +} + +pub async fn download_audio_by_id( + id: String, + download_path: String, + proxy_url: Option, +) -> Result<()> { + let id = Id::from_str(&id)?; + let client = client(proxy_url).await?; + + let video = VideoFetcher::from_id_with_client(id.into_owned(), client) + .fetch() + .await? + .descramble()?; + + match video.best_audio() { + Some(stream) => stream.download_to(download_path).await?, + None => match video.worst_video() { + None => anyhow::bail!("no audio"), + Some(stream) => stream.download_to(download_path).await?, + }, + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use tokio::fs; + + const PROXY_URL: &str = "socks5://127.0.0.1:1084"; + const VIDEO_URL: &str = "https://www.youtube.com/watch?v=vdMTIe5ihYg"; + const VIDEO_ID: &str = "vdMTIe5ihYg"; + + #[tokio::test] + async fn test_fetch_ids() -> Result<()> { + let ids = fetch_ids("泪桥".to_string(), 5, Some(PROXY_URL.to_string())).await?; + + for (index, id) in ids.into_iter().enumerate() { + println!("id[{index}]: {id}"); + } + Ok(()) + } + + #[tokio::test] + async fn test_download_video() -> Result<()> { + let path = "/tmp/1.mp4"; + _ = fs::remove_file(path).await; + download_video( + VIDEO_URL.to_string(), + path.to_string(), + Some(PROXY_URL.to_string()), + ) + .await?; + assert_eq!(fs::try_exists(path).await?, true); + + Ok(()) + } + + #[tokio::test] + async fn test_download_video_by_id() -> Result<()> { + let path = "/tmp/1-id.mp4"; + _ = fs::remove_file(path).await; + download_video_by_id( + VIDEO_ID.to_string(), + path.to_string(), + Some(PROXY_URL.to_string()), + ) + .await?; + assert_eq!(fs::try_exists(path).await?, true); + + Ok(()) + } + + #[tokio::test] + async fn test_inner_download_video_by_id_with_callback() -> Result<()> { + let path = "/tmp/1-id-cb.mp4"; + _ = fs::remove_file(path).await; + let (tx, mut rx) = mpsc::channel(1024); + + tokio::spawn(async move { + inner_download_video_by_id_with_callback( + VIDEO_ID.to_string(), + path.to_string(), + Some(PROXY_URL.to_string()), + tx, + ) + .await + .expect("inner_download_video_by_id_with_callback error"); + }); + + while let Some(item) = rx.recv().await { + println!( + "{}/{}", + item.current_chunk, + item.content_length.unwrap_or(0) + ); + } + + assert_eq!(fs::try_exists(path).await?, true); + + Ok(()) + } + + #[tokio::test] + async fn test_download_audio() -> Result<()> { + let path = "/tmp/1.webp"; + download_audio( + VIDEO_URL.to_string(), + path.to_string(), + Some(PROXY_URL.to_string()), + ) + .await?; + assert_eq!(fs::try_exists(path).await?, true); + + Ok(()) + } + + #[tokio::test] + async fn test_download_audio_by_id() -> Result<()> { + let path = "/tmp/1-id.webp"; + download_audio_by_id( + VIDEO_ID.to_string(), + path.to_string(), + Some(PROXY_URL.to_string()), + ) + .await?; + assert_eq!(fs::try_exists(path).await?, true); + + Ok(()) + } + + #[tokio::test] + async fn test_video_info() -> Result<()> { + let info = video_info(VIDEO_URL.to_string(), Some(PROXY_URL.to_string())).await?; + println!("{info:?}"); + assert_eq!(info.video_id, VIDEO_ID); + + Ok(()) + } + + #[tokio::test] + async fn test_video_info_by_id() -> Result<()> { + let info = video_info_by_id(VIDEO_ID.to_string(), Some(PROXY_URL.to_string())).await?; + println!("{info:?}"); + assert_eq!(info.video_id, VIDEO_ID); + + Ok(()) + } +} diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs index 81976ac..d5051a2 100644 --- a/rust/src/frb_generated.rs +++ b/rust/src/frb_generated.rs @@ -31,7 +31,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.33"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1162977169; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1177922139; // Section: executor @@ -484,6 +484,331 @@ fn wire_init_logger_impl( }, ) } +fn wire_download_audio_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "download_audio", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_url = ::sse_decode(&mut deserializer); + let api_download_path = ::sse_decode(&mut deserializer); + let api_proxy_url = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| async move { + transform_result_sse( + (move || async move { + crate::api::youtube::download_audio( + api_url, + api_download_path, + api_proxy_url, + ) + .await + })() + .await, + ) + } + }, + ) +} +fn wire_download_audio_by_id_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "download_audio_by_id", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_id = ::sse_decode(&mut deserializer); + let api_download_path = ::sse_decode(&mut deserializer); + let api_proxy_url = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| async move { + transform_result_sse( + (move || async move { + crate::api::youtube::download_audio_by_id( + api_id, + api_download_path, + api_proxy_url, + ) + .await + })() + .await, + ) + } + }, + ) +} +fn wire_download_video_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "download_video", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_url = ::sse_decode(&mut deserializer); + let api_download_path = ::sse_decode(&mut deserializer); + let api_proxy_url = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| async move { + transform_result_sse( + (move || async move { + crate::api::youtube::download_video( + api_url, + api_download_path, + api_proxy_url, + ) + .await + })() + .await, + ) + } + }, + ) +} +fn wire_download_video_by_id_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "download_video_by_id", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_id = ::sse_decode(&mut deserializer); + let api_download_path = ::sse_decode(&mut deserializer); + let api_proxy_url = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| async move { + transform_result_sse( + (move || async move { + crate::api::youtube::download_video_by_id( + api_id, + api_download_path, + api_proxy_url, + ) + .await + })() + .await, + ) + } + }, + ) +} +fn wire_download_video_by_id_with_callback_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "download_video_by_id_with_callback", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_sink = >::sse_decode(&mut deserializer); + let api_id = ::sse_decode(&mut deserializer); + let api_download_path = ::sse_decode(&mut deserializer); + let api_proxy_url = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| async move { + transform_result_sse( + (move || async move { + crate::api::youtube::download_video_by_id_with_callback( + api_sink, + api_id, + api_download_path, + api_proxy_url, + ) + .await + })() + .await, + ) + } + }, + ) +} +fn wire_fetch_ids_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "fetch_ids", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_keyword = ::sse_decode(&mut deserializer); + let api_max_id_count = ::sse_decode(&mut deserializer); + let api_proxy_url = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| async move { + transform_result_sse( + (move || async move { + crate::api::youtube::fetch_ids(api_keyword, api_max_id_count, api_proxy_url) + .await + })() + .await, + ) + } + }, + ) +} +fn wire_video_info_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "video_info", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_url = ::sse_decode(&mut deserializer); + let api_proxy_url = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| async move { + transform_result_sse( + (move || async move { + crate::api::youtube::video_info(api_url, api_proxy_url).await + })() + .await, + ) + } + }, + ) +} +fn wire_video_info_by_id_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "video_info_by_id", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_id = ::sse_decode(&mut deserializer); + let api_proxy_url = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| async move { + transform_result_sse( + (move || async move { + crate::api::youtube::video_info_by_id(api_id, api_proxy_url).await + })() + .await, + ) + } + }, + ) +} // Section: dart2rust @@ -502,6 +827,19 @@ impl SseDecode for std::collections::HashMap { } } +impl SseDecode + for StreamSink< + crate::api::video_info::ProgerssData, + flutter_rust_bridge::for_generated::SseCodec, + > +{ + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = ::sse_decode(deserializer); + return StreamSink::deserialize(inner); + } +} + impl SseDecode for String { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -517,6 +855,26 @@ impl SseDecode for i64 { } } +impl SseDecode for crate::api::video_info::Info { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_title = ::sse_decode(deserializer); + let mut var_author = ::sse_decode(deserializer); + let mut var_videoId = ::sse_decode(deserializer); + let mut var_shortDescription = ::sse_decode(deserializer); + let mut var_viewCount = ::sse_decode(deserializer); + let mut var_lengthSeconds = ::sse_decode(deserializer); + return crate::api::video_info::Info { + title: var_title, + author: var_author, + video_id: var_videoId, + short_description: var_shortDescription, + view_count: var_viewCount, + length_seconds: var_lengthSeconds, + }; + } +} + impl SseDecode for Vec> { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -531,6 +889,18 @@ impl SseDecode for Vec> { } } +impl SseDecode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = vec![]; + for idx_ in 0..len_ { + ans_.push(::sse_decode(deserializer)); + } + return ans_; + } +} + impl SseDecode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -555,6 +925,40 @@ impl SseDecode for Vec<(String, String)> { } } +impl SseDecode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(::sse_decode(deserializer)); + } else { + return None; + } + } +} + +impl SseDecode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(::sse_decode(deserializer)); + } else { + return None; + } + } +} + +impl SseDecode for crate::api::video_info::ProgerssData { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_currentSize = ::sse_decode(deserializer); + let mut var_totalSize = >::sse_decode(deserializer); + return crate::api::video_info::ProgerssData { + current_size: var_currentSize, + total_size: var_totalSize, + }; + } +} + impl SseDecode for (String, String) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -564,6 +968,13 @@ impl SseDecode for (String, String) { } } +impl SseDecode for u64 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u64::().unwrap() + } +} + impl SseDecode for u8 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -576,6 +987,13 @@ impl SseDecode for () { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {} } +impl SseDecode for usize { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u64::().unwrap() as _ + } +} + impl SseDecode for i32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -613,6 +1031,14 @@ fn pde_ffi_dispatcher_primary_impl( 6 => wire_update_impl(port, ptr, rust_vec_len, data_len), 13 => wire_init_impl(port, ptr, rust_vec_len, data_len), 14 => wire_init_logger_impl(port, ptr, rust_vec_len, data_len), + 21 => wire_download_audio_impl(port, ptr, rust_vec_len, data_len), + 22 => wire_download_audio_by_id_impl(port, ptr, rust_vec_len, data_len), + 18 => wire_download_video_impl(port, ptr, rust_vec_len, data_len), + 19 => wire_download_video_by_id_impl(port, ptr, rust_vec_len, data_len), + 20 => wire_download_video_by_id_with_callback_impl(port, ptr, rust_vec_len, data_len), + 15 => wire_fetch_ids_impl(port, ptr, rust_vec_len, data_len), + 16 => wire_video_info_impl(port, ptr, rust_vec_len, data_len), + 17 => wire_video_info_by_id_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -631,6 +1057,50 @@ fn pde_ffi_dispatcher_sync_impl( // Section: rust2dart +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::api::video_info::Info { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.title.into_into_dart().into_dart(), + self.author.into_into_dart().into_dart(), + self.video_id.into_into_dart().into_dart(), + self.short_description.into_into_dart().into_dart(), + self.view_count.into_into_dart().into_dart(), + self.length_seconds.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::api::video_info::Info {} +impl flutter_rust_bridge::IntoIntoDart + for crate::api::video_info::Info +{ + fn into_into_dart(self) -> crate::api::video_info::Info { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::api::video_info::ProgerssData { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.current_size.into_into_dart().into_dart(), + self.total_size.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::api::video_info::ProgerssData +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::api::video_info::ProgerssData +{ + fn into_into_dart(self) -> crate::api::video_info::ProgerssData { + self + } +} + impl SseEncode for flutter_rust_bridge::for_generated::anyhow::Error { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -645,6 +1115,18 @@ impl SseEncode for std::collections::HashMap { } } +impl SseEncode + for StreamSink< + crate::api::video_info::ProgerssData, + flutter_rust_bridge::for_generated::SseCodec, + > +{ + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + unimplemented!("") + } +} + impl SseEncode for String { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -659,6 +1141,18 @@ impl SseEncode for i64 { } } +impl SseEncode for crate::api::video_info::Info { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.title, serializer); + ::sse_encode(self.author, serializer); + ::sse_encode(self.video_id, serializer); + ::sse_encode(self.short_description, serializer); + ::sse_encode(self.view_count, serializer); + ::sse_encode(self.length_seconds, serializer); + } +} + impl SseEncode for Vec> { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -669,6 +1163,16 @@ impl SseEncode for Vec> { } } +impl SseEncode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + ::sse_encode(item, serializer); + } + } +} + impl SseEncode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -689,6 +1193,34 @@ impl SseEncode for Vec<(String, String)> { } } +impl SseEncode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + ::sse_encode(value, serializer); + } + } +} + +impl SseEncode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + ::sse_encode(value, serializer); + } + } +} + +impl SseEncode for crate::api::video_info::ProgerssData { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.current_size, serializer); + >::sse_encode(self.total_size, serializer); + } +} + impl SseEncode for (String, String) { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -697,6 +1229,13 @@ impl SseEncode for (String, String) { } } +impl SseEncode for u64 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_u64::(self).unwrap(); + } +} + impl SseEncode for u8 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -709,6 +1248,16 @@ impl SseEncode for () { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} } +impl SseEncode for usize { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer + .cursor + .write_u64::(self as _) + .unwrap(); + } +} + impl SseEncode for i32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {