about summary refs log tree commit diff stats
path: root/pkgs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pkgs/by-name/yt/yt/.cargo/config.toml3
-rw-r--r--pkgs/by-name/yt/yt/Cargo.lock256
-rw-r--r--pkgs/by-name/yt/yt/Cargo.toml19
-rw-r--r--pkgs/by-name/yt/yt/flake.lock6
-rw-r--r--pkgs/by-name/yt/yt/flake.nix9
-rw-r--r--pkgs/by-name/yt/yt/libmpv2/libmpv2-sys/build.rs9
-rw-r--r--pkgs/by-name/yt/yt/libmpv2/src/lib.rs83
-rw-r--r--pkgs/by-name/yt/yt/libmpv2/src/mpv/events.rs2
-rwxr-xr-xpkgs/by-name/yt/yt/scripts/mkdb.sh5
-rw-r--r--pkgs/by-name/yt/yt/src/cache/mod.rs6
-rw-r--r--pkgs/by-name/yt/yt/src/cli.rs84
-rw-r--r--pkgs/by-name/yt/yt/src/constants.rs42
-rw-r--r--pkgs/by-name/yt/yt/src/download/mod.rs17
-rw-r--r--pkgs/by-name/yt/yt/src/main.rs19
-rw-r--r--pkgs/by-name/yt/yt/src/select/cmds.rs50
-rw-r--r--pkgs/by-name/yt/yt/src/select/mod.rs58
-rw-r--r--pkgs/by-name/yt/yt/src/select/selection_file/display.rs4
-rw-r--r--pkgs/by-name/yt/yt/src/select/selection_file/duration.rs52
-rw-r--r--pkgs/by-name/yt/yt/src/select/selection_file/mod.rs82
-rw-r--r--pkgs/by-name/yt/yt/src/storage/video_database/downloader.rs52
-rw-r--r--pkgs/by-name/yt/yt/src/storage/video_database/extractor_hash.rs113
-rw-r--r--pkgs/by-name/yt/yt/src/storage/video_database/getters.rs96
-rw-r--r--pkgs/by-name/yt/yt/src/storage/video_database/schema.sql10
-rw-r--r--pkgs/by-name/yt/yt/src/storage/video_database/setters.rs76
-rw-r--r--pkgs/by-name/yt/yt/src/update/mod.rs60
-rw-r--r--pkgs/by-name/yt/yt/src/watch/events.rs160
-rw-r--r--pkgs/by-name/yt/yt/src/watch/mod.rs80
-rw-r--r--pkgs/by-name/yt/yt/yt_dlp/src/lib.rs6
-rw-r--r--pkgs/by-name/yt/yt/yt_dlp/src/wrapper/info_json.rs3
29 files changed, 1050 insertions, 412 deletions
diff --git a/pkgs/by-name/yt/yt/.cargo/config.toml b/pkgs/by-name/yt/yt/.cargo/config.toml
index 6c88c6ba..e5151a56 100644
--- a/pkgs/by-name/yt/yt/.cargo/config.toml
+++ b/pkgs/by-name/yt/yt/.cargo/config.toml
@@ -2,5 +2,4 @@
 PYO3_PYTHON = "/nix/store/7xzk119acyws2c4ysygdv66l0grxkr39-python3-3.11.9-env/bin/python3"
 
 [build]
-# rustflags = ["-C", "link-args=-fuse-ld=mold,--no-rosegment"]
-rustflags = ["-Clink-arg=-fuse-ld=mold", "-Clink-arg=-Wl,--no-rosegment"]
+rustflags = ["-Clink-arg=-fuse-ld=mold", "-Ctarget-cpu=native"]
diff --git a/pkgs/by-name/yt/yt/Cargo.lock b/pkgs/by-name/yt/yt/Cargo.lock
index 1342e29b..c949c313 100644
--- a/pkgs/by-name/yt/yt/Cargo.lock
+++ b/pkgs/by-name/yt/yt/Cargo.lock
@@ -27,7 +27,7 @@ dependencies = [
  "getrandom",
  "once_cell",
  "version_check",
- "zerocopy 0.7.35",
+ "zerocopy",
 ]
 
 [[package]]
@@ -123,9 +123,9 @@ checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a"
 
 [[package]]
 name = "arrayvec"
-version = "0.7.4"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
 
 [[package]]
 name = "atoi"
@@ -188,7 +188,7 @@ dependencies = [
  "regex",
  "rustc-hash",
  "shlex",
- "syn 2.0.72",
+ "syn 2.0.75",
  "which",
 ]
 
@@ -243,15 +243,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
-version = "1.7.0"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9"
+checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
 
 [[package]]
 name = "cc"
-version = "1.1.7"
+version = "1.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
+checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48"
+dependencies = [
+ "shlex",
+]
 
 [[package]]
 name = "cexpr"
@@ -304,9 +307,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.13"
+version = "4.5.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
+checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -314,9 +317,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.13"
+version = "4.5.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
+checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
 dependencies = [
  "anstream",
  "anstyle",
@@ -333,7 +336,7 @@ dependencies = [
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.75",
 ]
 
 [[package]]
@@ -362,15 +365,15 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
 
 [[package]]
 name = "core-foundation-sys"
-version = "0.8.6"
+version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.12"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
 dependencies = [
  "libc",
 ]
@@ -579,7 +582,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.75",
 ]
 
 [[package]]
@@ -686,6 +689,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
 
 [[package]]
+name = "hermit-abi"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
+
+[[package]]
 name = "hex"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -753,9 +762,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "2.3.0"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
+checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
 dependencies = [
  "equivalent",
  "hashbrown",
@@ -769,11 +778,11 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
 
 [[package]]
 name = "is-terminal"
-version = "0.4.12"
+version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
+checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
 dependencies = [
- "hermit-abi",
+ "hermit-abi 0.4.0",
  "libc",
  "windows-sys 0.52.0",
 ]
@@ -786,9 +795,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
 
 [[package]]
 name = "itertools"
-version = "0.10.5"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
 dependencies = [
  "either",
 ]
@@ -801,9 +810,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
 name = "js-sys"
-version = "0.3.69"
+version = "0.3.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -825,9 +834,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 
 [[package]]
 name = "libc"
-version = "0.2.155"
+version = "0.2.157"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+checksum = "374af5f94e54fa97cf75e945cce8a6b201e88a1a07e688b47dfd2a59c66dbd86"
 
 [[package]]
 name = "libloading"
@@ -936,11 +945,11 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
+checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
 dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.9",
  "libc",
  "wasi",
  "windows-sys 0.52.0",
@@ -1005,9 +1014,9 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.36.2"
+version = "0.36.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
+checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
 dependencies = [
  "memchr",
 ]
@@ -1063,6 +1072,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
+name = "pest"
+version = "2.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95"
+dependencies = [
+ "memchr",
+ "thiserror",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.75",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f"
+dependencies = [
+ "once_cell",
+ "pest",
+ "sha2",
+]
+
+[[package]]
 name = "pin-project-lite"
 version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1109,11 +1163,11 @@ checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.18"
+version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
 dependencies = [
- "zerocopy 0.6.6",
+ "zerocopy",
 ]
 
 [[package]]
@@ -1123,7 +1177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
 dependencies = [
  "proc-macro2",
- "syn 2.0.72",
+ "syn 2.0.75",
 ]
 
 [[package]]
@@ -1182,7 +1236,7 @@ dependencies = [
  "proc-macro2",
  "pyo3-macros-backend",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.75",
 ]
 
 [[package]]
@@ -1195,7 +1249,7 @@ dependencies = [
  "proc-macro2",
  "pyo3-build-config",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.75",
 ]
 
 [[package]]
@@ -1257,9 +1311,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.5"
+version = "1.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1343,29 +1397,29 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
 name = "serde"
-version = "1.0.204"
+version = "1.0.208"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
+checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.204"
+version = "1.0.208"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
+checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.75",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.121"
+version = "1.0.125"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609"
+checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
 dependencies = [
  "itoa",
  "memchr",
@@ -1716,9 +1770,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.72"
+version = "2.0.75"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
+checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1733,14 +1787,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
 
 [[package]]
 name = "tempfile"
-version = "3.10.1"
+version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
 dependencies = [
  "cfg-if",
  "fastrand",
+ "once_cell",
  "rustix",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1769,7 +1824,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.75",
 ]
 
 [[package]]
@@ -1799,9 +1854,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.39.2"
+version = "1.39.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
+checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5"
 dependencies = [
  "backtrace",
  "bytes",
@@ -1822,7 +1877,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.75",
 ]
 
 [[package]]
@@ -1856,7 +1911,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.75",
 ]
 
 [[package]]
@@ -1869,12 +1924,28 @@ dependencies = [
 ]
 
 [[package]]
+name = "trinitry"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f814008587cd653ef1f92f9caf321e86a6f53899ec118fd50eaed55974863a40"
+dependencies = [
+ "pest",
+ "pest_derive",
+]
+
+[[package]]
 name = "typenum"
 version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 
 [[package]]
+name = "ucd-trie"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
+
+[[package]]
 name = "unicode-bidi"
 version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1969,34 +2040,35 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
 dependencies = [
  "bumpalo",
  "log",
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.75",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -2004,22 +2076,22 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.75",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
 
 [[package]]
 name = "which"
@@ -2045,11 +2117,11 @@ dependencies = [
 
 [[package]]
 name = "winapi-util"
-version = "0.1.8"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
 dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -2080,6 +2152,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
 name = "windows-targets"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2225,6 +2306,7 @@ dependencies = [
  "stderrlog",
  "tempfile",
  "tokio",
+ "trinitry",
  "url",
  "xdg",
  "yt_dlp",
@@ -2243,32 +2325,12 @@ dependencies = [
 
 [[package]]
 name = "zerocopy"
-version = "0.6.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6"
-dependencies = [
- "byteorder",
- "zerocopy-derive 0.6.6",
-]
-
-[[package]]
-name = "zerocopy"
 version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
 dependencies = [
- "zerocopy-derive 0.7.35",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.6.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
+ "byteorder",
+ "zerocopy-derive",
 ]
 
 [[package]]
@@ -2279,7 +2341,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.75",
 ]
 
 [[package]]
diff --git a/pkgs/by-name/yt/yt/Cargo.toml b/pkgs/by-name/yt/yt/Cargo.toml
index d1e40cf0..78b9ab2c 100644
--- a/pkgs/by-name/yt/yt/Cargo.toml
+++ b/pkgs/by-name/yt/yt/Cargo.toml
@@ -10,20 +10,21 @@ anyhow = "1.0.86"
 blake3 = "1.5.3"
 chrono = { version = "0.4.38", features = ["now"] }
 chrono-humanize = "0.2.3"
-clap = { version = "4.5.13", features = ["derive"] }
+clap = { version = "4.5.16", features = ["derive"] }
 futures = "0.3.30"
 log = "0.4.22"
-regex = "1.10.5"
-serde = { version = "1.0.204", features = ["derive"] }
-serde_json = "1.0.121"
+regex = "1.10.6"
+serde = { version = "1.0.208", features = ["derive"] }
+serde_json = "1.0.125"
 sqlx = { version = "0.7.4", features = ["runtime-tokio", "sqlite"] }
 stderrlog = "0.6.0"
-tempfile = "3.10.1"
-tokio = { version = "1.39.2", features = ["rt-multi-thread", "macros", "process"] }
+tempfile = "3.12.0"
+tokio = { version = "1.39.3", features = ["rt-multi-thread", "macros", "process", "time"] }
 url = { version = "2.5.2", features = ["serde"] }
 xdg = "2.5.2"
 yt_dlp = { path = "./yt_dlp/" }
 libmpv2 = { path = "./libmpv2" }
+trinitry = { version = "0.2.2" }
 
 [[bin]]
 name = "yt"
@@ -31,3 +32,9 @@ name = "yt"
 [profile.profiling]
 inherits = "release"
 debug = true
+
+[profile.release]
+lto = true
+codegen-units = 1
+panic = "abort"
+split-debuginfo = "off"
diff --git a/pkgs/by-name/yt/yt/flake.lock b/pkgs/by-name/yt/yt/flake.lock
index cf99a35f..122b5b09 100644
--- a/pkgs/by-name/yt/yt/flake.lock
+++ b/pkgs/by-name/yt/yt/flake.lock
@@ -20,11 +20,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1722421184,
-        "narHash": "sha256-/DJBI6trCeVnasdjUo9pbnodCLZcFqnVZiLUfqLH4jA=",
+        "lastModified": 1723637854,
+        "narHash": "sha256-med8+5DSWa2UnOqtdICndjDAEjxr5D7zaIiK4pn0Q7c=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "9f918d616c5321ad374ae6cb5ea89c9e04bf3e58",
+        "rev": "c3aa7b8938b17aebd2deecf7be0636000d62a2b9",
         "type": "github"
       },
       "original": {
diff --git a/pkgs/by-name/yt/yt/flake.nix b/pkgs/by-name/yt/yt/flake.nix
index ea9b0de4..5ced0fbd 100644
--- a/pkgs/by-name/yt/yt/flake.nix
+++ b/pkgs/by-name/yt/yt/flake.nix
@@ -25,10 +25,15 @@
     ];
   in {
     devShells.default = pkgs.mkShell {
-      env = {
+      env = let
+        clang_version =
+          pkgs.lib.versions.major
+          pkgs.llvmPackages_latest.clang-unwrapped.version;
+      in {
         FFMPEG_LOCATION = "${pkgs.lib.getExe pkgs.ffmpeg}";
         LIBCLANG_PATH = "${pkgs.llvmPackages_latest.clang-unwrapped.lib}/lib/libclang.so";
-        C_INCLUDE_PATH = "${pkgs.llvmPackages_latest.clang-unwrapped.lib}/lib/clang/18/include";
+        LIBCLANG_INCLUDE_PATH = "${pkgs.llvmPackages_latest.clang-unwrapped.lib}/lib/clang/${clang_version}/include";
+        C_INCLUDE_PATH = "${pkgs.glibc.dev}/include";
       };
 
       inherit buildInputs nativeBuildInputs;
diff --git a/pkgs/by-name/yt/yt/libmpv2/libmpv2-sys/build.rs b/pkgs/by-name/yt/yt/libmpv2/libmpv2-sys/build.rs
index 5361e198..1380b58d 100644
--- a/pkgs/by-name/yt/yt/libmpv2/libmpv2-sys/build.rs
+++ b/pkgs/by-name/yt/yt/libmpv2/libmpv2-sys/build.rs
@@ -12,7 +12,14 @@ fn main() {
         .opaque_type("mpv_handle")
         .opaque_type("mpv_render_context")
         .enable_function_attribute_detection()
-        .clang_arg("-fretain-comments-from-system-headers")
+        .clang_args(&[
+            "-fretain-comments-from-system-headers",
+            &format!(
+                "--include-directory={}",
+                env::var("LIBCLANG_INCLUDE_PATH").unwrap()
+            ),
+            "--verbose",
+        ])
         .generate_comments(true)
         .generate()
         .expect("Unable to generate bindings");
diff --git a/pkgs/by-name/yt/yt/libmpv2/src/lib.rs b/pkgs/by-name/yt/yt/libmpv2/src/lib.rs
index 4b267e24..199d6ee9 100644
--- a/pkgs/by-name/yt/yt/libmpv2/src/lib.rs
+++ b/pkgs/by-name/yt/yt/libmpv2/src/lib.rs
@@ -19,6 +19,7 @@
 
 #![allow(non_upper_case_globals)]
 
+use std::fmt::Display;
 use std::os::raw as ctype;
 
 pub const MPV_CLIENT_API_MAJOR: ctype::c_ulong = 2;
@@ -86,11 +87,79 @@ pub mod mpv_log_level {
 }
 
 /// The reason a file stopped.
-pub use libmpv2_sys::mpv_end_file_reason as EndFileReason;
-pub mod mpv_end_file_reason {
-    pub use libmpv2_sys::mpv_end_file_reason_MPV_END_FILE_REASON_EOF as Eof;
-    pub use libmpv2_sys::mpv_end_file_reason_MPV_END_FILE_REASON_ERROR as Error;
-    pub use libmpv2_sys::mpv_end_file_reason_MPV_END_FILE_REASON_QUIT as Quit;
-    pub use libmpv2_sys::mpv_end_file_reason_MPV_END_FILE_REASON_REDIRECT as Redirect;
-    pub use libmpv2_sys::mpv_end_file_reason_MPV_END_FILE_REASON_STOP as Stop;
+#[derive(Debug, Clone, Copy)]
+pub enum EndFileReason {
+    /**
+     * The end of file was reached. Sometimes this may also happen on
+     * incomplete or corrupted files, or if the network connection was
+     * interrupted when playing a remote file. It also happens if the
+     * playback range was restricted with --end or --frames or similar.
+     */
+    Eof,
+
+    /**
+     * Playback was stopped by an external action (e.g. playlist controls).
+     */
+    Stop,
+
+    /**
+     * Playback was stopped by the quit command or player shutdown.
+     */
+    Quit,
+
+    /**
+     * Some kind of error happened that lead to playback abort. Does not
+     * necessarily happen on incomplete or broken files (in these cases, both
+     * MPV_END_FILE_REASON_ERROR or MPV_END_FILE_REASON_EOF are possible).
+     *
+     * mpv_event_end_file.error will be set.
+     */
+    Error,
+
+    /**
+     * The file was a playlist or similar. When the playlist is read, its
+     * entries will be appended to the playlist after the entry of the current
+     * file, the entry of the current file is removed, and a MPV_EVENT_END_FILE
+     * event is sent with reason set to MPV_END_FILE_REASON_REDIRECT. Then
+     * playback continues with the playlist contents.
+     * Since API version 1.18.
+     */
+    Redirect,
+}
+
+impl From<libmpv2_sys::mpv_end_file_reason> for EndFileReason {
+    fn from(value: libmpv2_sys::mpv_end_file_reason) -> Self {
+        match value {
+            0 => Self::Eof,
+            2 => Self::Stop,
+            3 => Self::Quit,
+            4 => Self::Error,
+            5 => Self::Redirect,
+            _ => unreachable!("Other enum variants do not exist yet"),
+        }
+    }
+}
+
+impl Display for EndFileReason {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            EndFileReason::Eof => f.write_str("The end of file was reached.")?,
+            EndFileReason::Error => {
+                f.write_str(
+                    "Playback was stopped by an external action (e.g. playlist controls).",
+                )?;
+            }
+            EndFileReason::Quit => {
+                f.write_str("Playback was stopped by the quit command or player shutdown.")?;
+            }
+            EndFileReason::Redirect => {
+                f.write_str("Some kind of error happened that lead to playback abort.")?;
+            }
+            EndFileReason::Stop => {
+                f.write_str("The file was a playlist or similar.")?;
+            }
+        }
+
+        Ok(())
+    }
 }
diff --git a/pkgs/by-name/yt/yt/libmpv2/src/mpv/events.rs b/pkgs/by-name/yt/yt/libmpv2/src/mpv/events.rs
index b6513890..115c23fc 100644
--- a/pkgs/by-name/yt/yt/libmpv2/src/mpv/events.rs
+++ b/pkgs/by-name/yt/yt/libmpv2/src/mpv/events.rs
@@ -270,7 +270,7 @@ impl EventContext {
                 if let Err(e) = mpv_err((), end_file.error) {
                     Some(Err(e))
                 } else {
-                    Some(Ok(Event::EndFile(end_file.reason as _)))
+                    Some(Ok(Event::EndFile(end_file.reason.into())))
                 }
             }
             mpv_event_id::FileLoaded => Some(Ok(Event::FileLoaded)),
diff --git a/pkgs/by-name/yt/yt/scripts/mkdb.sh b/pkgs/by-name/yt/yt/scripts/mkdb.sh
index 307e4459..f4246a49 100755
--- a/pkgs/by-name/yt/yt/scripts/mkdb.sh
+++ b/pkgs/by-name/yt/yt/scripts/mkdb.sh
@@ -1,9 +1,10 @@
 #!/usr/bin/env sh
 
 root="$(dirname "$0")/.."
+db="$root/target/database.sqlite"
 
-rm "$root/target/database.sqlite"
+[ -f "$db" ] && rm "$db"
 
-sqlite3 "$root/target/database.sqlite" <"$root/src/storage/video_database/schema.sql"
+sqlite3 "$db" <"$root/src/storage/video_database/schema.sql"
 
 # vim: ft=sh
diff --git a/pkgs/by-name/yt/yt/src/cache/mod.rs b/pkgs/by-name/yt/yt/src/cache/mod.rs
index b60cda89..f9715e40 100644
--- a/pkgs/by-name/yt/yt/src/cache/mod.rs
+++ b/pkgs/by-name/yt/yt/src/cache/mod.rs
@@ -6,16 +6,14 @@ use log::info;
 use crate::{
     app::App,
     storage::video_database::{
-        downloader::set_video_cache_path, getters::get_videos, setters::set_video_status, Video,
-        VideoStatus,
+        downloader::set_video_cache_path, getters::get_videos, Video, VideoStatus,
     },
 };
 
 async fn invalidate_video(app: &App, video: &Video) -> Result<()> {
     info!("Invalidating cache of video: '{}'", video.title);
 
-    set_video_status(app, &video.extractor_hash, VideoStatus::Watch, None).await?;
-    set_video_cache_path(app, &video, None).await?;
+    set_video_cache_path(app, &video.extractor_hash, None).await?;
 
     Ok(())
 }
diff --git a/pkgs/by-name/yt/yt/src/cli.rs b/pkgs/by-name/yt/yt/src/cli.rs
index 66b1d4c1..799c9ee4 100644
--- a/pkgs/by-name/yt/yt/src/cli.rs
+++ b/pkgs/by-name/yt/yt/src/cli.rs
@@ -1,12 +1,18 @@
 use std::path::PathBuf;
 
-use clap::{ArgAction, Parser, Subcommand};
+use chrono::NaiveDate;
+use clap::{ArgAction, Args, Parser, Subcommand};
 use url::Url;
 
+use crate::{
+    select::selection_file::duration::Duration,
+    storage::video_database::extractor_hash::LazyExtractorHash,
+};
+
 #[derive(Parser, Debug)]
 #[clap(author, version, about, long_about = None)]
 /// An command line interface to select, download and watch videos
-pub struct Args {
+pub struct CliArgs {
     #[command(subcommand)]
     /// The subcommand to execute [default: select]
     pub command: Option<Command>,
@@ -51,8 +57,11 @@ pub enum Command {
         command: CacheCommand,
     },
 
-    /// Open a `git rebase` like file to select the videos to watch
-    Select,
+    /// Change the state of videos in the database (the default)
+    Select {
+        #[command(subcommand)]
+        cmd: Option<SelectCommand>,
+    },
 
     /// Subscribe to an URL
     Subscribe {
@@ -73,12 +82,16 @@ pub enum Command {
     /// Update the video database
     Update {
         #[arg(short, long, default_value = "20")]
-        /// The number of videos to stop updating
+        /// The number of videos to updating
         max_backlog: u32,
 
         #[arg(short, long)]
         /// The subscriptions to update (can be given multiple times)
         subscriptions: Vec<String>,
+
+        #[arg(short, long, default_value = "6")]
+        /// How many processes to spawn at the same time
+        concurrent_processes: usize,
     },
 
     /// Update one subscription (this is here to parallelize the normal `update`)
@@ -95,6 +108,67 @@ pub enum Command {
     Subscriptions {},
 }
 
+impl Default for Command {
+    fn default() -> Self {
+        Self::Select {
+            cmd: Some(SelectCommand::default()),
+        }
+    }
+}
+
+#[derive(Clone, Debug, Args)]
+#[command(infer_subcommands = true)]
+/// Mark the video given by the hash to be watched
+pub struct SharedSelectionCommandArgs {
+    /// The short extractor hash
+    pub hash: LazyExtractorHash,
+
+    pub title: String,
+
+    pub date: NaiveDate,
+
+    pub publisher: String,
+
+    pub duration: Duration,
+
+    pub url: Url,
+}
+
+#[derive(Subcommand, Clone, Debug, Default)]
+#[command(infer_subcommands = true)]
+pub enum SelectCommand {
+    #[default]
+    /// Open a `git rebase` like file to select the videos to watch (the default)
+    File,
+
+    Watch {
+        #[command(flatten)]
+        shared: SharedSelectionCommandArgs,
+
+        #[arg(short, long, default_value = "0")]
+        /// The ordering priority (higher means more at the top)
+        priority: i64,
+    },
+
+    /// Mark the video given by the hash to be dropped
+    Drop {
+        #[command(flatten)]
+        shared: SharedSelectionCommandArgs,
+    },
+
+    /// Open the video URL in Firefox's `timesinks.youtube` profile
+    Url {
+        #[command(flatten)]
+        shared: SharedSelectionCommandArgs,
+    },
+
+    /// Reset the videos status to 'Pick'
+    Pick {
+        #[command(flatten)]
+        shared: SharedSelectionCommandArgs,
+    },
+}
+
 #[derive(Subcommand, Clone, Debug)]
 pub enum CheckCommand {
     /// Check if the given info.json is deserializable
diff --git a/pkgs/by-name/yt/yt/src/constants.rs b/pkgs/by-name/yt/yt/src/constants.rs
index 17e0f018..fbe51413 100644
--- a/pkgs/by-name/yt/yt/src/constants.rs
+++ b/pkgs/by-name/yt/yt/src/constants.rs
@@ -3,32 +3,9 @@ use std::{env::temp_dir, path::PathBuf};
 use anyhow::Context;
 
 pub const HELP_STR: &str = include_str!("./select/selection_file/help.str");
+pub const LOCAL_COMMENTS_LENGTH: usize = 1000;
 
-pub const YT_DLP_FLAGS: [&str; 13] = [
-    // Ignore errors arising of unavailable sponsor block API
-    "--ignore-errors",
-    "--format",
-    "bestvideo[height<=?1080]+bestaudio/best",
-    "--embed-chapters",
-    "--progress",
-    "--write-comments",
-    "--extractor-args",
-    "youtube:max_comments=150,all,100;comment_sort=top",
-    "--write-info-json",
-    "--sponsorblock-mark",
-    "default",
-    "--sponsorblock-remove",
-    "sponsor",
-];
-pub const MPV_FLAGS: [&str; 4] = [
-    "--speed=2.7",
-    "--volume=75",
-    "--keep-open=yes",
-    "--msg-level=osd/libass=fatal",
-];
-
-pub const CONCURRENT: u32 = 5;
-
+pub const CONCURRENT_DOWNLOADS: u32 = 5;
 // We download to the temp dir to avoid taxing the disk
 pub fn download_dir() -> PathBuf {
     const DOWNLOAD_DIR: &str = "/tmp/yt";
@@ -48,6 +25,21 @@ fn get_data_path(name: &'static str) -> anyhow::Result<PathBuf> {
         .place_data_file(name)
         .with_context(|| format!("Failed to place data file: '{}'", name))
 }
+fn get_config_path(name: &'static str) -> anyhow::Result<PathBuf> {
+    let xdg_dirs = xdg::BaseDirectories::with_prefix(PREFIX)?;
+    xdg_dirs
+        .place_config_file(name)
+        .with_context(|| format!("Failed to place config file: '{}'", name))
+}
+
+pub fn mpv_config_path() -> anyhow::Result<PathBuf> {
+    const MPV_CONFIG_PATH: &str = "mpv.conf";
+    get_config_path(MPV_CONFIG_PATH)
+}
+pub fn mpv_input_path() -> anyhow::Result<PathBuf> {
+    const MPV_INPUT_CONFIG_PATH: &str = "mpv.input.conf";
+    get_config_path(MPV_INPUT_CONFIG_PATH)
+}
 
 pub fn status_path() -> anyhow::Result<PathBuf> {
     const STATUS_PATH: &str = "running.info.json";
diff --git a/pkgs/by-name/yt/yt/src/download/mod.rs b/pkgs/by-name/yt/yt/src/download/mod.rs
index 2dc7c431..1499171f 100644
--- a/pkgs/by-name/yt/yt/src/download/mod.rs
+++ b/pkgs/by-name/yt/yt/src/download/mod.rs
@@ -6,8 +6,7 @@ use crate::{
     storage::video_database::{
         downloader::{get_next_uncached_video, set_video_cache_path},
         extractor_hash::ExtractorHash,
-        setters::set_video_status,
-        Video, VideoStatus,
+        Video,
     },
 };
 
@@ -64,10 +63,15 @@ impl Downloader {
             if let Some(_) = &self.current_download {
                 let current_download = self.current_download.take().expect("Is Some");
 
+                if current_download.task_handle.is_finished() {
+                    current_download.task_handle.await??;
+                    continue;
+                }
+
                 if next_video.extractor_hash != current_download.extractor_hash {
                     info!(
                     "Noticed, that the next video is not the video being downloaded, replacing it ('{}' vs. '{}')!",
-                        next_video.extractor_hash.format(app).await?, current_download.extractor_hash.format(app).await?
+                        next_video.extractor_hash.into_short_hash(app).await?, current_download.extractor_hash.into_short_hash(app).await?
                     );
 
                     // Replace the currently downloading video
@@ -79,7 +83,7 @@ impl Downloader {
                 } else {
                     debug!(
                         "Currently downloading '{}'",
-                        current_download.extractor_hash.format(app).await?
+                        current_download.extractor_hash.into_short_hash(app).await?
                     );
                     // Reset the taken value
                     self.current_download = Some(current_download);
@@ -130,6 +134,7 @@ impl Downloader {
     // }
 
     async fn actually_cache_video(app: &App, video: &Video) -> Result<()> {
+        debug!("Download started: {}", &video.title);
         let result = yt_dlp::download(&[video.url.clone()], &download_opts())
             .await
             .with_context(|| format!("Failed to download video: '{}'", video.title))?;
@@ -137,9 +142,7 @@ impl Downloader {
         assert_eq!(result.len(), 1);
         let result = &result[0];
 
-        set_video_cache_path(app, video, Some(&result)).await?;
-
-        set_video_status(app, &(video.extractor_hash), VideoStatus::Cached, None).await?;
+        set_video_cache_path(app, &video.extractor_hash, Some(&result)).await?;
 
         info!(
             "Video '{}' was downlaoded to path: {}",
diff --git a/pkgs/by-name/yt/yt/src/main.rs b/pkgs/by-name/yt/yt/src/main.rs
index b89c2eec..92fd1a8d 100644
--- a/pkgs/by-name/yt/yt/src/main.rs
+++ b/pkgs/by-name/yt/yt/src/main.rs
@@ -3,8 +3,9 @@ use std::fs;
 use anyhow::{bail, Context, Result};
 use app::App;
 use clap::Parser;
-use cli::{CacheCommand, CheckCommand};
+use cli::{CacheCommand, CheckCommand, SelectCommand};
 use log::info;
+use select::cmds::handle_select_cmd;
 use yt_dlp::wrapper::info_json::InfoJson;
 
 use crate::{cli::Command, storage::subscriptions::get_subscriptions};
@@ -25,7 +26,7 @@ pub mod watch;
 
 #[tokio::main]
 async fn main() -> Result<()> {
-    let args = cli::Args::parse();
+    let args = cli::CliArgs::parse();
     stderrlog::new()
         .module(module_path!())
         .modules(&["yt_dlp".to_owned(), "libmpv2".to_owned()])
@@ -39,7 +40,7 @@ async fn main() -> Result<()> {
 
     let app = App::new().await?;
 
-    match args.command.unwrap_or(Command::Select) {
+    match args.command.unwrap_or(Command::default()) {
         Command::Download { urls } => {
             if urls.is_empty() {
                 info!("Downloading urls from database");
@@ -51,8 +52,13 @@ async fn main() -> Result<()> {
                 todo!()
             }
         }
-        Command::Select => {
-            select::select(&app).await?;
+        Command::Select { cmd } => {
+            let cmd = cmd.unwrap_or(SelectCommand::default());
+
+            match cmd {
+                SelectCommand::File => select::select(&app).await?,
+                _ => handle_select_cmd(&app, cmd).await?,
+            }
         }
         Command::Subscribe { name, url } => {
             subscribe::subscribe(name, url)
@@ -67,6 +73,7 @@ async fn main() -> Result<()> {
         Command::Update {
             max_backlog,
             subscriptions,
+            concurrent_processes,
         } => {
             let all_subs = get_subscriptions()?;
 
@@ -79,7 +86,7 @@ async fn main() -> Result<()> {
                 }
             }
 
-            update::update(max_backlog, subscriptions).await?;
+            update::update(max_backlog, subscriptions, concurrent_processes).await?;
         }
         Command::UpdateOnce {
             sub_name,
diff --git a/pkgs/by-name/yt/yt/src/select/cmds.rs b/pkgs/by-name/yt/yt/src/select/cmds.rs
new file mode 100644
index 00000000..a2a440f8
--- /dev/null
+++ b/pkgs/by-name/yt/yt/src/select/cmds.rs
@@ -0,0 +1,50 @@
+use crate::{
+    app::App,
+    cli::SelectCommand,
+    storage::video_database::{getters::get_video_by_hash, setters::set_video_status, VideoStatus},
+};
+
+use anyhow::{Context, Result};
+
+pub async fn handle_select_cmd(app: &App, cmd: SelectCommand) -> Result<()> {
+    match cmd {
+        SelectCommand::Pick { shared } => {
+            set_video_status(
+                app,
+                &shared.hash.realize(app).await?,
+                VideoStatus::Pick,
+                None,
+            )
+            .await?
+        }
+        SelectCommand::Drop { shared } => {
+            set_video_status(
+                app,
+                &shared.hash.realize(app).await?,
+                VideoStatus::Drop,
+                None,
+            )
+            .await?
+        }
+        SelectCommand::Watch { shared, priority } => {
+            let hash = shared.hash.realize(&app).await?;
+            let video = get_video_by_hash(app, &hash).await?;
+
+            if let Some(_) = video.cache_path {
+                // Do nothing, as the video *should* already have a `Cached` status and a
+                // cache_path.
+            } else {
+                set_video_status(app, &hash, VideoStatus::Watch, Some(priority)).await?;
+            }
+        }
+
+        SelectCommand::Url { shared } => {
+            let mut firefox = std::process::Command::new("firefox");
+            firefox.args(["-P", "timesinks.youtube"]);
+            firefox.arg(shared.url.as_str());
+            let _handle = firefox.spawn().context("Failed to run firefox")?;
+        }
+        SelectCommand::File => unreachable!("This should have been filtered out"),
+    }
+    Ok(())
+}
diff --git a/pkgs/by-name/yt/yt/src/select/mod.rs b/pkgs/by-name/yt/yt/src/select/mod.rs
index abb389f0..34fcf56a 100644
--- a/pkgs/by-name/yt/yt/src/select/mod.rs
+++ b/pkgs/by-name/yt/yt/src/select/mod.rs
@@ -6,16 +6,20 @@ use std::{
 
 use crate::{
     app::App,
+    cli::CliArgs,
     constants::{last_select, HELP_STR},
-    storage::video_database::{getters::get_videos, setters::set_video_status, VideoStatus},
+    storage::video_database::{getters::get_videos, VideoStatus},
 };
 
 use anyhow::{bail, Context, Result};
+use clap::Parser;
+use cmds::handle_select_cmd;
 use futures::future::join_all;
+use log::debug;
+use selection_file::process_line;
 use tempfile::Builder;
 
-use self::selection_file::{filter_line, LineCommand};
-
+pub mod cmds;
 pub mod selection_file;
 
 pub async fn select(app: &App) -> Result<()> {
@@ -89,32 +93,32 @@ pub async fn select(app: &App) -> Result<()> {
 
     let reader = BufReader::new(&read_file);
 
-    let mut priority = 1;
     for line in reader.lines() {
         let line = line.context("Failed to read a line")?;
-
-        if let Some(line) = filter_line(app, &line)
-            .await
-            .with_context(|| format!("Failed to process line: '{}'", line))?
-        {
-            match line.cmd {
-                LineCommand::Pick => {
-                    set_video_status(app, &line.hash, VideoStatus::Pick, None).await?
-                }
-                LineCommand::Drop => {
-                    set_video_status(app, &line.hash, VideoStatus::Drop, None).await?
-                }
-                LineCommand::Watch => {
-                    set_video_status(app, &line.hash, VideoStatus::Watch, Some(priority)).await?;
-                    priority += 1;
-                }
-                LineCommand::Url => {
-                    let mut firefox = std::process::Command::new("firefox");
-                    firefox.args(["-P", "timesinks.youtube"]);
-                    firefox.arg(line.url.as_str());
-                    let _handle = firefox.spawn().context("Failed to run firefox")?;
-                }
-            }
+        if let Some(line) = process_line(&line)? {
+            debug!(
+                "Parsed command: `{}`",
+                line.iter()
+                    .map(|val| format!("\"{}\"", val))
+                    .collect::<Vec<String>>()
+                    .join(" ")
+            );
+
+            let arg_line = ["yt", "select"]
+                .into_iter()
+                .chain(line.iter().map(|val| val.as_str()));
+
+            let args = CliArgs::parse_from(arg_line);
+
+            let cmd = if let crate::cli::Command::Select { cmd } =
+                args.command.expect("This will be some")
+            {
+                cmd
+            } else {
+                unreachable!("This is checked in the `filter_line` function")
+            };
+
+            handle_select_cmd(&app, cmd.expect("This value should always be some here")).await?
         }
     }
 
diff --git a/pkgs/by-name/yt/yt/src/select/selection_file/display.rs b/pkgs/by-name/yt/yt/src/select/selection_file/display.rs
index 8a58ffdd..3649b2b8 100644
--- a/pkgs/by-name/yt/yt/src/select/selection_file/display.rs
+++ b/pkgs/by-name/yt/yt/src/select/selection_file/display.rs
@@ -36,12 +36,12 @@ impl Video {
             f,
             r#"{} {} "{}" "{}" "{}" "{}" "{}"{}"#,
             self.status.as_command(),
-            self.extractor_hash.format(app).await?,
+            self.extractor_hash.into_short_hash(app).await?,
             self.title.replace(['"', '„', '”'], "'"),
             publish_date,
             parent_subscription_name,
             Duration::from(self.duration),
-            self.url.as_str().replace('"', "'"),
+            self.url.as_str().replace('"', "\\\""),
             "\n"
         )?;
 
diff --git a/pkgs/by-name/yt/yt/src/select/selection_file/duration.rs b/pkgs/by-name/yt/yt/src/select/selection_file/duration.rs
index 700d9202..9e4b3a83 100644
--- a/pkgs/by-name/yt/yt/src/select/selection_file/duration.rs
+++ b/pkgs/by-name/yt/yt/src/select/selection_file/duration.rs
@@ -1,14 +1,46 @@
+use std::str::FromStr;
+
+use anyhow::{Context, Result};
+
+#[derive(Copy, Clone, Debug)]
 pub struct Duration {
     time: u32,
 }
 
-impl From<&str> for Duration {
-    fn from(v: &str) -> Self {
-        let buf: Vec<_> = v.split(':').take(2).collect();
-        Self {
-            time: (buf[0].parse::<u32>().expect("Should be a number") * 60)
-                + buf[1].parse::<u32>().expect("Should be a number"),
+impl FromStr for Duration {
+    type Err = anyhow::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        fn parse_num(str: &str, suffix: char) -> Result<u32> {
+            str.strip_suffix(suffix)
+                .expect("it has a 'h' suffix")
+                .parse::<u32>()
+                .context("Failed to parse hours")
         }
+
+        let buf: Vec<_> = s.split(' ').collect();
+
+        let hours;
+        let minutes;
+        let seconds;
+
+        assert_eq!(buf.len(), 2, "Other lengths should not happen");
+
+        if buf[0].ends_with('h') {
+            hours = parse_num(buf[0], 'h')?;
+            minutes = parse_num(buf[1], 'm')?;
+            seconds = 0;
+        } else if buf[0].ends_with('m') {
+            hours = 0;
+            minutes = parse_num(buf[0], 'm')?;
+            seconds = parse_num(buf[1], 's')?;
+        } else {
+            unreachable!("The first part always ends with 'h' or 'm'")
+        }
+
+        Ok(Self {
+            time: (hours * 60 * 60) + (minutes * 60) + seconds,
+        })
     }
 }
 
@@ -37,9 +69,9 @@ impl std::fmt::Display for Duration {
         if self.time == 0 {
             write!(f, "[No Duration]")
         } else if h > 0 {
-            write!(f, "[{h}h {m}m]")
+            write!(f, "{h}h {m}m")
         } else {
-            write!(f, "[{m}m {s}s]")
+            write!(f, "{m}m {s}s")
         }
     }
 }
@@ -50,11 +82,11 @@ mod test {
     #[test]
     fn test_display_duration_1h() {
         let dur = Duration { time: 60 * 60 };
-        assert_eq!("[1h 0m]".to_owned(), dur.to_string());
+        assert_eq!("1h 0m".to_owned(), dur.to_string());
     }
     #[test]
     fn test_display_duration_30min() {
         let dur = Duration { time: 60 * 30 };
-        assert_eq!("[30m 0s]".to_owned(), dur.to_string());
+        assert_eq!("30m 0s".to_owned(), dur.to_string());
     }
 }
diff --git a/pkgs/by-name/yt/yt/src/select/selection_file/mod.rs b/pkgs/by-name/yt/yt/src/select/selection_file/mod.rs
index 957fcd08..c63ca85a 100644
--- a/pkgs/by-name/yt/yt/src/select/selection_file/mod.rs
+++ b/pkgs/by-name/yt/yt/src/select/selection_file/mod.rs
@@ -1,79 +1,25 @@
 //! The data structures needed to express the file, which the user edits
 
-use anyhow::{bail, Context};
-use url::Url;
-
-use crate::{app::App, storage::video_database::extractor_hash::ExtractorHash};
+use anyhow::{Context, Result};
+use trinitry::Trinitry;
 
 pub mod display;
 pub mod duration;
 
-pub enum LineCommand {
-    Pick,
-    Drop,
-    Watch,
-    Url,
-}
-
-impl std::str::FromStr for LineCommand {
-    type Err = anyhow::Error;
-    fn from_str(v: &str) -> Result<Self, <Self as std::str::FromStr>::Err> {
-        match v {
-            "pick" | "p" => Ok(Self::Pick),
-            "drop" | "d" => Ok(Self::Drop),
-            "watch" | "w" => Ok(Self::Watch),
-            "url" | "u" => Ok(Self::Url),
-            other => bail!("'{}' is not a recognized command!", other),
-        }
-    }
-}
-
-pub struct Line {
-    pub cmd: LineCommand,
-    pub hash: ExtractorHash,
-    pub url: Url,
-}
-
-impl Line {
-    pub async fn from_str(app: &App, s: &str) -> anyhow::Result<Self> {
-        let buf: Vec<_> = s.split_whitespace().collect();
-
-        let url_as_str = buf
-            .last()
-            .with_context(|| format!("The line '{}' misses it's url field!'", s))?
-            .trim_matches('"');
+pub fn process_line(line: &str) -> Result<Option<Vec<String>>> {
+    // Filter out comments and empty lines
+    if line.starts_with('#') || line.trim().is_empty() {
+        Ok(None)
+    } else {
+        // pick 2195db "CouchRecherche? Gunnar und Han von STRG_F sind #mitfunkzuhause" "2020-04-01" "STRG_F - Live" "[1h 5m]" "https://www.youtube.com/watch?v=C8UXOaoMrXY"
 
-        let url: Url = Url::parse(url_as_str)
-            .with_context(|| format!("The url '{}' could not be parsed!", url_as_str))?;
+        let tri =
+            Trinitry::new(line).with_context(|| format!("Failed to parse line '{}'", line))?;
 
-        Ok(Line {
-            cmd: buf
-                .get(0)
-                .with_context(|| format!("The line '{}' is missing it's command!", s))?
-                .parse()?,
-            hash: ExtractorHash::parse_from_short_version(
-                app,
-                buf.get(1)
-                    .with_context(|| format!("The line '{}' is missing it's blake3 hash!", s))?,
-            )
-            .await
-            .with_context(|| {
-                format!(
-                    "Can't parse '{}' as blake3 hash!",
-                    buf.get(1).expect("Already checked"),
-                )
-            })?,
-            url,
-        })
-    }
-}
+        let mut vec = Vec::with_capacity(tri.arguments().len() + 1);
+        vec.push(tri.command().to_owned());
+        vec.extend(tri.arguments().to_vec().into_iter());
 
-pub async fn filter_line(app: &App, line: &str) -> anyhow::Result<Option<Line>> {
-    // Filter out comments and empty lines
-    if line.starts_with('#') || line.trim().is_empty() {
-        return Ok(None);
+        Ok(Some(vec))
     }
-
-    let line: Line = Line::from_str(app, line).await?;
-    Ok(Some(line))
 }
diff --git a/pkgs/by-name/yt/yt/src/storage/video_database/downloader.rs b/pkgs/by-name/yt/yt/src/storage/video_database/downloader.rs
index 5c472ac5..ca3a2ea3 100644
--- a/pkgs/by-name/yt/yt/src/storage/video_database/downloader.rs
+++ b/pkgs/by-name/yt/yt/src/storage/video_database/downloader.rs
@@ -12,14 +12,17 @@ use super::{ExtractorHash, Video};
 /// Returns to next video which should be downloaded. This respects the priority assigned by select.
 /// It does not return videos, which are already cached.
 pub async fn get_next_uncached_video(app: &App) -> Result<Option<Video>> {
+    let status = VideoStatus::Watch.as_db_integer();
+
     let result = query!(
         r#"
         SELECT *
         FROM videos
-        WHERE status = 'Watching' AND cache_path IS NULL
+        WHERE status = ? AND cache_path IS NULL
         ORDER BY priority ASC
         LIMIT 1;
-    "#
+    "#,
+        status
     )
     .fetch_one(&app.database)
     .await;
@@ -46,7 +49,11 @@ pub async fn get_next_uncached_video(app: &App) -> Result<Option<Video>> {
             cache_path: base.cache_path.as_ref().map(|val| PathBuf::from(val)),
             description: base.description.clone(),
             duration: base.duration,
-            extractor_hash: ExtractorHash::new(base.extractor_hash.parse()?),
+            extractor_hash: ExtractorHash::from_hash(
+                base.extractor_hash
+                    .parse()
+                    .expect("The hash in the db should be valid"),
+            ),
             last_status_change: base.last_status_change,
             parent_subscription_name: base.parent_subscription_name.clone(),
             priority: base.priority,
@@ -99,7 +106,11 @@ pub async fn get_next_video_watchable(app: &App) -> Result<Option<Video>> {
             cache_path: base.cache_path.as_ref().map(|val| PathBuf::from(val)),
             description: base.description.clone(),
             duration: base.duration,
-            extractor_hash: ExtractorHash::new(base.extractor_hash.parse()?),
+            extractor_hash: ExtractorHash::from_hash(
+                base.extractor_hash
+                    .parse()
+                    .expect("The db extractor_hash should be valid blake3 hash"),
+            ),
             last_status_change: base.last_status_change,
             parent_subscription_name: base.parent_subscription_name.clone(),
             priority: base.priority,
@@ -116,30 +127,32 @@ pub async fn get_next_video_watchable(app: &App) -> Result<Option<Video>> {
 }
 
 /// Update the cached path of a video. Will be set to NULL if the path is None
-pub async fn set_video_cache_path(app: &App, video: &Video, path: Option<&Path>) -> Result<()> {
-    debug!(
-        "Setting cache path from '{}' to '{:#?}'",
-        video.title,
-        path.unwrap_or(&PathBuf::from("None")).display()
-    );
-
+/// This will also set the status to `Cached` when path is Some, otherwise it set's the status to
+/// `Watch`.
+pub async fn set_video_cache_path(
+    app: &App,
+    video: &ExtractorHash,
+    path: Option<&Path>,
+) -> Result<()> {
     if let Some(path) = path {
         debug!(
             "Setting cache path from '{}' to '{}'",
-            video.title,
+            video.into_short_hash(app).await?,
             path.display()
         );
 
         let path_str = path.display().to_string();
-        let extractor_hash = video.extractor_hash.0.to_string();
+        let extractor_hash = video.hash().to_string();
+        let status = VideoStatus::Cached.as_db_integer();
 
         query!(
             r#"
             UPDATE videos
-            SET cache_path = ?
+            SET cache_path = ?, status = ?
             WHERE extractor_hash = ?;
         "#,
             path_str,
+            status,
             extractor_hash
         )
         .execute(&app.database)
@@ -147,14 +160,21 @@ pub async fn set_video_cache_path(app: &App, video: &Video, path: Option<&Path>)
 
         Ok(())
     } else {
-        let extractor_hash = video.extractor_hash.0.to_string();
+        debug!(
+            "Setting cache path from '{}' to 'NULL'",
+            video.into_short_hash(app).await?,
+        );
+
+        let extractor_hash = video.hash().to_string();
+        let status = VideoStatus::Watch.as_db_integer();
 
         query!(
             r#"
             UPDATE videos
-            SET cache_path = NULL
+            SET cache_path = NULL, status = ?
             WHERE extractor_hash = ?;
         "#,
+            status,
             extractor_hash
         )
         .execute(&app.database)
diff --git a/pkgs/by-name/yt/yt/src/storage/video_database/extractor_hash.rs b/pkgs/by-name/yt/yt/src/storage/video_database/extractor_hash.rs
index e90a5277..ac2f46ee 100644
--- a/pkgs/by-name/yt/yt/src/storage/video_database/extractor_hash.rs
+++ b/pkgs/by-name/yt/yt/src/storage/video_database/extractor_hash.rs
@@ -1,4 +1,4 @@
-use std::{collections::HashMap, fmt::Display};
+use std::{collections::HashMap, fmt::Display, str::FromStr};
 
 use anyhow::{bail, Result};
 use blake3::Hash;
@@ -9,35 +9,95 @@ use crate::{app::App, storage::video_database::getters::get_all_hashes};
 
 static EXTRACTOR_HASH_LENGTH: OnceCell<usize> = OnceCell::const_new();
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct ExtractorHash(pub(super) Hash);
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ExtractorHash {
+    hash: Hash,
+}
+
+#[derive(Debug, Clone)]
+pub struct ShortHash(String);
 
-impl Display for ExtractorHash {
+impl Display for ShortHash {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         self.0.fmt(f)
     }
 }
 
+#[derive(Debug, Clone)]
+pub struct LazyExtractorHash {
+    value: ShortHash,
+}
+
+impl FromStr for LazyExtractorHash {
+    type Err = anyhow::Error;
+
+    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+        // perform some cheap validation
+        if s.len() > 64 {
+            bail!("A hash can only contain 64 bytes!");
+        }
+
+        Ok(Self {
+            value: ShortHash(s.to_owned()),
+        })
+    }
+}
+
+impl LazyExtractorHash {
+    /// Turn the [`LazyExtractorHash`] into the [`ExtractorHash`]
+    pub async fn realize(self, app: &App) -> Result<ExtractorHash> {
+        ExtractorHash::from_short_hash(app, &self.value).await
+    }
+}
+
 impl ExtractorHash {
-    pub fn new(hash: Hash) -> Self {
-        Self(hash)
+    pub fn from_hash(hash: Hash) -> Self {
+        Self { hash }
+    }
+    pub async fn from_short_hash(app: &App, s: &ShortHash) -> Result<Self> {
+        Ok(Self {
+            hash: Self::short_hash_to_full_hash(app, s).await?,
+        })
+    }
+
+    pub fn hash(&self) -> &Hash {
+        &self.hash
     }
 
-    pub fn parse_from_full_version(s: &str) -> Result<Self> {
-        assert_eq!(s.len(), 64);
+    pub async fn into_short_hash(&self, app: &App) -> Result<ShortHash> {
+        let needed_chars = if let Some(needed_chars) = EXTRACTOR_HASH_LENGTH.get() {
+            debug!("Using cached char length: {}", needed_chars);
+            *needed_chars
+        } else {
+            let needed_chars = self.get_needed_char_len(app).await?;
+            debug!("Setting the needed has char lenght.");
+            EXTRACTOR_HASH_LENGTH
+                .set(needed_chars)
+                .expect("This should work at this stage");
+
+            needed_chars
+        };
+
+        debug!("Formatting a hash with char length: {}", needed_chars);
 
-        let hash = s.parse()?;
-        Ok(Self::new(hash))
+        Ok(ShortHash(
+            self.hash()
+                .to_hex()
+                .chars()
+                .into_iter()
+                .take(needed_chars)
+                .collect::<String>(),
+        ))
     }
 
-    pub async fn parse_from_short_version(app: &App, s: &str) -> Result<Self> {
+    async fn short_hash_to_full_hash(app: &App, s: &ShortHash) -> Result<Hash> {
         let all_hashes = get_all_hashes(app).await?;
 
-        let needed_chars = s.len();
+        let needed_chars = s.0.len();
 
         for hash in all_hashes {
-            if &hash.to_hex()[..needed_chars] == s {
-                return Ok(Self::new(hash));
+            if &hash.to_hex()[..needed_chars] == s.0 {
+                return Ok(hash);
             }
         }
 
@@ -78,29 +138,4 @@ impl ExtractorHash {
 
         Ok(needed_chars)
     }
-
-    pub async fn format(&self, app: &App) -> Result<String> {
-        let needed_chars = if let Some(needed_chars) = EXTRACTOR_HASH_LENGTH.get() {
-            debug!("Using cached char length: {}", needed_chars);
-            *needed_chars
-        } else {
-            let needed_chars = self.get_needed_char_len(app).await?;
-            debug!("Setting the needed has char lenght.");
-            EXTRACTOR_HASH_LENGTH
-                .set(needed_chars)
-                .expect("This should work at this stage");
-
-            needed_chars
-        };
-
-        debug!("Formatting a hash with char length: {}", needed_chars);
-
-        Ok(self
-            .0
-            .to_hex()
-            .chars()
-            .into_iter()
-            .take(needed_chars)
-            .collect::<String>())
-    }
 }
diff --git a/pkgs/by-name/yt/yt/src/storage/video_database/getters.rs b/pkgs/by-name/yt/yt/src/storage/video_database/getters.rs
index 6685603c..cec6c426 100644
--- a/pkgs/by-name/yt/yt/src/storage/video_database/getters.rs
+++ b/pkgs/by-name/yt/yt/src/storage/video_database/getters.rs
@@ -1,10 +1,7 @@
 //! These functions interact with the storage db in a read-only way. They are added on-demaned (as
 //! you could theoretically just could do everything with the `get_videos` function), as
 //! performance or convince requires.
-use std::{
-    fs::{File},
-    path::PathBuf,
-};
+use std::{fs::File, path::PathBuf};
 
 use anyhow::{bail, Context, Result};
 use blake3::Hash;
@@ -23,6 +20,42 @@ use crate::{
 
 use super::VideoStatus;
 
+macro_rules! video_from_record {
+    ($record:expr) => {
+        let thumbnail_url = if let Some(url) = &$record.thumbnail_url {
+            Some(Url::parse(&url)?)
+        } else {
+            None
+        };
+
+        Ok(Video {
+            cache_path: $record.cache_path.as_ref().map(|val| PathBuf::from(val)),
+            description: $record.description.clone(),
+            duration: $record.duration,
+            extractor_hash: ExtractorHash::from_hash(
+                $record
+                    .extractor_hash
+                    .parse()
+                    .expect("The db hash should be a valid blake3 hash"),
+            ),
+            last_status_change: $record.last_status_change,
+            parent_subscription_name: $record.parent_subscription_name.clone(),
+            publish_date: $record.publish_date,
+            status: VideoStatus::from_db_integer($record.status),
+            thumbnail_url,
+            title: $record.title.clone(),
+            url: Url::parse(&$record.url)?,
+            priority: $record.priority,
+            status_change: if $record.status_change == 1 {
+                true
+            } else {
+                assert_eq!($record.status_change, 0);
+                false
+            },
+        })
+    };
+}
+
 /// Get the lines to display at the selection file
 pub async fn get_videos(app: &App, allowed_states: &[VideoStatus]) -> Result<Vec<Video>> {
     let mut qb: QueryBuilder<Sqlite> = QueryBuilder::new(
@@ -77,9 +110,11 @@ pub async fn get_videos(app: &App, allowed_states: &[VideoStatus]) -> Result<Vec
                     .map(|val| PathBuf::from(val)),
                 description: base.get::<Option<String>, &str>("description").clone(),
                 duration: base.get("duration"),
-                extractor_hash: ExtractorHash::parse_from_full_version(
-                    &base.get::<String, &str>("extractor_hash"),
-                )?,
+                extractor_hash: ExtractorHash::from_hash(
+                    base.get::<String, &str>("extractor_hash")
+                        .parse()
+                        .expect("The db hash should be a valid blake3 hash"),
+                ),
                 last_status_change: base.get("last_status_change"),
                 parent_subscription_name: base
                     .get::<Option<String>, &str>("parent_subscription_name")
@@ -90,7 +125,15 @@ pub async fn get_videos(app: &App, allowed_states: &[VideoStatus]) -> Result<Vec
                 title: base.get::<String, &str>("title").to_owned(),
                 url: Url::parse(base.get("url"))?,
                 priority: base.get("priority"),
-                status_change: base.get("status_change"),
+                status_change: {
+                    let val = base.get::<i64, &str>("status_change");
+                    if val == 1 {
+                        true
+                    } else {
+                        assert_eq!(val, 0, "Can only be 1 or 0");
+                        false
+                    }
+                },
             })
         })
         .collect::<Result<Vec<Video>>>()?;
@@ -115,6 +158,21 @@ pub async fn get_video_info_json(video: &Video) -> Result<Option<InfoJson>> {
     }
 }
 
+pub async fn get_video_by_hash(app: &App, hash: &ExtractorHash) -> Result<Video> {
+    let ehash = hash.hash().to_string();
+
+    let raw_video = query!(
+        "
+        SELECT * FROM videos WHERE extractor_hash = ?;
+        ",
+        ehash
+    )
+    .fetch_one(&app.database)
+    .await?;
+
+    video_from_record! {raw_video}
+}
+
 pub async fn get_currently_playing_video(app: &App) -> Result<Option<Video>> {
     let mut videos: Vec<Video> = get_changing_videos(app, VideoStatus::Cached).await?;
 
@@ -132,7 +190,27 @@ pub async fn get_currently_playing_video(app: &App) -> Result<Option<Video>> {
 }
 
 pub async fn get_changing_videos(app: &App, old_state: VideoStatus) -> Result<Vec<Video>> {
-    todo!()
+    let status = old_state.as_db_integer();
+
+    let matching = query!(
+        r#"
+        SELECT *
+        FROM videos
+        WHERE status_change = 1 AND status = ?;
+    "#,
+        status
+    )
+    .fetch_all(&app.database)
+    .await?;
+
+    let real_videos: Vec<Video> = matching
+        .iter()
+        .map(|base| -> Result<Video> {
+            video_from_record! {base}
+        })
+        .collect::<Result<Vec<Video>>>()?;
+
+    Ok(real_videos)
 }
 
 pub async fn get_all_hashes(app: &App) -> Result<Vec<Hash>> {
diff --git a/pkgs/by-name/yt/yt/src/storage/video_database/schema.sql b/pkgs/by-name/yt/yt/src/storage/video_database/schema.sql
index e4d1e514..2634eef4 100644
--- a/pkgs/by-name/yt/yt/src/storage/video_database/schema.sql
+++ b/pkgs/by-name/yt/yt/src/storage/video_database/schema.sql
@@ -1,16 +1,16 @@
 -- The base schema
 -- Keep this table in sync with the `Video` structure
 CREATE TABLE IF NOT EXISTS videos (
-    cache_path                  TEXT UNIQUE,
+    cache_path                  TEXT UNIQUE                    CHECK (CASE WHEN cache_path IS NOT NULL THEN status == 2 ELSE 1 END),
     description                 TEXT,
     duration                    FLOAT,
     extractor_hash              TEXT UNIQUE NOT NULL PRIMARY KEY,
-    last_status_change          INTEGER        NOT NULL,
+    last_status_change          INTEGER     NOT NULL,
     parent_subscription_name    TEXT,
-    priority                    INTEGER        NOT NULL DEFAULT 0,
+    priority                    INTEGER     NOT NULL DEFAULT 0,
     publish_date                INTEGER,
-    status                      INTEGER        NOT NULL CHECK (status IN (0, 1, 2, 3, 4, 5)),
-    status_change               INTEGER        NOT NULL CHECK (status_change IN (0, 1)),
+    status                      INTEGER     NOT NULL DEFAULT 0 CHECK (status IN (0, 1, 2, 3, 4, 5) AND CASE WHEN status == 2 THEN cache_path IS NOT NULL ELSE 1 END AND CASE WHEN status != 2 THEN cache_path IS NULL ELSE 1 END),
+    status_change               INTEGER     NOT NULL DEFAULT 0 CHECK (status_change IN (0, 1)),
     thumbnail_url               TEXT,
     title                       TEXT        NOT NULL,
     url                         TEXT UNIQUE NOT NULL
diff --git a/pkgs/by-name/yt/yt/src/storage/video_database/setters.rs b/pkgs/by-name/yt/yt/src/storage/video_database/setters.rs
index 4f1d98cf..251f1e6f 100644
--- a/pkgs/by-name/yt/yt/src/storage/video_database/setters.rs
+++ b/pkgs/by-name/yt/yt/src/storage/video_database/setters.rs
@@ -16,7 +16,7 @@ pub async fn set_video_status(
     new_status: VideoStatus,
     new_priority: Option<i64>,
 ) -> Result<()> {
-    let video_hash = video_hash.0.to_string();
+    let video_hash = video_hash.hash().to_string();
     let new_status = new_status.as_db_integer();
 
     let old = query!(
@@ -74,13 +74,54 @@ pub async fn set_video_status(
     Ok(())
 }
 
+/// Mark a video as watched.
+/// This will both set the status to `Watched` and the cache_path to Null.
+pub async fn set_video_watched(app: &App, video_hash: &ExtractorHash) -> Result<()> {
+    // FIXME: Also delete the cache file <2024-08-19>
+
+    let video_hash = video_hash.hash().to_string();
+    let new_status = VideoStatus::Watched.as_db_integer();
+
+    let old = query!(
+        r#"
+    SELECT status, priority
+    FROM videos
+    WHERE extractor_hash = ?
+    "#,
+        video_hash
+    )
+    .fetch_one(&app.database)
+    .await?;
+
+    if old.status == new_status {
+        return Ok(());
+    }
+
+    let now = Utc::now().timestamp();
+
+    query!(
+        r#"
+        UPDATE videos
+        SET status = ?, last_status_change = ?, cache_path = NULL
+        WHERE extractor_hash = ?;
+        "#,
+        new_status,
+        now,
+        video_hash
+    )
+    .execute(&app.database)
+    .await?;
+
+    Ok(())
+}
+
 pub async fn set_state_change(
     app: &App,
     video_extractor_hash: &ExtractorHash,
     changing: bool,
 ) -> Result<()> {
     let state_change = if changing { 1 } else { 0 };
-    let video_extractor_hash = video_extractor_hash.to_string();
+    let video_extractor_hash = video_extractor_hash.hash().to_string();
 
     query!(
         r#"
@@ -111,14 +152,37 @@ pub async fn add_video(app: &App, video: Video) -> Result<()> {
     };
 
     let status = video.status.as_db_integer();
+    let status_change = if video.status_change { 1 } else { 0 };
     let url = video.url.to_string();
-    let extractor_hash = video.extractor_hash.0.to_string();
+    let extractor_hash = video.extractor_hash.hash().to_string();
 
     query!(
         r#"
-        INSERT INTO videos (parent_subscription_name, status, last_status_change, title, url, description, duration, publish_date, thumbnail_url, extractor_hash)
-        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
-    "#, parent_subscription_name, status, video.last_status_change, video.title, url, video.description, video.duration, video.publish_date, thumbnail_url, extractor_hash
+        INSERT INTO videos (
+            parent_subscription_name,
+            status,
+            status_change,
+            last_status_change,
+            title,
+            url,
+            description,
+            duration,
+            publish_date,
+            thumbnail_url,
+            extractor_hash)
+        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
+    "#,
+        parent_subscription_name,
+        status,
+        status_change,
+        video.last_status_change,
+        video.title,
+        url,
+        video.description,
+        video.duration,
+        video.publish_date,
+        thumbnail_url,
+        extractor_hash
     )
     .execute(&app.database)
     .await?;
diff --git a/pkgs/by-name/yt/yt/src/update/mod.rs b/pkgs/by-name/yt/yt/src/update/mod.rs
index 2f97361d..2cbae9fd 100644
--- a/pkgs/by-name/yt/yt/src/update/mod.rs
+++ b/pkgs/by-name/yt/yt/src/update/mod.rs
@@ -1,10 +1,10 @@
-use std::{env::current_exe, str::FromStr};
+use std::{env::current_exe, str::FromStr, sync::Arc};
 
 use anyhow::{bail, Context, Ok, Result};
 use chrono::{DateTime, Utc};
 use log::{debug, error, info, warn};
 use serde_json::{json, Value};
-use tokio::{process::Command, task::JoinSet};
+use tokio::{process::Command, sync::Semaphore, task::JoinSet};
 use yt_dlp::unsmuggle_url;
 
 use crate::{
@@ -12,37 +12,51 @@ use crate::{
     storage::{
         subscriptions::{get_subscriptions, Subscription},
         video_database::{
-            extractor_hash::ExtractorHash, getters::get_video_hashes, setters::add_video,
-            Video, VideoStatus,
+            extractor_hash::ExtractorHash, getters::get_video_hashes, setters::add_video, Video,
+            VideoStatus,
         },
     },
 };
 
-pub async fn update(max_backlog: u32, subs_to_update: Vec<String>) -> Result<()> {
+pub async fn update(
+    max_backlog: u32,
+    subs_to_update: Vec<String>,
+    concurrent_processes: usize,
+) -> Result<()> {
     let subscriptions = get_subscriptions()?;
 
     let mut join_set = JoinSet::new();
+    let permits = Arc::new(Semaphore::const_new(concurrent_processes));
 
-    for key in subscriptions.0.keys() {
+    for key in subscriptions.0.into_keys() {
         if subs_to_update.contains(&key) || subs_to_update.is_empty() {
-            let exe_name = current_exe().context("Failed to get the current executable")?;
+            let new_permits = Arc::clone(&permits);
 
-            let mut child = Command::new(exe_name)
-                // TODO: Add currying of the verbosity flags <2024-07-28>
-                // .arg("-vvvv")
-                .arg("update-once")
-                .arg(&key)
-                .arg(max_backlog.to_string())
-                .spawn()
-                .context("Failed to call yt update-once")?;
+            join_set.spawn(async move {
+                let _permit = new_permits
+                    .acquire()
+                    .await
+                    .expect("The semaphore should not be closed");
+
+                debug!(
+                    "Started downloading: `yt 'update-once' '{}' '{}'`",
+                    &key,
+                    max_backlog.to_string()
+                );
 
-            let new_key = key.clone();
+                let exe_name = current_exe().context("Failed to get the current executable")?;
+                let mut child = Command::new(exe_name)
+                    // TODO: Add currying of the verbosity flags <2024-07-28>
+                    // .arg("-vvvv")
+                    .arg("update-once")
+                    .arg(&key)
+                    .arg(max_backlog.to_string())
+                    .spawn()
+                    .context("Failed to call yt update-once")?;
 
-            // PERFORMANCE: Switch back to a somewhat parallellied version <2024-07-29>
-            let output = child.wait().await;
-            join_set.spawn(async move {
-                // let output = child.wait().await;
-                (output, new_key)
+                let output = child.wait().await;
+
+                Ok((output, key))
             });
         } else {
             info!(
@@ -53,7 +67,7 @@ pub async fn update(max_backlog: u32, subs_to_update: Vec<String>) -> Result<()>
     }
 
     while let Some(res) = join_set.join_next().await {
-        let (output, key) = res?;
+        let (output, key) = res??;
 
         debug!("{} finished its update.", &key);
         match output {
@@ -199,7 +213,7 @@ async fn update_subscription(app: &App, sub: &Subscription, max_backlog: u32) ->
                     cache_path: None,
                     description: entry.description.clone(),
                     duration: entry.duration,
-                    extractor_hash: ExtractorHash::new(extractor_hash),
+                    extractor_hash: ExtractorHash::from_hash(extractor_hash),
                     last_status_change: Utc::now().timestamp(),
                     parent_subscription_name: Some(sub.name.clone()),
                     priority: 0,
diff --git a/pkgs/by-name/yt/yt/src/watch/events.rs b/pkgs/by-name/yt/yt/src/watch/events.rs
new file mode 100644
index 00000000..cab6807f
--- /dev/null
+++ b/pkgs/by-name/yt/yt/src/watch/events.rs
@@ -0,0 +1,160 @@
+use std::{env::current_exe, usize};
+
+use anyhow::{bail, Result};
+use libmpv2::{events::Event, EndFileReason, Mpv};
+use log::{debug, info};
+use tokio::process::Command;
+
+use crate::{
+    app::App,
+    comments::get_comments,
+    constants::LOCAL_COMMENTS_LENGTH,
+    storage::video_database::{
+        extractor_hash::ExtractorHash,
+        setters::{set_state_change, set_video_watched},
+    },
+};
+
+pub struct MpvEventHandler {
+    currently_playing_index: Option<usize>,
+    current_playlist_position: usize,
+    current_playlist: Vec<ExtractorHash>,
+}
+
+impl MpvEventHandler {
+    pub fn from_playlist(playlist: Vec<ExtractorHash>) -> Self {
+        Self {
+            currently_playing_index: None,
+            current_playlist: playlist,
+            current_playlist_position: 0,
+        }
+    }
+
+    async fn mark_cvideo_watched(&mut self, app: &App) -> Result<()> {
+        if let Some(index) = self.currently_playing_index {
+            let video_hash = &self.current_playlist[(index) as usize];
+            set_video_watched(&app, video_hash).await?;
+        }
+        Ok(())
+    }
+
+    async fn mark_cvideo_inactive(&mut self, app: &App) -> Result<()> {
+        if let Some(index) = self.currently_playing_index {
+            let video_hash = &self.current_playlist[(index) as usize];
+            self.currently_playing_index = None;
+            set_state_change(&app, video_hash, false).await?;
+        }
+        Ok(())
+    }
+    async fn mark_video_active(&mut self, app: &App, playlist_index: usize) -> Result<()> {
+        let video_hash = &self.current_playlist[(playlist_index) as usize];
+        self.currently_playing_index = Some(playlist_index);
+        set_state_change(&app, video_hash, true).await?;
+        Ok(())
+    }
+
+    /// This will return [`true`], if the event handling should be stopped
+    pub async fn handle_mpv_event<'a>(
+        &mut self,
+        app: &App,
+        mpv: &Mpv,
+        event: Event<'a>,
+    ) -> Result<bool> {
+        match event {
+            Event::EndFile(r) => match r {
+                EndFileReason::Eof => {
+                    info!("Mpv reached eof of current video. Marking it watched.");
+
+                    self.mark_cvideo_watched(app).await?;
+                    self.mark_cvideo_inactive(app).await?;
+                }
+                EndFileReason::Stop => {}
+                EndFileReason::Quit => {
+                    info!("Mpv quit. Exiting playback");
+
+                    self.mark_cvideo_watched(app).await?;
+                    self.mark_cvideo_inactive(app).await?;
+                    return Ok(true);
+                }
+                EndFileReason::Error => {
+                    unreachable!("This have raised a separate error")
+                }
+                EndFileReason::Redirect => {
+                    todo!("We probably need to handle this somehow");
+                }
+            },
+            Event::StartFile(playlist_index) => {
+                self.mark_video_active(app, (playlist_index - 1) as usize)
+                    .await?;
+                self.current_playlist_position = (playlist_index - 1) as usize;
+            }
+            Event::FileLoaded => {}
+            Event::ClientMessage(a) => {
+                debug!("Got Client Message event: '{}'", a.join(" "));
+
+                match a.as_slice() {
+                    &["yt-comments-external"] => {
+                        let binary = current_exe().expect("A current exe should exist");
+
+                        let status = Command::new("riverctl")
+                            .args(["focus-output", "next"])
+                            .status()
+                            .await?;
+                        if !status.success() {
+                            bail!("focusing the next output failed!");
+                        }
+
+                        let status = Command::new("alacritty")
+                            .args(&[
+                                "--title",
+                                "floating please",
+                                "--command",
+                                binary.to_str().expect("Should be valid unicode"),
+                                "comments",
+                            ])
+                            .status()
+                            .await?;
+                        if !status.success() {
+                            bail!("Falied to start `yt comments`");
+                        }
+
+                        let status = Command::new("riverctl")
+                            .args(["focus-output", "next"])
+                            .status()
+                            .await?;
+                        if !status.success() {
+                            bail!("focusing the next output failed!");
+                        }
+                    }
+                    &["yt-comments-local"] => {
+                        let comments: String = get_comments(app)
+                            .await?
+                            .render(false)
+                            .replace("\"", "")
+                            .replace("'", "")
+                            .chars()
+                            .take(LOCAL_COMMENTS_LENGTH)
+                            .collect();
+
+                        mpv.execute("show-text", &[&format!("'{}'", comments), "6000"])?;
+                    }
+                    &["yt-description"] => {
+                        // let description = description(app).await?;
+                        mpv.execute("script-message", &["osc-message", "'<YT Description>'"])?;
+                    }
+                    &["yt-mark-watch-later"] => {
+                        self.mark_cvideo_inactive(app).await?;
+                        mpv.execute("write-watch-later-config", &[])?;
+                        mpv.execute("playlist-remove", &["current"])?;
+                    }
+                    other => {
+                        debug!("Unknown message: {}", other.join(" "))
+                    }
+                }
+            }
+            _ => {}
+        }
+
+        Ok(false)
+    }
+}
diff --git a/pkgs/by-name/yt/yt/src/watch/mod.rs b/pkgs/by-name/yt/yt/src/watch/mod.rs
index 683d49c1..3a4f8983 100644
--- a/pkgs/by-name/yt/yt/src/watch/mod.rs
+++ b/pkgs/by-name/yt/yt/src/watch/mod.rs
@@ -1,19 +1,17 @@
 use anyhow::Result;
-use libmpv2::{
-    events::{Event, EventContext},
-    Mpv,
-};
-use log::{debug, info};
+use events::MpvEventHandler;
+use libmpv2::{events::EventContext, Mpv};
+use log::{debug, info, warn};
 
 use crate::{
     app::App,
     cache::maintain,
-    comments::get_comments,
-    storage::video_database::{
-        extractor_hash::ExtractorHash, getters::get_videos, setters::set_state_change, VideoStatus,
-    },
+    constants::{mpv_config_path, mpv_input_path},
+    storage::video_database::{extractor_hash::ExtractorHash, getters::get_videos, VideoStatus},
 };
 
+pub mod events;
+
 pub async fn watch(app: &App) -> Result<()> {
     maintain(app, false).await?;
 
@@ -22,11 +20,43 @@ pub async fn watch(app: &App) -> Result<()> {
         // the player (and e.g. close the window).
         mpv.set_property("input-default-bindings", "yes")?;
         mpv.set_property("input-vo-keyboard", "yes")?;
+
         // Show the on screen controller.
         mpv.set_property("osc", "yes")?;
+
+        // Don't automatically advance to the next video (or exit the player)
+        mpv.set_option("keep-open", "always")?;
         Ok(())
     })?;
 
+    let config_path = mpv_config_path()?;
+    if config_path.try_exists()? {
+        info!("Found mpv.conf at '{}'!", config_path.display());
+        mpv.execute(
+            "load-config-file",
+            &[config_path.to_str().expect("This should be utf8-able")],
+        )?;
+    } else {
+        warn!(
+            "Did not find a mpv.conf file at '{}'",
+            config_path.display()
+        );
+    }
+
+    let input_path = mpv_input_path()?;
+    if input_path.try_exists()? {
+        info!("Found mpv.input.conf at '{}'!", input_path.display());
+        mpv.execute(
+            "load-input-conf",
+            &[input_path.to_str().expect("This should be utf8-able")],
+        )?;
+    } else {
+        warn!(
+            "Did not find a mpv.input.conf file at '{}'",
+            input_path.display()
+        );
+    }
+
     let mut ev_ctx = EventContext::new(mpv.ctx);
     ev_ctx.disable_deprecated_events()?;
 
@@ -58,37 +88,17 @@ pub async fn watch(app: &App) -> Result<()> {
         playlist_cache.push(play_thing.extractor_hash);
     }
 
+    let mut mpv_event_handler = MpvEventHandler::from_playlist(playlist_cache);
     loop {
         if let Some(ev) = ev_ctx.wait_event(600.) {
             match ev {
-                Ok(Event::EndFile(r)) => {
-                    println!("Exiting! Reason: {:?}", r);
-                    break;
-                }
-                Ok(Event::StartFile(playlist_index)) => {
-                    let video_hash = &playlist_cache[playlist_index as usize];
-                    set_state_change(&app, video_hash, true).await?;
-                }
-                Ok(Event::ClientMessage(a)) => {
-                    debug!("Got Client Message event: '{}'", a.join(" "));
-
-                    match a.as_slice() {
-                        &["yt-comments"] => {
-                            let comments = get_comments(app).await?.render(false);
-                            mpv.execute("script-message", &["osc-message", &comments])?;
-                        }
-                        &["yt-description"] => {
-                            // let description = description(app).await?;
-                            mpv.execute("script-message", &["osc-message", "'<YT Description>'"])?;
-                        }
-                        other => {
-                            debug!("Unknown message: {}", other.join(" "))
-                        }
+                Ok(event) => {
+                    debug!("Mpv event triggered: {:#?}", event);
+                    if mpv_event_handler.handle_mpv_event(app, &mpv, event).await? {
+                        break;
                     }
                 }
-
-                Ok(e) => println!("Event triggered: {:#?}", e),
-                Err(e) => println!("Event errored: {}", e),
+                Err(e) => debug!("Mpv Event errored: {}", e),
             }
         }
     }
diff --git a/pkgs/by-name/yt/yt/yt_dlp/src/lib.rs b/pkgs/by-name/yt/yt/yt_dlp/src/lib.rs
index 8425d0d3..851c9ab7 100644
--- a/pkgs/by-name/yt/yt/yt_dlp/src/lib.rs
+++ b/pkgs/by-name/yt/yt/yt_dlp/src/lib.rs
@@ -1,4 +1,4 @@
-use std::{fs::File, io::Write};
+// use std::{fs::File, io::Write};
 
 use std::{path::PathBuf, sync::Once};
 
@@ -283,8 +283,8 @@ pub async fn extract_info(
 
         let result_str = json_dumps(py, result)?;
 
-        let mut file = File::create("output.info.json").unwrap();
-        write!(file, "{}", result_str).unwrap();
+        //let mut file = File::create("output.info.json").unwrap();
+        //write!(file, "{}", result_str).unwrap();
 
         Ok(serde_json::from_str(&result_str)
             .expect("Python should be able to produce correct json"))
diff --git a/pkgs/by-name/yt/yt/yt_dlp/src/wrapper/info_json.rs b/pkgs/by-name/yt/yt/yt_dlp/src/wrapper/info_json.rs
index 8673998e..e9bf0402 100644
--- a/pkgs/by-name/yt/yt/yt_dlp/src/wrapper/info_json.rs
+++ b/pkgs/by-name/yt/yt/yt_dlp/src/wrapper/info_json.rs
@@ -155,7 +155,7 @@ pub struct RequestedDownloads {
     pub fps: f64,
     pub height: u32,
     pub infojson_filename: PathBuf,
-    pub language: String,
+    pub language: Option<String>,
     pub protocol: String,
     pub requested_formats: Vec<Format>,
     pub resolution: String,
@@ -444,6 +444,7 @@ pub struct Format {
     pub format_index: Option<String>,
     pub format_note: Option<String>,
     pub fps: Option<f64>,
+    pub fragment_base_url: Option<Todo>,
     pub fragments: Option<Vec<Fragment>>,
     pub has_drm: Option<bool>,
     pub height: Option<u32>,