From 59e1f8a987ffe8c232ee1552ecd40bd2ba79ba0c Mon Sep 17 00:00:00 2001
From: Paul van Tilburg
Date: Mon, 15 Aug 2022 20:36:16 +0200
Subject: [PATCH 1/9] Add first version of the YouTube back-end
---
Cargo.lock | 597 +++++++++++++++++++++++++++-------------
Cargo.toml | 1 +
src/backends.rs | 8 +-
src/backends/youtube.rs | 327 ++++++++++++++++++++++
src/lib.rs | 20 ++
5 files changed, 760 insertions(+), 193 deletions(-)
create mode 100644 src/backends/youtube.rs
diff --git a/Cargo.lock b/Cargo.lock
index ce1c21c..6738e8d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -14,9 +14,9 @@ dependencies = [
[[package]]
name = "aes"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba"
+checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241"
dependencies = [
"cfg-if 1.0.0",
"cipher",
@@ -39,9 +39,9 @@ dependencies = [
[[package]]
name = "aho-corasick"
-version = "0.7.19"
+version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
dependencies = [
"memchr",
]
@@ -78,9 +78,9 @@ dependencies = [
[[package]]
name = "async-trait"
-version = "0.1.57"
+version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
+checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3"
dependencies = [
"proc-macro2",
"quote",
@@ -134,9 +134,15 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64"
-version = "0.13.0"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "base64"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
[[package]]
name = "binascii"
@@ -170,15 +176,15 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.11.0"
+version = "3.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
+checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]]
name = "bytes"
-version = "1.2.1"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
[[package]]
name = "cached"
@@ -219,9 +225,9 @@ checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663"
[[package]]
name = "cc"
-version = "1.0.73"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]]
name = "cfg-if"
@@ -237,16 +243,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
-version = "0.4.22"
+version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
+checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
dependencies = [
"iana-time-zone",
"js-sys",
"num-integer",
"num-traits",
"serde",
- "time 0.1.44",
+ "time 0.1.45",
"wasm-bindgen",
"winapi 0.3.9",
]
@@ -295,19 +301,19 @@ dependencies = [
[[package]]
name = "cookie"
-version = "0.16.1"
+version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "344adc371239ef32293cb1c4fe519592fcf21206c79c02854320afcdf3ab4917"
+checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
dependencies = [
"aes-gcm",
- "base64",
+ "base64 0.20.0",
"hkdf",
"hmac",
"percent-encoding",
"rand",
"sha2",
"subtle",
- "time 0.3.15",
+ "time 0.3.17",
"version_check",
]
@@ -338,9 +344,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
-version = "0.8.12"
+version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
+checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
"cfg-if 1.0.0",
]
@@ -367,9 +373,9 @@ dependencies = [
[[package]]
name = "cxx"
-version = "1.0.79"
+version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f83d0ebf42c6eafb8d7c52f7e5f2d3003b89c7aa4fd2b79229209459a849af8"
+checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd"
dependencies = [
"cc",
"cxxbridge-flags",
@@ -379,9 +385,9 @@ dependencies = [
[[package]]
name = "cxx-build"
-version = "1.0.79"
+version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07d050484b55975889284352b0ffc2ecbda25c0c55978017c132b29ba0818a86"
+checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0"
dependencies = [
"cc",
"codespan-reporting",
@@ -394,15 +400,15 @@ dependencies = [
[[package]]
name = "cxxbridge-flags"
-version = "1.0.79"
+version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99d2199b00553eda8012dfec8d3b1c75fce747cf27c169a270b3b99e3448ab78"
+checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59"
[[package]]
name = "cxxbridge-macro"
-version = "1.0.79"
+version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcb67a6de1f602736dd7eaead0080cf3435df806c61b24b13328db128c58868f"
+checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6"
dependencies = [
"proc-macro2",
"quote",
@@ -551,9 +557,9 @@ dependencies = [
[[package]]
name = "digest"
-version = "0.10.5"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
+checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"crypto-common",
@@ -621,14 +627,14 @@ dependencies = [
[[package]]
name = "filetime"
-version = "0.2.17"
+version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c"
+checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
- "windows-sys",
+ "windows-sys 0.42.0",
]
[[package]]
@@ -698,9 +704,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "futures"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c"
+checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
dependencies = [
"futures-channel",
"futures-core",
@@ -712,9 +718,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050"
+checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
dependencies = [
"futures-core",
"futures-sink",
@@ -722,33 +728,33 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
+checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]]
name = "futures-io"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68"
+checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
[[package]]
name = "futures-sink"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56"
+checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
[[package]]
name = "futures-task"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1"
+checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
[[package]]
name = "futures-util"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
+checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [
"futures-channel",
"futures-core",
@@ -763,9 +769,9 @@ dependencies = [
[[package]]
name = "generator"
-version = "0.7.1"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc184cace1cea8335047a471cc1da80f18acf8a76f3bab2028d499e328948ec7"
+checksum = "d266041a359dfa931b370ef684cceb84b166beb14f7f0421f4a6a3d0c446d12e"
dependencies = [
"cc",
"libc",
@@ -786,9 +792,9 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.7"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if 1.0.0",
"libc",
@@ -837,9 +843,9 @@ dependencies = [
[[package]]
name = "h2"
-version = "0.3.14"
+version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be"
+checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4"
dependencies = [
"bytes",
"fnv",
@@ -929,9 +935,9 @@ checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026"
[[package]]
name = "hyper"
-version = "0.14.20"
+version = "0.14.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
+checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
dependencies = [
"bytes",
"futures-channel",
@@ -951,6 +957,19 @@ dependencies = [
"want",
]
+[[package]]
+name = "hyper-rustls"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c"
+dependencies = [
+ "http",
+ "hyper",
+ "rustls",
+ "tokio",
+ "tokio-rustls",
+]
+
[[package]]
name = "hyper-tls"
version = "0.5.0"
@@ -966,9 +985,9 @@ dependencies = [
[[package]]
name = "iana-time-zone"
-version = "0.1.51"
+version = "0.1.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed"
+checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -1024,9 +1043,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "1.9.1"
+version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
"hashbrown",
@@ -1088,15 +1107,15 @@ dependencies = [
[[package]]
name = "ipnet"
-version = "2.5.0"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
+checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e"
[[package]]
name = "itoa"
-version = "1.0.4"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
+checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "js-sys"
@@ -1131,15 +1150,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
-version = "0.2.135"
+version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
+checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]]
name = "link-cplusplus"
-version = "1.0.7"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
+checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
dependencies = [
"cc",
]
@@ -1220,14 +1239,14 @@ dependencies = [
[[package]]
name = "mio"
-version = "0.8.4"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
+checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys",
+ "windows-sys 0.42.0",
]
[[package]]
@@ -1268,7 +1287,7 @@ dependencies = [
"log",
"memchr",
"mime",
- "spin",
+ "spin 0.9.4",
"tokio",
"tokio-util",
"version_check",
@@ -1276,9 +1295,9 @@ dependencies = [
[[package]]
name = "native-tls"
-version = "0.2.10"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9"
+checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
dependencies = [
"lazy_static",
"libc",
@@ -1294,9 +1313,9 @@ dependencies = [
[[package]]
name = "net2"
-version = "0.2.37"
+version = "0.2.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
+checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631"
dependencies = [
"cfg-if 0.1.10",
"libc",
@@ -1367,28 +1386,19 @@ dependencies = [
[[package]]
name = "num_cpus"
-version = "1.13.1"
+version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [
"hermit-abi",
"libc",
]
-[[package]]
-name = "num_threads"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
-dependencies = [
- "libc",
-]
-
[[package]]
name = "once_cell"
-version = "1.15.0"
+version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
+checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "opaque-debug"
@@ -1398,9 +1408,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
-version = "0.10.42"
+version = "0.10.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13"
+checksum = "29d971fd5722fec23977260f6e81aa67d2f22cadbdc2aa049f1022d9a3be1566"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
@@ -1430,9 +1440,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
-version = "0.9.76"
+version = "0.9.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce"
+checksum = "5454462c0eced1e97f2ec09036abc8da362e66802f66fd20f86854d9d8cbcbc4"
dependencies = [
"autocfg",
"cc",
@@ -1459,15 +1469,15 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.3"
+version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
+checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
"smallvec",
- "windows-sys",
+ "windows-sys 0.42.0",
]
[[package]]
@@ -1510,9 +1520,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "pest"
-version = "2.4.0"
+version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a"
+checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0"
dependencies = [
"thiserror",
"ucd-trie",
@@ -1520,9 +1530,9 @@ dependencies = [
[[package]]
name = "pest_derive"
-version = "2.4.0"
+version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b75706b9642ebcb34dab3bc7750f811609a0eb1dd8b88c2d15bf628c1c65b2"
+checksum = "cdc078600d06ff90d4ed238f0119d84ab5d43dbaad278b0e33a8820293b32344"
dependencies = [
"pest",
"pest_generator",
@@ -1530,9 +1540,9 @@ dependencies = [
[[package]]
name = "pest_generator"
-version = "2.4.0"
+version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4f9272122f5979a6511a749af9db9bfc810393f63119970d7085fed1c4ea0db"
+checksum = "28a1af60b1c4148bb269006a750cff8e2ea36aff34d2d96cf7be0b14d1bed23c"
dependencies = [
"pest",
"pest_meta",
@@ -1543,9 +1553,9 @@ dependencies = [
[[package]]
name = "pest_meta"
-version = "2.4.0"
+version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c8717927f9b79515e565a64fe46c38b8cd0427e64c40680b14a7365ab09ac8d"
+checksum = "fec8605d59fc2ae0c6c1aefc0c7c7a9769732017c0ce07f7a9cfffa7b4404f20"
dependencies = [
"once_cell",
"pest",
@@ -1605,9 +1615,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
-version = "0.3.25"
+version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
+checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
name = "podbringer"
@@ -1624,6 +1634,7 @@ dependencies = [
"thiserror",
"url",
"youtube_dl",
+ "ytextract",
]
[[package]]
@@ -1640,15 +1651,15 @@ dependencies = [
[[package]]
name = "ppv-lite86"
-version = "0.2.16"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
-version = "1.0.47"
+version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
dependencies = [
"unicode-ident",
]
@@ -1678,9 +1689,9 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.21"
+version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
@@ -1726,18 +1737,18 @@ dependencies = [
[[package]]
name = "ref-cast"
-version = "1.0.12"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12a733f1746c929b4913fe48f8697fcf9c55e3304ba251a79ffb41adfeaf49c2"
+checksum = "8c78fb8c9293bcd48ef6fce7b4ca950ceaf21210de6e105a883ee280c0f7b9ed"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
-version = "1.0.12"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5887de4a01acafd221861463be6113e6e87275e79804e56779f4cdc131c60368"
+checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f"
dependencies = [
"proc-macro2",
"quote",
@@ -1746,9 +1757,9 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
dependencies = [
"aho-corasick",
"memchr",
@@ -1766,9 +1777,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.6.27"
+version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "remove_dir_all"
@@ -1781,11 +1792,11 @@ dependencies = [
[[package]]
name = "reqwest"
-version = "0.11.12"
+version = "0.11.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc"
+checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
dependencies = [
- "base64",
+ "base64 0.13.1",
"bytes",
"encoding_rs",
"futures-core",
@@ -1794,6 +1805,7 @@ dependencies = [
"http",
"http-body",
"hyper",
+ "hyper-rustls",
"hyper-tls",
"ipnet",
"js-sys",
@@ -1803,19 +1815,39 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
+ "rustls",
+ "rustls-pemfile",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-native-tls",
+ "tokio-rustls",
+ "tokio-util",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
+ "webpki-roots",
"winreg",
]
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin 0.5.2",
+ "untrusted",
+ "web-sys",
+ "winapi 0.3.9",
+]
+
[[package]]
name = "rocket"
version = "0.5.0-rc.2"
@@ -1846,7 +1878,7 @@ dependencies = [
"serde_json",
"state",
"tempfile",
- "time 0.3.15",
+ "time 0.3.17",
"tokio",
"tokio-stream",
"tokio-util",
@@ -1906,7 +1938,7 @@ dependencies = [
"smallvec",
"stable-pattern",
"state",
- "time 0.3.15",
+ "time 0.3.17",
"tokio",
"uncased",
]
@@ -1924,16 +1956,37 @@ dependencies = [
]
[[package]]
-name = "rustversion"
-version = "1.0.9"
+name = "rustls"
+version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
+checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c"
+dependencies = [
+ "log",
+ "ring",
+ "sct",
+ "webpki",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
+dependencies = [
+ "base64 0.13.1",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
[[package]]
name = "ryu"
-version = "1.0.11"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "same-file"
@@ -1951,14 +2004,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
dependencies = [
"lazy_static",
- "windows-sys",
+ "windows-sys 0.36.1",
]
[[package]]
name = "scoped-tls"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
@@ -1968,9 +2021,19 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scratch"
-version = "1.0.2"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
+checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
+
+[[package]]
+name = "sct"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
+dependencies = [
+ "ring",
+ "untrusted",
+]
[[package]]
name = "security-framework"
@@ -1997,18 +2060,18 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.145"
+version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
+checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.145"
+version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
+checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
dependencies = [
"proc-macro2",
"quote",
@@ -2017,9 +2080,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.86"
+version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
+checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
dependencies = [
"itoa",
"ryu",
@@ -2038,6 +2101,28 @@ 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_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",
+]
+
[[package]]
name = "sha1"
version = "0.10.5"
@@ -2118,6 +2203,12 @@ dependencies = [
"winapi 0.3.9",
]
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
[[package]]
name = "spin"
version = "0.9.4"
@@ -2156,9 +2247,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
-version = "1.0.102"
+version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
+checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
@@ -2212,18 +2303,18 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "1.0.37"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.37"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
@@ -2241,9 +2332,9 @@ dependencies = [
[[package]]
name = "time"
-version = "0.1.44"
+version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
@@ -2252,21 +2343,30 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.15"
+version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c"
+checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
dependencies = [
"itoa",
- "libc",
- "num_threads",
+ "serde",
+ "time-core",
"time-macros",
]
[[package]]
-name = "time-macros"
-version = "0.2.4"
+name = "time-core"
+version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
+checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
+
+[[package]]
+name = "time-macros"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
+dependencies = [
+ "time-core",
+]
[[package]]
name = "tinyvec"
@@ -2285,28 +2385,28 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
-version = "1.21.2"
+version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
+checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",
- "mio 0.8.4",
+ "mio 0.8.5",
"num_cpus",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
- "winapi 0.3.9",
+ "windows-sys 0.42.0",
]
[[package]]
name = "tokio-macros"
-version = "1.8.0"
+version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
+checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
dependencies = [
"proc-macro2",
"quote",
@@ -2323,6 +2423,17 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "tokio-rustls"
+version = "0.23.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
+dependencies = [
+ "rustls",
+ "tokio",
+ "webpki",
+]
+
[[package]]
name = "tokio-stream"
version = "0.1.11"
@@ -2350,9 +2461,9 @@ dependencies = [
[[package]]
name = "toml"
-version = "0.5.9"
+version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
dependencies = [
"serde",
]
@@ -2433,9 +2544,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "typenum"
-version = "1.15.0"
+version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "ubyte"
@@ -2520,9 +2631,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
-version = "1.0.5"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-normalization"
@@ -2555,6 +2666,12 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
[[package]]
name = "url"
version = "2.3.1"
@@ -2703,6 +2820,25 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "webpki"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.22.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
+dependencies = [
+ "webpki",
+]
+
[[package]]
name = "winapi"
version = "0.2.8"
@@ -2748,15 +2884,15 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
-version = "0.32.0"
+version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbedf6db9096bc2364adce0ae0aa636dcd89f3c3f2cd67947062aaf0ca2a10ec"
+checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a"
dependencies = [
- "windows_aarch64_msvc 0.32.0",
- "windows_i686_gnu 0.32.0",
- "windows_i686_msvc 0.32.0",
- "windows_x86_64_gnu 0.32.0",
- "windows_x86_64_msvc 0.32.0",
+ "windows_aarch64_msvc 0.39.0",
+ "windows_i686_gnu 0.39.0",
+ "windows_i686_msvc 0.39.0",
+ "windows_x86_64_gnu 0.39.0",
+ "windows_x86_64_msvc 0.39.0",
]
[[package]]
@@ -2773,10 +2909,25 @@ dependencies = [
]
[[package]]
-name = "windows_aarch64_msvc"
-version = "0.32.0"
+name = "windows-sys"
+version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc 0.42.0",
+ "windows_i686_gnu 0.42.0",
+ "windows_i686_msvc 0.42.0",
+ "windows_x86_64_gnu 0.42.0",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc 0.42.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_msvc"
@@ -2785,10 +2936,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
-name = "windows_i686_gnu"
-version = "0.32.0"
+name = "windows_aarch64_msvc"
+version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
+checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
@@ -2797,10 +2954,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
-name = "windows_i686_msvc"
-version = "0.32.0"
+name = "windows_i686_gnu"
+version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
+checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
@@ -2809,10 +2972,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
-name = "windows_x86_64_gnu"
-version = "0.32.0"
+name = "windows_i686_msvc"
+version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
+checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
@@ -2821,10 +2990,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
-name = "windows_x86_64_msvc"
-version = "0.32.0"
+name = "windows_x86_64_gnu"
+version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
+checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
@@ -2832,6 +3013,18 @@ version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+
[[package]]
name = "winreg"
version = "0.10.1"
@@ -2869,3 +3062,23 @@ dependencies = [
"tokio",
"wait-timeout",
]
+
+[[package]]
+name = "ytextract"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca88fc42bde556e9f8343d9f0f8a13eba27f54445529dbb6df3b69fea52236a2"
+dependencies = [
+ "async-stream",
+ "base64 0.13.1",
+ "bytes",
+ "chrono",
+ "futures-core",
+ "log",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "serde_with",
+ "thiserror",
+ "url",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 6c1ea10..b2c64af 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,6 +19,7 @@ rss = "2.0.1"
thiserror = "1.0.31"
url = { version = "2.2.2", features = ["serde"] }
youtube_dl = { version = "0.7.0", features = ["tokio"] }
+ytextract = "0.11.0"
[package.metadata.deb]
maintainer = "Paul van Tilburg "
diff --git a/src/backends.rs b/src/backends.rs
index e0a9746..8253d85 100644
--- a/src/backends.rs
+++ b/src/backends.rs
@@ -15,19 +15,25 @@ use reqwest::Url;
use crate::{Error, Result};
pub(crate) mod mixcloud;
+pub(crate) mod youtube;
/// Retrieves the back-end for the provided ID (if supported).
pub(crate) fn get(backend: &str) -> Result {
match backend {
"mixcloud" => Ok(Backends::Mixcloud(mixcloud::backend())),
+ "youtube" => Ok(Backends::YouTube(youtube::backend())),
_ => Err(Error::UnsupportedBackend(backend.to_string())),
}
}
-/// The support back-ends.
+/// The supported back-ends.
#[enum_dispatch(Backend)]
pub(crate) enum Backends {
+ /// Mixcloud ()
Mixcloud(mixcloud::Backend),
+
+ /// YouTube ()
+ YouTube(youtube::Backend),
}
/// Functionality of a content back-end.
diff --git a/src/backends/youtube.rs b/src/backends/youtube.rs
new file mode 100644
index 0000000..6e735d6
--- /dev/null
+++ b/src/backends/youtube.rs
@@ -0,0 +1,327 @@
+//! The YouTube back-end.
+//!
+//! It uses the `ytextract` crate to retrieve the feed (channel or playlist) and items (videos).
+
+use std::path::{Path, PathBuf};
+
+use async_trait::async_trait;
+use cached::proc_macro::cached;
+use chrono::Utc;
+use reqwest::Url;
+use rocket::futures::StreamExt;
+use ytextract::playlist::video::{Error as YouTubeVideoError, Video as YouTubeVideo};
+use ytextract::{
+ Channel as YouTubeChannel, Client, Playlist as YouTubePlaylist, Stream as YouTubeStream,
+};
+
+use super::{Channel, Enclosure, Item};
+use crate::{Error, Result};
+
+/// The base URL for YouTube channels.
+const CHANNEL_BASE_URL: &str = "https://www.youtube.com/channel";
+
+/// The base URL for YouTube playlists.
+const PLAYLIST_BASE_URL: &str = "https://www.youtube.com/channel";
+
+/// The base URL for YouTube videos.
+const VIDEO_BASE_URL: &str = "https://www.youtube.com/watch";
+
+/// Creates a YouTube back-end.
+pub(crate) fn backend() -> Backend {
+ Backend::new()
+}
+
+/// The YouTube back-end.
+pub struct Backend {
+ /// The client capable of interacting with YouTube.
+ client: Client,
+}
+
+impl Backend {
+ /// Creates a new YouTube back-end.
+ fn new() -> Self {
+ let client = Client::new();
+
+ Self { client }
+ }
+}
+
+#[async_trait]
+impl super::Backend for Backend {
+ fn name(&self) -> &'static str {
+ "YouTube"
+ }
+
+ async fn channel(&self, channel_id: &str, item_limit: Option) -> Result {
+ // We assume it is a YouTube playlist ID if the channel ID starts with
+ // "PL"/"OLAK"/"RDCLAK"; it is considered to be a YouTube channel ID otherwise.
+ if channel_id.starts_with("PL")
+ || channel_id.starts_with("OLAK")
+ || channel_id.starts_with("RDCLAK")
+ {
+ let (yt_playlist, yt_videos_w_streams) =
+ fetch_playlist_videos(&self.client, channel_id, item_limit).await?;
+
+ Ok(Channel::from(YouTubePlaylistWithVideos(
+ yt_playlist,
+ yt_videos_w_streams,
+ )))
+ } else {
+ let (yt_channel, yt_videos_w_streams) =
+ fetch_channel_videos(&self.client, channel_id, item_limit).await?;
+
+ Ok(Channel::from(YouTubeChannelWithVideos(
+ yt_channel,
+ yt_videos_w_streams,
+ )))
+ }
+ }
+
+ async fn redirect_url(&self, file: &Path) -> Result {
+ let id_part = file.with_extension("");
+ let video_id = id_part.to_string_lossy();
+
+ retrieve_redirect_url(&self.client, &video_id).await
+ }
+}
+
+/// A YouTube playlist with its videos.
+#[derive(Clone, Debug)]
+pub(crate) struct YouTubePlaylistWithVideos(YouTubePlaylist, Vec);
+
+/// A YouTube channel with its videos.
+#[derive(Clone, Debug)]
+pub(crate) struct YouTubeChannelWithVideos(YouTubeChannel, Vec);
+
+/// A YouTube video with its stream.
+#[derive(Clone, Debug)]
+struct YouTubeVideoWithStream {
+ /// The information of the YouTube video.
+ video: YouTubeVideo,
+
+ /// The metadata of the selected YouTube stream.
+ stream: YouTubeStream,
+
+ /// The content of the selected YouTube stream.
+ content_length: u64,
+}
+
+impl From for Channel {
+ fn from(
+ YouTubeChannelWithVideos(yt_channel, yt_videos_w_streams): YouTubeChannelWithVideos,
+ ) -> Self {
+ let mut link = Url::parse(CHANNEL_BASE_URL).expect("valid URL");
+ link.path_segments_mut()
+ .expect("valid URL")
+ .push(&yt_channel.id());
+ let author = Some(yt_channel.name().to_string());
+ let categories = Vec::from([String::from("Video")]);
+ let image = yt_channel
+ .avatar()
+ .max_by_key(|av| av.width * av.height)
+ .map(|av| av.url.clone());
+ let items = yt_videos_w_streams.into_iter().map(Item::from).collect();
+
+ Channel {
+ title: format!("{0} (via YouTube)", yt_channel.name()),
+ link,
+ description: yt_channel.description().to_string(),
+ author,
+ categories,
+ image,
+ items,
+ }
+ }
+}
+
+impl From for Channel {
+ fn from(
+ YouTubePlaylistWithVideos(yt_playlist, yt_videos_w_streams): YouTubePlaylistWithVideos,
+ ) -> Self {
+ let mut link = Url::parse(PLAYLIST_BASE_URL).expect("valid URL");
+ link.query_pairs_mut()
+ .append_pair("list", &yt_playlist.id().to_string());
+ let author = yt_playlist.channel().map(|chan| chan.name().to_string());
+ // FIXME: Don't hardcode the category!
+ let categories = Vec::from([String::from("Video")]);
+ let image = yt_playlist
+ .thumbnails()
+ .iter()
+ .max_by_key(|tn| tn.width * tn.height)
+ .map(|tn| tn.url.clone());
+ let items = yt_videos_w_streams.into_iter().map(Item::from).collect();
+
+ Channel {
+ title: format!("{0} (via YouTube)", yt_playlist.title()),
+ link,
+ description: yt_playlist.description().to_string(),
+ author,
+ categories,
+ image,
+ items,
+ }
+ }
+}
+
+impl From for Item {
+ fn from(
+ YouTubeVideoWithStream {
+ video,
+ stream,
+ content_length,
+ }: YouTubeVideoWithStream,
+ ) -> Self {
+ let id = video.id().to_string();
+ let mut link = Url::parse(VIDEO_BASE_URL).expect("valid URL");
+ let description = Some(format!("Taken from YouTube: {0}", link));
+ link.query_pairs_mut().append_pair("v", &id);
+ let enclosure = Enclosure {
+ file: PathBuf::from(&format!("{id}.webm")), // FIXME: Don't hardcode the extension!
+ mime_type: stream.mime_type().to_string(),
+ length: content_length,
+ };
+
+ let duration = Some(video.length().as_secs() as u32);
+ let image = video
+ .thumbnails()
+ .iter()
+ .max_by_key(|tn| tn.width * tn.height)
+ .map(|tn| tn.url.clone());
+
+ Item {
+ title: video.title().to_string(),
+ link,
+ description,
+ categories: Default::default(),
+ enclosure,
+ duration,
+ guid: id,
+ keywords: Default::default(),
+ image,
+ updated_at: Utc::now(), // TODO: Get a decent timestamp somewhere?!
+ }
+ }
+}
+
+/// Fetches the YouTube playlist videos for the given ID.
+///
+/// If the result is [`Ok`], the playlist will be cached for 24 hours for the given playlist ID.
+#[cached(
+ key = "(String, Option)",
+ convert = r#"{ (playlist_id.to_owned(), item_limit) }"#,
+ time = 86400,
+ result = true
+)]
+async fn fetch_playlist_videos(
+ client: &Client,
+ playlist_id: &str,
+ item_limit: Option,
+) -> Result<(YouTubePlaylist, Vec)> {
+ let id = playlist_id.parse()?;
+ let yt_playlist = client.playlist(id).await?;
+ let yt_videos_w_streams: Vec<_> = match item_limit {
+ Some(n) => {
+ yt_playlist
+ .videos()
+ .filter_map(fetch_stream)
+ .take(n)
+ .collect()
+ .await
+ }
+ None => {
+ yt_playlist
+ .videos()
+ .filter_map(fetch_stream)
+ .collect()
+ .await
+ }
+ };
+
+ Ok((yt_playlist, yt_videos_w_streams))
+}
+
+/// Fetches the YouTube channel videos for the given ID.
+#[cached(
+ key = "(String, Option)",
+ convert = r#"{ (channel_id.to_owned(), item_limit) }"#,
+ time = 86400,
+ result = true
+)]
+async fn fetch_channel_videos(
+ client: &Client,
+ channel_id: &str,
+ item_limit: Option,
+) -> Result<(YouTubeChannel, Vec)> {
+ let id = channel_id.parse()?;
+ let yt_channel = client.channel(id).await?;
+ let yt_videos_w_streams: Vec<_> = match item_limit {
+ Some(n) => {
+ yt_channel
+ .uploads()
+ .await?
+ .take(n)
+ .filter_map(fetch_stream)
+ .collect()
+ .await
+ }
+ None => {
+ yt_channel
+ .uploads()
+ .await?
+ .filter_map(fetch_stream)
+ .collect()
+ .await
+ }
+ };
+
+ Ok((yt_channel, yt_videos_w_streams))
+}
+
+/// Fetches the stream and relevant metadata for a YouTube video result.
+///
+/// If there is a video retieving the metadata, the video is discarded/ignored.
+/// If there are problems retrieving the streams or metadata, the video is also discarded.
+async fn fetch_stream(
+ yt_video: Result,
+) -> Option {
+ match yt_video {
+ Ok(video) => {
+ let stream = video
+ .streams()
+ .await
+ .ok()?
+ .filter(|v| v.is_audio())
+ .max_by_key(|v| v.bitrate())?;
+ let content_length = stream.content_length().await.ok()?;
+
+ Some(YouTubeVideoWithStream {
+ video,
+ stream,
+ content_length,
+ })
+ }
+ Err(_) => None,
+ }
+}
+
+/// Retrieves the redirect URL for the provided YouTube video ID.
+///
+/// If the result is [`Ok`], the redirect URL will be cached for 24 hours for the given video ID.
+#[cached(
+ key = "String",
+ convert = r#"{ video_id.to_owned() }"#,
+ time = 86400,
+ result = true
+)]
+async fn retrieve_redirect_url(client: &Client, video_id: &str) -> Result {
+ let video_id = video_id.parse()?;
+ let video = client.video(video_id).await?;
+ let stream = video
+ .streams()
+ .await?
+ .filter(|v| v.is_audio())
+ .max_by_key(|v| v.bitrate())
+ .ok_or(Error::NoRedirectUrlFound)?;
+
+ Ok(stream.url().to_string())
+}
diff --git a/src/lib.rs b/src/lib.rs
index 6869f6c..7cef003 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -54,6 +54,26 @@ pub(crate) enum Error {
/// An error occurred in youtube-dl.
#[error("Youtube-dl failed: {0}")]
YoutubeDl(#[from] youtube_dl::Error),
+
+ /// An YouTube extract error occured.
+ #[error("YouTube extract error: {0}")]
+ YtExtract(#[from] ytextract::Error),
+
+ /// An YouTube extract ID parsing error occured.
+ #[error("YouTube extract ID parsing error: {0}")]
+ YtExtractId0(#[from] ytextract::error::Id<0>),
+
+ /// An YouTube extract ID parsing error occured.
+ #[error("YouTube extract ID parsing error: {0}")]
+ YtExtractId11(#[from] ytextract::error::Id<11>),
+
+ /// An YouTube extract ID parsing error occured.
+ #[error("YouTube extract ID parsing error: {0}")]
+ YtExtractId24(#[from] ytextract::error::Id<24>),
+
+ /// An YouTube extract playlist video error occured.
+ #[error("YouTube extract playlist video error: {0}")]
+ YtExtractPlaylistVideo(#[from] ytextract::playlist::video::Error),
}
impl<'r, 'o: 'r> rocket::response::Responder<'r, 'o> for Error {
From 3a3fbc96f4a6c16a7a3e552d1e7f62d3595be84d Mon Sep 17 00:00:00 2001
From: Paul van Tilburg
Date: Mon, 15 Aug 2022 21:06:00 +0200
Subject: [PATCH 2/9] Use a MIME DB to determine the download URL file
extensions
* Also apply it to the default MIME type for Mixcloud posts
* Add a dependency on the `mime_db` crate
---
Cargo.lock | 12 ++++++++++++
Cargo.toml | 1 +
src/backends/mixcloud.rs | 3 ++-
src/backends/youtube.rs | 20 +++++++++++++-------
4 files changed, 28 insertions(+), 8 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 6738e8d..cdb9fd8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1218,6 +1218,17 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
+[[package]]
+name = "mime-db"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d7c816ec30c41f873e1eea969aa2261d78756629e468a427244ff8658b75e7d"
+dependencies = [
+ "reqwest",
+ "serde",
+ "tokio",
+]
+
[[package]]
name = "mio"
version = "0.6.23"
@@ -1627,6 +1638,7 @@ dependencies = [
"cached",
"chrono",
"enum_dispatch",
+ "mime-db",
"reqwest",
"rocket",
"rocket_dyn_templates",
diff --git a/Cargo.toml b/Cargo.toml
index b2c64af..1906e03 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,6 +12,7 @@ async-trait = "0.1.57"
cached = { version = "0.39.0", features = ["async"] }
chrono = { version = "0.4.19", features = ["serde"] }
enum_dispatch = "0.3.8"
+mime-db = "1.6.0"
reqwest = { version = "0.11.10", features = ["json"] }
rocket = { version = "0.5.0-rc.2", features = ["json"] }
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] }
diff --git a/src/backends/mixcloud.rs b/src/backends/mixcloud.rs
index 56d32c1..9329144 100644
--- a/src/backends/mixcloud.rs
+++ b/src/backends/mixcloud.rs
@@ -199,7 +199,8 @@ impl From for Channel {
impl From for Item {
fn from(cloudcast: Cloudcast) -> Self {
let mut file = PathBuf::from(cloudcast.key.trim_end_matches('/'));
- file.set_extension("m4a"); // FIXME: Don't hardcoded the extension!
+ let extension = mime_db::extension(DEFAULT_FILE_TYPE).expect("MIME type has extension");
+ file.set_extension(extension);
// FIXME: Don't hardcode the description!
let description = Some(format!("Taken from Mixcloud: {0}", cloudcast.url));
diff --git a/src/backends/youtube.rs b/src/backends/youtube.rs
index 6e735d6..f36c4ed 100644
--- a/src/backends/youtube.rs
+++ b/src/backends/youtube.rs
@@ -168,19 +168,25 @@ impl From for Item {
YouTubeVideoWithStream {
video,
stream,
- content_length,
+ content_length: length,
}: YouTubeVideoWithStream,
) -> Self {
let id = video.id().to_string();
- let mut link = Url::parse(VIDEO_BASE_URL).expect("valid URL");
- let description = Some(format!("Taken from YouTube: {0}", link));
- link.query_pairs_mut().append_pair("v", &id);
+
+ let mime_type = stream.mime_type().to_string();
+ // Ignore everything from MIME type parameter seperator on for extension look-up.
+ let mime_sep = mime_type.find(';').unwrap_or(mime_type.len());
+ let extension = mime_db::extension(&mime_type[..mime_sep]).unwrap_or_default();
+ let file = PathBuf::from(&id).with_extension(extension);
let enclosure = Enclosure {
- file: PathBuf::from(&format!("{id}.webm")), // FIXME: Don't hardcode the extension!
- mime_type: stream.mime_type().to_string(),
- length: content_length,
+ file,
+ mime_type,
+ length,
};
+ let mut link = Url::parse(VIDEO_BASE_URL).expect("valid URL");
+ link.query_pairs_mut().append_pair("v", &id);
+ let description = Some(format!("Taken from YouTube: {0}", link));
let duration = Some(video.length().as_secs() as u32);
let image = video
.thumbnails()
From 8e73deb0424df6d0249137a585213009cd44651a Mon Sep 17 00:00:00 2001
From: Paul van Tilburg
Date: Mon, 15 Aug 2022 21:24:05 +0200
Subject: [PATCH 3/9] Mention YouTube support in the public documentation
---
Cargo.toml | 3 ++-
README.md | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 1906e03..22c740b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,7 +31,8 @@ Podbringer is a web service that provides podcasts for services that don't
offer them (anymore). It provides a way to get the RSS feed for your podcast
client and it facilites the downloads of the pods (enclosures).
-It currently only supports [Mixcloud](https://mixcloud.com).
+It currently only supports [Mixcloud](https://www.mixcloud.com) and
+[YouTube](https://www.youtube.com).
Other back-ends might be added in the future.
"""
section = "net"
diff --git a/README.md b/README.md
index 829e956..6233f43 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,8 @@ Podbringer is a web service that provides podcasts for services that don't
offer them (anymore). It provides a way to get the RSS feed for your podcast
client and it facilites the downloads of the pods (enclosures).
-It currently only supports [Mixcloud](https://mixcloud.com).
+It currently only supports [Mixcloud](https://www.mixcloud.com) and
+[YouTube](https://www.youtube.com).
Other back-ends might be added in the future.
## Building & running
From 94121c082853acfef6c6a915e0f26c219bdaea0e Mon Sep 17 00:00:00 2001
From: Paul van Tilburg
Date: Sat, 20 Aug 2022 14:52:11 +0200
Subject: [PATCH 4/9] Apply a default item limit of 50
---
src/backends/youtube.rs | 54 ++++++++++++++---------------------------
1 file changed, 18 insertions(+), 36 deletions(-)
diff --git a/src/backends/youtube.rs b/src/backends/youtube.rs
index f36c4ed..f8a1a61 100644
--- a/src/backends/youtube.rs
+++ b/src/backends/youtube.rs
@@ -20,6 +20,9 @@ use crate::{Error, Result};
/// The base URL for YouTube channels.
const CHANNEL_BASE_URL: &str = "https://www.youtube.com/channel";
+/// The default item limit.
+const DEFAULT_ITEM_LIMIT: usize = 50;
+
/// The base URL for YouTube playlists.
const PLAYLIST_BASE_URL: &str = "https://www.youtube.com/channel";
@@ -224,24 +227,14 @@ async fn fetch_playlist_videos(
item_limit: Option,
) -> Result<(YouTubePlaylist, Vec)> {
let id = playlist_id.parse()?;
+ let limit = item_limit.unwrap_or(DEFAULT_ITEM_LIMIT);
let yt_playlist = client.playlist(id).await?;
- let yt_videos_w_streams: Vec<_> = match item_limit {
- Some(n) => {
- yt_playlist
- .videos()
- .filter_map(fetch_stream)
- .take(n)
- .collect()
- .await
- }
- None => {
- yt_playlist
- .videos()
- .filter_map(fetch_stream)
- .collect()
- .await
- }
- };
+ let yt_videos_w_streams = yt_playlist
+ .videos()
+ .filter_map(fetch_stream)
+ .take(limit)
+ .collect()
+ .await;
Ok((yt_playlist, yt_videos_w_streams))
}
@@ -259,26 +252,15 @@ async fn fetch_channel_videos(
item_limit: Option,
) -> Result<(YouTubeChannel, Vec)> {
let id = channel_id.parse()?;
+ let limit = item_limit.unwrap_or(DEFAULT_ITEM_LIMIT);
let yt_channel = client.channel(id).await?;
- let yt_videos_w_streams: Vec<_> = match item_limit {
- Some(n) => {
- yt_channel
- .uploads()
- .await?
- .take(n)
- .filter_map(fetch_stream)
- .collect()
- .await
- }
- None => {
- yt_channel
- .uploads()
- .await?
- .filter_map(fetch_stream)
- .collect()
- .await
- }
- };
+ let yt_videos_w_streams = yt_channel
+ .uploads()
+ .await?
+ .take(limit)
+ .filter_map(fetch_stream)
+ .collect()
+ .await;
Ok((yt_channel, yt_videos_w_streams))
}
From 9f88f4f9a3691009e18f2fb9eb0473453ba19c32 Mon Sep 17 00:00:00 2001
From: Paul van Tilburg
Date: Mon, 19 Dec 2022 21:38:12 +0100
Subject: [PATCH 5/9] Bump the depend on ytextract
This newer version is able to correctly parse the date of streamed
videos.
Also use the full `ytextract::Video` structs which should have have all
the metadata.
---
Cargo.lock | 61 ++++++++++++++++++++++++++++++++++++-----
Cargo.toml | 2 +-
src/backends/youtube.rs | 10 ++++---
3 files changed, 61 insertions(+), 12 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index cdb9fd8..1a97900 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -435,6 +435,16 @@ dependencies = [
"darling_macro 0.13.4",
]
+[[package]]
+name = "darling"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
+dependencies = [
+ "darling_core 0.14.2",
+ "darling_macro 0.14.2",
+]
+
[[package]]
name = "darling_core"
version = "0.12.4"
@@ -463,6 +473,20 @@ dependencies = [
"syn",
]
+[[package]]
+name = "darling_core"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+]
+
[[package]]
name = "darling_macro"
version = "0.12.4"
@@ -485,6 +509,17 @@ dependencies = [
"syn",
]
+[[package]]
+name = "darling_macro"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
+dependencies = [
+ "darling_core 0.14.2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "derive_builder"
version = "0.10.2"
@@ -875,6 +910,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
[[package]]
name = "hkdf"
version = "0.12.3"
@@ -2115,21 +2156,27 @@ dependencies = [
[[package]]
name = "serde_with"
-version = "1.14.0"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
+checksum = "25bf4a5a814902cd1014dbccfa4d4560fb8432c779471e96e035602519f82eef"
dependencies = [
+ "base64 0.13.1",
+ "chrono",
+ "hex",
+ "indexmap",
"serde",
+ "serde_json",
"serde_with_macros",
+ "time 0.3.17",
]
[[package]]
name = "serde_with_macros"
-version = "1.5.2"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
+checksum = "e3452b4c0f6c1e357f73fdb87cd1efabaa12acf328c7a528e252893baeb3f4aa"
dependencies = [
- "darling 0.13.4",
+ "darling 0.14.2",
"proc-macro2",
"quote",
"syn",
@@ -3077,9 +3124,9 @@ dependencies = [
[[package]]
name = "ytextract"
-version = "0.11.0"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca88fc42bde556e9f8343d9f0f8a13eba27f54445529dbb6df3b69fea52236a2"
+checksum = "d23e95563efc0a066ac0e6491ecd7012940ef495ce050c9f09de145a034ccabf"
dependencies = [
"async-stream",
"base64 0.13.1",
diff --git a/Cargo.toml b/Cargo.toml
index 22c740b..9e784bb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,7 +20,7 @@ rss = "2.0.1"
thiserror = "1.0.31"
url = { version = "2.2.2", features = ["serde"] }
youtube_dl = { version = "0.7.0", features = ["tokio"] }
-ytextract = "0.11.0"
+ytextract = "0.11.1"
[package.metadata.deb]
maintainer = "Paul van Tilburg "
diff --git a/src/backends/youtube.rs b/src/backends/youtube.rs
index f8a1a61..568f07a 100644
--- a/src/backends/youtube.rs
+++ b/src/backends/youtube.rs
@@ -9,9 +9,10 @@ use cached::proc_macro::cached;
use chrono::Utc;
use reqwest::Url;
use rocket::futures::StreamExt;
-use ytextract::playlist::video::{Error as YouTubeVideoError, Video as YouTubeVideo};
+use ytextract::playlist::video::{Error as YouTubeVideoError, Video as YouTubePlaylistVideo};
use ytextract::{
Channel as YouTubeChannel, Client, Playlist as YouTubePlaylist, Stream as YouTubeStream,
+ Video as YouTubeVideo,
};
use super::{Channel, Enclosure, Item};
@@ -190,7 +191,7 @@ impl From for Item {
let mut link = Url::parse(VIDEO_BASE_URL).expect("valid URL");
link.query_pairs_mut().append_pair("v", &id);
let description = Some(format!("Taken from YouTube: {0}", link));
- let duration = Some(video.length().as_secs() as u32);
+ let duration = Some(video.duration().as_secs() as u32);
let image = video
.thumbnails()
.iter()
@@ -267,13 +268,14 @@ async fn fetch_channel_videos(
/// Fetches the stream and relevant metadata for a YouTube video result.
///
-/// If there is a video retieving the metadata, the video is discarded/ignored.
+/// If there is a error retrieving the metadata, the video is discarded/ignored.
/// If there are problems retrieving the streams or metadata, the video is also discarded.
async fn fetch_stream(
- yt_video: Result,
+ yt_video: Result,
) -> Option {
match yt_video {
Ok(video) => {
+ let video = video.upgrade().await.ok()?;
let stream = video
.streams()
.await
From 4177e1c6f90e71d12ef34ca4e8ce23707783af35 Mon Sep 17 00:00:00 2001
From: Paul van Tilburg
Date: Mon, 19 Dec 2022 21:53:32 +0100
Subject: [PATCH 6/9] Set updated at timestamp for videos
Since the metadata only provides a date, set the time part to 12:00:00
(UTC).
Also fix up the deprecation warning for the creation of the initial zero
last build timestamp.
---
src/backends/youtube.rs | 9 +++++++--
src/feed.rs | 4 +++-
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/backends/youtube.rs b/src/backends/youtube.rs
index 568f07a..eb6cc39 100644
--- a/src/backends/youtube.rs
+++ b/src/backends/youtube.rs
@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
use async_trait::async_trait;
use cached::proc_macro::cached;
-use chrono::Utc;
+use chrono::{DateTime, Utc};
use reqwest::Url;
use rocket::futures::StreamExt;
use ytextract::playlist::video::{Error as YouTubeVideoError, Video as YouTubePlaylistVideo};
@@ -197,6 +197,11 @@ impl From for Item {
.iter()
.max_by_key(|tn| tn.width * tn.height)
.map(|tn| tn.url.clone());
+ let timestamp = video
+ .date()
+ .and_hms_opt(12, 0, 0)
+ .expect("Invalid hour, minute and/or second");
+ let updated_at = DateTime::from_utc(timestamp, Utc);
Item {
title: video.title().to_string(),
@@ -208,7 +213,7 @@ impl From for Item {
guid: id,
keywords: Default::default(),
image,
- updated_at: Utc::now(), // TODO: Get a decent timestamp somewhere?!
+ updated_at,
}
}
}
diff --git a/src/feed.rs b/src/feed.rs
index 20d46fc..a932c04 100644
--- a/src/feed.rs
+++ b/src/feed.rs
@@ -28,7 +28,9 @@ pub(crate) fn construct(backend_id: &str, config: &Config, channel: Channel) ->
.unwrap_or_default(),
)
.build();
- let mut last_build = DateTime::::from_utc(NaiveDateTime::from_timestamp(0, 0), Utc);
+ let unix_timestamp = NaiveDateTime::from_timestamp_opt(0, 0)
+ .expect("Out-of-range seconds or invalid nanoseconds");
+ let mut last_build = DateTime::from_utc(unix_timestamp, Utc);
let generator = String::from(concat!(
env!("CARGO_PKG_NAME"),
" ",
From a855c9839911bc0d61789466a92d58476e1158bc Mon Sep 17 00:00:00 2001
From: Paul van Tilburg
Date: Mon, 19 Dec 2022 21:54:59 +0100
Subject: [PATCH 7/9] Always apply limit after filtering successful streams
---
src/backends/youtube.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backends/youtube.rs b/src/backends/youtube.rs
index eb6cc39..c08912c 100644
--- a/src/backends/youtube.rs
+++ b/src/backends/youtube.rs
@@ -263,8 +263,8 @@ async fn fetch_channel_videos(
let yt_videos_w_streams = yt_channel
.uploads()
.await?
- .take(limit)
.filter_map(fetch_stream)
+ .take(limit)
.collect()
.await;
From cd831a5145630be66eaf483e29b7dbb6a8c3e11f Mon Sep 17 00:00:00 2001
From: Paul van Tilburg
Date: Tue, 20 Dec 2022 11:04:21 +0100
Subject: [PATCH 8/9] Update documentation
---
README.md | 54 ++++++++++++++++++++++++++++++++-------
templates/index.html.tera | 16 ++++++++----
2 files changed, 56 insertions(+), 14 deletions(-)
diff --git a/README.md b/README.md
index 6233f43..6e10fdf 100644
--- a/README.md
+++ b/README.md
@@ -26,8 +26,8 @@ builds when you don't add `--release`.)
### Configuration
For now, you will need to provide Rocket with configuration to tell it at which
-public URL Podbringer is hosted. This needs to be done even if you are not using a
-reverse proxy, in which case you need to provide it with the proxied URL. You
+public URL Podbringer is hosted. This needs to be done even if you are not using
+a reverse proxy, in which case you need to provide it with the proxied URL. You
can also use the configuration to configure a different address and/or port.
Just create a `Rocket.toml` file that contains (or copy `Rocket.toml.example`):
@@ -45,17 +45,17 @@ configuration, see: .
Podbringer currently has no front-end or web interface yet that can help you
use it. Until then, you just have to enter the right service-specific RSS feed
-URL in your favorite podcast client to start using it.
-
-Given the Mixcloud URL , the URL you
-need to use for Podbringer is comprised of the following parts:
+URL in your favorite podcast client to start using it. For example:
```text
https://my.domain.tld/podbringer/feed/mixcloud/myfavouriteband
- |------------------------------| |-------||--------------|
- The Podbringer public URL Service User @ service
+ |------------------------------| |------| |-------------|
+ The Podbringer public URL Service Service ID
```
+So, the URL consists of the location of Podbringer, the fact that you want the feed,
+the name of the service and the ID that identifies something list on that service.
+
### Feed item limit
To prevent feeds with a very large number of items, any feed that is returned
@@ -63,7 +63,43 @@ contains at most 50 items by default. If you want to have more (or less) items,
provide the limit in the URL by setting the `limit` parameter.
For example, to get up until 1000 items the URL becomes:
-`https://my.domain.tld/podbringer/feed/mixcloud/myfavouriteband?limit=1000`
+
+```text
+ https://my.domain.tld/podbringer/feed/mixcloud/myfavouriteband?limit=1000`
+```
+
+### Service: Mixcloud
+
+For Mixcloud, a feed can be constructed of everything that a user posted.
+Given the Mixcloud URL like , the
+`myfavouriteband` part of the URL is the Mixcloud username and can be used as
+the service ID.
+
+```text
+ https://my.domain.tld/podbringer/feed/mixcloud/myfavouriteband
+ |------------------------------| |------| |-------------|
+ The Podbringer public URL Service Username
+```
+
+### Service: YouTube
+
+For YouTube, a feed can either be constructed of a channel or a playlist.
+Given the YouTube channel URL like ,
+the `favouritechannel` part of the URL is the YouTube channel ID.
+Given the YouTube playlist URL
+, the
+`PLsomeplaylistidentifier` part of the URL is the YouTube playlist ID.
+Either the channel or playlist ID can be used as the service ID.
+
+```text
+ https://my.domain.tld/podbringer/feed/youtube/favouritechannel
+ |------------------------------| |-----| |--------------|
+ The Podbringer public URL Service Channel ID
+
+ https://my.domain.tld/podbringer/feed/youtube/PLsomeplaylistidentifier
+ |------------------------------| |-----| |----------------------|
+ The Podbringer public URL Service Playlist ID
+```
## License
diff --git a/templates/index.html.tera b/templates/index.html.tera
index c9a3af7..be7d078 100644
--- a/templates/index.html.tera
+++ b/templates/index.html.tera
@@ -5,15 +5,21 @@
URL in your favorite podcast client to start using it.
- Given the Mixcloud URL , the URL you
- need to use for Podbringer is comprised of the following parts:
+ The URL you need to use for Podbringer is comprised of the following parts:
https://my.domain.tld/podbringer/feed/mixcloud/myfavouriteband
- |------------------------------| |-------||--------------|
- The Podbringer public URL Service User @ service
+ |------------------------------| |------| |-------------|
+ The Podbringer public URL Service Service ID
- The Podbringer location URL of this instance is: {{ url }}
+ Supported services are:
+
+ - Mixcloud (service ID is Mixcloud username)
+ - YouTube (service ID is YouTube channel or playlist ID)
+
+
+
+ The Podbringer location URL of this instance is: {{ url }}.
From a6c9275d939036540eaa71022aa862ff67acc839 Mon Sep 17 00:00:00 2001
From: Paul van Tilburg
Date: Fri, 23 Dec 2022 21:58:30 +0100
Subject: [PATCH 9/9] Add more channel & item metadata
This includes categories (from hashtags), descriptions and keywords.
---
src/backends/youtube.rs | 38 +++++++++++++++++++++++++++++---------
1 file changed, 29 insertions(+), 9 deletions(-)
diff --git a/src/backends/youtube.rs b/src/backends/youtube.rs
index c08912c..35cc8fc 100644
--- a/src/backends/youtube.rs
+++ b/src/backends/youtube.rs
@@ -115,11 +115,14 @@ impl From for Channel {
YouTubeChannelWithVideos(yt_channel, yt_videos_w_streams): YouTubeChannelWithVideos,
) -> Self {
let mut link = Url::parse(CHANNEL_BASE_URL).expect("valid URL");
+ let title = format!("{0} (via YouTube)", yt_channel.name());
+ let description = yt_channel.description().to_string();
link.path_segments_mut()
.expect("valid URL")
.push(&yt_channel.id());
let author = Some(yt_channel.name().to_string());
- let categories = Vec::from([String::from("Video")]);
+ // FIXME: Don't hardcode the category!
+ let categories = Vec::from([String::from("Channel")]);
let image = yt_channel
.avatar()
.max_by_key(|av| av.width * av.height)
@@ -127,9 +130,9 @@ impl From for Channel {
let items = yt_videos_w_streams.into_iter().map(Item::from).collect();
Channel {
- title: format!("{0} (via YouTube)", yt_channel.name()),
+ title,
link,
- description: yt_channel.description().to_string(),
+ description,
author,
categories,
image,
@@ -142,12 +145,14 @@ impl From for Channel {
fn from(
YouTubePlaylistWithVideos(yt_playlist, yt_videos_w_streams): YouTubePlaylistWithVideos,
) -> Self {
+ let title = format!("{0} (via YouTube)", yt_playlist.title());
let mut link = Url::parse(PLAYLIST_BASE_URL).expect("valid URL");
+ let description = yt_playlist.description().to_string();
link.query_pairs_mut()
.append_pair("list", &yt_playlist.id().to_string());
let author = yt_playlist.channel().map(|chan| chan.name().to_string());
// FIXME: Don't hardcode the category!
- let categories = Vec::from([String::from("Video")]);
+ let categories = Vec::from([String::from("Playlist")]);
let image = yt_playlist
.thumbnails()
.iter()
@@ -156,9 +161,9 @@ impl From for Channel {
let items = yt_videos_w_streams.into_iter().map(Item::from).collect();
Channel {
- title: format!("{0} (via YouTube)", yt_playlist.title()),
+ title,
link,
- description: yt_playlist.description().to_string(),
+ description,
author,
categories,
image,
@@ -190,8 +195,23 @@ impl From for Item {
let mut link = Url::parse(VIDEO_BASE_URL).expect("valid URL");
link.query_pairs_mut().append_pair("v", &id);
- let description = Some(format!("Taken from YouTube: {0}", link));
+ let video_description = video.description();
+ let description = Some(format!("{video_description}\n\nTaken from YouTube: {link}"));
+ let categories = video
+ .hashtags()
+ .filter(|hashtag| !hashtag.trim().is_empty())
+ .map(|hashtag| {
+ let url = Url::parse(&format!(
+ "https://www.youtube.com/hashtag/{}",
+ hashtag.trim_start_matches('#')
+ ))
+ .expect("valid URL");
+
+ (hashtag.to_string(), url)
+ })
+ .collect();
let duration = Some(video.duration().as_secs() as u32);
+ let keywords = video.keywords().clone();
let image = video
.thumbnails()
.iter()
@@ -207,11 +227,11 @@ impl From for Item {
title: video.title().to_string(),
link,
description,
- categories: Default::default(),
+ categories,
enclosure,
duration,
guid: id,
- keywords: Default::default(),
+ keywords,
image,
updated_at,
}