start with offlineparser and grandmother
This commit is contained in:
parent
da1e754ec3
commit
2b30b5335f
127
Cargo.lock
generated
127
Cargo.lock
generated
@ -8,6 +8,15 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-compression"
|
name = "async-compression"
|
||||||
version = "0.3.15"
|
version = "0.3.15"
|
||||||
@ -21,6 +30,17 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-recursion"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
@ -74,6 +94,21 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term",
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"strsim",
|
||||||
|
"textwrap",
|
||||||
|
"unicode-width",
|
||||||
|
"vec_map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colored"
|
name = "colored"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@ -304,6 +339,15 @@ version = "0.12.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
@ -420,6 +464,7 @@ dependencies = [
|
|||||||
name = "ilovetv"
|
name = "ilovetv"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-recursion",
|
||||||
"bytes",
|
"bytes",
|
||||||
"colored",
|
"colored",
|
||||||
"directories",
|
"directories",
|
||||||
@ -428,6 +473,7 @@ dependencies = [
|
|||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"structopt",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -686,6 +732,30 @@ version = "0.3.19"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b"
|
checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.50"
|
version = "1.0.50"
|
||||||
@ -951,6 +1021,36 @@ version = "0.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt"
|
||||||
|
version = "0.3.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"lazy_static",
|
||||||
|
"structopt-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt-derive"
|
||||||
|
version = "0.4.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.107"
|
version = "1.0.107"
|
||||||
@ -976,6 +1076,15 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.38"
|
version = "1.0.38"
|
||||||
@ -1130,6 +1239,12 @@ dependencies = [
|
|||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
@ -1159,6 +1274,18 @@ version = "0.2.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -4,6 +4,7 @@ version = "1.0.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-recursion = "1.0.2"
|
||||||
bytes = "1.3.0"
|
bytes = "1.3.0"
|
||||||
colored = "2.0.0"
|
colored = "2.0.0"
|
||||||
directories = "4.0.1"
|
directories = "4.0.1"
|
||||||
@ -12,4 +13,5 @@ indicatif = { version = "0.17.3", features = ["tokio"] }
|
|||||||
reqwest = { version = "0.11.13", features = ["blocking", "deflate", "gzip", "rustls", "rustls-tls", "stream"] }
|
reqwest = { version = "0.11.13", features = ["blocking", "deflate", "gzip", "rustls", "rustls-tls", "stream"] }
|
||||||
serde = { version = "1.0.152", features = ["serde_derive","rc"] }
|
serde = { version = "1.0.152", features = ["serde_derive","rc"] }
|
||||||
serde_json = "1.0.93"
|
serde_json = "1.0.93"
|
||||||
|
structopt = { version = "0.3.26", features = ["color", "suggestions"] }
|
||||||
tokio = { version = "1.24.2", features = ["full"] }
|
tokio = { version = "1.24.2", features = ["full"] }
|
||||||
|
@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
get_mut_ref, m3u8::DataEntry, Readline, APP_IDENTIFIER, JSON_CONFIG_FILENAME,
|
get_mut_ref, m3u8::OfflineEntry, Readline, APP_IDENTIFIER, JSON_CONFIG_FILENAME,
|
||||||
STANDARD_OFFLINE_FILENAME, STANDARD_PLAYLIST_FILENAME, STANDARD_SEEN_LINKS_FILENAME,
|
STANDARD_OFFLINE_FILENAME, STANDARD_PLAYLIST_FILENAME, STANDARD_SEEN_LINKS_FILENAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ pub struct Configuration {
|
|||||||
pub seen_links: Vec<String>,
|
pub seen_links: Vec<String>,
|
||||||
config_file_path: PathBuf,
|
config_file_path: PathBuf,
|
||||||
pub data_dir: PathBuf,
|
pub data_dir: PathBuf,
|
||||||
pub datafile_content: Vec<DataEntry>,
|
pub offlinefile_content: Rc<Vec<OfflineEntry>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Configuration {
|
impl Configuration {
|
||||||
@ -94,8 +94,8 @@ impl Configuration {
|
|||||||
// Make sure all the dirs for the project are setup correctly
|
// Make sure all the dirs for the project are setup correctly
|
||||||
let config_dir = project_dirs.config_dir();
|
let config_dir = project_dirs.config_dir();
|
||||||
let cache_dir = project_dirs.cache_dir().to_path_buf();
|
let cache_dir = project_dirs.cache_dir().to_path_buf();
|
||||||
let data_dir = project_dirs.data_local_dir().to_path_buf();
|
let offline_dir = project_dirs.data_local_dir().to_path_buf();
|
||||||
for dir in [&config_dir, &cache_dir.as_path(), &data_dir.as_path()].iter() {
|
for dir in [&config_dir, &cache_dir.as_path(), &offline_dir.as_path()].iter() {
|
||||||
if !dir.exists() {
|
if !dir.exists() {
|
||||||
let _ = fs::create_dir_all(dir);
|
let _ = fs::create_dir_all(dir);
|
||||||
}
|
}
|
||||||
@ -115,8 +115,9 @@ impl Configuration {
|
|||||||
let seen_links = Self::get_watched(&seen_links_path).unwrap_or_default();
|
let seen_links = Self::get_watched(&seen_links_path).unwrap_or_default();
|
||||||
|
|
||||||
// Datadir
|
// Datadir
|
||||||
let datafile = data_dir.join(STANDARD_OFFLINE_FILENAME);
|
let offlinefile = offline_dir.join(STANDARD_OFFLINE_FILENAME);
|
||||||
let datafile_content = Self::get_datafile_content(&datafile).unwrap_or_default();
|
let offlinefile_content =
|
||||||
|
Rc::new(Self::get_offline_content(&offlinefile).unwrap_or_default());
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
conf: configuration,
|
conf: configuration,
|
||||||
@ -124,8 +125,8 @@ impl Configuration {
|
|||||||
seen_links,
|
seen_links,
|
||||||
seen_links_path,
|
seen_links_path,
|
||||||
config_file_path,
|
config_file_path,
|
||||||
data_dir,
|
data_dir: offline_dir,
|
||||||
datafile_content,
|
offlinefile_content,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,13 +138,13 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_datafile_ugly(&self, data_entry: DataEntry) {
|
pub fn push_offlinefile_ugly(&self, data_entry: OfflineEntry) {
|
||||||
unsafe { get_mut_ref(&self.datafile_content) }.push(data_entry);
|
unsafe { get_mut_ref(&*self.offlinefile_content) }.push(data_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_datafile(&self) -> Result<(), io::Error> {
|
pub fn write_datafile(&self) -> Result<(), io::Error> {
|
||||||
let path = self.data_dir.join(STANDARD_OFFLINE_FILENAME);
|
let path = self.data_dir.join(STANDARD_OFFLINE_FILENAME);
|
||||||
fs::write(path, serde_json::to_string(&self.datafile_content)?)
|
fs::write(path, serde_json::to_string(&self.offlinefile_content)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_watched(path: &Path) -> Option<Vec<String>> {
|
fn get_watched(path: &Path) -> Option<Vec<String>> {
|
||||||
@ -151,7 +152,7 @@ impl Configuration {
|
|||||||
serde_json::from_reader(reader).ok()
|
serde_json::from_reader(reader).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_datafile_content(datafile: &PathBuf) -> Option<Vec<DataEntry>> {
|
fn get_offline_content(datafile: &PathBuf) -> Option<Vec<OfflineEntry>> {
|
||||||
let reader = BufReader::new(File::open(datafile).ok()?);
|
let reader = BufReader::new(File::open(datafile).ok()?);
|
||||||
serde_json::from_reader(reader).ok()
|
serde_json::from_reader(reader).ok()
|
||||||
}
|
}
|
||||||
|
27
src/getm3u8.rs
Normal file
27
src/getm3u8.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use crate::M3u8;
|
||||||
|
|
||||||
|
pub trait GetM3u8 {
|
||||||
|
fn get_m3u8(&self) -> Vec<&M3u8>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WatchedFind {
|
||||||
|
fn find(&self, name: &str) -> Vec<&M3u8>;
|
||||||
|
fn get_watched(&self) -> Vec<&M3u8>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> WatchedFind for T
|
||||||
|
where
|
||||||
|
T: GetM3u8,
|
||||||
|
{
|
||||||
|
fn find(&self, name: &str) -> Vec<&M3u8> {
|
||||||
|
let name = name.to_lowercase();
|
||||||
|
self.get_m3u8()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|item| item.name.to_lowercase().contains(&name) || item.tvg_id.contains(&name))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_watched(&self) -> Vec<&M3u8> {
|
||||||
|
self.get_m3u8().into_iter().filter(|x| x.watched).collect()
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,17 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use crate::{Configuration, Parser, Playlist, MAX_TRIES};
|
use crate::{getm3u8::WatchedFind, Configuration, GetM3u8, Parser, Playlist, MAX_TRIES};
|
||||||
|
|
||||||
pub struct GrandMother {
|
pub struct GrandMother<T>
|
||||||
pub parser: Parser,
|
where
|
||||||
|
T: GetM3u8,
|
||||||
|
{
|
||||||
|
pub parser: T,
|
||||||
pub playlist: Playlist,
|
pub playlist: Playlist,
|
||||||
pub config: Configuration,
|
pub config: Configuration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GrandMother {
|
impl GrandMother<Parser> {
|
||||||
pub async fn new(config: Configuration) -> Result<Self, String> {
|
pub async fn new(config: Configuration) -> Result<Self, String> {
|
||||||
let playlist = Playlist::new(config.playlist_path.clone(), config.playlist_url.clone());
|
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 seen_links = config.seen_links.iter().map(|x| x.as_str()).collect();
|
||||||
@ -43,8 +46,12 @@ impl GrandMother {
|
|||||||
println!("Retrying {}/{}", counter, MAX_TRIES);
|
println!("Retrying {}/{}", counter, MAX_TRIES);
|
||||||
};
|
};
|
||||||
|
|
||||||
let watched_links = self.parser.get_watched();
|
let watched_links = self
|
||||||
let watched_links = watched_links.iter().map(|x| x.as_str()).collect();
|
.parser
|
||||||
|
.get_watched()
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.link.as_str())
|
||||||
|
.collect();
|
||||||
self.parser = Parser::new(&content, &watched_links).await;
|
self.parser = Parser::new(&content, &watched_links).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,17 +2,21 @@ mod config;
|
|||||||
mod downloader;
|
mod downloader;
|
||||||
mod grandmother;
|
mod grandmother;
|
||||||
mod m3u8;
|
mod m3u8;
|
||||||
|
mod opt;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod playlist;
|
mod playlist;
|
||||||
|
mod getm3u8;
|
||||||
|
|
||||||
use std::io::{stdin, stdout, Stdin, StdoutLock, Write};
|
use std::io::{stdin, stdout, Stdin, StdoutLock, Write};
|
||||||
|
|
||||||
pub use config::Configuration;
|
pub use config::Configuration;
|
||||||
pub use downloader::download_with_progress;
|
pub use downloader::download_with_progress;
|
||||||
pub use grandmother::GrandMother;
|
pub use grandmother::GrandMother;
|
||||||
pub use m3u8::{DataEntry, M3u8};
|
pub use m3u8::{OfflineEntry, M3u8};
|
||||||
|
pub use opt::{Mode, Opt};
|
||||||
pub use parser::Parser;
|
pub use parser::Parser;
|
||||||
pub use playlist::Playlist;
|
pub use playlist::Playlist;
|
||||||
|
pub use getm3u8::GetM3u8;
|
||||||
|
|
||||||
pub const JSON_CONFIG_FILENAME: &'static str = "config.json";
|
pub const JSON_CONFIG_FILENAME: &'static str = "config.json";
|
||||||
pub const APP_IDENTIFIER: [&'static str; 3] = ["com", "billenius", "ilovetv"];
|
pub const APP_IDENTIFIER: [&'static str; 3] = ["com", "billenius", "ilovetv"];
|
||||||
@ -20,6 +24,7 @@ pub const STANDARD_PLAYLIST_FILENAME: &'static str = "playlist.m3u8";
|
|||||||
pub const STANDARD_SEEN_LINKS_FILENAME: &'static str = "watched_links.json";
|
pub const STANDARD_SEEN_LINKS_FILENAME: &'static str = "watched_links.json";
|
||||||
pub const STANDARD_OFFLINE_FILENAME: &'static str = "ilovetv_offline.json";
|
pub const STANDARD_OFFLINE_FILENAME: &'static str = "ilovetv_offline.json";
|
||||||
pub const MAX_TRIES: u8 = 4;
|
pub const MAX_TRIES: u8 = 4;
|
||||||
|
|
||||||
pub struct Readline<'a> {
|
pub struct Readline<'a> {
|
||||||
stdout: StdoutLock<'a>,
|
stdout: StdoutLock<'a>,
|
||||||
stdin: Stdin,
|
stdin: Stdin,
|
||||||
|
29
src/m3u8.rs
29
src/m3u8.rs
@ -1,6 +1,8 @@
|
|||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{fmt::Display, ops::Deref};
|
use std::{fmt::Display, ops::Deref, rc::Rc};
|
||||||
|
|
||||||
|
use crate::{Configuration, GetM3u8};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Hash)]
|
#[derive(Serialize, Deserialize, Clone, Hash)]
|
||||||
pub struct M3u8 {
|
pub struct M3u8 {
|
||||||
@ -26,20 +28,39 @@ impl Display for M3u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Hash)]
|
#[derive(Serialize, Deserialize, Clone, Hash)]
|
||||||
pub struct DataEntry {
|
pub struct OfflineEntry {
|
||||||
m3u8: M3u8,
|
m3u8: M3u8,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
}
|
}
|
||||||
impl DataEntry {
|
|
||||||
|
impl OfflineEntry {
|
||||||
pub fn new(m3u8: M3u8, path: String) -> Self {
|
pub fn new(m3u8: M3u8, path: String) -> Self {
|
||||||
Self { m3u8, path }
|
Self { m3u8, path }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for DataEntry {
|
impl Deref for OfflineEntry {
|
||||||
type Target = M3u8;
|
type Target = M3u8;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.m3u8
|
&self.m3u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct OfflineParser {
|
||||||
|
entries: Rc<Vec<OfflineEntry>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
39
src/main.rs
39
src/main.rs
@ -2,13 +2,18 @@ use std::num::ParseIntError;
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use async_recursion::async_recursion;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use ilovetv::{
|
use ilovetv::{
|
||||||
download_with_progress, get_mut_ref, Configuration, DataEntry, GrandMother, M3u8, Readline,
|
download_with_progress, get_mut_ref, Configuration, GetM3u8, GrandMother, M3u8, Mode,
|
||||||
|
OfflineEntry, Opt, Parser, Readline,
|
||||||
};
|
};
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
let opt = Opt::from_args();
|
||||||
|
|
||||||
// Greet the user
|
// Greet the user
|
||||||
[
|
[
|
||||||
format!(
|
format!(
|
||||||
@ -144,8 +149,8 @@ async fn main() {
|
|||||||
let path = gm.config.data_dir.join(&to_download.name);
|
let path = gm.config.data_dir.join(&to_download.name);
|
||||||
let path = path.to_string_lossy().to_string();
|
let path = path.to_string_lossy().to_string();
|
||||||
download_m3u8(to_download, Some(&path)).await;
|
download_m3u8(to_download, Some(&path)).await;
|
||||||
let data_entry = DataEntry::new((*to_download).clone(), path);
|
let data_entry = OfflineEntry::new((*to_download).clone(), path);
|
||||||
gm.config.push_datafile_ugly(data_entry);
|
gm.config.push_offlinefile_ugly(data_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = gm.config.write_datafile() {
|
if let Err(e) = gm.config.write_datafile() {
|
||||||
@ -170,6 +175,34 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_recursion(?Send)]
|
||||||
|
async fn get_gm(
|
||||||
|
mode: Mode,
|
||||||
|
readline: &mut Readline<'_>,
|
||||||
|
config: Configuration,
|
||||||
|
) -> Result<GrandMother<Parser>, 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::<u8>();
|
||||||
|
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>(
|
fn ask_which_to_download<'a>(
|
||||||
readline: &mut Readline,
|
readline: &mut Readline,
|
||||||
search_result: &Rc<Vec<&'a M3u8>>,
|
search_result: &Rc<Vec<&'a M3u8>>,
|
||||||
|
37
src/opt.rs
Normal file
37
src/opt.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
#[derive(StructOpt, Debug)]
|
||||||
|
#[structopt(name = "ilovetv")]
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Mode {
|
||||||
|
Online,
|
||||||
|
Offline,
|
||||||
|
Ask,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Mode {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Ask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Mode {
|
||||||
|
type Err = &'static str;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"online" => Ok(Self::Online),
|
||||||
|
"offline" => Ok(Self::Offline),
|
||||||
|
"ask" | "default" => Ok(Self::Ask),
|
||||||
|
_ => Err("No such enum"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,12 @@
|
|||||||
use std::ops::Deref;
|
use std::{
|
||||||
|
fs::{self, File},
|
||||||
|
io::BufReader,
|
||||||
|
ops::Deref,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::m3u8::M3u8;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::{m3u8::M3u8, Configuration, GetM3u8};
|
||||||
|
|
||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
m3u8_items: Vec<M3u8>,
|
m3u8_items: Vec<M3u8>,
|
||||||
@ -40,14 +46,6 @@ impl Parser {
|
|||||||
self.m3u8_items = Self::parse_m3u8(content, seen_links);
|
self.m3u8_items = Self::parse_m3u8(content, seen_links);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_watched(&self) -> Vec<&String> {
|
|
||||||
self.m3u8_items
|
|
||||||
.iter()
|
|
||||||
.filter(|x| x.watched)
|
|
||||||
.map(|x| &x.link)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_m3u8(content: &str, watched_links: &Vec<&str>) -> Vec<M3u8> {
|
fn parse_m3u8(content: &str, watched_links: &Vec<&str>) -> Vec<M3u8> {
|
||||||
let mut m3u8_items: Vec<M3u8> = Vec::new();
|
let mut m3u8_items: Vec<M3u8> = Vec::new();
|
||||||
let interesting_lines: Vec<String> = content
|
let interesting_lines: Vec<String> = content
|
||||||
@ -93,3 +91,24 @@ impl Deref for Parser {
|
|||||||
&self.m3u8_items
|
&self.m3u8_items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GetM3u8 for Parser {
|
||||||
|
fn get_m3u8(&self) -> Vec<&M3u8> {
|
||||||
|
self.m3u8_items.iter().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct OfflineEntry {
|
||||||
|
m3u8: M3u8,
|
||||||
|
path: String,
|
||||||
|
}
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct OfflineParser {
|
||||||
|
m3u8_items: Vec<OfflineEntry>,
|
||||||
|
}
|
||||||
|
impl OfflineParser {
|
||||||
|
pub fn new(config: &Configuration) -> Result<Self, std::io::Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,7 +10,7 @@ use crate::{download_with_progress, downloader::DualWriter, MAX_TRIES};
|
|||||||
pub struct Playlist {
|
pub struct Playlist {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
path_to_playlist: Rc<PathBuf>,
|
path_to_playlist: Rc<PathBuf>,
|
||||||
url: Rc<String>,
|
url: Option<Rc<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Playlist {
|
impl Playlist {
|
||||||
@ -18,7 +18,7 @@ impl Playlist {
|
|||||||
let mut me = Self {
|
let mut me = Self {
|
||||||
content: String::new(),
|
content: String::new(),
|
||||||
path_to_playlist,
|
path_to_playlist,
|
||||||
url,
|
url: Some(url),
|
||||||
};
|
};
|
||||||
me.content = me.get_saved_or_download().await?;
|
me.content = me.get_saved_or_download().await?;
|
||||||
|
|
||||||
@ -76,7 +76,13 @@ impl Playlist {
|
|||||||
loop {
|
loop {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
|
|
||||||
let downloaded = download_with_progress(&self.url, None)
|
let url = self
|
||||||
|
.url
|
||||||
|
.as_ref()
|
||||||
|
.clone()
|
||||||
|
.ok_or_else(|| String::from("In offline mode"))?;
|
||||||
|
|
||||||
|
let downloaded = download_with_progress(url, None)
|
||||||
.await
|
.await
|
||||||
.and_then(DualWriter::get_string);
|
.and_then(DualWriter::get_string);
|
||||||
if let Ok(content) = downloaded {
|
if let Ok(content) = downloaded {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user