From 8290b67f2595488e63680068ba3f60eeb019f586 Mon Sep 17 00:00:00 2001 From: lov3b Date: Sun, 5 Mar 2023 19:14:26 +0100 Subject: [PATCH] Early working version --- src/getm3u8.rs | 13 +++++--- src/grandmother.rs | 57 ++++++++++++++++++++----------- src/lib.rs | 37 ++++++++++++++++++--- src/m3u8.rs | 27 +++------------ src/main.rs | 83 +++++++++++++++++++++++----------------------- src/opt.rs | 2 +- src/parser.rs | 53 ++++++++++++++++++----------- 7 files changed, 161 insertions(+), 111 deletions(-) diff --git a/src/getm3u8.rs b/src/getm3u8.rs index 47f2530..defa90b 100644 --- a/src/getm3u8.rs +++ b/src/getm3u8.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use crate::M3u8; pub trait GetM3u8 { @@ -9,10 +11,7 @@ pub trait WatchedFind { fn get_watched(&self) -> Vec<&M3u8>; } -impl WatchedFind for T -where - T: GetM3u8, -{ +impl WatchedFind for T { fn find(&self, name: &str) -> Vec<&M3u8> { let name = name.to_lowercase(); self.get_m3u8() @@ -25,3 +24,9 @@ where self.get_m3u8().into_iter().filter(|x| x.watched).collect() } } +pub trait GetPlayPath { + fn get_path_to_play(&self, link: Rc) -> Result, String>; +} + +pub trait M3u8PlayPath: GetM3u8 + GetPlayPath {} +impl M3u8PlayPath for T {} diff --git a/src/grandmother.rs b/src/grandmother.rs index 9701cf6..ffc3fca 100644 --- a/src/grandmother.rs +++ b/src/grandmother.rs @@ -1,44 +1,61 @@ +#[allow(unused_imports)] +use crate::GetM3u8; +use crate::{ + getm3u8::{M3u8PlayPath, WatchedFind}, + Configuration, OfflineParser, Parser, Playlist, MAX_TRIES, +}; use std::fs; -use crate::{getm3u8::WatchedFind, Configuration, GetM3u8, Parser, Playlist, MAX_TRIES}; +type Error = String; -pub struct GrandMother -where - T: GetM3u8, -{ - pub parser: T, - pub playlist: Playlist, +pub struct GrandMother { + pub parser: Box, + pub playlist: Option, pub config: Configuration, } -impl GrandMother { - pub async fn new(config: Configuration) -> Result { +impl GrandMother { + pub async fn new(config: Configuration) -> Result { let playlist = Playlist::new(config.playlist_path.clone(), config.playlist_url.clone()); let seen_links = config.seen_links.iter().map(|x| x.as_str()).collect(); let playlist = playlist.await?; let playlist_content = playlist.get_saved_or_download().await?; - - let parser = Parser::new(&playlist_content, &seen_links).await; + let parser: Box = + Box::new(Parser::new(&playlist_content, &seen_links).await); Ok(Self { parser, - playlist, + playlist: Some(playlist), config, }) } - pub async fn refresh_dirty(&self) { - let ptr = self as *const Self as *mut Self; - unsafe { &mut *ptr }.refresh().await; + pub fn new_offline(config: Configuration) -> Self { + let parser: Box = Box::new(OfflineParser::new(&config)); + Self { + parser, + playlist: None, + config, + } } - pub async fn refresh(&mut self) { + pub async fn refresh_dirty(&self) -> Result<(), Error> { + let ptr = self as *const Self as *mut Self; + unsafe { &mut *ptr }.refresh().await + } + + pub async fn refresh(&mut self) -> Result<(), Error> { let mut counter = 0; let content = loop { counter += 1; - let content = self.playlist.download().await; + let content = self + .playlist + .as_ref() + .ok_or_else(|| "Cannot refresh playlist in offlinemode")? + .download() + .await; if counter > MAX_TRIES { - return; + return Ok(()); } if let Ok(content) = content { break content; @@ -52,7 +69,9 @@ impl GrandMother { .iter() .map(|x| x.link.as_str()) .collect(); - self.parser = Parser::new(&content, &watched_links).await; + self.parser = Box::new(Parser::new(&content, &watched_links).await); + + Ok(()) } pub fn save_watched(&self) { diff --git a/src/lib.rs b/src/lib.rs index 52dc792..7bf07dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,22 +1,23 @@ mod config; mod downloader; +pub mod getm3u8; mod grandmother; mod m3u8; mod opt; mod parser; mod playlist; -mod getm3u8; use std::io::{stdin, stdout, Stdin, StdoutLock, Write}; +use async_recursion::async_recursion; pub use config::Configuration; pub use downloader::download_with_progress; +pub use getm3u8::{GetM3u8, GetPlayPath, WatchedFind}; pub use grandmother::GrandMother; -pub use m3u8::{OfflineEntry, M3u8}; +pub use m3u8::{M3u8, OfflineEntry}; pub use opt::{Mode, Opt}; -pub use parser::Parser; +pub use parser::{OfflineParser, Parser}; pub use playlist::Playlist; -pub use getm3u8::GetM3u8; pub const JSON_CONFIG_FILENAME: &'static str = "config.json"; pub const APP_IDENTIFIER: [&'static str; 3] = ["com", "billenius", "ilovetv"]; @@ -56,3 +57,31 @@ pub unsafe fn get_mut_ref(reference: &T) -> &mut T { let ptr = reference as *const T as *mut T; &mut *ptr } + +#[async_recursion(?Send)] +pub async fn get_gm( + mode: Mode, + readline: &mut Readline<'_>, + config: Configuration, +) -> Result { + match mode { + Mode::Online => GrandMother::new(config).await, + Mode::Offline => Ok(GrandMother::new_offline(config)), + Mode::Ask => loop { + let input = readline + .input("Online/Offline mode? [1/2] ") + .trim() + .parse::(); + if let Ok(num) = input { + if num == 1 { + return get_gm(Mode::Online, readline, config).await; + } else if num == 2 { + return get_gm(Mode::Offline, readline, config).await; + } + println!("Has to be either 1 (Onine) or 2 (Offline)"); + } else { + println!("Has to be a number"); + } + }, + } +} diff --git a/src/m3u8.rs b/src/m3u8.rs index 49d58a2..9e74ecc 100644 --- a/src/m3u8.rs +++ b/src/m3u8.rs @@ -2,7 +2,8 @@ use colored::Colorize; use serde::{Deserialize, Serialize}; use std::{fmt::Display, ops::Deref, rc::Rc}; -use crate::{Configuration, GetM3u8}; +#[allow(unused_imports)] +use crate::GetM3u8; #[derive(Serialize, Deserialize, Clone, Hash)] pub struct M3u8 { @@ -11,7 +12,7 @@ pub struct M3u8 { pub tvg_logo: String, pub group_title: String, pub name: String, - pub link: String, + pub link: Rc, pub watched: bool, } @@ -30,11 +31,11 @@ impl Display for M3u8 { #[derive(Serialize, Deserialize, Clone, Hash)] pub struct OfflineEntry { m3u8: M3u8, - pub path: String, + pub path: Rc, } impl OfflineEntry { - pub fn new(m3u8: M3u8, path: String) -> Self { + pub fn new(m3u8: M3u8, path: Rc) -> Self { Self { m3u8, path } } } @@ -46,21 +47,3 @@ impl Deref for OfflineEntry { &self.m3u8 } } - -struct OfflineParser { - entries: Rc>, -} - -impl OfflineParser { - pub fn new(conf: &Configuration) -> Self { - Self { - entries: conf.offlinefile_content.clone(), - } - } -} - -impl GetM3u8 for OfflineParser { - fn get_m3u8(&self) -> Vec<&M3u8> { - self.entries.iter().map(|x| &**x).collect() - } -} diff --git a/src/main.rs b/src/main.rs index cfcb3a1..d7961c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,14 +2,16 @@ use std::num::ParseIntError; use std::process::Command; use std::rc::Rc; -use async_recursion::async_recursion; use colored::Colorize; -use ilovetv::{ - download_with_progress, get_mut_ref, Configuration, GetM3u8, GrandMother, M3u8, Mode, - OfflineEntry, Opt, Parser, Readline, -}; use structopt::StructOpt; +use ilovetv::{ + download_with_progress, get_gm, get_mut_ref, Configuration, M3u8, OfflineEntry, Opt, Readline, + WatchedFind, +}; +#[allow(unused_imports)] +use ilovetv::{GetM3u8, GetPlayPath, OfflineParser}; + #[tokio::main] async fn main() { let opt = Opt::from_args(); @@ -37,14 +39,21 @@ async fn main() { .iter() .for_each(|s| println!("{}", &s)); - let gm = GrandMother::new(Configuration::new().expect("Failed to write to configfile")) - .await - .unwrap(); + // let gm = GrandMother::new(Configuration::new().expect("Failed to write to configfile")) + // .await + // .unwrap(); // let parser = Parser::new(config.clone()).await; let mut mpv_fs = false; let mut search_result: Option>> = None; let mut readline = Readline::new(); + let gm = get_gm( + opt.mode, + &mut readline, + Configuration::new().expect("Failed to write to configfile"), + ) + .await + .expect("Failed to retrive online playlist"); loop { // Dont't perform a search if user has just watched, instead present the previous search @@ -61,7 +70,12 @@ async fn main() { // Refresh playlist "r" => { search_result = None; - gm.refresh_dirty().await; + if let Err(e) = gm.refresh_dirty().await { + println!( + "Cannot refresh. This is probably due to offlinemode {:?}", + e + ); + }; continue; } // Toggle fullscreen for mpv @@ -120,7 +134,12 @@ async fn main() { "r" => { println!("Refreshing local m3u8-file"); search_result = None; - gm.refresh_dirty().await; + if let Err(e) = gm.refresh_dirty().await { + println!( + "Cannot refresh. This is probably due to offlinemode {:?}", + e + ); + }; continue; } "f" => { @@ -147,7 +166,7 @@ async fn main() { for to_download in download_selections.iter() { let path = gm.config.data_dir.join(&to_download.name); - let path = path.to_string_lossy().to_string(); + let path = Rc::new(path.to_string_lossy().to_string()); download_m3u8(to_download, Some(&path)).await; let data_entry = OfflineEntry::new((*to_download).clone(), path); gm.config.push_offlinefile_ugly(data_entry); @@ -167,7 +186,15 @@ async fn main() { match choosen { Ok(k) => { let search_result = search_result.as_ref().unwrap(); - stream(search_result[k - 1], mpv_fs); + let to_play = search_result[k - 1]; + let path_link = if let Ok(link) = gm.parser.get_path_to_play(to_play.link.clone()) { + link + } else { + println!("Not possible to refresh playlist while in offlinemode"); + continue; + }; + + stream(to_play, &*path_link, mpv_fs); gm.save_watched(); } Err(e) => println!("Have to be a valid number! {:?}", e), @@ -175,34 +202,6 @@ async fn main() { } } -#[async_recursion(?Send)] -async fn get_gm( - mode: Mode, - readline: &mut Readline<'_>, - config: Configuration, -) -> Result, String> { - match mode { - Mode::Online => GrandMother::new(config).await, - Mode::Offline => GrandMother::new_in_offline(config), - Mode::Ask => loop { - let input = readline - .input("Online/Offline mode? [1/2]") - .trim() - .parse::(); - if let Ok(num) = input { - if num == 1 { - return get_gm(Mode::Online, readline, config).await; - } else if num == 2 { - return get_gm(Mode::Offline, readline, config).await; - } - println!("Has to be either 1 (Onine) or 2 (Offline)"); - } else { - println!("Has to be a number"); - } - }, - } -} - fn ask_which_to_download<'a>( readline: &mut Readline, search_result: &Rc>, @@ -272,10 +271,10 @@ async fn download_m3u8(file_to_download: &M3u8, path: Option<&str>) { * in this context and also the most efficient way. * With other words, it's BLAZINGLY FAST */ -fn stream(m3u8item: &M3u8, launch_in_fullscreen: bool) { +fn stream(m3u8item: &M3u8, link: &String, launch_in_fullscreen: bool) { let mut m3u8item = unsafe { get_mut_ref(m3u8item) }; m3u8item.watched = true; - let mut args: Vec<&str> = vec![&m3u8item.link]; + let mut args: Vec<&str> = vec![link]; if launch_in_fullscreen { args.push("--fs"); } diff --git a/src/opt.rs b/src/opt.rs index d0939ac..168e3b8 100644 --- a/src/opt.rs +++ b/src/opt.rs @@ -7,7 +7,7 @@ pub struct Opt { #[structopt(short, long, default_value = "ask")] /// Choose whether to launch in offlinemode, onlinemode or wheter to ask during startup. /// In offlinemode it's only possible to watch downloaded entries - pub offline_mode: Mode, + pub mode: Mode, } #[derive(Debug)] diff --git a/src/parser.rs b/src/parser.rs index 26c588d..8931569 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,12 +1,8 @@ -use std::{ - fs::{self, File}, - io::BufReader, - ops::Deref, -}; +use std::{ops::Deref, rc::Rc}; use serde::Serialize; -use crate::{m3u8::M3u8, Configuration, GetM3u8}; +use crate::{m3u8::M3u8, Configuration, GetM3u8, GetPlayPath, OfflineEntry}; pub struct Parser { m3u8_items: Vec, @@ -75,7 +71,7 @@ impl Parser { tvg_logo: items[2].to_owned(), group_title: items[3].to_owned(), name: name.to_owned(), - link: link.to_string(), + link: Rc::new(link.to_string()), watched: is_watched, }; m3u8_items.push(m3u8_item); @@ -98,17 +94,36 @@ impl GetM3u8 for Parser { } } -#[derive(Serialize)] -struct OfflineEntry { - m3u8: M3u8, - path: String, -} -#[derive(Serialize)] -struct OfflineParser { - m3u8_items: Vec, -} -impl OfflineParser { - pub fn new(config: &Configuration) -> Result { - todo!() +impl GetPlayPath for Parser { + fn get_path_to_play<'a>(&'a self, link: Rc) -> Result, String> { + Ok(link.clone()) + } +} +#[derive(Serialize)] +pub struct OfflineParser { + m3u8_items: Rc>, +} +impl OfflineParser { + pub fn new(config: &Configuration) -> Self { + Self { + m3u8_items: config.offlinefile_content.clone(), + } + } +} + +impl GetPlayPath for OfflineParser { + fn get_path_to_play(&self, link: Rc) -> Result, String> { + for offline_entry in &*self.m3u8_items { + if *offline_entry.link == *link { + return Ok(offline_entry.path.clone()); + } + } + Err("Not stored for offline use".to_owned()) + } +} + +impl GetM3u8 for OfflineParser { + fn get_m3u8(&self) -> Vec<&M3u8> { + self.m3u8_items.iter().map(|x| &**x).collect() } }