A lot of refactoring
Made the program a lot more configurable and also cleaned up the parser struct
This commit is contained in:
		
							
								
								
									
										188
									
								
								src/config.rs
									
									
									
									
									
								
							
							
						
						
									
										188
									
								
								src/config.rs
									
									
									
									
									
								
							@@ -0,0 +1,188 @@
 | 
			
		||||
use std::{
 | 
			
		||||
    fs::{self, File},
 | 
			
		||||
    io::{self, BufReader},
 | 
			
		||||
    ops::Deref,
 | 
			
		||||
    path::{Path, PathBuf},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use directories::ProjectDirs;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use serde_json;
 | 
			
		||||
 | 
			
		||||
use crate::{download_with_progress, Readline};
 | 
			
		||||
 | 
			
		||||
const JSON_CONFIG_FILENAME: &'static str = "iptvnator_config.json";
 | 
			
		||||
const APP_IDENTIFIER: [&'static str; 3] = ["com", "billenius", "iptvnator"];
 | 
			
		||||
const STANDARD_PLAYLIST_FILENAME: &'static str = "ilovetv.m3u8";
 | 
			
		||||
const STANDARD_SEEN_LINKS_FILENAME: &'static str = "watched_links.json";
 | 
			
		||||
const MAX_TRIES: u8 = 4;
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug)]
 | 
			
		||||
pub struct Conf {
 | 
			
		||||
    pub playlist_filename: String,
 | 
			
		||||
    pub playlist_url: String,
 | 
			
		||||
    pub last_search: Option<String>,
 | 
			
