From 8cffac4f2b4943ea68fe61e346937e7bf94028a3 Mon Sep 17 00:00:00 2001 From: Caitlin Wilks Date: Thu, 22 Oct 2026 00:56:02 +0100 Subject: [PATCH 1/2] YouTube: related videos when a video finishes --- crates/flex-app/src/player.rs | 7 ++++++- crates/flex-app/src/screens/youtube.rs | 23 ++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/crates/flex-app/src/player.rs b/crates/flex-app/src/player.rs index fd1508f..6f23705 100644 --- a/crates/flex-app/src/player.rs +++ b/crates/flex-app/src/player.rs @@ -25,7 +25,7 @@ use crate::mpv::{self, GetProcAddress, Mpv, Renderer}; use crate::picture::Picture; use crate::shared::TrackMemory; use flex_core::language; -use crate::screens::{Nav, Screen, up_next::UpNext}; +use crate::screens::{Nav, Screen, up_next::UpNext, youtube::RelatedVideos}; use crate::shared::{PlayRequest, Shared, SkipMode}; use crate::theme; @@ -475,6 +475,8 @@ impl Player { return Some(Nav::Pop); } yt.loaded = true; + // Ready for the page shown when it finishes. + sh.youtube.start_related(&yt.id); // Where playback starts, so a segment right there (e.g. a sponsor read // at 0:00) counts as played into. yt.last_pos = Some(self.req.start.unwrap_or(0.0)); @@ -530,6 +532,9 @@ impl Player { } self.log_session(sh); self.finished = true; + if let Some(id) = self.req.youtube_id() { + return Nav::replace(RelatedVideos::after(sh, id, &self.req.title)); + } return match sh.next_after(&self.req) { Some(next) => Nav::replace(UpNext::new(self.req.title.clone(), next)), None => Nav::Pop, diff --git a/crates/flex-app/src/screens/youtube.rs b/crates/flex-app/src/screens/youtube.rs index 600bccb..0764e4a 100644 --- a/crates/flex-app/src/screens/youtube.rs +++ b/crates/flex-app/src/screens/youtube.rs @@ -604,28 +604,41 @@ impl Screen for SearchResults { } } -/// The videos YouTube suggests after one (from its options). +/// The videos YouTube suggests after one (from its options, or when it finishes playing). pub struct RelatedVideos { title: String, results: VideoResults, + /// Shown after the video finished: a video picked here replaces this page, so Back + /// still returns to wherever playback was started from. + finished: bool, } impl RelatedVideos { fn new(sh: &mut Shared, item: &VideoItem) -> Self { - Self { title: item.title.clone(), results: VideoResults::related(sh, &item.id) } + Self { title: item.title.clone(), results: VideoResults::related(sh, &item.id), finished: false } + } + + /// For the video that just finished playing. + pub fn after(sh: &mut Shared, id: &str, title: &str) -> Self { + Self { title: title.to_owned(), results: VideoResults::related(sh, id), finished: true } } } impl Screen for RelatedVideos { fn handle(&mut self, sh: &mut Shared, press: Press) -> Nav { - if press.is(Action::Back) { + if press.is(Action::Back) || (self.finished && press.is(Action::Stop)) { return Nav::Pop; } - self.results.handle(sh, press) + let nav = self.results.handle(sh, press); + match nav { + Nav::Play { req, .. } if self.finished => Nav::Play { req, replace: true }, + nav => nav, + } } fn ui(&mut self, ui: &mut Ui, sh: &mut Shared, rect: Rect, focused: bool) -> Nav { - let body = widgets::header(ui.painter(), rect, &self.title, "Related videos", ui.style()); + let subtitle = if self.finished { "Finished ยท related videos" } else { "Related videos" }; + let body = widgets::header(ui.painter(), rect, &self.title, subtitle, ui.style()); self.results.ui(ui, sh, body, &[], focused); Nav::None } From ee9a354f2ce04a7c7b098b3b28304582968a3275 Mon Sep 17 00:00:00 2001 From: Caitlin Wilks Date: Thu, 22 Oct 2026 00:57:21 +0100 Subject: [PATCH 2/2] Version 0.20.0 --- Cargo.lock | 14 +++++++------- Cargo.toml | 2 +- crates/flex-app/Cargo.toml | 6 +++--- crates/flex-core/Cargo.toml | 2 +- crates/flex-web/Cargo.toml | 2 +- plugins/anilist/Cargo.toml | 2 +- plugins/anilist/plugin.toml | 2 +- plugins/lotus-lamp/Cargo.toml | 2 +- plugins/lotus-lamp/plugin.toml | 2 +- plugins/trakt/Cargo.toml | 2 +- plugins/trakt/plugin.toml | 2 +- 11 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 191e793..bbc9003 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1280,7 +1280,7 @@ dependencies = [ [[package]] name = "flex" -version = "0.19.0" +version = "0.20.0" dependencies = [ "anyhow", "chrono", @@ -1304,7 +1304,7 @@ dependencies = [ [[package]] name = "flex-anilist" -version = "0.19.0" +version = "0.20.0" dependencies = [ "flex-plugin", "ureq", @@ -1312,7 +1312,7 @@ dependencies = [ [[package]] name = "flex-core" -version = "0.19.0" +version = "0.20.0" dependencies = [ "anyhow", "directories", @@ -1337,7 +1337,7 @@ dependencies = [ [[package]] name = "flex-lotus-lamp" -version = "0.19.0" +version = "0.20.0" dependencies = [ "flex-plugin", "lotus-lamp", @@ -1346,7 +1346,7 @@ dependencies = [ [[package]] name = "flex-plugin" -version = "0.19.0" +version = "0.20.0" dependencies = [ "serde", "serde_json", @@ -1354,7 +1354,7 @@ dependencies = [ [[package]] name = "flex-trakt" -version = "0.19.0" +version = "0.20.0" dependencies = [ "flex-plugin", "ureq", @@ -1362,7 +1362,7 @@ dependencies = [ [[package]] name = "flex-web" -version = "0.19.0" +version = "0.20.0" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index 87cbd6a..0ab02a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ default-members = ["crates/*"] [workspace.package] edition = "2024" -version = "0.19.0" +version = "0.20.0" license = "MIT" [profile.release] diff --git a/crates/flex-app/Cargo.toml b/crates/flex-app/Cargo.toml index 311c843..dc2dcf3 100644 --- a/crates/flex-app/Cargo.toml +++ b/crates/flex-app/Cargo.toml @@ -9,9 +9,9 @@ anyhow = "1.0.104" chrono = { version = "0.4.45", default-features = false, features = ["clock"] } eframe = { version = "0.36", default-features = false, features = ["glow", "default_fonts"] } env_logger = "0.11.11" -flex-core = { version = "0.19.0", path = "../flex-core" } -flex-plugin = { version = "0.19.0", path = "../flex-plugin" } -flex-web = { version = "0.19.0", path = "../flex-web" } +flex-core = { version = "0.20.0", path = "../flex-core" } +flex-plugin = { version = "0.20.0", path = "../flex-plugin" } +flex-web = { version = "0.20.0", path = "../flex-web" } image = { version = "0.25", default-features = false, features = ["jpeg", "png", "webp"] } libloading = "0.9" log = "0.4.34" diff --git a/crates/flex-core/Cargo.toml b/crates/flex-core/Cargo.toml index 96bada8..9eb9c90 100644 --- a/crates/flex-core/Cargo.toml +++ b/crates/flex-core/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] anyhow = "1.0.104" directories = "6.0.0" -flex-plugin = { version = "0.19.0", path = "../flex-plugin" } +flex-plugin = { version = "0.20.0", path = "../flex-plugin" } hunch = "2" lofty = "0.25.4" log = "0.4.34" diff --git a/crates/flex-web/Cargo.toml b/crates/flex-web/Cargo.toml index 34ac317..223e388 100644 --- a/crates/flex-web/Cargo.toml +++ b/crates/flex-web/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] anyhow = "1.0.104" axum = "0.8.9" -flex-core = { version = "0.19.0", path = "../flex-core" } +flex-core = { version = "0.20.0", path = "../flex-core" } log = "0.4.34" serde = { version = "1.0.229", features = ["derive", "rc"] } serde_json = "1.0.151" diff --git a/plugins/anilist/Cargo.toml b/plugins/anilist/Cargo.toml index d8474a7..0c9ac36 100644 --- a/plugins/anilist/Cargo.toml +++ b/plugins/anilist/Cargo.toml @@ -7,5 +7,5 @@ license.workspace = true publish = false [dependencies] -flex-plugin = { version = "0.19.0", path = "../../crates/flex-plugin" } +flex-plugin = { version = "0.20.0", path = "../../crates/flex-plugin" } ureq = { version = "3", features = ["json"] } diff --git a/plugins/anilist/plugin.toml b/plugins/anilist/plugin.toml index 419adee..5fb50c2 100644 --- a/plugins/anilist/plugin.toml +++ b/plugins/anilist/plugin.toml @@ -1,6 +1,6 @@ name = "AniList" description = "Updates your AniList progress as you watch anime in Flex" -version = "0.19.0" +version = "0.20.0" command = "flex-anilist" subscribe = ["playback"] diff --git a/plugins/lotus-lamp/Cargo.toml b/plugins/lotus-lamp/Cargo.toml index b30bb8e..95237b8 100644 --- a/plugins/lotus-lamp/Cargo.toml +++ b/plugins/lotus-lamp/Cargo.toml @@ -7,6 +7,6 @@ license.workspace = true publish = false [dependencies] -flex-plugin = { version = "0.19.0", path = "../../crates/flex-plugin" } +flex-plugin = { version = "0.20.0", path = "../../crates/flex-plugin" } lotus-lamp = { git = "https://github.com/bombpersons/lotus-lamp-rs", rev = "59cf5ccaa0007f006f1812fd02cc5991aff11e72" } tokio = { version = "1.53.2", features = ["rt", "time", "sync", "macros"] } diff --git a/plugins/lotus-lamp/plugin.toml b/plugins/lotus-lamp/plugin.toml index d345ef4..bacd176 100644 --- a/plugins/lotus-lamp/plugin.toml +++ b/plugins/lotus-lamp/plugin.toml @@ -1,6 +1,6 @@ name = "LED strip (Lotus Lamp)" description = "Lights a Bluetooth LED strip behind the TV with the colours on screen" -version = "0.19.0" +version = "0.20.0" command = "flex-lotus-lamp" subscribe = ["light"] diff --git a/plugins/trakt/Cargo.toml b/plugins/trakt/Cargo.toml index f654768..262020e 100644 --- a/plugins/trakt/Cargo.toml +++ b/plugins/trakt/Cargo.toml @@ -7,5 +7,5 @@ license.workspace = true publish = false [dependencies] -flex-plugin = { version = "0.19.0", path = "../../crates/flex-plugin" } +flex-plugin = { version = "0.20.0", path = "../../crates/flex-plugin" } ureq = { version = "3", features = ["json"] } diff --git a/plugins/trakt/plugin.toml b/plugins/trakt/plugin.toml index 1bd9032..8a101d6 100644 --- a/plugins/trakt/plugin.toml +++ b/plugins/trakt/plugin.toml @@ -1,6 +1,6 @@ name = "Trakt" description = "Keeps your Trakt history up to date with what you watch in Flex" -version = "0.19.0" +version = "0.20.0" command = "flex-trakt" subscribe = ["playback"]