odysee-dl

odysee.com channel content downloader
git clone https://git.ea.contact/odysee-dl
Log | Files | Refs | README

main.rs (3777B)


      1 use odysee_dl::config::Config;
      2 use odysee_dl::event::Event;
      3 use odysee_dl::run;
      4 
      5 static HELP: &str = "Usage: odysee-dl-cli [OPTIONS] <URL>
      6 
      7 Download all content from an Odysee channel. <URL> should be the URL of the channel,
      8 e.g. https://odysee.com/@channel:1
      9 
     10 Options:
     11     -h, --help          Print help information
     12     -d, --dir <DIR>     Output directory (default: .)
     13     -r, --resume        Resume an interrupted download
     14 ";
     15 
     16 fn main() {
     17     let config = parse_args().unwrap_or_else(|e| handle_error(e));
     18     let (tx, rx) = std::sync::mpsc::channel();
     19     let handle = std::thread::spawn(move || {
     20         run(config, tx).unwrap_or_else(|e| handle_error(e));
     21     });
     22 
     23     for event in rx {
     24         render_event(&event);
     25     }
     26 
     27     handle.join().map_err(|e| {
     28         eprintln!("ERROR: {:?}", e);
     29         std::process::exit(1);
     30     }).ok();
     31 }
     32 
     33 enum ParseArgsErr {
     34     MissingUrl,
     35     MissingDirValue,
     36     UnknownOption(String),
     37 }
     38 
     39 impl std::fmt::Display for ParseArgsErr {
     40     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     41         match self {
     42             ParseArgsErr::MissingUrl => write!(f, "Missing URL"),
     43             ParseArgsErr::MissingDirValue => write!(f, "Missing value for --dir"),
     44             ParseArgsErr::UnknownOption(option) => write!(f, "Unknown option: {}", option),
     45         }
     46     }
     47 }
     48 
     49 fn handle_error(e: impl std::fmt::Display) -> ! {
     50     eprintln!("Error: {}", e);
     51     std::process::exit(1);
     52 }
     53 
     54 fn parse_args() -> Result<Config, ParseArgsErr> {
     55     let args: Vec<String> = std::env::args().collect();
     56     if args.len() < 2 {
     57         return Err(ParseArgsErr::MissingUrl);
     58     }
     59 
     60     let mut output_dir = std::path::PathBuf::from(".");
     61     let mut resume = false;
     62     let mut url: Option<String> = None;
     63     let mut i = 1;
     64 
     65     while i < args.len() {
     66         match args[i].as_str() {
     67             "-h" | "--help" => {
     68                 println!("{}", HELP);
     69                 std::process::exit(0);
     70             }
     71             "-d" | "--dir" => {
     72                 i += 1;
     73                 if i >= args.len() {
     74                     return Err(ParseArgsErr::MissingDirValue);
     75                 }
     76                 output_dir = std::path::PathBuf::from(&args[i]);
     77             }
     78             "-r" | "--resume" => {
     79                 resume = true;
     80             }
     81             arg if !arg.starts_with('-') => {
     82                 url = Some(arg.to_string());
     83             }
     84             _ => return Err(ParseArgsErr::UnknownOption(args[i].clone())),
     85         }
     86         i += 1;
     87     }
     88 
     89     let url = url.ok_or(ParseArgsErr::MissingUrl)?;
     90     Ok(Config { url, output_dir, resume })
     91 }
     92 
     93 fn render_event(event: &Event) {
     94     use std::io::Write;
     95     match event {
     96         Event::GetChannelStarted(url) => {
     97             print!("Fetching channel: {}...", url);
     98             std::io::stdout().flush().ok();
     99         }
    100         Event::GetChannelFailed(url, err) => eprintln!("\nFailed to fetch channel {}: {}", url, err),
    101         Event::GetChannelFinished(_) => println!(" Done"),
    102         Event::GetPostsStarted(url) => {
    103             print!("Fetching posts from: {}...", url);
    104             std::io::stdout().flush().ok();
    105         }
    106         Event::GetPostsFailed(url, err) => eprintln!("\nFailed to fetch posts from {}: {}", url, err),
    107         Event::GetPostsFinished(_) => println!(" Done"),
    108         Event::DownloadPostStarted(name) => {
    109             print!("Downloading: {}...", name);
    110             std::io::stdout().flush().ok();
    111         }
    112         Event::DownloadPostFailed(_, err) => println!(" Failed: {}", err),
    113         Event::DownloadPostSkipped(_) => println!(" Skipped"),
    114         Event::DownloadPostFinished(_) => println!(" Done"),
    115         Event::RateLimited(ctx) => println!("\nRate limited ({}), waiting 60s...", ctx),
    116         Event::Done => println!("Done."),
    117     }
    118 }