		||||
    pub seen_links_filename: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Conf {
 | 
			
		||||
    /**
 | 
			
		||||
     * Read configurationfile or ask user for link input if it isn't created.
 | 
			
		||||
     * Will error if it fails to write config file
 | 
			
		||||
     */
 | 
			
		||||
    pub fn new(ilovetv_config_file: &Path) -> Result<Conf, io::Error> {
 | 
			
		||||
        // Read the configuraionfile if it exists
 | 
			
		||||
        if ilovetv_config_file.exists() {
 | 
			
		||||
            let config_file = Self::read_configfile(&ilovetv_config_file);
 | 
			
		||||
            if let Ok(cfg) = config_file {
 | 
			
		||||
                return Ok(cfg);
 | 
			
		||||
            } else {
 | 
			
		||||
                println!("There are some problem with the configurationfile");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get fresh config with url from user
 | 
			
		||||
        let playlist_url = Self::user_setup();
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            playlist_filename: STANDARD_PLAYLIST_FILENAME.to_owned(),
 | 
			
		||||
            playlist_url,
 | 
			
		||||
            last_search: None,
 | 
			
		||||
            seen_links_filename: STANDARD_SEEN_LINKS_FILENAME.to_owned(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn read_configfile(config_file: &Path) -> Result<Conf, io::Error> {
 | 
			
		||||
        let reader = BufReader::new(File::open(config_file)?);
 | 
			
		||||
        let conf: Conf = serde_json::from_reader(reader)?;
 | 
			
		||||
        Ok(conf)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn user_setup() -> String {
 | 
			
		||||
        let mut readline = Readline::new();
 | 
			
		||||
 | 
			
		||||
        println!("Hello, I would need an url to your iptv/m3u/m3u8 stream");
 | 
			
		||||
        loop {
 | 
			
		||||
            let url = readline.input("enter url: ");
 | 
			
		||||
            let yn = readline.input("Are you sure? (Y/n) ");
 | 
			
		||||
 | 
			
		||||
            if yn.trim().to_lowercase() != "n" {
 | 
			
		||||
                break url.trim().to_owned();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Configuration {
 | 
			
		||||
    pub conf: Conf,
 | 
			
		||||
    pub playlist_path: PathBuf,
 | 
			
		||||
    pub seen_links_path: PathBuf,
 | 
			
		||||
    pub seen_links: Vec<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Configuration {
 | 
			
		||||
    pub fn new() -> Result<Self, io::Error> {
 | 
			
		||||
        let project_dirs =
 | 
			
		||||
            ProjectDirs::from(APP_IDENTIFIER[0], APP_IDENTIFIER[1], APP_IDENTIFIER[2]).unwrap();
 | 
			
		||||
        let config_dir = project_dirs.config_dir();
 | 
			
		||||
        let _ = fs::create_dir_all(config_dir);
 | 
			
		||||
        let config_file = config_dir.join(JSON_CONFIG_FILENAME);
 | 
			
		||||
 | 
			
		||||
        let configuration = Conf::new(&config_file)?;
 | 
			
		||||
 | 
			
		||||
        fs::write(config_file, serde_json::to_string(&configuration).unwrap())?;
 | 
			
		||||
 | 
			
		||||
        // Setup dirs for playlist
 | 
			
		||||
        let cache_dir = project_dirs.cache_dir().to_path_buf();
 | 
			
		||||
        let playlist_path = cache_dir.join(&configuration.playlist_filename);
 | 
			
		||||
        let seen_links_path = cache_dir.join(&configuration.seen_links_filename);
 | 
			
		||||
        let _ = fs::create_dir_all(&cache_dir);
 | 
			
		||||
 | 
			
		||||
        let seen_links = Self::get_watched(&seen_links_path).unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            conf: configuration,
 | 
			
		||||
            playlist_path,
 | 
			
		||||
            seen_links,
 | 
			
		||||
            seen_links_path,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_watched(path: &Path) -> Option<Vec<String>> {
 | 
			
		||||
        let reader = BufReader::new(File::open(&path).ok()?);
 | 
			
		||||
        serde_json::from_reader(reader).ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn should_update_playlist(&self) -> bool {
 | 
			
		||||
        fs::metadata(&self.playlist_path)
 | 
			
		||||
            .and_then(|metadata| {
 | 
			
		||||
                Ok({
 | 
			
		||||
                    let seconds = metadata.modified()?;
 | 
			
		||||
                    seconds
 | 
			
		||||
                        .elapsed()
 | 
			
		||||
                        .expect("Failed to get systemtime")
 | 
			
		||||
                        .as_secs()
 | 
			
		||||
                        > 60 * 60 * 24 * 3
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
            .map_or_else(
 | 
			
		||||
                |_| {
 | 
			
		||||
                    println!("Could not find playlist-file, Downloading a new one");
 | 
			
		||||
                    false
 | 
			
		||||
                },
 | 
			
		||||
                |x| x,
 | 
			
		||||
            )
 | 
			
		||||
    }
 | 
			
		||||
    pub async fn get_playlist(&self) -> Result<String, String> {
 | 
			
		||||
        let content = if let Some(content) = self.get_saved_playlist() {
 | 
			
		||||
            content
 | 
			
		||||
        } else {
 | 
			
		||||
            let downloaded = self.download_playlist().await?;
 | 
			
		||||
            if let Err(e) = fs::write(&self.playlist_path, &downloaded) {
 | 
			
		||||
                println!(
 | 
			
		||||
                    "Failed to save downloaded playlist to file, {:?}, path: '{}'",
 | 
			
		||||
                    e,
 | 
			
		||||
                    &self.playlist_path.as_os_str().to_str().unwrap()
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            downloaded
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Ok(content)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_saved_playlist(&self) -> Option<String> {
 | 
			
		||||
        if !self.should_update_playlist() {
 | 
			
		||||
            return fs::read_to_string(&self.playlist_path).ok();
 | 
			
		||||
        }
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn download_playlist(&self) -> Result<String, String> {
 | 
			
		||||
        let mut counter: u8 = 0;
 | 
			
		||||
        loop {
 | 
			
		||||
            counter += 1;
 | 
			
		||||
 | 
			
		||||
            if let Ok(content) = self.just_download().await {
 | 
			
		||||
                break Ok(content);
 | 
			
		||||
            } else if counter > MAX_TRIES {
 | 
			
		||||
                break Err("Failed to download playlist".to_owned());
 | 
			
		||||
            }
 | 
			
		||||
            println!("Retrying {}/{}", counter + 1, MAX_TRIES);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn just_download(&self) -> Result<String, String> {
 | 
			
		||||
        download_with_progress(&self.playlist_url, None)
 | 
			
		||||
            .await?
 | 
			
		||||
            .get_string()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Deref for Configuration {
 | 
			
		||||
    type Target = Conf;
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &Self::Target {
 | 
			
		||||
        &self.conf
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										39
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								src/lib.rs
									
									
									
									
									
								
							@@ -1,47 +1,14 @@
 | 
			
		||||
mod m3u8;
 | 
			
		||||
mod parser;
 | 
			
		||||
use std::{
 | 
			
		||||
    fs,
 | 
			
		||||
    io::{stdin, stdout, Stdin, StdoutLock, Write},
 | 
			
		||||
    process,
 | 
			
		||||
};
 | 
			
		||||
use std::io::{stdin, stdout, Stdin, StdoutLock, Write};
 | 
			
		||||
 | 
			
		||||
pub use m3u8::M3u8;
 | 
			
		||||
pub use parser::Parser;
 | 
			
		||||
mod config;
 | 
			
		||||
pub use config::Configuration;
 | 
			
		||||
mod downloader;
 | 
			
		||||
use directories::ProjectDirs;
 | 
			
		||||
pub use downloader::download_with_progress;
 | 
			
		||||
 | 
			
		||||
pub fn setup() -> String {
 | 
			
		||||
    let project_dirs = ProjectDirs::from("com", "billenius", "iptvnator_rs").unwrap();
 | 
			
		||||
    let config_dir = project_dirs.config_dir();
 | 
			
		||||
    let ilovetv_config_file = config_dir.join("iptv_url.txt");
 | 
			
		||||
    if ilovetv_config_file.exists() {
 | 
			
		||||
        return fs::read_to_string(&ilovetv_config_file).expect("Failed to read iptv_url");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut readline = Readline::new();
 | 
			
		||||
 | 
			
		||||
    println!("Hello, I would need an url to your iptv/m3u/m3u8 stream");
 | 
			
		||||
    let url = loop {
 | 
			
		||||
        let url = readline.input("enter url: ");
 | 
			
		||||
        let yn = readline.input("Are you sure? (Y/n) ");
 | 
			
		||||
 | 
			
		||||
        if yn.trim().to_lowercase() != "n" {
 | 
			
		||||
            break url.trim().to_string();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let _ = fs::create_dir_all(config_dir);
 | 
			
		||||
    if let Err(e) = fs::write(ilovetv_config_file, &url) {
 | 
			
		||||
        eprintln!("{:?}", e);
 | 
			
		||||
        process::exit(-1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    url
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Readline<'a> {
 | 
			
		||||
    stdout: StdoutLock<'a>,
 | 
			
		||||
    stdin: Stdin,
 | 
			
		||||
@@ -66,7 +33,7 @@ impl<'a> Readline<'a> {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * I know that this isn't considered true rusty code, but the places it's used in is
 | 
			
		||||
 * safe. For this I'll leave the funciton as unsafe, so to better see it's uses. 
 | 
			
		||||
 * safe. For this I'll leave the funciton as unsafe, so to better see it's uses.
 | 
			
		||||
 * This solution makes the uses BLAZINGLY FAST which moreover is the most rusty you can get.
 | 
			
		||||
 */
 | 
			
		||||
pub unsafe fn get_mut_ref<T>(reference: &T) -> &mut T {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ use std::process::Command;
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
 | 
			
		||||
use colored::Colorize;
 | 
			
		||||
use iptvnator::{download_with_progress, get_mut_ref, setup, M3u8, Parser, Readline};
 | 
			
		||||
use iptvnator::{download_with_progress, get_mut_ref, Configuration, M3u8, Parser, Readline};
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() {
 | 
			
		||||
@@ -17,7 +17,9 @@ async fn main() {
 | 
			
		||||
        "r".bold(),"q".bold(),"d".bold(),"s".bold(),"a".bold(), "f".bold()
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let parser = Parser::new("iptv.m3u8".to_owned(), setup(), "watched.txt".to_owned()).await;
 | 
			
		||||
    let config = Rc::new(Configuration::new().expect("Failed to write to configfile"));
 | 
			
		||||
 | 
			
		||||
    let parser = Parser::new(config.clone()).await;
 | 
			
		||||
 | 
			
		||||
    let mut mpv_fs = false;
 | 
			
		||||
    let mut search_result: Option<Rc<Vec<&M3u8>>> = None;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										141
									
								
								src/parser.rs
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								src/parser.rs
									
									
									
									
									
								
							@@ -1,36 +1,24 @@
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::fs;
 | 
			
		||||
use std::ops::Deref;
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
use std::{fs, process};
 | 
			
		||||
 | 
			
		||||
use directories::ProjectDirs;
 | 
			
		||||
 | 
			
		||||
use crate::downloader::download_with_progress;
 | 
			
		||||
use crate::m3u8::M3u8;
 | 
			
		||||
use crate::Configuration;
 | 
			
		||||
 | 
			
		||||
const MAX_TRIES: usize = 4;
 | 
			
		||||
 | 
			
		||||
pub struct Parser {
 | 
			
		||||
    watched_name: Rc<PathBuf>,
 | 
			
		||||
    configuration: Rc<Configuration>,
 | 
			
		||||
    m3u8_items: Vec<M3u8>,
 | 
			
		||||
    ilovetv_url: Rc<String>,
 | 
			
		||||
    file_name: Rc<PathBuf>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Parser {
 | 
			
		||||
    pub async fn new(file_name: String, iptv_url: String, watched_name: String) -> Self {
 | 
			
		||||
        let project_dirs = ProjectDirs::from("com", "billenius", "iptvnator_rs").unwrap();
 | 
			
		||||
        let cache = project_dirs.cache_dir();
 | 
			
		||||
        let _ = fs::create_dir_all(&cache);
 | 
			
		||||
 | 
			
		||||
        let file_name = Rc::new(cache.join(file_name));
 | 
			
		||||
        let ilovetv_url = Rc::new(iptv_url);
 | 
			
		||||
        let watched_name = Rc::new(cache.join(watched_name));
 | 
			
		||||
    pub async fn new(configuration: Rc<Configuration>) -> Self {
 | 
			
		||||
        let m3u8_items = Self::get_parsed_m3u8(&configuration).await.unwrap();
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            watched_name: watched_name.clone(),
 | 
			
		||||
            m3u8_items: Self::get_parsed_content(&ilovetv_url, &file_name, &watched_name).await,
 | 
			
		||||
            ilovetv_url,
 | 
			
		||||
            file_name,
 | 
			
		||||
            configuration,
 | 
			
		||||
            m3u8_items,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -42,42 +30,20 @@ impl Parser {
 | 
			
		||||
            .collect()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn should_update(file_name: &PathBuf) -> bool {
 | 
			
		||||
        fs::metadata(&file_name)
 | 
			
		||||
            .and_then(|metadata| {
 | 
			
		||||
                Ok({
 | 
			
		||||
                    let seconds = metadata.modified()?;
 | 
			
		||||
                    seconds
 | 
			
		||||
                        .elapsed()
 | 
			
		||||
                        .expect("Failed to get systemtime")
 | 
			
		||||
                        .as_secs()
 | 
			
		||||
                        > 60 * 60 * 24 * 3
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
            .map_or_else(
 | 
			
		||||
                |_| {
 | 
			
		||||
                    println!("Could not find playlist-file, Downloading a new one");
 | 
			
		||||
                    false
 | 
			
		||||
                },
 | 
			
		||||
                |x| x,
 | 
			
		||||
            )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn forcefully_update(&mut self) {
 | 
			
		||||
        let mut counter = 0;
 | 
			
		||||
        let content = loop {
 | 
			
		||||
            counter += 1;
 | 
			
		||||
            let content = Self::download(&self.ilovetv_url).await.ok();
 | 
			
		||||
            let content = self.download_playlist().await;
 | 
			
		||||
            if counter > MAX_TRIES {
 | 
			
		||||
                return;
 | 
			
		||||
            } else if content.is_some() {
 | 
			
		||||
            } else if content.is_ok() {
 | 
			
		||||
                break content.unwrap();
 | 
			
		||||
            }
 | 
			
		||||
            println!("Retrying {}/{}", counter, MAX_TRIES);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let _ = fs::write(&*self.file_name, &content);
 | 
			
		||||
        self.m3u8_items = Self::parse_m3u8(content, &self.watched_name.clone());
 | 
			
		||||
        self.m3u8_items = Self::parse_m3u8(content, &self.seen_links);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn save_watched(&self) {
 | 
			
		||||
@@ -88,36 +54,17 @@ impl Parser {
 | 
			
		||||
            .map(|item| item.link.clone())
 | 
			
		||||
            .collect::<Vec<String>>();
 | 
			
		||||
 | 
			
		||||
        let _ = fs::create_dir_all(&*self.watched_name.parent().unwrap());
 | 
			
		||||
 | 
			
		||||
        if let Err(e) = fs::write(&*self.watched_name, watched_items.join("\n")) {
 | 
			
		||||
        let resp = fs::write(
 | 
			
		||||
            &self.seen_links_path,
 | 
			
		||||
            serde_json::to_string(&watched_items).unwrap(),
 | 
			
		||||
        );
 | 
			
		||||
        
 | 
			
		||||
        if let Err(e) = resp {
 | 
			
		||||
            eprintln!("Failed to write watched links {:?}", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_parsed_content(
 | 
			
		||||
        link: &String,
 | 
			
		||||
        file_name: &PathBuf,
 | 
			
		||||
        watched_name: &PathBuf,
 | 
			
		||||
    ) -> Vec<M3u8> {
 | 
			
		||||
        Self::parse_m3u8(
 | 
			
		||||
            Self::get_stringcontent(link, file_name)
 | 
			
		||||
                .await
 | 
			
		||||
                .expect("Failed to retrieve playlist"),
 | 
			
		||||
            watched_name,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn parse_m3u8(content: String, watched_name: &PathBuf) -> Vec<M3u8> {
 | 
			
		||||
        let saved_watches = fs::read_to_string(&watched_name);
 | 
			
		||||
        let saved_watches = if saved_watches.is_ok() {
 | 
			
		||||
            saved_watches.unwrap()
 | 
			
		||||
        } else {
 | 
			
		||||
            String::from("")
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let watched: Vec<String> = saved_watches.lines().map(String::from).collect();
 | 
			
		||||
 | 
			
		||||
    fn parse_m3u8(content: String, watched_links: &Vec<String>) -> Vec<M3u8> {
 | 
			
		||||
        let mut m3u8_items: Vec<M3u8> = Vec::new();
 | 
			
		||||
        let interesting_lines: Vec<String> = content
 | 
			
		||||
            .replacen("#EXTM3U\n", "", 1)
 | 
			
		||||
@@ -139,7 +86,7 @@ impl Parser {
 | 
			
		||||
            let name_start = interesting_lines[i].rfind(",").unwrap() + 1;
 | 
			
		||||
            let name = &interesting_lines[i][name_start..];
 | 
			
		||||
            let link = &interesting_lines[i + 1];
 | 
			
		||||
            let is_watched = watched.contains(link);
 | 
			
		||||
            let is_watched = watched_links.contains(link);
 | 
			
		||||
            let m3u8_item = M3u8 {
 | 
			
		||||
                tvg_id: items[0].to_owned(),
 | 
			
		||||
                tvg_name: items[1].to_owned(),
 | 
			
		||||
@@ -154,42 +101,18 @@ impl Parser {
 | 
			
		||||
        m3u8_items
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_stringcontent(link: &String, file_name: &PathBuf) -> Result<String, String> {
 | 
			
		||||
        if !Self::should_update(file_name) {
 | 
			
		||||
            let content = fs::read_to_string(&file_name);
 | 
			
		||||
            if content.is_ok() {
 | 
			
		||||
                return Ok(content.unwrap());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut counter: usize = 0;
 | 
			
		||||
        let content = loop {
 | 
			
		||||
            counter += 1;
 | 
			
		||||
 | 
			
		||||
            if let Ok(content) = Self::download(link).await {
 | 
			
		||||
                break Ok(content);
 | 
			
		||||
            } else if counter > MAX_TRIES {
 | 
			
		||||
                break Err("".to_owned());
 | 
			
		||||
            }
 | 
			
		||||
            println!("Retrying {}/{}", counter + 1, MAX_TRIES);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        match content {
 | 
			
		||||
            Ok(s) => {
 | 
			
		||||
                let _ = fs::write(&file_name, s.as_bytes());
 | 
			
		||||
                Ok(s)
 | 
			
		||||
            }
 | 
			
		||||
            Err(_) => {
 | 
			
		||||
                println!("Couldn't get m3u8 file!");
 | 
			
		||||
                process::exit(-1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn download(link: &String) -> Result<String, String> {
 | 
			
		||||
        Ok(download_with_progress(link, None)
 | 
			
		||||
            .await?
 | 
			
		||||
            .get_string()
 | 
			
		||||
            .unwrap())
 | 
			
		||||
    async fn get_parsed_m3u8(config: &Configuration) -> Result<Vec<M3u8>, String> {
 | 
			
		||||
        Ok(Self::parse_m3u8(
 | 
			
		||||
            config.get_playlist().await?,
 | 
			
		||||
            &config.seen_links,
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Deref for Parser {
 | 
			
		||||
    type Target = Configuration;
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &Self::Target {
 | 
			
		||||
        &self.configuration
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user