about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-08-25 15:53:05 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-08-25 15:53:05 +0200
commit83643e0370b101968bd3de5e9a81c2b309955cbd (patch)
treed1c0f7543db0ad700e0433334cedf4bd051d8b0a
parentfix(storage/extractor_hash): Remove useless logs (diff)
downloadyt-83643e0370b101968bd3de5e9a81c2b309955cbd.tar.gz
yt-83643e0370b101968bd3de5e9a81c2b309955cbd.zip
refactor(watch/playlist_handler): Init
This facilitates outsourcing the mpv playlist operations and overlaying
them with an cache that provides the facility to convert for `playlist_entry_id`s
to `ExtractorHash`es even after their corresponding video has been
removed from the playlist.
-rw-r--r--src/watch/events/mod.rs (renamed from src/watch/events.rs)94
-rw-r--r--src/watch/events/playlist_handler.rs96
2 files changed, 120 insertions, 70 deletions
diff --git a/src/watch/events.rs b/src/watch/events/mod.rs
index c1a2d13..9ca12fd 100644
--- a/src/watch/events.rs
+++ b/src/watch/events/mod.rs
@@ -8,12 +8,11 @@
 // You should have received a copy of the License along with this program.
 // If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
-use std::{collections::HashMap, env::current_exe, time::Duration, usize};
+use std::{collections::HashMap, env::current_exe, mem, time::Duration, usize};
 
 use anyhow::{bail, Result};
 use libmpv2::{
     events::{Event, PlaylistEntryId},
-    mpv_node::MpvNode,
     EndFileReason, Mpv,
 };
 use log::{debug, info, warn};
@@ -30,72 +29,25 @@ use crate::{
     },
 };
 
