about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-08-25 15:48:54 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-08-25 15:48:54 +0200
commit09d03dc79c6e39f12392b3a9edf5c121519bfc92 (patch)
tree58b4d43f696a4e1de16bedb931677a577b81d3ea
parentdocs(cache): Add context to the cache_path deletion error (diff)
downloadyt-09d03dc79c6e39f12392b3a9edf5c121519bfc92.tar.gz
yt-09d03dc79c6e39f12392b3a9edf5c121519bfc92.zip
fix(downloader): Be smarter, when checking for available cache
-rw-r--r--src/download/mod.rs98
1 files changed, 86 insertions, 12 deletions
diff --git a/src/download/mod.rs b/src/download/mod.rs
index d1de3ff..b29594f 100644
--- a/src/download/mod.rs
+++ b/src/download/mod.rs
@@ -53,9 +53,22 @@ impl CurrentDownload {
     }
 }
 
+enum CacheSizeCheck {
+    /// The video can be downloaded
+    Fits,
+
+    /// The video and the current cache size together would exceed the size
+    TooLarge,
+
+    /// The video would not even fit into the empty cache
+    ExceedsMaxCacheSize,
+}
+
 pub struct Downloader {
     current_download: Option<CurrentDownload>,
     video_size_cache: HashMap<ExtractorHash, u64>,
+    printed_warning: bool,
+    cached_cache_allocation: Option<u64>,
 }
 
 impl Downloader {
@@ -63,6 +76,71 @@ impl Downloader {
         Self {
             current_download: None,
             video_size_cache: HashMap::new(),
+            printed_warning: false,
+            cached_cache_allocation: None,
+        }
+    }
+
+    /// Check if enough cache is available. Will wait for 10s if it's not.
+    async fn is_enough_cache_available(
+        &mut self,
+        app: &App,
+        max_cache_size: u64,
+        next_video: &Video,
+    ) -> Result<CacheSizeCheck> {
+        if let Some(cdownload) = &self.current_download {
+            if cdownload.extractor_hash == next_video.extractor_hash {
+                // If the video is already being downloaded it will always fit. Otherwise the
+                // download would not have been started.
+                return Ok(CacheSizeCheck::Fits);
+            }
+        }
+        let cache_allocation = Self::get_current_cache_allocation(&app).await?;
+        let video_size = self.get_approx_video_size(&app, &next_video).await?;
+
+        if video_size >= max_cache_size {
+            error!(
+                "The video '{}' ({}) exceeds the maximum cache size ({})! \
+                 Please set a bigger maximum (`--max-cache-size`) or skip it.",
+                next_video.title,
+                Bytes::new(video_size),
+                Bytes::new(max_cache_size)
+            );
+
+            return Ok(CacheSizeCheck::ExceedsMaxCacheSize);
+        }
+
+        if cache_allocation + video_size >= max_cache_size {
+            if !self.printed_warning {
+                warn!(
+                    "Can't download video: '{}' ({}) as it's too large for the cache ({} of {} allocated). \
+                     Waiting for cache size reduction..",
+                    next_video.title, Bytes::new(video_size), Bytes::new(cache_allocation), Bytes::new(max_cache_size)
+                    );
+                self.printed_warning = true;
+            }
+            if let Some(cca) = self.cached_cache_allocation {
+                if cca != cache_allocation {
+                    warn!(
+                        "Current cache size has changed, it's now: '{}'",
+                        Bytes::new(cache_allocation)
+                    );
+                    self.cached_cache_allocation = Some(cache_allocation);
+                }
+            } else {
+                info!(
+                    "Current cache size allocation: '{}'",
+                    Bytes::new(cache_allocation)
+                );
+                self.cached_cache_allocation = Some(cache_allocation);
+            }
+
+            // Wait and hope, that a large video is deleted from the cache.
+            time::sleep(Duration::from_secs(10)).await;
+            Ok(CacheSizeCheck::TooLarge)
+        } else {
+            self.printed_warning = false;
+            Ok(CacheSizeCheck::Fits)
         }
     }
 
@@ -72,18 +150,14 @@ impl Downloader {
     /// This will run, until the database doesn't contain any watchable videos
     pub async fn consume(&mut self, app: Arc<App>, max_cache_size: u64) -> Result<()> {
         while let Some(next_video) = get_next_uncached_video(&app).await? {
-            let cache_allocation = Self::get_current_cache_allocation(&app).await?;
-            let video_size = self.get_approx_video_size(&app, &next_video).await?;
-            if cache_allocation + video_size >= max_cache_size {
-                warn!(
-                    "Can't download video: '{}' ({}) as it's too large for the cache ({} of {} allocated).",
-                    next_video.title, Bytes::new(video_size), Bytes::new(cache_allocation), Bytes::new(max_cache_size)
-                );
-
-                // Wait and hope, that a large video is deleted from the cache.
-                time::sleep(Duration::from_secs(10)).await;
-                continue;
-            }
+            match self
+                .is_enough_cache_available(&app, max_cache_size, &next_video)
+                .await?
+            {
+                CacheSizeCheck::Fits => (),
+                CacheSizeCheck::TooLarge => continue,
+                CacheSizeCheck::ExceedsMaxCacheSize => bail!("Giving up."),
+            };
 
             if let Some(_) = &self.current_download {
                 let current_download = self.current_download.take().expect("Is Some");