msansm1/rss-podcast-dlpublic Fork
⑂ 90dae95
Commits
⬇ Clone ▾
git clone https://git.rickub.com/msansm1/rss-podcast-dl.git
git clone ssh://git@rickub.com/msansm1/rss-podcast-dl.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

Suppression de la synchro bloquante morte et des dépendances inutilisées

- suppression de src/rss/sync.rs : l'implémentation de synchronisation
  bloquante n'était plus appelée que par du code mort de app.rs ; la
  chaîne vivante reste sync_task -> async_sync -> downloader/parser
- suppression des méthodes mortes save_feeds / sync_all_feeds /
  sync_single_feed de RssPodcastApp (app.rs) et de leurs import
- suppression consécutive de set_error / set_info / set_sync_state
  dans AppState (plus aucun appelant)
- Cargo.toml : retrait de tokio, md5, pathdiff, diligent-date-parser,
  mp3-metadata et futures-util (jamais référencées dans le code) ;
  tempfile passe en dev-dependencies ; features reqwest réduites à
  blocking (json/stream inutilisées)
msansm1 committed 2026-09-28T22:20:50+02:00 Browse files
90dae95 parent: 812d86c
modified Cargo.lock +0 -54
@@ -1395,7 +1395,6 @@ dependencies = [
13951395 "futures-core",
13961396 "futures-io",
13971397 "futures-macro",
1398- "futures-sink",
13991398 "futures-task",
14001399 "memchr",
14011400 "pin-project-lite",
@@ -2234,12 +2233,6 @@ dependencies = [
22342233 "libc",
22352234 ]
22362235
2237-[[package]]
2238-name = "md5"
2239-version = "0.7.0"
2240-source = "registry+https://github.com/rust-lang/crates.io-index"
2241-checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
2242-
22432236 [[package]]
22442237 name = "memchr"
22452238 version = "2.8.2"
@@ -2301,12 +2294,6 @@ dependencies = [
23012294 "pxfm",
23022295 ]
23032296
2304-[[package]]
2305-name = "mp3-metadata"
2306-version = "0.3.4"
2307-source = "registry+https://github.com/rust-lang/crates.io-index"
2308-checksum = "e4eaa3a228dc218005d769b88ce3a0310f49152f2029125289e94c49474c9cc4"
2309-
23102297 [[package]]
23112298 name = "naga"
23122299 version = "29.0.4"
@@ -2884,12 +2871,6 @@ dependencies = [
28842871 "windows-link",
28852872 ]
28862873
2887-[[package]]
2888-name = "pathdiff"
2889-version = "0.2.3"
2890-source = "registry+https://github.com/rust-lang/crates.io-index"
2891-checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
2892-
28932874 [[package]]
28942875 name = "peniko"
28952876 version = "0.6.1"
@@ -3283,12 +3264,10 @@ dependencies = [
32833264 "system-configuration",
32843265 "tokio",
32853266 "tokio-native-tls",
3286- "tokio-util",
32873267 "tower-service",
32883268 "url",
32893269 "wasm-bindgen",
32903270 "wasm-bindgen-futures",
3291- "wasm-streams",
32923271 "web-sys",
32933272 "winreg",
32943273 ]
@@ -3332,16 +3311,11 @@ name = "rss-podcast-dl-egui"
33323311 version = "0.2.0"
33333312 dependencies = [
33343313 "chrono",
3335- "diligent-date-parser",
33363314 "eframe",
33373315 "egui",
33383316 "env_logger",
3339- "futures-util",
33403317 "id3",
33413318 "log",
3342- "md5",
3343- "mp3-metadata",
3344- "pathdiff",
33453319 "reqwest",
33463320 "rfd",
33473321 "rss",
@@ -3349,7 +3323,6 @@ dependencies = [
33493323 "serde_json",
33503324 "tempfile",
33513325 "thiserror 1.0.69",
3352- "tokio",
33533326 "uuid",
33543327 ]
33553328
@@ -3962,25 +3935,11 @@ dependencies = [
39623935 "bytes",
39633936 "libc",
39643937 "mio",
3965- "parking_lot",
39663938 "pin-project-lite",
3967- "signal-hook-registry",
39683939 "socket2 0.6.4",
3969- "tokio-macros",
39703940 "windows-sys 0.61.2",
39713941 ]
39723942
3973-[[package]]
3974-name = "tokio-macros"
3975-version = "2.7.0"
3976-source = "registry+https://github.com/rust-lang/crates.io-index"
3977-checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
3978-dependencies = [
3979- "proc-macro2",
3980- "quote",
3981- "syn",
3982-]
3983-
39843943 [[package]]
39853944 name = "tokio-native-tls"
39863945 version = "0.3.1"
@@ -4327,19 +4286,6 @@ dependencies = [
43274286 "unicode-ident",
43284287 ]
43294288
4330-[[package]]
4331-name = "wasm-streams"
4332-version = "0.4.2"
4333-source = "registry+https://github.com/rust-lang/crates.io-index"
4334-checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
4335-dependencies = [
4336- "futures-util",
4337- "js-sys",
4338- "wasm-bindgen",
4339- "wasm-bindgen-futures",
4340- "web-sys",
4341-]
4342-
43434289 [[package]]
43444290 name = "wayland-backend"
43454291 version = "0.3.15"
@@ -1395,7 +1395,6 @@ dependencies = [
1395 "futures-core",1395 "futures-core",
1396 "futures-io",1396 "futures-io",
1397 "futures-macro",1397 "futures-macro",
1398- "futures-sink",
1399 "futures-task",1398 "futures-task",
1400 "memchr",1399 "memchr",
1401 "pin-project-lite",1400 "pin-project-lite",
@@ -2234,12 +2233,6 @@ dependencies = [
2234 "libc",2233 "libc",
2235 ]2234 ]
2236 2235
2237-[[package]]
2238-name = "md5"
2239-version = "0.7.0"
2240-source = "registry+https://github.com/rust-lang/crates.io-index"
2241-checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
2242-
2243 [[package]]2236 [[package]]
2244 name = "memchr"2237 name = "memchr"
2245 version = "2.8.2"2238 version = "2.8.2"
@@ -2301,12 +2294,6 @@ dependencies = [
2301 "pxfm",2294 "pxfm",
2302 ]2295 ]
2303 2296
2304-[[package]]
2305-name = "mp3-metadata"
2306-version = "0.3.4"
2307-source = "registry+https://github.com/rust-lang/crates.io-index"
2308-checksum = "e4eaa3a228dc218005d769b88ce3a0310f49152f2029125289e94c49474c9cc4"
2309-
2310 [[package]]2297 [[package]]
2311 name = "naga"2298 name = "naga"
2312 version = "29.0.4"2299 version = "29.0.4"
@@ -2884,12 +2871,6 @@ dependencies = [
2884 "windows-link",2871 "windows-link",
2885 ]2872 ]
2886 2873
2887-[[package]]
2888-name = "pathdiff"
2889-version = "0.2.3"
2890-source = "registry+https://github.com/rust-lang/crates.io-index"
2891-checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
2892-
2893 [[package]]2874 [[package]]
2894 name = "peniko"2875 name = "peniko"
2895 version = "0.6.1"2876 version = "0.6.1"
@@ -3283,12 +3264,10 @@ dependencies = [
3283 "system-configuration",3264 "system-configuration",
3284 "tokio",3265 "tokio",
3285 "tokio-native-tls",3266 "tokio-native-tls",
3286- "tokio-util",
3287 "tower-service",3267 "tower-service",
3288 "url",3268 "url",
3289 "wasm-bindgen",3269 "wasm-bindgen",
3290 "wasm-bindgen-futures",3270 "wasm-bindgen-futures",
3291- "wasm-streams",
3292 "web-sys",3271 "web-sys",
3293 "winreg",3272 "winreg",
3294 ]3273 ]
@@ -3332,16 +3311,11 @@ name = "rss-podcast-dl-egui"
3332 version = "0.2.0"3311 version = "0.2.0"
3333 dependencies = [3312 dependencies = [
3334 "chrono",3313 "chrono",
3335- "diligent-date-parser",
3336 "eframe",3314 "eframe",
3337 "egui",3315 "egui",
3338 "env_logger",3316 "env_logger",
3339- "futures-util",
3340 "id3",3317 "id3",
3341 "log",3318 "log",
3342- "md5",
3343- "mp3-metadata",
3344- "pathdiff",
3345 "reqwest",3319 "reqwest",
3346 "rfd",3320 "rfd",
3347 "rss",3321 "rss",
@@ -3349,7 +3323,6 @@ dependencies = [
3349 "serde_json",3323 "serde_json",
3350 "tempfile",3324 "tempfile",
3351 "thiserror 1.0.69",3325 "thiserror 1.0.69",
3352- "tokio",
3353 "uuid",3326 "uuid",
3354 ]3327 ]
3355 3328
@@ -3962,25 +3935,11 @@ dependencies = [
3962 "bytes",3935 "bytes",
3963 "libc",3936 "libc",
3964 "mio",3937 "mio",
3965- "parking_lot",
3966 "pin-project-lite",3938 "pin-project-lite",
3967- "signal-hook-registry",
3968 "socket2 0.6.4",3939 "socket2 0.6.4",
3969- "tokio-macros",
3970 "windows-sys 0.61.2",3940 "windows-sys 0.61.2",
3971 ]3941 ]
3972 3942
3973-[[package]]
3974-name = "tokio-macros"
3975-version = "2.7.0"
3976-source = "registry+https://github.com/rust-lang/crates.io-index"
3977-checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
3978-dependencies = [
3979- "proc-macro2",
3980- "quote",
3981- "syn",
3982-]
3983-
3984 [[package]]3943 [[package]]
3985 name = "tokio-native-tls"3944 name = "tokio-native-tls"
3986 version = "0.3.1"3945 version = "0.3.1"
@@ -4327,19 +4286,6 @@ dependencies = [
4327 "unicode-ident",4286 "unicode-ident",
4328 ]4287 ]
4329 4288
4330-[[package]]
4331-name = "wasm-streams"
4332-version = "0.4.2"
4333-source = "registry+https://github.com/rust-lang/crates.io-index"
4334-checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
4335-dependencies = [
4336- "futures-util",
4337- "js-sys",
4338- "wasm-bindgen",
4339- "wasm-bindgen-futures",
4340- "web-sys",
4341-]
4342-
4343 [[package]]4289 [[package]]
4344 name = "wayland-backend"4290 name = "wayland-backend"
4345 version = "0.3.15"4291 version = "0.3.15"
modified Cargo.toml +5 -21
@@ -21,10 +21,7 @@ serde = { version = "1.0", features = ["derive"] }
2121 serde_json = "1.0"
2222
2323 # HTTP requests
24-reqwest = { version = "0.11", features = ["json", "stream", "blocking"] }
25-
26-# Async runtime
27-tokio = { version = "1.0", features = ["full"] }
24+reqwest = { version = "0.11", features = ["blocking"] }
2825
2926 # RSS parsing
3027 rss = "2.0"
@@ -39,32 +36,19 @@ env_logger = "0.10"
3936 # UUID generation
4037 uuid = { version = "1.0", features = ["v4", "serde"] }
4138
42-# Path handling
43-pathdiff = "0.2"
44-
4539 # Error handling
4640 thiserror = "1.0"
4741
48-# Testing utilities
49-tempfile = "3.0"
50-
51-# Async utilities
52-futures-util = "0.3"
53-
54-# Hashing for filename generation
55-md5 = "0.7"
56-
5742 # File dialogs for export/import
5843 rfd = "0.12"
5944
60-# Date parsing
61-# The rss crate already includes chrono, but we need additional date parsing
62-diligent-date-parser = "0.1"
63-
6445 # Audio metadata parsing
65-mp3-metadata = "0.3"
6646 id3 = "1.13"
6747
48+[dev-dependencies]
49+# Testing utilities
50+tempfile = "3.0"
51+
6852 # Windows API compatibility for older eframe versions - not needed with eframe 0.35
6953 # [target.'cfg(windows)'.dependencies]
7054 # winapi = { version = "0.3", features = ["winuser", "windef"] }
@@ -21,10 +21,7 @@ serde = { version = "1.0", features = ["derive"] }
21 serde_json = "1.0"21 serde_json = "1.0"
22 22
23 # HTTP requests23 # HTTP requests
24-reqwest = { version = "0.11", features = ["json", "stream", "blocking"] }24+reqwest = { version = "0.11", features = ["blocking"] }
25-
26-# Async runtime
27-tokio = { version = "1.0", features = ["full"] }
28 25
29 # RSS parsing26 # RSS parsing
30 rss = "2.0"27 rss = "2.0"
@@ -39,32 +36,19 @@ env_logger = "0.10"
39 # UUID generation36 # UUID generation
40 uuid = { version = "1.0", features = ["v4", "serde"] }37 uuid = { version = "1.0", features = ["v4", "serde"] }
41 38
42-# Path handling
43-pathdiff = "0.2"
44-
45 # Error handling39 # Error handling
46 thiserror = "1.0"40 thiserror = "1.0"
47 41
48-# Testing utilities
49-tempfile = "3.0"
50-
51-# Async utilities
52-futures-util = "0.3"
53-
54-# Hashing for filename generation
55-md5 = "0.7"
56-
57 # File dialogs for export/import42 # File dialogs for export/import
58 rfd = "0.12"43 rfd = "0.12"
59 44
60-# Date parsing
61-# The rss crate already includes chrono, but we need additional date parsing
62-diligent-date-parser = "0.1"
63-
64 # Audio metadata parsing45 # Audio metadata parsing
65-mp3-metadata = "0.3"
66 id3 = "1.13"46 id3 = "1.13"
67 47
48+[dev-dependencies]
49+# Testing utilities
50+tempfile = "3.0"
51+
68 # Windows API compatibility for older eframe versions - not needed with eframe 0.3552 # Windows API compatibility for older eframe versions - not needed with eframe 0.35
69 # [target.'cfg(windows)'.dependencies]53 # [target.'cfg(windows)'.dependencies]
70 # winapi = { version = "0.3", features = ["winuser", "windef"] }54 # winapi = { version = "0.3", features = ["winuser", "windef"] }
modified src/app.rs +1 -71
@@ -3,7 +3,7 @@
33 //! This module contains the main egui application and state management.
44
55 use crate::config::AppState;
6-use crate::config::storage::{load_feeds_default, save_feeds_default};
6+use crate::config::storage::load_feeds_default;
77 use crate::config::Theme;
88 use eframe::egui;
99 use log::{error, info};
@@ -24,76 +24,6 @@ impl RssPodcastApp {
2424 clear_color: egui::Color32::from_rgb(30, 30, 30),
2525 }
2626 }
27-
28- /// Saves the current feeds to the default configuration
29- #[allow(dead_code)]
30- fn save_feeds(&self) {
31- if let Err(e) = save_feeds_default(&self.state.feeds) {
32- error!("Failed to save feeds: {}", e);
33- }
34- }
35-
36- /// Synchronizes all configured feeds
37- /// Note: This method is currently duplicated in ui/home.rs for technical reasons
38- #[allow(dead_code)]
39- fn sync_all_feeds(&mut self) {
40- if !self.state.has_feeds() {
41- self.state.set_info("Aucun flux à synchroniser".to_string());
42- return;
43- }
44-
45- self.state.set_sync_state(true);
46- self.state.set_info("Synchronisation de tous les flux...".to_string());
47-
48- info!("Starting synchronization of all feeds");
49-
50- // Perform synchronization
51- let result = crate::rss::sync::sync_all_feeds(&mut self.state.feeds);
52-
53- self.state.set_sync_state(false);
54-
55- match result {
56- Ok(success_count) => {
57- self.state.set_info(format!("{} flux synchronisés avec succès", success_count));
58- // Save the updated feeds
59- self.save_feeds();
60- }
61- Err(e) => {
62- self.state.set_error(format!("Erreur de synchronisation: {}", e));
63- }
64- }
65- }
66-
67- /// Synchronizes a single feed by index
68- /// Note: This method is currently duplicated in ui/feed_list.rs for technical reasons
69- #[allow(dead_code)]
70- fn sync_single_feed(&mut self, index: usize) {
71- if index >= self.state.feeds.len() {
72- self.state.set_error("Index de flux invalide".to_string());
73- return;
74- }
75-
76- self.state.set_sync_state(true);
77- self.state.set_info(format!("Synchronisation du flux: {}", self.state.feeds[index].name));
78-
79- info!("Starting synchronization of feed: {}", self.state.feeds[index].name);
80-
81- // Perform synchronization for single feed
82- let result = crate::rss::sync::sync_feed(&mut self.state.feeds[index]);
83-
84- self.state.set_sync_state(false);
85-
86- match result {
87- Ok(episodes) => {
88- self.state.set_info(format!("Flux synchronisé: {} épisodes trouvés", episodes.len()));
89- // Save the updated feeds
90- self.save_feeds();
91- }
92- Err(e) => {
93- self.state.set_error(format!("Erreur de synchronisation: {}", e));
94- }
95- }
96- }
9727 }
9828
9929 impl Default for RssPodcastApp {
@@ -3,7 +3,7 @@
3 //! This module contains the main egui application and state management.3 //! This module contains the main egui application and state management.
4 4
5 use crate::config::AppState;5 use crate::config::AppState;
6-use crate::config::storage::{load_feeds_default, save_feeds_default};6+use crate::config::storage::load_feeds_default;
7 use crate::config::Theme;7 use crate::config::Theme;
8 use eframe::egui;8 use eframe::egui;
9 use log::{error, info};9 use log::{error, info};
@@ -24,76 +24,6 @@ impl RssPodcastApp {
24 clear_color: egui::Color32::from_rgb(30, 30, 30),24 clear_color: egui::Color32::from_rgb(30, 30, 30),
25 }25 }
26 }26 }
27-
28- /// Saves the current feeds to the default configuration
29- #[allow(dead_code)]
30- fn save_feeds(&self) {
31- if let Err(e) = save_feeds_default(&self.state.feeds) {
32- error!("Failed to save feeds: {}", e);
33- }
34- }
35-
36- /// Synchronizes all configured feeds
37- /// Note: This method is currently duplicated in ui/home.rs for technical reasons
38- #[allow(dead_code)]
39- fn sync_all_feeds(&mut self) {
40- if !self.state.has_feeds() {
41- self.state.set_info("Aucun flux à synchroniser".to_string());
42- return;
43- }
44-
45- self.state.set_sync_state(true);
46- self.state.set_info("Synchronisation de tous les flux...".to_string());
47-
48- info!("Starting synchronization of all feeds");
49-
50- // Perform synchronization
51- let result = crate::rss::sync::sync_all_feeds(&mut self.state.feeds);
52-
53- self.state.set_sync_state(false);
54-
55- match result {
56- Ok(success_count) => {
57- self.state.set_info(format!("{} flux synchronisés avec succès", success_count));
58- // Save the updated feeds
59- self.save_feeds();
60- }
61- Err(e) => {
62- self.state.set_error(format!("Erreur de synchronisation: {}", e));
63- }
64- }
65- }
66-
67- /// Synchronizes a single feed by index
68- /// Note: This method is currently duplicated in ui/feed_list.rs for technical reasons
69- #[allow(dead_code)]
70- fn sync_single_feed(&mut self, index: usize) {
71- if index >= self.state.feeds.len() {
72- self.state.set_error("Index de flux invalide".to_string());
73- return;
74- }
75-
76- self.state.set_sync_state(true);
77- self.state.set_info(format!("Synchronisation du flux: {}", self.state.feeds[index].name));
78-
79- info!("Starting synchronization of feed: {}", self.state.feeds[index].name);
80-
81- // Perform synchronization for single feed
82- let result = crate::rss::sync::sync_feed(&mut self.state.feeds[index]);
83-
84- self.state.set_sync_state(false);
85-
86- match result {
87- Ok(episodes) => {
88- self.state.set_info(format!("Flux synchronisé: {} épisodes trouvés", episodes.len()));
89- // Save the updated feeds
90- self.save_feeds();
91- }
92- Err(e) => {
93- self.state.set_error(format!("Erreur de synchronisation: {}", e));
94- }
95- }
96- }
97 }27 }
98 28
99 impl Default for RssPodcastApp {29 impl Default for RssPodcastApp {
modified src/config/app_state.rs +0 -33
@@ -301,16 +301,6 @@ impl AppState {
301301 }
302302 }
303303
304- /// Sets an error message
305- pub fn set_error(&mut self, message: impl Into<String>) {
306- self.error_message = Some(message.into());
307- }
308-
309- /// Sets an info message
310- pub fn set_info(&mut self, message: impl Into<String>) {
311- self.info_message = Some(message.into());
312- }
313-
314304 /// Clears all messages
315305 #[allow(dead_code)]
316306 pub fn clear_messages(&mut self) {
@@ -352,11 +342,6 @@ impl AppState {
352342 self.sync_in_progress
353343 }
354344
355- /// Sets the synchronization state
356- pub fn set_sync_state(&mut self, syncing: bool) {
357- self.sync_in_progress = syncing;
358- }
359-
360345 /// Updates the download progress for a feed
361346 #[allow(dead_code)]
362347 pub fn update_download_progress(&mut self, feed_id: &str, progress: f32) {
@@ -647,24 +632,6 @@ mod tests {
647632 assert_eq!(app_state.enabled_feed_count(), 2);
648633 }
649634
650- #[test]
651- fn test_messages() {
652- let mut app_state = AppState::new("test/config.json");
653-
654- assert!(app_state.error_message.is_none());
655- assert!(app_state.info_message.is_none());
656-
657- app_state.set_error("Test error");
658- assert_eq!(app_state.error_message, Some("Test error".to_string()));
659-
660- app_state.set_info("Test info");
661- assert_eq!(app_state.info_message, Some("Test info".to_string()));
662-
663- app_state.clear_messages();
664- assert!(app_state.error_message.is_none());
665- assert!(app_state.info_message.is_none());
666- }
667-
668635 #[test]
669636 fn test_download_progress() {
670637 let mut app_state = AppState::new("test/config.json");
@@ -301,16 +301,6 @@ impl AppState {
301 }301 }
302 }302 }
303 303
304- /// Sets an error message
305- pub fn set_error(&mut self, message: impl Into<String>) {
306- self.error_message = Some(message.into());
307- }
308-
309- /// Sets an info message
310- pub fn set_info(&mut self, message: impl Into<String>) {
311- self.info_message = Some(message.into());
312- }
313-
314 /// Clears all messages304 /// Clears all messages
315 #[allow(dead_code)]305 #[allow(dead_code)]
316 pub fn clear_messages(&mut self) {306 pub fn clear_messages(&mut self) {
@@ -352,11 +342,6 @@ impl AppState {
352 self.sync_in_progress342 self.sync_in_progress
353 }343 }
354 344
355- /// Sets the synchronization state
356- pub fn set_sync_state(&mut self, syncing: bool) {
357- self.sync_in_progress = syncing;
358- }
359-
360 /// Updates the download progress for a feed345 /// Updates the download progress for a feed
361 #[allow(dead_code)]346 #[allow(dead_code)]
362 pub fn update_download_progress(&mut self, feed_id: &str, progress: f32) {347 pub fn update_download_progress(&mut self, feed_id: &str, progress: f32) {
@@ -647,24 +632,6 @@ mod tests {
647 assert_eq!(app_state.enabled_feed_count(), 2);632 assert_eq!(app_state.enabled_feed_count(), 2);
648 }633 }
649 634
650- #[test]
651- fn test_messages() {
652- let mut app_state = AppState::new("test/config.json");
653-
654- assert!(app_state.error_message.is_none());
655- assert!(app_state.info_message.is_none());
656-
657- app_state.set_error("Test error");
658- assert_eq!(app_state.error_message, Some("Test error".to_string()));
659-
660- app_state.set_info("Test info");
661- assert_eq!(app_state.info_message, Some("Test info".to_string()));
662-
663- app_state.clear_messages();
664- assert!(app_state.error_message.is_none());
665- assert!(app_state.info_message.is_none());
666- }
667-
668 #[test]635 #[test]
669 fn test_download_progress() {636 fn test_download_progress() {
670 let mut app_state = AppState::new("test/config.json");637 let mut app_state = AppState::new("test/config.json");
modified src/rss/mod.rs +0 -4
@@ -7,7 +7,6 @@ pub mod audio_metadata;
77 pub mod downloader;
88 pub mod episode_extractor;
99 pub mod parser;
10-pub mod sync;
1110
1211 // Re-export main types for convenience
1312 // Note: Some exports may not be directly used in the current codebase but are
@@ -27,6 +26,3 @@ pub use episode_extractor::{ExtractionError, extract_episodes, extract_episodes_
2726 #[allow(dead_code)]
2827 #[allow(unused_imports)]
2928 pub use parser::{FeedClient, ParserError, RssFeed, fetch_feed, parse_rss_content};
30-#[allow(dead_code)]
31-#[allow(unused_imports)]
32-pub use sync::{download_new_episodes, resolve_filename_conflict, sync_all_feeds, sync_feed, SyncError};
@@ -7,7 +7,6 @@ pub mod audio_metadata;
7 pub mod downloader;7 pub mod downloader;
8 pub mod episode_extractor;8 pub mod episode_extractor;
9 pub mod parser;9 pub mod parser;
10-pub mod sync;
11 10
12 // Re-export main types for convenience11 // Re-export main types for convenience
13 // Note: Some exports may not be directly used in the current codebase but are12 // Note: Some exports may not be directly used in the current codebase but are
@@ -27,6 +26,3 @@ pub use episode_extractor::{ExtractionError, extract_episodes, extract_episodes_
27 #[allow(dead_code)]26 #[allow(dead_code)]
28 #[allow(unused_imports)]27 #[allow(unused_imports)]
29 pub use parser::{FeedClient, ParserError, RssFeed, fetch_feed, parse_rss_content};28 pub use parser::{FeedClient, ParserError, RssFeed, fetch_feed, parse_rss_content};
30-#[allow(dead_code)]
31-#[allow(unused_imports)]
32-pub use sync::{download_new_episodes, resolve_filename_conflict, sync_all_feeds, sync_feed, SyncError};
deleted src/rss/sync.rs +0 -217
deleted file mode 100644
@@ -1,217 +0,0 @@
1-//! Synchronization module for RSS Podcast Downloader
2-//!
3-//! This module handles the synchronization of RSS feeds, including fetching,
4-//! parsing, episode extraction, and downloading new episodes.
5-
6-use crate::config::{Episode, Feed};
7-use crate::rss::downloader::download_episode;
8-use crate::rss::episode_extractor::extract_episodes;
9-use crate::rss::parser::{fetch_feed, parse_rss_content};
10-use chrono::Utc;
11-use log::{error, info, warn};
12-use std::path::Path;
13-
14-/// Error types for synchronization operations
15-#[derive(Debug)]
16-pub enum SyncError {
17- /// Failed to fetch the RSS feed
18- FetchError(String),
19- /// Failed to parse the RSS content
20- ParseError(String),
21- /// Failed to extract episodes from the feed
22- ExtractionError(String),
23- /// Failed to download an episode
24- DownloadError(String),
25- /// No episodes found in the feed
26- NoEpisodes,
27- /// Feed is disabled
28- FeedDisabled,
29- /// Invalid URL
30- InvalidUrl(String),
31- /// Invalid download path
32- InvalidPath(String),
33- /// Episode already downloaded
34- #[allow(dead_code)]
35- EpisodeAlreadyExists(String),
36-}
37-
38-impl std::fmt::Display for SyncError {
39- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40- match self {
41- SyncError::FetchError(msg) => write!(f, "Fetch error: {}", msg),
42- SyncError::ParseError(msg) => write!(f, "Parse error: {}", msg),
43- SyncError::ExtractionError(msg) => write!(f, "Extraction error: {}", msg),
44- SyncError::DownloadError(msg) => write!(f, "Download error: {}", msg),
45- SyncError::NoEpisodes => write!(f, "No episodes found in feed"),
46- SyncError::FeedDisabled => write!(f, "Feed is disabled"),
47- SyncError::InvalidUrl(msg) => write!(f, "Invalid URL: {}", msg),
48- SyncError::InvalidPath(msg) => write!(f, "Invalid path: {}", msg),
49- SyncError::EpisodeAlreadyExists(msg) => write!(f, "Episode already exists: {}", msg),
50- }
51- }
52-}
53-
54-impl std::error::Error for SyncError {}
55-
56-/// Synchronizes a single feed: fetches, parses, extracts episodes, and downloads new ones
57-pub fn sync_feed(feed: &mut Feed) -> Result<Vec<Episode>, SyncError> {
58- if !feed.enabled {
59- return Err(SyncError::FeedDisabled);
60- }
61-
62- // Validate URL
63- if feed.url.is_empty() {
64- return Err(SyncError::InvalidUrl("URL cannot be empty".to_string()));
65- }
66-
67- // Validate download path
68- if feed.download_path.to_string_lossy().is_empty() {
69- return Err(SyncError::InvalidPath("Download path cannot be empty".to_string()));
70- }
71-
72- info!("Starting synchronization for feed: {} ({})", feed.name, feed.url);
73-
74- // Fetch the feed content
75- let content = fetch_feed(&feed.url)
76- .map_err(|e| {
77- error!("Failed to fetch feed {}: {}", feed.url, e);
78- SyncError::FetchError(e.to_string())
79- })?;
80-
81- // Parse the RSS content
82- let channel = parse_rss_content(&content)
83- .map_err(|e| {
84- error!("Failed to parse feed {}: {}", feed.url, e);
85- SyncError::ParseError(e.to_string())
86- })?;
87-
88- // Extract episodes from the channel
89- let episodes = extract_episodes(&channel)
90- .map_err(|e| {
91- error!("Failed to extract episodes from feed {}: {}", feed.url, e);
92- SyncError::ExtractionError(e.to_string())
93- })?;
94-
95- if episodes.is_empty() {
96- warn!("No episodes found in feed: {}", feed.url);
97- return Err(SyncError::NoEpisodes);
98- }
99-
100- info!("Found {} episodes in feed: {}", episodes.len(), feed.url);
101-
102- // Update feed metadata
103- feed.last_checked = Some(Utc::now());
104- if let Some(latest) = episodes.iter().max_by(|a, b| a.pub_date.cmp(&b.pub_date)) {
105- feed.last_episode_date = Some(latest.pub_date);
106- }
107-
108- // Download new episodes
109- let downloaded_count = download_new_episodes(feed, &episodes)?;
110- info!("Downloaded {} new episodes for feed: {}", downloaded_count, feed.name);
111-
112- Ok(episodes)
113-}
114-
115-/// Downloads new episodes that haven't been downloaded yet
116-pub fn download_new_episodes(feed: &mut Feed, episodes: &[Episode]) -> Result<usize, SyncError> {
117- let mut downloaded_count = 0;
118-
119- for episode in episodes {
120- // Skip episodes that are already downloaded
121- if is_episode_downloaded(feed, episode) {
122- info!("Episode already downloaded: {}", episode.title);
123- continue;
124- }
125-
126- // Download the episode
127- match download_episode(episode, &feed.download_path, Box::new(|_progress| {})) {
128- Ok(_) => {
129- info!("Successfully downloaded episode: {}", episode.title);
130- downloaded_count += 1;
131-
132- // Update feed download statistics
133- feed.downloaded_episodes_count += 1;
134- feed.last_downloaded_episode_name = Some(episode.title.clone());
135- feed.last_downloaded_episode_date = Some(Utc::now());
136- }
137- Err(e) => {
138- error!("Failed to download episode {}: {}", episode.title, e);
139- return Err(SyncError::DownloadError(e.to_string()));
140- }
141- }
142- }
143-
144- Ok(downloaded_count)
145-}
146-
147-/// Checks if an episode has already been downloaded
148-fn is_episode_downloaded(feed: &Feed, episode: &Episode) -> bool {
149- !crate::rss::downloader::needs_download(episode, std::path::Path::new(&feed.download_path))
150-}
151-
152-/// Synchronizes all feeds in the collection
153-pub fn sync_all_feeds(feeds: &mut [Feed]) -> Result<usize, SyncError> {
154- info!("Starting synchronization of all {} feeds", feeds.len());
155-
156- let mut successful_feeds = 0;
157- let mut failed_feeds = 0;
158-
159- for feed in feeds.iter_mut() {
160- match sync_feed(feed) {
161- Ok(_) => {
162- successful_feeds += 1;
163- }
164- Err(e) => {
165- error!("Failed to sync feed {}: {}", feed.name, e);
166- failed_feeds += 1;
167- }
168- }
169- }
170-
171- info!(
172- "Synchronization complete: {} feeds succeeded, {} feeds failed",
173- successful_feeds, failed_feeds
174- );
175-
176- Ok(successful_feeds)
177-}
178-
179-/// Resolves filename conflicts by adding a numeric suffix
180-#[allow(dead_code)]
181-pub fn resolve_filename_conflict(original_path: &Path) -> std::path::PathBuf {
182- let mut path = original_path.to_path_buf();
183- let mut counter = 1;
184-
185- while path.exists() {
186- let stem = path.file_stem().unwrap_or_default().to_string_lossy();
187- let extension = path.extension().map(|ext| format!(".{}", ext.to_string_lossy())).unwrap_or_default();
188- let new_stem = format!("{}_{}", stem, counter);
189- let new_filename = format!("{}{}", new_stem, extension);
190- path.set_file_name(new_filename);
191- counter += 1;
192- }
193-
194- path
195-}
196-
197-#[cfg(test)]
198-mod tests {
199- use super::*;
200-
201- #[test]
202- fn test_resolve_filename_conflict() {
203- use tempfile::tempdir;
204-
205- // Create a temporary directory and a test file
206- let temp_dir = tempdir().unwrap();
207- let existing_file = temp_dir.path().join("test.mp3");
208- std::fs::File::create(&existing_file).unwrap();
209-
210- // Try to resolve conflict for the same path
211- let resolved = resolve_filename_conflict(&existing_file);
212-
213- // Should have added suffix since file exists
214- assert!(resolved.to_string_lossy().contains("_1"));
215- assert_ne!(resolved, existing_file);
216- }
217-}
deleted file mode 100644
@@ -1,217 +0,0 @@
1-//! Synchronization module for RSS Podcast Downloader
2-//!
3-//! This module handles the synchronization of RSS feeds, including fetching,
4-//! parsing, episode extraction, and downloading new episodes.
5-
6-use crate::config::{Episode, Feed};
7-use crate::rss::downloader::download_episode;
8-use crate::rss::episode_extractor::extract_episodes;
9-use crate::rss::parser::{fetch_feed, parse_rss_content};
10-use chrono::Utc;
11-use log::{error, info, warn};
12-use std::path::Path;
13-
14-/// Error types for synchronization operations
15-#[derive(Debug)]
16-pub enum SyncError {
17- /// Failed to fetch the RSS feed
18- FetchError(String),
19- /// Failed to parse the RSS content
20- ParseError(String),
21- /// Failed to extract episodes from the feed
22- ExtractionError(String),
23- /// Failed to download an episode
24- DownloadError(String),
25- /// No episodes found in the feed
26- NoEpisodes,
27- /// Feed is disabled
28- FeedDisabled,
29- /// Invalid URL
30- InvalidUrl(String),
31- /// Invalid download path
32- InvalidPath(String),
33- /// Episode already downloaded
34- #[allow(dead_code)]
35- EpisodeAlreadyExists(String),
36-}
37-
38-impl std::fmt::Display for SyncError {
39- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40- match self {
41- SyncError::FetchError(msg) => write!(f, "Fetch error: {}", msg),
42- SyncError::ParseError(msg) => write!(f, "Parse error: {}", msg),
43- SyncError::ExtractionError(msg) => write!(f, "Extraction error: {}", msg),
44- SyncError::DownloadError(msg) => write!(f, "Download error: {}", msg),
45- SyncError::NoEpisodes => write!(f, "No episodes found in feed"),
46- SyncError::FeedDisabled => write!(f, "Feed is disabled"),
47- SyncError::InvalidUrl(msg) => write!(f, "Invalid URL: {}", msg),
48- SyncError::InvalidPath(msg) => write!(f, "Invalid path: {}", msg),
49- SyncError::EpisodeAlreadyExists(msg) => write!(f, "Episode already exists: {}", msg),
50- }
51- }
52-}
53-
54-impl std::error::Error for SyncError {}
55-
56-/// Synchronizes a single feed: fetches, parses, extracts episodes, and downloads new ones
57-pub fn sync_feed(feed: &mut Feed) -> Result<Vec<Episode>, SyncError> {
58- if !feed.enabled {
59- return Err(SyncError::FeedDisabled);
60- }
61-
62- // Validate URL
63- if feed.url.is_empty() {
64- return Err(SyncError::InvalidUrl("URL cannot be empty".to_string()));
65- }
66-
67- // Validate download path
68- if feed.download_path.to_string_lossy().is_empty() {
69- return Err(SyncError::InvalidPath("Download path cannot be empty".to_string()));
70- }
71-
72- info!("Starting synchronization for feed: {} ({})", feed.name, feed.url);
73-
74- // Fetch the feed content
75- let content = fetch_feed(&feed.url)
76- .map_err(|e| {
77- error!("Failed to fetch feed {}: {}", feed.url, e);
78- SyncError::FetchError(e.to_string())
79- })?;
80-
81- // Parse the RSS content
82- let channel = parse_rss_content(&content)
83- .map_err(|e| {
84- error!("Failed to parse feed {}: {}", feed.url, e);
85- SyncError::ParseError(e.to_string())
86- })?;
87-
88- // Extract episodes from the channel
89- let episodes = extract_episodes(&channel)
90- .map_err(|e| {
91- error!("Failed to extract episodes from feed {}: {}", feed.url, e);
92- SyncError::ExtractionError(e.to_string())
93- })?;
94-
95- if episodes.is_empty() {
96- warn!("No episodes found in feed: {}", feed.url);
97- return Err(SyncError::NoEpisodes);
98- }
99-
100- info!("Found {} episodes in feed: {}", episodes.len(), feed.url);
101-
102- // Update feed metadata
103- feed.last_checked = Some(Utc::now());
104- if let Some(latest) = episodes.iter().max_by(|a, b| a.pub_date.cmp(&b.pub_date)) {
105- feed.last_episode_date = Some(latest.pub_date);
106- }
107-
108- // Download new episodes
109- let downloaded_count = download_new_episodes(feed, &episodes)?;
110- info!("Downloaded {} new episodes for feed: {}", downloaded_count, feed.name);
111-
112- Ok(episodes)
113-}
114-
115-/// Downloads new episodes that haven't been downloaded yet
116-pub fn download_new_episodes(feed: &mut Feed, episodes: &[Episode]) -> Result<usize, SyncError> {
117- let mut downloaded_count = 0;
118-
119- for episode in episodes {
120- // Skip episodes that are already downloaded
121- if is_episode_downloaded(feed, episode) {
122- info!("Episode already downloaded: {}", episode.title);
123- continue;
124- }
125-
126- // Download the episode
127- match download_episode(episode, &feed.download_path, Box::new(|_progress| {})) {
128- Ok(_) => {
129- info!("Successfully downloaded episode: {}", episode.title);
130- downloaded_count += 1;
131-
132- // Update feed download statistics
133- feed.downloaded_episodes_count += 1;
134- feed.last_downloaded_episode_name = Some(episode.title.clone());
135- feed.last_downloaded_episode_date = Some(Utc::now());
136- }
137- Err(e) => {
138- error!("Failed to download episode {}: {}", episode.title, e);
139- return Err(SyncError::DownloadError(e.to_string()));
140- }
141- }
142- }
143-
144- Ok(downloaded_count)
145-}
146-
147-/// Checks if an episode has already been downloaded
148-fn is_episode_downloaded(feed: &Feed, episode: &Episode) -> bool {
149- !crate::rss::downloader::needs_download(episode, std::path::Path::new(&feed.download_path))
150-}
151-
152-/// Synchronizes all feeds in the collection
153-pub fn sync_all_feeds(feeds: &mut [Feed]) -> Result<usize, SyncError> {
154- info!("Starting synchronization of all {} feeds", feeds.len());
155-
156- let mut successful_feeds = 0;
157- let mut failed_feeds = 0;
158-
159- for feed in feeds.iter_mut() {
160- match sync_feed(feed) {
161- Ok(_) => {
162- successful_feeds += 1;
163- }
164- Err(e) => {
165- error!("Failed to sync feed {}: {}", feed.name, e);
166- failed_feeds += 1;
167- }
168- }
169- }
170-
171- info!(
172- "Synchronization complete: {} feeds succeeded, {} feeds failed",
173- successful_feeds, failed_feeds
174- );
175-
176- Ok(successful_feeds)
177-}
178-
179-/// Resolves filename conflicts by adding a numeric suffix
180-#[allow(dead_code)]
181-pub fn resolve_filename_conflict(original_path: &Path) -> std::path::PathBuf {
182- let mut path = original_path.to_path_buf();
183- let mut counter = 1;
184-
185- while path.exists() {
186- let stem = path.file_stem().unwrap_or_default().to_string_lossy();
187- let extension = path.extension().map(|ext| format!(".{}", ext.to_string_lossy())).unwrap_or_default();
188- let new_stem = format!("{}_{}", stem, counter);
189- let new_filename = format!("{}{}", new_stem, extension);
190- path.set_file_name(new_filename);
191- counter += 1;
192- }
193-
194- path
195-}
196-
197-#[cfg(test)]
198-mod tests {
199- use super::*;
200-
201- #[test]
202- fn test_resolve_filename_conflict() {
203- use tempfile::tempdir;
204-
205- // Create a temporary directory and a test file
206- let temp_dir = tempdir().unwrap();
207- let existing_file = temp_dir.path().join("test.mp3");
208- std::fs::File::create(&existing_file).unwrap();
209-
210- // Try to resolve conflict for the same path
211- let resolved = resolve_filename_conflict(&existing_file);
212-
213- // Should have added suffix since file exists
214- assert!(resolved.to_string_lossy().contains("_1"));
215- assert_ne!(resolved, existing_file);
216- }
217-}