+use playlist_handler::PlaylistHandler;
+
+mod playlist_handler;
+
 #[derive(Debug)]
 pub struct MpvEventHandler {
     watch_later_block_list: HashMap<ExtractorHash, ()>,
-    // current_playlist: HashMap<PlaylistEntryId, ExtractorHash>,
-    playlist_cache: HashMap<String, ExtractorHash>,
+    playlist_handler: PlaylistHandler,
 }
 
 impl MpvEventHandler {
     pub fn from_playlist(playlist_cache: HashMap<String, ExtractorHash>) -> Self {
+        let playlist_handler = PlaylistHandler::from_cache(playlist_cache);
         Self {
-            // current_playlist,
-            playlist_cache,
+            playlist_handler,
             watch_later_block_list: HashMap::new(),
         }
     }
 
-    fn get_current_mpv_playlist(
-        &self,
-        mpv: &Mpv,
-    ) -> Result<HashMap<PlaylistEntryId, ExtractorHash>> {
-        let mpv_playlist: Vec<(String, PlaylistEntryId)> = match mpv.get_property("playlist")? {
-            MpvNode::ArrayIter(array) => array
-                .map(|val| match val {
-                    MpvNode::MapIter(map) => {
-                        struct BuildPlaylistEntry {
-                            filename: Option<String>,
-                            id: Option<PlaylistEntryId>,
-                        }
-                        let mut entry = BuildPlaylistEntry {
-                            filename: None,
-                            id: None,
-                        };
-
-                        map.for_each(|(key, value)| match key.as_str() {
-                            "filename" => {
-                                entry.filename = Some(value.str().expect("work").to_owned())
-                            }
-                            "id" => {
-                                entry.id = Some(PlaylistEntryId::new(value.i64().expect("Works")))
-                            }
-                            _ => (),
-                        });
-                        (entry.filename.expect("is some"), entry.id.expect("is some"))
-                    }
-                    _ => unreachable!(),
-                })
-                .collect(),
-            _ => unreachable!(),
-        };
-
-        let mut playlist: HashMap<PlaylistEntryId, ExtractorHash> =
-            HashMap::with_capacity(mpv_playlist.len());
-        for (path, key) in mpv_playlist {
-            let hash = self
-                .playlist_cache
-                .get(&path)
-                .expect("All path should also be stored in the cache")
-                .to_owned();
-            playlist.insert(key, hash);
-        }
-
-        // debug!("Requested the current playlist: '{:#?}'", &playlist);
-
-        Ok(playlist)
-    }
-
     /// Checks, whether new videos are ready to be played
     pub async fn possibly_add_new_videos(
         &mut self,
@@ -114,7 +66,7 @@ impl MpvEventHandler {
         }
 
         let mut blocked_videos = 0;
-        let current_playlist = self.get_current_mpv_playlist(mpv)?;
+        let current_playlist = self.playlist_handler.playlist_ids(mpv)?;
         let play_things = play_things
             .into_iter()
             .filter(|val| {
@@ -142,9 +94,8 @@ impl MpvEventHandler {
             blocked_videos
         );
 
-        self.playlist_cache.reserve(play_things.len());
-
         let num = play_things.len();
+        self.playlist_handler.reserve(play_things.len());
         for play_thing in play_things {
             debug!("Adding '{}' to playlist.", play_thing.title);
 
@@ -155,8 +106,8 @@ impl MpvEventHandler {
             let args = &[&fmt_cache_path, "append-play"];
 
             mpv.execute("loadfile", args)?;
-            self.playlist_cache
-                .insert(cache_path.to_owned(), play_thing.extractor_hash);
+            self.playlist_handler
+                .add(cache_path.to_owned(), play_thing.extractor_hash);
         }
 
         if force_message || num > 0 {
@@ -182,7 +133,7 @@ impl MpvEventHandler {
     /// You can specify an offset, which is added to the playlist_position to get, for example, the
     /// previous video (-1) or the next video (+1).
     /// Beware that setting an offset can cause an property error if it's out of bound.
-    fn get_cvideo_hash(&self, mpv: &Mpv, offset: i64) -> Result<ExtractorHash> {
+    fn get_cvideo_hash(&mut self, mpv: &Mpv, offset: i64) -> Result<ExtractorHash> {
         let playlist_entry_id = {
             let playlist_position = {
                 let raw = mpv.get_property::<i64>("playlist-pos")?;
@@ -201,9 +152,11 @@ impl MpvEventHandler {
         // debug!("Trying to get playlist entry: '{}'", playlist_entry_id);
 
         let video_hash = self
-            .get_current_mpv_playlist(mpv)?
-            .remove(&playlist_entry_id)
-            .expect("The stored playling index should always be in the playlist");
+            .playlist_handler
+            .playlist_ids(mpv)?
+            .get(&playlist_entry_id)
+            .expect("The stored playling index should always be in the playlist")
+            .to_owned();
 
         Ok(video_hash)
     }
@@ -220,7 +173,7 @@ impl MpvEventHandler {
         mpv: &Mpv,
         playlist_index: PlaylistEntryId,
     ) -> Result<()> {
-        let current_playlist = self.get_current_mpv_playlist(mpv)?;
+        let current_playlist = self.playlist_handler.playlist_ids(mpv)?;
         let video_hash = current_playlist
             .get(&playlist_index)
             .expect("The video index should always be correctly tracked");
@@ -234,7 +187,7 @@ impl MpvEventHandler {
         mpv: &Mpv,
         playlist_index: PlaylistEntryId,
     ) -> Result<()> {
-        let current_playlist = self.get_current_mpv_playlist(mpv)?;
+        let current_playlist = self.playlist_handler.playlist_ids(mpv)?;
         let video_hash = current_playlist
             .get(&playlist_index)
             .expect("The video index should always be correctly tracked");
@@ -304,7 +257,8 @@ impl MpvEventHandler {
                     info!("Mpv quit. Exiting playback");
 
                     // draining the playlist is okay, as mpv is done playing
-                    let videos = self.get_current_mpv_playlist(mpv)?;
+                    let mut handler = mem::take(&mut self.playlist_handler);
+                    let videos = handler.playlist_ids(mpv)?;
                     for (_, hash) in videos {
                         self.mark_video_watched(app, &hash).await?;
                         set_state_change(&app, &hash, false).await?;
@@ -324,8 +278,8 @@ impl MpvEventHandler {
                 // We don't need to check, whether other videos are still active, as they should
                 // have been marked inactive in the `Stop` handler.
                 self.mark_video_active(app, mpv, entry_id).await?;
-                self.apply_options(app, mpv, &self.get_cvideo_hash(mpv, 0)?)
-                    .await?;
+                let hash = self.get_cvideo_hash(mpv, 0)?;
+                self.apply_options(app, mpv, &hash).await?;
             }
             Event::ClientMessage(a) => {
                 debug!("Got Client Message event: '{}'", a.join(" "));
diff --git a/src/watch/events/playlist_handler.rs b/src/watch/events/playlist_handler.rs
new file mode 100644
index 0000000..8f2f322
--- /dev/null
+++ b/src/watch/events/playlist_handler.rs
@@ -0,0 +1,96 @@
+// yt - A fully featured command line YouTube client
+//
+// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of Yt.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::collections::HashMap;
+
+use anyhow::Result;
+use libmpv2::{events::PlaylistEntryId, mpv_node::MpvNode, Mpv};
+
+use crate::storage::video_database::extractor_hash::ExtractorHash;
+
+#[derive(Debug, Default)]
+pub struct PlaylistHandler {
+    /// A map of the original file paths to the videos extractor hashes.
+    /// Used to get the extractor hash from a video returned by mpv
+    playlist_cache: HashMap<String, ExtractorHash>,
+
+    /// A map of the playlist_entry_id field to their corresponding extractor hashes.
+    playlist_ids: HashMap<PlaylistEntryId, ExtractorHash>,
+}
+impl PlaylistHandler {
+    pub fn from_cache(cache: HashMap<String, ExtractorHash>) -> Self {
+        Self {
+            playlist_cache: cache,
+            playlist_ids: HashMap::new(),
+        }
+    }
+
+    pub fn reserve(&mut self, len: usize) {
+        self.playlist_cache.reserve(len)
+    }
+    pub fn add(&mut self, cache_path: String, extractor_hash: ExtractorHash) {
+        assert_eq!(
+            self.playlist_cache.insert(cache_path, extractor_hash),
+            None,
+            "Only new video should ever be added"
+        );
+    }
+
+    pub fn playlist_ids(&mut self, mpv: &Mpv) -> Result<&HashMap<PlaylistEntryId, ExtractorHash>> {
+        let mpv_playlist: Vec<(String, PlaylistEntryId)> = match mpv.get_property("playlist")? {
+            MpvNode::ArrayIter(array) => array
+                .map(|val| match val {
+                    MpvNode::MapIter(map) => {
+                        struct BuildPlaylistEntry {
+                            filename: Option<String>,
+                            id: Option<PlaylistEntryId>,
+                        }
+                        let mut entry = BuildPlaylistEntry {
+                            filename: None,
+                            id: None,
+                        };
+
+                        map.for_each(|(key, value)| match key.as_str() {
+                            "filename" => {
+                                entry.filename = Some(value.str().expect("work").to_owned())
+                            }
+                            "id" => {
+                                entry.id = Some(PlaylistEntryId::new(value.i64().expect("Works")))
+                            }
+                            _ => (),
+                        });
+                        (entry.filename.expect("is some"), entry.id.expect("is some"))
+                    }
+                    _ => unreachable!(),
+                })
+                .collect(),
+            _ => unreachable!(),
+        };
+
+        let mut playlist: HashMap<PlaylistEntryId, ExtractorHash> =
+            HashMap::with_capacity(mpv_playlist.len());
+        for (path, key) in mpv_playlist {
+            let hash = self
+                .playlist_cache
+                .get(&path)
+                .expect("All path should also be stored in the cache")
+                .to_owned();
+            playlist.insert(key, hash);
+        }
+
+        for (id, hash) in playlist {
+            if !self.playlist_ids.contains_key(&id) {
+                self.playlist_ids.insert(id, hash);
+            }
+        }
+
+        Ok(&self.playlist_ids)
+    }
+}