summaryrefslogtreecommitdiff
path: root/src/app.rs
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-03 23:13:50 -0600
committermo khan <mo@mokhan.ca>2025-07-03 23:13:50 -0600
commit5d07af632e9680416f28571cca6ff3ccd5e1b1a7 (patch)
treeeee72adb86fdf9a1999c399a9658135045b4fc67 /src/app.rs
parent99b5dd9c1a6fb5022232864005072c39497d0f0e (diff)
feat: implement CJSW music discovery featuremain
Added music discovery screen with CJSW radio program browsing: - Added MusicDiscovery screen with genre-based navigation - Implemented CJSW show data structure with 8 sample programs - Added keyboard controls: 'm' key to access from episode/now playing screens - Genre browsing with j/k navigation showing show schedules - Updated help text to include music discovery option 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'src/app.rs')
-rw-r--r--src/app.rs122
1 files changed, 122 insertions, 0 deletions
diff --git a/src/app.rs b/src/app.rs
index 39c2925..7431011 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -8,6 +8,7 @@ pub enum CurrentScreen {
FeedList,
EpisodeList,
NowPlaying,
+ MusicDiscovery,
}
pub struct App {
@@ -19,6 +20,8 @@ pub struct App {
pub player: Player,
pub current_track: Option<CurrentTrack>,
pub volume: u8,
+ pub selected_genre: usize,
+ pub cjsw_shows: Vec<CjswShow>,
feed_receiver: mpsc::Receiver<(usize, Feed)>,
}
@@ -30,6 +33,15 @@ pub struct CurrentTrack {
pub _position: f64, // seconds
}
+#[derive(Debug, Clone)]
+pub struct CjswShow {
+ pub name: String,
+ pub genre: String,
+ pub time_slot: String,
+ pub description: String,
+ pub day: String,
+}
+
impl App {
pub fn new() -> Result<Self> {
let config = Config::load()?;
@@ -64,6 +76,8 @@ impl App {
player: Player::new()?,
current_track: None,
volume: 70, // Default volume 70%
+ selected_genre: 0,
+ cjsw_shows: Self::load_cjsw_shows(),
feed_receiver: receiver,
})
}
@@ -212,4 +226,112 @@ impl App {
}
}
}
+
+ // Music discovery navigation
+ pub fn go_to_music_discovery(&mut self) {
+ self.current_screen = CurrentScreen::MusicDiscovery;
+ self.selected_genre = 0;
+ }
+
+ pub fn next_genre(&mut self) {
+ if !self.cjsw_shows.is_empty() {
+ let unique_genres = self.get_unique_genres();
+ if !unique_genres.is_empty() {
+ self.selected_genre = (self.selected_genre + 1) % unique_genres.len();
+ }
+ }
+ }
+
+ pub fn previous_genre(&mut self) {
+ if !self.cjsw_shows.is_empty() {
+ let unique_genres = self.get_unique_genres();
+ if !unique_genres.is_empty() {
+ self.selected_genre = if self.selected_genre == 0 {
+ unique_genres.len() - 1
+ } else {
+ self.selected_genre - 1
+ };
+ }
+ }
+ }
+
+ pub fn get_unique_genres(&self) -> Vec<String> {
+ let mut genres: Vec<String> = self.cjsw_shows
+ .iter()
+ .map(|show| show.genre.clone())
+ .collect::<std::collections::HashSet<_>>()
+ .into_iter()
+ .collect();
+ genres.sort();
+ genres
+ }
+
+ pub fn get_shows_by_genre(&self, genre: &str) -> Vec<&CjswShow> {
+ self.cjsw_shows
+ .iter()
+ .filter(|show| show.genre == genre)
+ .collect()
+ }
+
+ // Load CJSW shows data
+ fn load_cjsw_shows() -> Vec<CjswShow> {
+ vec![
+ CjswShow {
+ name: "Black Milk".to_string(),
+ genre: "Electronic/Experimental".to_string(),
+ time_slot: "Monday 1:00-3:00 AM".to_string(),
+ description: "Electronic and experimental music exploration".to_string(),
+ day: "Monday".to_string(),
+ },
+ CjswShow {
+ name: "Soular Power".to_string(),
+ genre: "R&B/Soul".to_string(),
+ time_slot: "Tuesday 7:00-9:00 PM".to_string(),
+ description: "Classic and contemporary R&B and soul".to_string(),
+ day: "Tuesday".to_string(),
+ },
+ CjswShow {
+ name: "Fade to Bass".to_string(),
+ genre: "Electronic".to_string(),
+ time_slot: "Friday 11:00 PM-1:00 AM".to_string(),
+ description: "House and techno music journey".to_string(),
+ day: "Friday".to_string(),
+ },
+ CjswShow {
+ name: "Noise".to_string(),
+ genre: "Experimental".to_string(),
+ time_slot: "Wednesday 2:00-4:00 AM".to_string(),
+ description: "30+ years of avant-garde experimental music".to_string(),
+ day: "Wednesday".to_string(),
+ },
+ CjswShow {
+ name: "Local Singles".to_string(),
+ genre: "Local/Indie".to_string(),
+ time_slot: "Thursday 6:00-8:00 PM".to_string(),
+ description: "Featuring Calgary and Alberta local artists".to_string(),
+ day: "Thursday".to_string(),
+ },
+ CjswShow {
+ name: "CantoStars".to_string(),
+ genre: "Multicultural".to_string(),
+ time_slot: "Sunday 10:00 AM-12:00 PM".to_string(),
+ description: "Cantonese music and cultural programming".to_string(),
+ day: "Sunday".to_string(),
+ },
+ CjswShow {
+ name: "Sonic Cycle".to_string(),
+ genre: "Indie Pop/Rock".to_string(),
+ time_slot: "Saturday 3:00-5:00 PM".to_string(),
+ description: "Genre-blending indie music journey".to_string(),
+ day: "Saturday".to_string(),
+ },
+ CjswShow {
+ name: "Jazz Spectrum".to_string(),
+ genre: "Jazz".to_string(),
+ time_slot: "Monday 8:00-10:00 PM".to_string(),
+ description: "Classic to contemporary jazz exploration".to_string(),
+ day: "Monday".to_string(),
+ },
+ ]
+ }
}