hiyodori

rumi

Dec 11th, 2024
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 11.66 KB | Source Code | 0 0
  1. /// main.rs
  2.  
  3. #[derive(Debug)]
  4. enum AppEvent {
  5.     Key(crossterm::event::KeyEvent),
  6.     SongFinished,
  7.     Quit,
  8. }
  9.  
  10. pub struct StatefulList<T> {
  11.     pub state: ListState,
  12.     pub items: Vec<T>,
  13.     pub current: usize,
  14. }
  15.  
  16. impl<T> StatefulList<T> {
  17.     pub fn with_items(items: Vec<T>) -> StatefulList<T> {
  18.         StatefulList {
  19.             state: ListState::default(),
  20.             items,
  21.             current: 0,
  22.         }
  23.     }
  24.  
  25.     pub fn select_item(&mut self) {
  26.         self.state.select(Some(self.current));
  27.     }
  28. }
  29.  
  30. pub struct App {
  31.     exit: bool,
  32.     controls: Controls,
  33.     items: StatefulList<String>,
  34.     autoplay: bool,
  35.     event_sender: mpsc::Sender<AppEvent>,
  36.     event_receiver: mpsc::Receiver<AppEvent>,
  37.     last_song_finished: Option<Instant>,
  38. }
  39.  
  40. impl App {
  41.     pub fn new(controls: Controls) -> App {
  42.         let songlist = controls.songlist.clone();
  43.  
  44.         let (event_sender, event_receiver) = mpsc::channel();
  45.  
  46.         let mut app = App {
  47.             exit: false,
  48.             controls,
  49.             items: StatefulList::with_items(songlist),
  50.             autoplay: true,
  51.             event_sender,
  52.             event_receiver,
  53.             last_song_finished: None,
  54.         };
  55.  
  56.         app.start_background_song_checker();
  57.  
  58.         app
  59.     }
  60.  
  61.     fn start_background_song_checker(&self) {
  62.         let event_sender = self.event_sender.clone();
  63.  
  64.         // Create a weak reference to the sink
  65.         let sink_ref = Arc::new(Mutex::new(Some(self.controls.sink.clone())));
  66.         let weak_sink = Arc::downgrade(&sink_ref);
  67.  
  68.         thread::spawn(move || {
  69.             loop {
  70.                 println!("checking sink");
  71.  
  72.                 // Use weak reference to check if sink is still valid
  73.                 if let Some(sink_guard) = weak_sink.upgrade() {
  74.                     let sink = sink_guard.lock().unwrap();
  75.  
  76.                     // Check if the sink is empty
  77.                     if let Some(sink) = sink.as_ref() {
  78.                         if sink.lock().unwrap().empty() {
  79.                             println!("empty sink");
  80.                             // Send song finished event
  81.                             if let Err(e) = event_sender.send(AppEvent::SongFinished) {
  82.                                 eprintln!("Error sending song finished event: {}", e);
  83.                                 break;
  84.                             }
  85.                         }
  86.                     }
  87.                 } else {
  88.                     // Sink reference is no longer valid, exit thread
  89.                     break;
  90.                 }
  91.  
  92.                 // Sleep to prevent constant checking
  93.                 thread::sleep(Duration::from_millis(500));
  94.             }
  95.         });
  96.     }
  97.  
  98.     pub fn run(&mut self, terminal: &mut tui::Tui) -> Result<()> {
  99.         // Input event handler thread
  100.         let event_sender = self.event_sender.clone();
  101.         thread::spawn(move || loop {
  102.             match event::read() {
  103.                 Ok(Event::Key(key_event)) if key_event.kind == KeyEventKind::Press => {
  104.                     if let Err(e) = event_sender.send(AppEvent::Key(key_event)) {
  105.                         eprintln!("Error sending key event: {}", e);
  106.                         break;
  107.                     }
  108.                 }
  109.                 Err(e) => {
  110.                     eprintln!("Error reading event: {}", e);
  111.                     break;
  112.                 }
  113.                 _ => {}
  114.             }
  115.         });
  116.  
  117.         // Main event loop
  118.         while !self.exit {
  119.             terminal.draw(|frame| ui(frame, self))?;
  120.  
  121.             // Receive and process events
  122.             match self.event_receiver.recv() {
  123.                 Ok(AppEvent::Key(key_event)) => {
  124.                     self.handle_key_event(key_event);
  125.                 }
  126.                 Ok(AppEvent::SongFinished) => {
  127.                     // Add a delay between song switches to prevent rapid switching
  128.                     let now = Instant::now();
  129.                     let can_switch = self.last_song_finished.map_or(true, |last| {
  130.                         now.duration_since(last) > Duration::from_secs(1)
  131.                     });
  132.  
  133.                     if self.autoplay && can_switch {
  134.                         self.next();
  135.                         self.items.select_item();
  136.                         self.last_song_finished = Some(now);
  137.                     }
  138.                 }
  139.                 Ok(AppEvent::Quit) => {
  140.                     self.exit = true;
  141.                 }
  142.                 Err(_) => {
  143.                     // Channel closed, exit loop
  144.                     break;
  145.                 }
  146.             }
  147.         }
  148.  
  149.         Ok(())
  150.     }
  151.     fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) {
  152.         match key_event.code {
  153.             KeyCode::Char('q') => self.exit = true,
  154.             KeyCode::Char('p') => self.play_pause(),
  155.             KeyCode::Char('n') => {
  156.                 self.next();
  157.                 self.items.select_item()
  158.             }
  159.             KeyCode::Char('m') => {
  160.                 self.previous();
  161.                 self.items.select_item()
  162.             }
  163.             KeyCode::Char('s') => {
  164.                 self.previous();
  165.                 self.items.select_item()
  166.             }
  167.             KeyCode::Char('r') => {
  168.                 self.repeat_infintie();
  169.             }
  170.             KeyCode::Char('a') => {
  171.                 // Toggle autoplay
  172.                 self.autoplay = !self.autoplay;
  173.             }
  174.             KeyCode::Right => {
  175.                 self.skip_duration();
  176.             }
  177.             _ => {}
  178.         }
  179.     }
  180.  
  181.     fn exit(&mut self) {
  182.         self.exit = true;
  183.     }
  184.  
  185.     fn play_pause(&self) {
  186.         if self.controls.sink.lock().unwrap().is_paused() {
  187.             self.controls.sink.lock().unwrap().play();
  188.         } else {
  189.             self.controls.sink.lock().unwrap().pause();
  190.         }
  191.     }
  192.  
  193.     fn next(&mut self) {
  194.         self.items.current += 1;
  195.         if self.items.current >= self.controls.playlistz.len() {
  196.             self.items.current = 0;
  197.         }
  198.         self.start();
  199.     }
  200.  
  201.     fn previous(&mut self) {
  202.         if self.items.current == 0 {
  203.             self.items.current = self.controls.playlistz.len() - 1;
  204.         } else {
  205.             self.items.current -= 1;
  206.         }
  207.  
  208.         self.start();
  209.     }
  210.  
  211.     fn repeat_infintie(&mut self) {
  212.         let path = self.controls.playlistz[self.items.current].clone();
  213.         let source = Controls::get_source(path).repeat_infinite();
  214.  
  215.         self.controls.sink.lock().unwrap().clear();
  216.         self.controls.sink.lock().unwrap().append(source);
  217.         self.controls.sink.lock().unwrap().play();
  218.     }
  219.  
  220.     fn skip_duration(&mut self) {
  221.         // let path = self.controls.playlistz[self.items.current].clone();
  222.         // let source = Controls::get_source(path).skip_duration(Duration::new(5, 0));
  223.         // self.controls.sink.lock().unwrap().clear();
  224.         // self.controls.sink.lock().unwrap().append(source);
  225.         // self.controls.sink.lock().unwrap().play();
  226.     }
  227.  
  228.     fn start(&mut self) {
  229.         let path = self.controls.playlistz[self.items.current].clone();
  230.         let source = Controls::get_source(path);
  231.  
  232.         self.controls.sink.lock().unwrap().clear();
  233.         self.controls.sink.lock().unwrap().append(source);
  234.         self.controls.sink.lock().unwrap().play();
  235.     }
  236. }
  237. fn ui(f: &mut Frame, app: &mut App) {
  238.     let chunks = Layout::default()
  239.         .direction(Direction::Horizontal)
  240.         .constraints([Constraint::Percentage(25), Constraint::Percentage(75)])
  241.         .split(f.size());
  242.  
  243.     let left_block = Block::default()
  244.         .borders(Borders::RIGHT)
  245.         .style(Style::default());
  246.  
  247.     let title_block = Block::default()
  248.         .borders(Borders::NONE)
  249.         .style(Style::default());
  250.  
  251.     let minichunks = Layout::default()
  252.         .direction(Direction::Vertical)
  253.         .constraints([Constraint::Percentage(10), Constraint::Percentage(90)])
  254.         .split(chunks[0]);
  255.  
  256.     let title = Paragraph::new("RUMI: A rusty music player".bold().red())
  257.         .block(title_block)
  258.         .alignment(Alignment::Center);
  259.  
  260.     let image = StatefulImage::new(None);
  261.  
  262.     f.render_widget(left_block, chunks[0]);
  263.     f.render_widget(title, minichunks[0]);
  264.  
  265.     let info_chunk = Layout::default()
  266.         .direction(Direction::Vertical)
  267.         .constraints([Constraint::Percentage(75), Constraint::Percentage(25)])
  268.         .split(minichunks[1]);
  269.  
  270.     let image_chunk = Layout::default()
  271.         .direction(Direction::Horizontal)
  272.         .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
  273.         .split(info_chunk[0]);
  274.  
  275.     let author_block = Block::default()
  276.         .borders(Borders::NONE)
  277.         .style(Style::default());
  278.  
  279.  
  280.  
  281.     let items: Vec<ListItem> = app
  282.         .items
  283.         .items
  284.         .clone()
  285.         .into_iter()
  286.         .map(|i| ListItem::new(i).style(Style::default().fg(Color::Black).bg(Color::White)))
  287.         .collect();
  288.  
  289.     let items_list = List::new(items)
  290.         .block(Block::default().borders(Borders::TOP).title("PlayList"))
  291.         .highlight_style(
  292.             Style::default()
  293.                 .bg(Color::LightGreen)
  294.                 .add_modifier(Modifier::BOLD),
  295.         )
  296.         .highlight_symbol(">> ");
  297.  
  298.     f.render_stateful_widget(items_list, chunks[1], &mut app.items.state)
  299. }
  300.  
  301. fn main() -> Result<()> {
  302.     errors::install_hooks()?;
  303.     let controls = Controls::new();
  304.     // let app_list = list::App_list::new(controls.songlist.clone());
  305.     let mut terminal = tui::init()?;
  306.     let app_result = App::new(controls).run(&mut terminal);
  307.     tui::restore()?;
  308.     app_result
  309. }
  310.  
  311. // audio.rs
  312.  
  313. use rodio::{Decoder, OutputStream, OutputStreamHandle, Sink};
  314. use std::fs::{self, File};
  315. use std::io::BufReader;
  316. use std::path::PathBuf;
  317. use std::sync::{Arc, Mutex};
  318.  
  319. pub struct Controls {
  320.     pub sink: Arc<Mutex<Sink>>,
  321.     output_stream: OutputStream,
  322.     stream_handle: OutputStreamHandle,
  323.     pub playlistz: Vec<PathBuf>,
  324.     pub songlist: Vec<String>,
  325. }
  326.  
  327. impl Controls {
  328.     pub fn new() -> Self {
  329.         let (_stream, stream_handle) = OutputStream::try_default().unwrap();
  330.         let sink = Arc::new(Mutex::new(Sink::try_new(&stream_handle).unwrap()));
  331.        
  332.         let mut playlistz: Vec<PathBuf> = Vec::new();
  333.         let mut songlist: Vec<String> = Vec::new();
  334.         let folder_path = PathBuf::from("music");
  335.        
  336.         for entry in fs::read_dir(folder_path).unwrap() {
  337.             let path = entry.unwrap().path();
  338.             if path.is_file() {
  339.                 if let Some(filename) = path.file_name() {
  340.                     if let Some(filename_str) = filename.to_str() {
  341.                         songlist.push(filename_str.to_string());
  342.                     }
  343.                     playlistz.push(path);
  344.                 }
  345.             }
  346.         }
  347.        
  348.         Controls {
  349.             sink,
  350.             output_stream: _stream,
  351.             stream_handle,
  352.             playlistz,
  353.             songlist,
  354.         }
  355.     }
  356.  
  357.     pub fn get_source(path: PathBuf) -> Decoder<BufReader<File>> {
  358.         let file = std::fs::File::open(&path).unwrap();
  359.         let source: Decoder<BufReader<File>> = Decoder::new(BufReader::new(file)).unwrap();
  360.         source
  361.     }
  362. }
  363.  
  364. impl Clone for Controls {
  365.     fn clone(&self) -> Self {
  366.         let (_stream, stream_handle) = OutputStream::try_default().unwrap();
  367.         let sink = Arc::new(Mutex::new(Sink::try_new(&stream_handle).unwrap()));
  368.        
  369.         Controls {
  370.             sink,
  371.             output_stream: _stream,
  372.             stream_handle,
  373.             playlistz: self.playlistz.clone(),
  374.             songlist: self.songlist.clone(),
  375.         }
  376.     }
  377. }
  378.  
Advertisement
Add Comment
Please, Sign In to add comment