mirror of
https://framagit.org/veretcle/tootube.git
synced 2025-07-21 04:51:18 +02:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1db91ec3dc | ||
![]() |
1d4ae8f152 | ||
![]() |
58f36a56f7 | ||
![]() |
e17ba6cf7e | ||
![]() |
3d53ad7bbd | ||
![]() |
3f482ff258 | ||
![]() |
ae3fa74d9b | ||
![]() |
62641f526f | ||
![]() |
d83985394c | ||
![]() |
ebc42f915d |
234
Cargo.lock
generated
234
Cargo.lock
generated
@@ -61,7 +61,7 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -71,7 +71,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-stream"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
|
||||||
|
dependencies = [
|
||||||
|
"async-stream-impl",
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-stream-impl"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -109,9 +131,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.4.0"
|
version = "2.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
@@ -173,6 +195,19 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console"
|
||||||
|
version = "0.15.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
|
||||||
|
dependencies = [
|
||||||
|
"encode_unicode",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"unicode-width",
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
@@ -189,6 +224,12 @@ version = "0.8.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.33"
|
version = "0.8.33"
|
||||||
@@ -218,7 +259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
|
checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -452,6 +493,28 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indicatif"
|
||||||
|
version = "0.17.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"instant",
|
||||||
|
"number_prefix",
|
||||||
|
"portable-atomic",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.8.0"
|
version = "2.8.0"
|
||||||
@@ -466,7 +529,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -557,7 +620,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -588,6 +651,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "number_prefix"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.32.1"
|
version = "0.32.1"
|
||||||
@@ -609,7 +678,7 @@ version = "0.10.57"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
|
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.0",
|
"bitflags 2.4.1",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -667,7 +736,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -694,6 +763,12 @@ version = "0.3.27"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
@@ -723,9 +798,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.0"
|
version = "1.10.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87"
|
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -735,9 +810,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.1"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b"
|
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -746,9 +821,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.0"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3cbb081b9784b07cceb8824c8583f86db4814d172ab043f3c23f7dc600bf83d"
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
@@ -799,15 +874,15 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.18"
|
version = "0.38.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a74ee2d7c2581cd139b42447d7d9389b889bdaad3a73f1ebb16f2a3237bb19c"
|
checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.0",
|
"bitflags 2.4.1",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -822,7 +897,7 @@ version = "0.1.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
|
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -856,18 +931,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.188"
|
version = "1.0.189"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
|
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.188"
|
version = "1.0.189"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -938,7 +1013,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e"
|
checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -989,7 +1064,7 @@ dependencies = [
|
|||||||
"fastrand",
|
"fastrand",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1032,7 +1107,7 @@ dependencies = [
|
|||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2 0.5.4",
|
"socket2 0.5.4",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1081,12 +1156,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tootube"
|
name = "tootube"
|
||||||
version = "0.3.0"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-stream",
|
||||||
"bytes",
|
"bytes",
|
||||||
"clap",
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures-core",
|
"futures-util",
|
||||||
|
"indicatif",
|
||||||
"log",
|
"log",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -1102,20 +1179,19 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.37"
|
version = "0.1.39"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.31"
|
version = "0.1.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
|
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
@@ -1156,6 +1232,12 @@ dependencies = [
|
|||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
@@ -1320,13 +1402,37 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.45.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets",
|
"windows-targets 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.42.2",
|
||||||
|
"windows_aarch64_msvc 0.42.2",
|
||||||
|
"windows_i686_gnu 0.42.2",
|
||||||
|
"windows_i686_msvc 0.42.2",
|
||||||
|
"windows_x86_64_gnu 0.42.2",
|
||||||
|
"windows_x86_64_gnullvm 0.42.2",
|
||||||
|
"windows_x86_64_msvc 0.42.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1335,51 +1441,93 @@ version = "0.48.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm 0.48.5",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc 0.48.5",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu 0.48.5",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc 0.48.5",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu 0.48.5",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm 0.48.5",
|
||||||
"windows_x86_64_msvc",
|
"windows_x86_64_msvc 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -1393,5 +1541,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tootube"
|
name = "tootube"
|
||||||
authors = ["VC <veretcle+framagit@mateu.be>"]
|
authors = ["VC <veretcle+framagit@mateu.be>"]
|
||||||
version = "0.3.0"
|
version = "0.5.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
@@ -10,12 +10,14 @@ edition = "2021"
|
|||||||
reqwest = { version = "^0.11", features = ["json", "stream", "multipart"] }
|
reqwest = { version = "^0.11", features = ["json", "stream", "multipart"] }
|
||||||
tokio = { version = "^1", features = ["full"] }
|
tokio = { version = "^1", features = ["full"] }
|
||||||
clap = "^4"
|
clap = "^4"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "^1", features = ["derive"] }
|
||||||
toml = "^0.5"
|
toml = "^0.5"
|
||||||
log = "^0.4"
|
log = "^0.4"
|
||||||
env_logger = "^0.10"
|
env_logger = "^0.10"
|
||||||
futures-core = "0.3.28"
|
bytes = "^1.5"
|
||||||
bytes = "1.5.0"
|
indicatif = "^0.17"
|
||||||
|
async-stream = "^0.3"
|
||||||
|
futures-util = "^0.3"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true
|
strip = true
|
||||||
|
55
README.md
55
README.md
@@ -6,50 +6,57 @@ This program takes the very last video published on a PeerTube instance and push
|
|||||||
|
|
||||||
This is an early prototype not really suited for production purposes for now:
|
This is an early prototype not really suited for production purposes for now:
|
||||||
* it still relies way too much on pre-determined value to upload the video to YouTube
|
* it still relies way too much on pre-determined value to upload the video to YouTube
|
||||||
* it cannot determine the playlists it needs to put them in
|
|
||||||
* it cannot determine the recording date (believe me, I tried!)
|
* it cannot determine the recording date (believe me, I tried!)
|
||||||
* there are still a lot of static values that I would rather not have static (like the cache directory…)
|
|
||||||
* it is 100% sync, meaning it’s clearly not optimal for now and probably won’t be for the next releases
|
|
||||||
|
|
||||||
So consider this a work in progress that will slowly get better with time
|
So consider this a work in progress that will slowly get better with time.
|
||||||
|
|
||||||
# What does it do exactly?
|
# What does it do exactly?
|
||||||
|
|
||||||
* it downloads the latest PeerTube video from an instance
|
* it retrieves the latest PeerTube video download URL from an instance
|
||||||
* stores it in cache directory
|
* it creates a resumable upload into the target YouTube account
|
||||||
* uploads it to YouTube
|
* it downloads/uploads the latest PeerTube video to YouTube without using a cache (stream-to-stream)
|
||||||
|
|
||||||
# What doesn’t it do exactly?
|
# What doesn’t it do exactly?
|
||||||
|
|
||||||
* it cannot register the original key (see below)
|
|
||||||
* it relies on a local cache despite the fact that it might well be possible to just download from PT/upload to YT at the same time (I don’t see why not in fact)
|
|
||||||
* it doesn’t retrieve ALL the original PeerTube video properties like licences, languages, categories, etc… again: early prototype
|
* it doesn’t retrieve ALL the original PeerTube video properties like licences, languages, categories, etc… again: early prototype
|
||||||
|
|
||||||
# Obtain Authorization Token from Google
|
# Howtos
|
||||||
|
## General usage
|
||||||
|
|
||||||
That the complicated part:
|
Once you fill your `tootube.toml` config file (see below), you can run `tootube` like this:
|
||||||
* create an OAuth2.0 application with authorization for Youtube DATA Api v3 Upload
|
|
||||||
|
```bash
|
||||||
|
tootube --config tootube.toml --playlist playlist_1 "Things You Might Like"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can turn on debug using env var `RUST_LOG`.
|
||||||
|
|
||||||
|
## Obtain Authorization Token from Google
|
||||||
|
|
||||||
|
The complicated part:
|
||||||
|
* create an OAuth2.0 application with authorization for Youtube DATA Api v3 Upload and Youtube DATA Api v3 (generally referenced as `../auth/youtube.upload` and `../auth/youtube`)
|
||||||
* create a OAuth2.0 client with Desktop client
|
* create a OAuth2.0 client with Desktop client
|
||||||
|
|
||||||
You’ll need:
|
You’ll need:
|
||||||
* the `client_id` from your OAuth2.0 client
|
* the `client_id` from your OAuth2.0 client
|
||||||
* the `client_secret` from you OAuth2.0 client
|
* the `client_secret` from you OAuth2.0 client
|
||||||
|
|
||||||
Then enter in:
|
Create your `tootube.toml` config file:
|
||||||
|
|
||||||
```
|
```toml
|
||||||
https://accounts.google.com/o/oauth2/v2/auth?client_id=XXX.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/youtube.upload&response_type=code
|
[peertube]
|
||||||
|
base_url="https://p.nintendojo.fr"
|
||||||
|
|
||||||
|
[youtube]
|
||||||
|
refresh_token="" # leave empty for now
|
||||||
|
client_id="<YOUR CLIENT_ID>"
|
||||||
|
client_secret="<YOUR CLIENT_SECRET>"
|
||||||
```
|
```
|
||||||
|
|
||||||
And accept that your YouTube account might be modified. You’ll get a code then; enter this `curl` post:
|
Then run:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
curl -s \
|
tootube register --config <PATH TO YOUR TOOTUBE.TOML FILE>
|
||||||
--request POST \
|
|
||||||
--data "code=[THE_CODE]&client_id=XXX.apps.googleusercontent.com&client_secret=[THE_CLIENT_SECRET]&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code" \
|
|
||||||
https://accounts.google.com/o/oauth2/token
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You’ll get a Token. The only important part is the `refresh_token`
|
You’ll be then prompted with all the necessary information to register `tootube`. You’ll end with a `refresh_token` that you can now paste inside your tootube configuration.
|
||||||
|
|
||||||
In your `tootube.toml` config file, put the `refresh_token`.
|
|
||||||
|
29
src/lib.rs
29
src/lib.rs
@@ -1,5 +1,6 @@
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::stream::Stream;
|
use futures_util::Stream;
|
||||||
|
use log::debug;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
@@ -13,16 +14,21 @@ use peertube::{get_latest_video, get_max_resolution_dl};
|
|||||||
|
|
||||||
mod youtube;
|
mod youtube;
|
||||||
pub use youtube::register;
|
pub use youtube::register;
|
||||||
use youtube::{create_resumable_upload, now_kiss};
|
use youtube::{add_video_to_playlists, create_resumable_upload, now_kiss};
|
||||||
|
|
||||||
async fn get_dl_video_stream(
|
async fn get_dl_video_stream(
|
||||||
u: &str,
|
u: &str,
|
||||||
) -> Result<impl Stream<Item = Result<Bytes, reqwest::Error>>, Box<dyn Error>> {
|
) -> Result<(impl Stream<Item = Result<Bytes, reqwest::Error>>, u64), Box<dyn Error>> {
|
||||||
Ok(reqwest::get(u).await?.bytes_stream())
|
let res = reqwest::get(u).await?;
|
||||||
|
let content_lengh = res
|
||||||
|
.content_length()
|
||||||
|
.ok_or(format!("Cannot get content length from {}", u))?;
|
||||||
|
|
||||||
|
Ok((res.bytes_stream(), content_lengh))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn run(config: Config) {
|
pub async fn run(config: Config, pl: Vec<String>) {
|
||||||
// Get the latest video object
|
// Get the latest video object
|
||||||
let latest_vid = get_latest_video(&config.peertube.base_url)
|
let latest_vid = get_latest_video(&config.peertube.base_url)
|
||||||
.await
|
.await
|
||||||
@@ -31,16 +37,25 @@ pub async fn run(config: Config) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let dl_url = get_max_resolution_dl(latest_vid.streaming_playlists.as_ref().unwrap());
|
let dl_url = get_max_resolution_dl(latest_vid.streaming_playlists.as_ref().unwrap());
|
||||||
|
debug!("PT download URL: {}", &dl_url);
|
||||||
|
|
||||||
let pt_stream = get_dl_video_stream(&dl_url)
|
let (pt_stream, size) = get_dl_video_stream(&dl_url)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| panic!("Cannot download video at URL {}: {}", dl_url, e));
|
.unwrap_or_else(|e| panic!("Cannot download video at URL {}: {}", dl_url, e));
|
||||||
|
|
||||||
let resumable_upload_url = create_resumable_upload(&config.youtube, &latest_vid)
|
let resumable_upload_url = create_resumable_upload(&config.youtube, &latest_vid)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| panic!("Cannot retrieve the upload’s resumable id: {e}"));
|
.unwrap_or_else(|e| panic!("Cannot retrieve the upload’s resumable id: {e}"));
|
||||||
|
debug!("YT upload URL: {}", &resumable_upload_url);
|
||||||
|
|
||||||
now_kiss(pt_stream, &resumable_upload_url, &config.youtube)
|
let yt_video_id = now_kiss(size, pt_stream, &resumable_upload_url, &config.youtube)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| panic!("Cannot resume upload!: {e}"));
|
.unwrap_or_else(|e| panic!("Cannot resume upload!: {e}"));
|
||||||
|
debug!("YT video ID: {}", &yt_video_id);
|
||||||
|
|
||||||
|
if !pl.is_empty() {
|
||||||
|
add_video_to_playlists(&config.youtube, &yt_video_id, &pl)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|e| panic!("Cannot add video to playlist(s): {e}"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
17
src/main.rs
17
src/main.rs
@@ -17,6 +17,15 @@ fn main() {
|
|||||||
.default_value(DEFAULT_CONFIG_PATH)
|
.default_value(DEFAULT_CONFIG_PATH)
|
||||||
.display_order(1),
|
.display_order(1),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("playlists")
|
||||||
|
.short('p')
|
||||||
|
.long("playlist")
|
||||||
|
.value_name("PLAYLIST")
|
||||||
|
.help("List of playlists to add the video to")
|
||||||
|
.num_args(0..)
|
||||||
|
.display_order(2),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
Command::new("register")
|
Command::new("register")
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
@@ -43,7 +52,13 @@ fn main() {
|
|||||||
|
|
||||||
let config = parse_toml(matches.get_one::<String>("config").unwrap());
|
let config = parse_toml(matches.get_one::<String>("config").unwrap());
|
||||||
|
|
||||||
|
let playlists: Vec<String> = matches
|
||||||
|
.get_many::<String>("playlists")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.map(|v| v.to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
run(config);
|
run(config, playlists);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{boxed::Box, error::Error};
|
use std::{boxed::Box, cmp::Ordering, error::Error};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct PeerTubeVideos {
|
pub struct PeerTubeVideos {
|
||||||
@@ -22,7 +22,7 @@ pub struct PeerTubeVideoStreamingPlaylists {
|
|||||||
pub files: Vec<PeerTubeVideoStreamingPlaylistsFiles>,
|
pub files: Vec<PeerTubeVideoStreamingPlaylistsFiles>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Eq, Debug, Deserialize)]
|
||||||
pub struct PeerTubeVideoStreamingPlaylistsFiles {
|
pub struct PeerTubeVideoStreamingPlaylistsFiles {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub resolution: PeerTubeVideoStreamingPlaylistsFilesResolution,
|
pub resolution: PeerTubeVideoStreamingPlaylistsFilesResolution,
|
||||||
@@ -30,7 +30,25 @@ pub struct PeerTubeVideoStreamingPlaylistsFiles {
|
|||||||
pub file_download_url: String,
|
pub file_download_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
impl Ord for PeerTubeVideoStreamingPlaylistsFiles {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.resolution.id.cmp(&other.resolution.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for PeerTubeVideoStreamingPlaylistsFiles {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for PeerTubeVideoStreamingPlaylistsFiles {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.resolution.id == other.resolution.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, Debug, Deserialize, PartialEq)]
|
||||||
pub struct PeerTubeVideoStreamingPlaylistsFilesResolution {
|
pub struct PeerTubeVideoStreamingPlaylistsFilesResolution {
|
||||||
pub id: u16,
|
pub id: u16,
|
||||||
}
|
}
|
||||||
@@ -49,17 +67,7 @@ pub async fn get_latest_video(u: &str) -> Result<PeerTubeVideo, Box<dyn Error>>
|
|||||||
|
|
||||||
/// This returns the direct download URL for with the maximum resolution
|
/// This returns the direct download URL for with the maximum resolution
|
||||||
pub fn get_max_resolution_dl(p: &[PeerTubeVideoStreamingPlaylists]) -> String {
|
pub fn get_max_resolution_dl(p: &[PeerTubeVideoStreamingPlaylists]) -> String {
|
||||||
let mut res = 0;
|
p[0].files.iter().max().unwrap().file_download_url.clone()
|
||||||
let mut dl_url = String::new();
|
|
||||||
|
|
||||||
for i in p[0].files.iter() {
|
|
||||||
if i.resolution.id > res {
|
|
||||||
res = i.resolution.id;
|
|
||||||
dl_url = i.file_download_url.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dl_url
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This gets all the crispy details about one particular video
|
/// This gets all the crispy details about one particular video
|
||||||
|
200
src/youtube.rs
200
src/youtube.rs
@@ -1,9 +1,17 @@
|
|||||||
use crate::{config::YoutubeConfig, error::TootubeError, peertube::PeerTubeVideo};
|
use crate::{config::YoutubeConfig, error::TootubeError, peertube::PeerTubeVideo};
|
||||||
|
use async_stream::stream;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::stream::Stream;
|
use futures_util::{Stream, StreamExt};
|
||||||
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
|
use log::debug;
|
||||||
use reqwest::{multipart::Form, Body, Client};
|
use reqwest::{multipart::Form, Body, Client};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{error::Error, io::stdin};
|
use std::{
|
||||||
|
cmp::min,
|
||||||
|
error::Error,
|
||||||
|
io::stdin,
|
||||||
|
marker::{Send, Sync, Unpin},
|
||||||
|
};
|
||||||
use tokio::sync::OnceCell;
|
use tokio::sync::OnceCell;
|
||||||
|
|
||||||
static ACCESS_TOKEN: OnceCell<String> = OnceCell::const_new();
|
static ACCESS_TOKEN: OnceCell<String> = OnceCell::const_new();
|
||||||
@@ -87,6 +95,71 @@ impl Default for YoutubeUploadParamsStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
struct YoutubePlaylistItemsParams {
|
||||||
|
snippet: YoutubePlaylistItemsParamsSnippet,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
struct YoutubePlaylistItemsParamsSnippet {
|
||||||
|
#[serde(rename = "playlistId")]
|
||||||
|
playlist_id: String,
|
||||||
|
position: u16,
|
||||||
|
#[serde(rename = "resourceId")]
|
||||||
|
resource_id: YoutubePlaylistItemsParamsSnippetResourceId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for YoutubePlaylistItemsParamsSnippet {
|
||||||
|
fn default() -> Self {
|
||||||
|
YoutubePlaylistItemsParamsSnippet {
|
||||||
|
playlist_id: "".to_string(),
|
||||||
|
position: 0,
|
||||||
|
resource_id: YoutubePlaylistItemsParamsSnippetResourceId {
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
struct YoutubePlaylistItemsParamsSnippetResourceId {
|
||||||
|
kind: String,
|
||||||
|
#[serde(rename = "videoId")]
|
||||||
|
video_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for YoutubePlaylistItemsParamsSnippetResourceId {
|
||||||
|
fn default() -> Self {
|
||||||
|
YoutubePlaylistItemsParamsSnippetResourceId {
|
||||||
|
kind: "youtube#video".to_string(),
|
||||||
|
video_id: "".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct YoutubeVideos {
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct YoutubePlaylistListResponse {
|
||||||
|
#[serde(rename = "nextPageToken")]
|
||||||
|
next_page_token: Option<String>,
|
||||||
|
items: Vec<YoutubePlaylistListResponseItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct YoutubePlaylistListResponseItem {
|
||||||
|
id: String,
|
||||||
|
snippet: YoutubePlaylistListResponseItemSnippet,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct YoutubePlaylistListResponseItemSnippet {
|
||||||
|
title: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// This function makes the registration process a little bit easier
|
/// This function makes the registration process a little bit easier
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn register(config: &YoutubeConfig) -> Result<(), Box<dyn Error>> {
|
pub async fn register(config: &YoutubeConfig) -> Result<(), Box<dyn Error>> {
|
||||||
@@ -142,12 +215,96 @@ async fn refresh_token(config: &YoutubeConfig) -> Result<String, reqwest::Error>
|
|||||||
|
|
||||||
let access_token: AccessTokenResponse = res.json().await?;
|
let access_token: AccessTokenResponse = res.json().await?;
|
||||||
|
|
||||||
|
debug!("YT Access Token: {}", &access_token.access_token);
|
||||||
Ok(access_token.access_token)
|
Ok(access_token.access_token)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This function takes a list of playlists keyword and returns a list of playlist ID
|
||||||
|
async fn get_playlist_ids(
|
||||||
|
config: &YoutubeConfig,
|
||||||
|
pl: &[String],
|
||||||
|
) -> Result<Vec<String>, Box<dyn Error>> {
|
||||||
|
let mut page_token = String::new();
|
||||||
|
let mut playlists: Vec<String> = vec![];
|
||||||
|
|
||||||
|
let access_token = refresh_token(config).await?;
|
||||||
|
let client = Client::new();
|
||||||
|
|
||||||
|
while let Ok(local_pl) = client
|
||||||
|
.get(&format!(
|
||||||
|
"https://www.googleapis.com/youtube/v3/playlists?part=snippet&mine=true&pageToken={}",
|
||||||
|
page_token
|
||||||
|
))
|
||||||
|
.header("Authorization", format!("Bearer {}", access_token))
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json::<YoutubePlaylistListResponse>()
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
playlists.append(
|
||||||
|
&mut local_pl
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.filter_map(|s| pl.contains(&s.snippet.title).then_some(s.id.clone()))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// if nextPageToken is present, continue the loop
|
||||||
|
match local_pl.next_page_token {
|
||||||
|
None => break,
|
||||||
|
Some(a) => page_token = a.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Playlists IDs: {:?}", &playlists);
|
||||||
|
Ok(playlists)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function adds the video id to the corresponding named playlist(s)
|
||||||
|
pub async fn add_video_to_playlists(
|
||||||
|
config: &YoutubeConfig,
|
||||||
|
v: &str,
|
||||||
|
pl: &[String],
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
let access_token = refresh_token(config).await?;
|
||||||
|
let playlists_ids = get_playlist_ids(config, pl).await?;
|
||||||
|
|
||||||
|
for pl_id in playlists_ids {
|
||||||
|
let yt_pl_upload_params = YoutubePlaylistItemsParams {
|
||||||
|
snippet: YoutubePlaylistItemsParamsSnippet {
|
||||||
|
playlist_id: pl_id.clone(),
|
||||||
|
resource_id: YoutubePlaylistItemsParamsSnippetResourceId {
|
||||||
|
video_id: v.to_string(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let client = Client::new();
|
||||||
|
let res = client
|
||||||
|
.post("https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet")
|
||||||
|
.header("Authorization", format!("Bearer {}", access_token))
|
||||||
|
.json(&yt_pl_upload_params)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !res.status().is_success() {
|
||||||
|
return Err(TootubeError::new(&format!(
|
||||||
|
"Something went wrong when trying to add the video to a playlist: {}",
|
||||||
|
res.text().await?
|
||||||
|
))
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function creates a resumable YT upload, putting all the parameters in
|
||||||
pub async fn create_resumable_upload(
|
pub async fn create_resumable_upload(
|
||||||
config: &YoutubeConfig,
|
config: &YoutubeConfig,
|
||||||
vid: &PeerTubeVideo,
|
vid: &PeerTubeVideo,
|
||||||
@@ -169,9 +326,9 @@ pub async fn create_resumable_upload(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
debug!("YT upload params: {:?}", &upload_params);
|
||||||
|
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
||||||
let res = client.post("https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet%2Cstatus")
|
let res = client.post("https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet%2Cstatus")
|
||||||
.header("Authorization", format!("Bearer {}", access_token))
|
.header("Authorization", format!("Bearer {}", access_token))
|
||||||
.json(&upload_params)
|
.json(&upload_params)
|
||||||
@@ -189,29 +346,50 @@ pub async fn create_resumable_upload(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This takes the PT stream for download, connects it to YT stream for upload
|
||||||
pub async fn now_kiss<'a>(
|
pub async fn now_kiss<'a>(
|
||||||
stream: impl Stream<Item = Result<Bytes, reqwest::Error>>
|
size: u64,
|
||||||
+ std::marker::Send
|
mut stream: impl Stream<Item = Result<Bytes, reqwest::Error>> + Send + Sync + Unpin + 'a + 'static,
|
||||||
+ std::marker::Sync
|
|
||||||
+ 'a + 'static,
|
|
||||||
r_url: &'a str,
|
r_url: &'a str,
|
||||||
config: &'a YoutubeConfig,
|
config: &'a YoutubeConfig,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<String, Box<dyn Error>> {
|
||||||
// Get access token
|
// Get access token
|
||||||
let access_token = refresh_token(config).await?;
|
let access_token = refresh_token(config).await?;
|
||||||
|
|
||||||
|
// Create the progress bar
|
||||||
|
let pb = ProgressBar::new(size);
|
||||||
|
pb.set_style(ProgressStyle::default_bar()
|
||||||
|
.template("{msg}\n{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})")?
|
||||||
|
.progress_chars("#>-"));
|
||||||
|
pb.set_message("Transferring…");
|
||||||
|
let mut transferring: u64 = 0;
|
||||||
|
|
||||||
|
let async_stream = stream! {
|
||||||
|
while let Some(chunk) = stream.next().await {
|
||||||
|
if let Ok(chunk) = &chunk {
|
||||||
|
let new = min(transferring + (chunk.len() as u64), size);
|
||||||
|
transferring = new;
|
||||||
|
pb.set_position(new);
|
||||||
|
if transferring >= size {
|
||||||
|
pb.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield chunk;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.put(r_url)
|
.put(r_url)
|
||||||
.header("Authorization", format!("Bearer {}", access_token))
|
.header("Authorization", format!("Bearer {}", access_token))
|
||||||
.body(Body::wrap_stream(stream))
|
.body(Body::wrap_stream(async_stream))
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if res.status().is_success() {
|
if res.status().is_success() {
|
||||||
Ok(())
|
let yt_videos: YoutubeVideos = res.json().await?;
|
||||||
|
Ok(yt_videos.id)
|
||||||
} else {
|
} else {
|
||||||
Err(TootubeError::new(&format!("Cannot upload video: {:?}", res.text().await?)).into())
|
Err(TootubeError::new(&format!("Cannot upload video: {:?}", res.text().await?)).into())
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user