playlists + player: every service's list through one door with its names matched for real, and shuffle that readies exactly what it promises

This commit is contained in:
KeelrLeek committed 2026-08-24 18:10:34 -05:00
1 parent 30eb6fe025
commit 50414dae5c
16 files changed
+4157 -154

No files matched your search

+3 -3
View File
@@ -122,7 +122,7 @@ pub(crate) fn slice_json_after<'a>(html: &'a str, needle: &str) -> Option<&'a st
/// applying `f` to the value. Youtube's trees are deep, volatile, and
/// renderer-name-happy; searching for leaf keys survives their reshuffles
/// better than hardcoded paths.
fn walk<'a>(root: &'a Value, key: &str, f: &mut dyn FnMut(&'a Value)) {
pub(crate) fn walk<'a>(root: &'a Value, key: &str, f: &mut dyn FnMut(&'a Value)) {
match root {
Value::Object(map) => {
if let Some(v) = map.get(key) {
@@ -143,7 +143,7 @@ fn walk<'a>(root: &'a Value, key: &str, f: &mut dyn FnMut(&'a Value)) {
/// First string under any of `keys` in reading order — for label-ish leaves
/// ("simpleText", nested runs) where youtube varies the wrapper.
fn first_string(v: &Value, keys: &[&str]) -> Option<String> {
pub(crate) fn first_string(v: &Value, keys: &[&str]) -> Option<String> {
match v {
Value::String(s) => Some(s.clone()),
_ => {
@@ -292,7 +292,7 @@ pub(crate) fn comment_texts(next_response: &Value) -> Vec<(String, String)> {
/// Flatten a `runs`/`simpleText` rich-text node into plain text, preserving
/// the run boundaries' own newlines (timestamps arrive as separate runs).
fn collect_runs(v: &Value) -> String {
pub(crate) fn collect_runs(v: &Value) -> String {
if let Some(s) = v.get("simpleText").and_then(Value::as_str) {
return s.to_string();
}