zik 0.1.0

A TUI web radio player with audio spectrum visualizer
use std::sync::Arc;

use crate::audio::{self, SPECTRUM_SIZE, SpectrumBuffer};
use crate::config::Config;
use crate::player::Player;

pub struct App {
    pub radio_names: Vec<String>,
    pub urls: std::collections::HashMap<String, String>,
    pub selected: usize,
    pub player: Player,
    pub playing: Option<String>,
    pub spectrum: Vec<f32>,
    pub running: bool,
    pub show_selector: bool,
    pub show_help: bool,
    audio_spectrum: Arc<SpectrumBuffer>,
}

impl App {
    pub fn new(config: Config, initial_radio: Option<&str>) -> Self {
        let radio_names = config.sorted_names();
        let selected = initial_radio
            .and_then(|name| radio_names.iter().position(|n| n == name))
            .unwrap_or(0);

        let mut app = Self {
            radio_names,
            urls: config.radios,
            selected,
            player: Player::new(),
            playing: None,
            spectrum: vec![0.0f32; SPECTRUM_SIZE],
            running: true,
            show_selector: false,
            show_help: false,
            audio_spectrum: audio::spawn_capture(),
        };

        app.play_selected();
        app
    }

    fn play_selected(&mut self) {
        if let Some(url) = self
            .radio_names
            .get(self.selected)
            .and_then(|n| self.urls.get(n))
        {
            let url = url.clone();
            if self.player.play(&url).is_ok() {
                self.playing = Some(self.radio_names[self.selected].clone());
            }
        }
    }

    pub fn toggle_selector(&mut self) {
        self.show_selector = !self.show_selector;
    }

    pub fn next(&mut self) {
        if !self.radio_names.is_empty() {
            self.selected = (self.selected + 1) % self.radio_names.len();
        }
    }

    pub fn previous(&mut self) {
        if !self.radio_names.is_empty() {
            self.selected = self
                .selected
                .checked_sub(1)
                .unwrap_or(self.radio_names.len() - 1);
        }
    }

    pub fn toggle_play(&mut self) {
        let name = &self.radio_names[self.selected];
        if self.playing.as_deref() == Some(name.as_str()) {
            self.player.stop();
            self.playing = None;
        } else if let Some(url) = self.urls.get(name)
            && self.player.play(url).is_ok() {
                self.playing = Some(name.clone());
            }
    }

    pub fn tick(&mut self) {
        self.spectrum.copy_from_slice(self.audio_spectrum.read());
    }

    pub fn quit(&mut self) {
        self.running = false;
    }
}