use anyhow::Result;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use tokio::runtime::Runtime;
use crate::cli::Args;
use crate::commands::Command;
use crate::config::Config;
use crate::ffplay::VlcController;
use crate::tui::{TuiApp, TuiManager};
use crate::types::{MediaSource, PlayerState, TuiLogger};
use crate::utils;
use crate::playlists::PlaylistsManager;
pub struct App {
args: Args,
config: Config,
}
impl App {
pub fn new(args: Args) -> Result<Self> {
args.validate().map_err(|e| anyhow::anyhow!("Invalid arguments: {}", e))?;
let config = Config::load()?;
Ok(Self {
args,
config,
})
}
pub fn run(&mut self) -> Result<()> {
let (command_tx, command_rx) = mpsc::channel::<Command>();
let (state_tx, state_rx) = mpsc::channel::<PlayerState>();
let (log_tx, log_rx) = mpsc::channel::<Command>();
let logger = TuiLogger::new(log_tx.clone());
logger.info("Application starting");
logger.debug("Channels created successfully");
logger.debug("Starting VLC and initializing HTTP controller");
let mut vlc = VlcController::new(Some("127.0.0.1"), Some(8080))?;
logger.info("VLC started and HTTP controller initialized");
let rt = Runtime::new()?;
logger.debug("Applying initial settings");
if let Some(volume) = self.args.volume {
logger.debug(&format!("Setting initial volume to: {}", volume));
if let Err(e) = rt.block_on(vlc.set_volume(volume)) {
logger.error(&format!("Failed to set initial volume: {}", e));
}
} else {
logger.debug(&format!("Setting default volume to: {}", self.config.default_volume));
if let Err(e) = rt.block_on(vlc.set_volume(self.config.default_volume)) {
logger.error(&format!("Failed to set default volume: {}", e));
}
}
if self.args.muted {
logger.debug("Setting initial mute state");
if let Err(e) = rt.block_on(vlc.mute()) {
logger.error(&format!("Failed to set initial mute: {}", e));
}
}
logger.debug(&format!("Setting initial speed to: {}", self.args.speed));
if let Err(e) = rt.block_on(vlc.set_speed(self.args.speed)) {
logger.error(&format!("Failed to set initial speed: {}", e));
}
if !self.args.windowed && self.config.fullscreen_by_default {
logger.debug("Setting fullscreen mode");
if let Err(e) = rt.block_on(vlc.toggle_fullscreen()) {
logger.error(&format!("Failed to set fullscreen: {}", e));
}
}
if let Some(ref media) = self.args.media {
logger.info(&format!("Loading initial media: {}", media));
let source = if utils::is_youtube_url(media) {
logger.info("Media identified as YouTube URL, downloading...");
match crate::youtube::download_youtube_content_with_logger(media, Some(logger.clone())) {
Ok(downloaded_source) => {
logger.info("YouTube content downloaded successfully");
downloaded_source
}
Err(e) => {
logger.error(&format!("Failed to download YouTube content: {}", e));
MediaSource::Url(media.clone())
}
}
} else if utils::is_url(media) {
logger.debug("Media identified as URL");
MediaSource::Url(media.clone())
} else {
logger.debug("Media identified as local file");
MediaSource::LocalFile(media.clone())
};
if let Err(e) = rt.block_on(vlc.load_media(&source)) {
logger.error(&format!("Failed to load initial media: {}", e));
} else {
logger.info("Initial media loaded successfully");
}
}
logger.debug("Setting up TUI");
let terminal = TuiManager::setup()?;
let mut tui_app = TuiApp::new(command_tx.clone(), state_rx, log_rx);
logger.debug("Starting TUI thread");
let tui_handle = thread::spawn(move || {
tui_app.run(terminal)
});
logger.info("Starting main player loop");
let mut running = true;
let mut command_count = 0;
let mut last_state_update = std::time::Instant::now();
let mut playlist_playback: Option<(String, usize)> = None; let mut at_end_last_tick = false;
while running {
match command_rx.recv_timeout(Duration::from_millis(100)) {
Ok(command) => {
command_count += 1;
if !matches!(command, Command::UpdateState) {
logger.debug(&format!("Main thread received command #{}: {:?}", command_count, command));
}
if let Command::PlaylistPlay { playlist_id, index } = &command {
playlist_playback = Some((playlist_id.clone(), *index));
}
if let Command::Open(_) | Command::Load(_) = &command {
playlist_playback = None; }
match Self::handle_command(command, &mut vlc, &rt, &self.config, &self.args, &state_tx, &logger, &log_tx) {
Ok(should_quit) => {
if should_quit {
logger.info("Main thread received quit signal");
running = false;
}
}
Err(e) => {
logger.error(&format!("Error handling command: {}", e));
}
}
}
Err(mpsc::RecvTimeoutError::Timeout) => {
if last_state_update.elapsed() >= Duration::from_millis(500) {
if command_tx.send(Command::UpdateState).is_err() {
logger.warn("Failed to send UpdateState command - channel closed");
}
let state = rt.block_on(vlc.get_state());
if state_tx.send(state.clone()).is_err() {
logger.warn("Failed to send state update - channel closed");
running = false;
}
last_state_update = std::time::Instant::now();
if let Some(dur) = state.media_info.duration {
let pos = state.media_info.position;
let near_end = dur > 0.0 && (dur - pos) <= 2.0;
if near_end && !at_end_last_tick {
if let Some((pid, idx)) = playlist_playback.clone() {
let mut mgr = PlaylistsManager::new_or_default();
let next: Option<(usize, crate::playlists::PlaylistItem)> = {
if let Some(pl) = mgr.get_mut(&pid) {
if pl.items.is_empty() { None } else {
let next_idx = (idx + 1) % pl.items.len();
let item = pl.items[next_idx].clone();
pl.increment_play(&item.id);
Some((next_idx, item))
}
} else { None }
};
if let Some((next_idx, item)) = next {
let _ = mgr.save();
let _ = mgr.reload();
let _ = log_tx.send(Command::PlaylistReload);
let source = Self::path_to_media_source(&item.url, &logger)?;
if let Err(e) = rt.block_on(vlc.load_media(&source)) {
logger.error(&format!("Failed to auto-advance playlist: {}", e));
} else {
playlist_playback = Some((pid.clone(), next_idx));
logger.info(&format!("Advanced to next playlist item: {}", item.name));
let _ = log_tx.send(Command::PlaylistNowPlaying { playlist_id: pid.clone(), item_id: item.id.clone() });
}
}
} else {
if pos > 0.1 {
if let Err(e) = rt.block_on(vlc.seek(-pos)) {
logger.warn(&format!("Failed to loop current item: {}", e));
} else {
logger.info("Looped current item to start");
}
}
}
}
at_end_last_tick = near_end;
} else {
at_end_last_tick = false;
}
}
}
Err(mpsc::RecvTimeoutError::Disconnected) => {
logger.warn("Command channel disconnected");
running = false;
}
}
}
logger.info("Main loop ending, cleaning up VLC");
if let Err(e) = rt.block_on(vlc.quit()) {
logger.error(&format!("Error during VLC cleanup: {}", e));
} else {
logger.info("VLC cleanup completed successfully");
}
logger.debug("Signaling TUI thread to quit");
if let Err(e) = log_tx.send(Command::Quit) {
logger.warn(&format!("Failed to send quit signal to TUI thread: {}", e));
} else {
logger.debug("Quit signal sent to TUI thread");
}
logger.debug("Waiting for TUI thread to finish");
let join_timeout = Duration::from_secs(5);
let join_start = std::time::Instant::now();
let (join_tx, join_rx) = mpsc::channel();
let join_handle_for_timeout = thread::spawn(move || {
let result = tui_handle.join().map_err(|_| anyhow::anyhow!("TUI thread panicked"));
let _ = join_tx.send(result);
});
let tui_result = match join_rx.recv_timeout(join_timeout) {
Ok(result) => {
logger.debug(&format!("TUI thread finished after {:?}", join_start.elapsed()));
result?
}
Err(mpsc::RecvTimeoutError::Timeout) => {
logger.error(&format!("TUI thread did not respond within {:?}, forcing cleanup", join_timeout));
TuiManager::cleanup()?;
return Ok(());
}
Err(mpsc::RecvTimeoutError::Disconnected) => {
logger.error("TUI join channel disconnected unexpectedly");
return Err(anyhow::anyhow!("TUI join channel disconnected"));
}
};
let _ = join_handle_for_timeout.join();
logger.debug("Starting cleanup");
TuiManager::cleanup()?;
tui_result?;
logger.debug("Saving configuration");
if let Err(e) = self.config.save() {
logger.warn(&format!("Failed to save configuration: {}", e));
} else {
logger.info("Configuration saved successfully");
}
logger.info(&format!("Application shutdown complete after processing {} commands", command_count));
Ok(())
}
fn handle_command(
command: Command,
vlc: &mut VlcController,
rt: &Runtime,
config: &Config,
_args: &Args,
state_tx: &mpsc::Sender<PlayerState>,
logger: &TuiLogger,
log_tx: &mpsc::Sender<Command>,
) -> Result<bool> {
if !matches!(command, Command::UpdateState) {
logger.debug(&format!("Handling command: {:?}", command));
}
let mut suppress_state = false;
match command {
Command::Quit => {
logger.info("Quit command received");
return Ok(true);
}
Command::Play => {
logger.debug("Command: play");
rt.block_on(vlc.play_pause())?;
}
Command::Pause => {
logger.debug("Command: pause");
rt.block_on(vlc.play_pause())?;
}
Command::Toggle => {
logger.debug("Command: toggle play/pause");
rt.block_on(vlc.play_pause())?;
}
Command::Stop => {
logger.debug("Command: stop");
rt.block_on(vlc.stop())?;
let _ = log_tx.send(Command::PlaylistClearNowPlaying);
}
Command::VolumeUp => {
let current_state = rt.block_on(vlc.get_state());
let old_volume = current_state.media_info.volume;
let new_volume = (current_state.media_info.volume + config.ui.volume_step).min(100.0);
logger.debug(&format!("Volume up: {} -> {}", old_volume, new_volume));
rt.block_on(vlc.set_volume(new_volume))?;
}
Command::VolumeDown => {
let current_state = rt.block_on(vlc.get_state());
let old_volume = current_state.media_info.volume;
let new_volume = (current_state.media_info.volume - config.ui.volume_step).max(0.0);
logger.debug(&format!("Volume down: {} -> {}", old_volume, new_volume));
rt.block_on(vlc.set_volume(new_volume))?;
}
Command::Volume(volume) => {
logger.debug(&format!("Command: set volume to {}", volume));
rt.block_on(vlc.set_volume(volume))?;
}
Command::Mute => {
logger.debug("Command: mute");
rt.block_on(vlc.mute())?;
}
Command::Unmute => {
logger.debug("Command: unmute");
let current_state = rt.block_on(vlc.get_state());
if current_state.media_info.muted {
rt.block_on(vlc.mute())?;
}
}
Command::SeekForward => {
logger.debug(&format!("Seek forward: {}s", config.ui.seek_step));
rt.block_on(vlc.seek(config.ui.seek_step))?;
}
Command::SeekBackward => {
logger.debug(&format!("Seek backward: -{}s", config.ui.seek_step));
rt.block_on(vlc.seek(-config.ui.seek_step))?;
}
Command::Seek(seconds) => {
logger.debug(&format!("Command: seek {} seconds", seconds));
rt.block_on(vlc.seek(seconds))?;
}
Command::SeekTo(position) => {
logger.debug(&format!("Command: seek to position {} seconds", position));
rt.block_on(vlc.seek(position))?; }
Command::Forward(seconds) => {
logger.debug(&format!("Command: forward {} seconds", seconds));
rt.block_on(vlc.seek(seconds))?;
}
Command::Backward(seconds) => {
logger.debug(&format!("Command: backward {} seconds", seconds));
rt.block_on(vlc.seek(-seconds))?;
}
Command::SpeedUp => {
let current_state = rt.block_on(vlc.get_state());
let old_speed = current_state.media_info.speed;
let new_speed = (current_state.media_info.speed + config.ui.speed_step).min(5.0);
logger.debug(&format!("Speed up: {}x -> {}x", old_speed, new_speed));
rt.block_on(vlc.set_speed(new_speed))?;
}
Command::SpeedDown => {
let current_state = rt.block_on(vlc.get_state());
let old_speed = current_state.media_info.speed;
let new_speed = (current_state.media_info.speed - config.ui.speed_step).max(0.1);
logger.debug(&format!("Speed down: {}x -> {}x", old_speed, new_speed));
rt.block_on(vlc.set_speed(new_speed))?;
}
Command::Speed(speed) => {
logger.debug(&format!("Command: set speed to {}", speed));
rt.block_on(vlc.set_speed(speed))?;
}
Command::SpeedReset | Command::ResetSpeed => {
logger.debug("Command: reset speed to 1.0x");
rt.block_on(vlc.set_speed(1.0))?;
}
Command::ToggleFullscreen => {
logger.debug("Command: toggle fullscreen");
rt.block_on(vlc.toggle_fullscreen())?;
}
Command::Fullscreen => {
logger.debug("Command: enter fullscreen");
rt.block_on(vlc.toggle_fullscreen())?; }
Command::Windowed => {
logger.debug("Command: exit fullscreen");
rt.block_on(vlc.toggle_fullscreen())?; }
Command::ToggleLoop => {
logger.debug("Command: toggle loop");
rt.block_on(vlc.toggle_loop())?;
}
Command::Loop => {
logger.debug("Command: toggle loop (alias)");
rt.block_on(vlc.toggle_loop())?;
}
Command::Open(ref path) | Command::Load(ref path) => {
let source = Self::path_to_media_source(path, logger)?;
Self::load_media_source(vlc, rt, &source, logger)?;
let _ = log_tx.send(Command::PlaylistClearNowPlaying);
}
Command::TestConnection => {
logger.debug("Command: test connection");
match rt.block_on(vlc.test_connection()) {
Ok(connected) => {
if connected {
logger.info("VLC connection test successful");
} else {
logger.warn("VLC connection test failed");
}
}
Err(e) => {
logger.error(&format!("VLC connection test error: {}", e));
}
}
let state = rt.block_on(vlc.get_state());
let _ = state_tx.send(state);
suppress_state = true;
}
Command::ShowHelp | Command::HideHelp | Command::Help => {
logger.debug("Help command - handled by TUI");
}
Command::UpdateState => {
let state = rt.block_on(vlc.get_state());
let _ = state_tx.send(state);
suppress_state = true;
}
Command::Log(_) => {
suppress_state = true;
}
Command::ExecuteCommand(ref command_str) => {
if let Some(path_str) = command_str.strip_prefix("export_to ") {
let export_path = std::path::PathBuf::from(path_str);
logger.info(&format!("Config export command with custom path: {:?}", export_path));
use crate::config_manager::ConfigManager;
match ConfigManager::new() {
Ok(manager) => {
match manager.export_to(&export_path) {
Ok(()) => {
logger.info(&format!("Configuration exported to: {:?}", export_path));
}
Err(e) => {
logger.error(&format!("Failed to export configuration: {}", e));
}
}
}
Err(e) => {
logger.error(&format!("Failed to create config manager: {}", e));
}
}
} else if let Some(path_str) = command_str.strip_prefix("import_from ") {
let import_path = std::path::PathBuf::from(path_str);
logger.info(&format!("Config import command with custom path: {:?}", import_path));
use crate::config_manager::ConfigManager;
if !import_path.exists() {
logger.error(&format!("Config import file not found: {:?}", import_path));
return Ok(false);
}
match ConfigManager::new() {
Ok(manager) => {
match manager.import_from(&import_path) {
Ok(()) => {
logger.info(&format!("Configuration imported from: {:?}", import_path));
}
Err(e) => {
logger.error(&format!("Failed to import configuration: {}", e));
}
}
}
Err(e) => {
logger.error(&format!("Failed to create config manager: {}", e));
}
}
} else {
match crate::commands::parse_command(command_str) {
Ok(parsed_command) => {
logger.debug(&format!("Executing parsed command: {:?}", parsed_command));
return Self::handle_command(parsed_command, vlc, rt, config, _args, state_tx, logger, log_tx);
}
Err(e) => {
logger.error(&format!("Failed to parse command '{}': {}", command_str, e));
}
}
}
}
Command::Connect(ref host, ref port) => {
logger.info(&format!("Connect command not implemented: {}:{:?}", host, port));
}
Command::Disconnect => {
logger.info("Disconnect command not implemented");
}
Command::Config => {
logger.info("Config command received, forwarding to TUI");
if let Err(e) = log_tx.send(Command::ShowConfig) {
logger.error(&format!("Failed to send ShowConfig command to TUI: {}", e));
}
}
Command::Save => {
logger.info("Save command received, saving configuration");
use crate::config_manager::ConfigManager;
match ConfigManager::new() {
Ok(manager) => {
match manager.sync() {
Ok(()) => {
logger.info("Configuration saved successfully");
}
Err(e) => {
logger.error(&format!("Failed to save configuration: {}", e));
}
}
}
Err(e) => {
logger.error(&format!("Failed to create config manager: {}", e));
}
}
}
Command::Reload => {
logger.info("Reload command received, reloading configuration");
match Config::load() {
Ok(_new_config) => {
logger.info("Configuration reloaded successfully");
logger.warn("Config reload loaded but cannot update runtime config");
}
Err(e) => {
logger.error(&format!("Failed to reload configuration: {}", e));
}
}
}
Command::ShowConfig => {
logger.info("Show config command received, forwarding to TUI");
if let Err(e) = log_tx.send(Command::ShowConfig) {
logger.error(&format!("Failed to send ShowConfig command to TUI: {}", e));
}
}
Command::HideConfig => {
logger.info("Hide config command received, forwarding to TUI");
if let Err(e) = log_tx.send(Command::HideConfig) {
logger.error(&format!("Failed to send HideConfig command to TUI: {}", e));
}
}
Command::ConfigNext => {
logger.info("Config next command received (handled by TUI)");
}
Command::ConfigPrev => {
logger.info("Config prev command received (handled by TUI)");
}
Command::ConfigEdit => {
logger.info("Config edit command received (handled by TUI)");
}
Command::ConfigReset => {
logger.info("Config reset command received, resetting configuration to defaults");
use crate::config_manager::ConfigManager;
match ConfigManager::new() {
Ok(manager) => {
match manager.reset_all() {
Ok(()) => {
logger.info("Configuration reset to defaults successfully");
}
Err(e) => {
logger.error(&format!("Failed to reset configuration: {}", e));
}
}
}
Err(e) => {
logger.error(&format!("Failed to create config manager: {}", e));
}
}
}
Command::ConfigExport => {
logger.info("Config export command received");
use crate::config_manager::ConfigManager;
use std::path::PathBuf;
let export_path = dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("bckplayer_config_export.json");
match ConfigManager::new() {
Ok(manager) => {
match manager.export_to(&export_path) {
Ok(()) => {
logger.info(&format!("Configuration exported to: {:?}", export_path));
}
Err(e) => {
logger.error(&format!("Failed to export configuration: {}", e));
}
}
}
Err(e) => {
logger.error(&format!("Failed to create config manager: {}", e));
}
}
}
Command::ConfigImport => {
logger.info("Config import command received");
use crate::config_manager::ConfigManager;
use std::path::PathBuf;
let import_path = dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("bckplayer_config_export.json");
if !import_path.exists() {
logger.error(&format!("Config import file not found: {:?}", import_path));
return Ok(false);
}
match ConfigManager::new() {
Ok(manager) => {
match manager.import_from(&import_path) {
Ok(()) => {
logger.info(&format!("Configuration imported from: {:?}", import_path));
}
Err(e) => {
logger.error(&format!("Failed to import configuration: {}", e));
}
}
}
Err(e) => {
logger.error(&format!("Failed to create config manager: {}", e));
}
}
}
Command::YoutubeShowCache => {
logger.info("Command: show YouTube cache statistics");
match crate::youtube::YoutubeManager::new_with_logger(Some(logger.clone())) {
Ok(manager) => {
match manager.get_cache_stats() {
Ok(stats) => {
logger.info("YouTube Cache Stats:");
logger.info(&format!(" Files: {}", stats.total_files));
logger.info(&format!(" Size: {:.2} MB / {:.2} MB ({:.1}%)",
stats.total_size_mb, stats.max_size_mb, stats.usage_percentage));
}
Err(e) => {
logger.error(&format!("Failed to get cache stats: {}", e));
}
}
}
Err(e) => {
logger.error(&format!("Failed to create YouTube manager: {}", e));
}
}
suppress_state = true;
}
Command::YoutubeClearCache => {
logger.info("Command: clear YouTube cache");
match crate::youtube::YoutubeManager::new_with_logger(Some(logger.clone())) {
Ok(mut manager) => {
match manager.clear_cache() {
Ok(()) => {
logger.info("YouTube cache cleared successfully");
}
Err(e) => {
logger.error(&format!("Failed to clear cache: {}", e));
}
}
}
Err(e) => {
logger.error(&format!("Failed to create YouTube manager: {}", e));
}
}
suppress_state = true;
}
Command::YoutubeDownload(ref url) => {
logger.info(&format!("Command: download YouTube content from {}", url));
match crate::youtube::download_youtube_content_with_logger(url, Some(logger.clone())) {
Ok(source) => {
logger.info(&format!("Downloaded YouTube content: {:?}", source));
if let Err(e) = rt.block_on(vlc.load_media(&source)) {
logger.error(&format!("Failed to load downloaded content: {}", e));
}
}
Err(e) => {
logger.error(&format!("Failed to download YouTube content: {}", e));
}
}
}
Command::PlaylistAdd { name } => {
let mut mgr = PlaylistsManager::new_or_default();
let added_name = {
let p = mgr.add_playlist(name);
p.name.clone()
};
let _ = mgr.save();
let _ = mgr.reload();
let _ = log_tx.send(Command::PlaylistReload);
logger.info(&format!("Playlist added: {}", added_name));
}
Command::PlaylistDelete { playlist_id } => {
let mut mgr = PlaylistsManager::new_or_default();
mgr.remove_playlist(&playlist_id);
let _ = mgr.save();
let _ = mgr.reload();
let _ = log_tx.send(Command::PlaylistReload);
logger.info(&format!("Playlist deleted: {}", playlist_id));
}
Command::PlaylistItemAdd { playlist_id, item } => {
let mut mgr = PlaylistsManager::new_or_default();
let playlist_name = {
if let Some(pl) = mgr.get_mut(&playlist_id) {
pl.add_item(item);
pl.sort_items();
Some(pl.name.clone())
} else { None }
};
let _ = mgr.save();
let _ = mgr.reload();
let _ = log_tx.send(Command::PlaylistReload);
if let Some(name) = playlist_name {
logger.info(&format!("Item added to playlist: {}", name));
} else {
logger.warn("Playlist not found for adding item");
}
}
Command::PlaylistItemDelete { playlist_id, item_id } => {
let mut mgr = PlaylistsManager::new_or_default();
let playlist_name = {
if let Some(pl) = mgr.get_mut(&playlist_id) {
let name = pl.name.clone();
if pl.remove_item(&item_id) { Some(name) } else { None }
} else { None }
};
let _ = mgr.save();
let _ = mgr.reload();
let _ = log_tx.send(Command::PlaylistReload);
if let Some(name) = playlist_name {
logger.info(&format!("Item removed from playlist: {}", name));
}
}
Command::PlaylistPlay { playlist_id, index } => {
let mut mgr = PlaylistsManager::new_or_default();
let selected_item = {
if let Some(pl) = mgr.get_mut(&playlist_id) {
if let Some(item) = pl.items.get(index).cloned() {
pl.increment_play(&item.id);
Some(item)
} else { None }
} else { None }
};
if let Some(item) = selected_item {
let _ = mgr.save();
let _ = mgr.reload();
let _ = log_tx.send(Command::PlaylistReload);
let source = Self::path_to_media_source(&item.url, logger)?;
Self::load_media_source(vlc, rt, &source, logger)?;
let _ = log_tx.send(Command::PlaylistNowPlaying { playlist_id, item_id: item.id });
} else {
logger.warn("Playlist or item not found for play");
}
}
Command::PlaylistReload => {
logger.debug("Received PlaylistReload command (no-op in core)");
suppress_state = true;
}
Command::PlaylistNowPlaying { .. } | Command::PlaylistClearNowPlaying => {
suppress_state = true;
}
}
if !suppress_state {
let state = rt.block_on(vlc.get_state());
let _ = state_tx.send(state);
}
Ok(false)
}
fn path_to_media_source(path: &str, logger: &TuiLogger) -> Result<MediaSource> {
let source = if utils::is_youtube_url(path) {
logger.info(&format!("Loading YouTube URL: {}", path));
match crate::youtube::download_youtube_content_with_logger(path, Some(logger.clone())) {
Ok(downloaded_source) => {
logger.info("YouTube content downloaded successfully");
downloaded_source
}
Err(e) => {
logger.error(&format!("Failed to download YouTube content: {}", e));
MediaSource::Url(path.to_string())
}
}
} else if utils::is_url(path) {
MediaSource::Url(path.to_string())
} else {
MediaSource::LocalFile(path.to_string())
};
Ok(source)
}
fn load_media_source(
vlc: &mut VlcController,
rt: &Runtime,
source: &MediaSource,
logger: &TuiLogger,
) -> Result<()> {
logger.info(&format!("Loading media: {:?}", source));
rt.block_on(vlc.load_media(source))?;
Ok(())
}
}