Compare commits
36 Commits
main
...
offline_mo
Author | SHA1 | Date | |
---|---|---|---|
71ded77fbc | |||
37bc5b11c7 | |||
6da49ec983 | |||
0d903f80ff | |||
38b0e405d0 | |||
81006d4c5c | |||
2b30b5335f | |||
da1e754ec3 | |||
92f2031dad | |||
c162450bdd | |||
af1d257c0a | |||
8417969b9d | |||
bc6b5cba34 | |||
eb1e4e7ce1 | |||
0f611f9aa6 | |||
0999f24691 | |||
d07d4d70b4 | |||
3171e5568d | |||
0d02826b77 | |||
c27d0c4cb9 | |||
41a4af4667 | |||
6197c52bf8 | |||
7d6aa90dad | |||
f69a040287 | |||
304fa44629 | |||
39ced4e0d7 | |||
3a977a8358 | |||
5637f62a8b | |||
dccb4f1c40 | |||
c7fff46ff3 | |||
866e56c247 | |||
bef4208351 | |||
2dda01e651 | |||
f4c184b2c7 | |||
608176262d | |||
2f5fcdb9dd |
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [],
|
|
||||||
}
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -462,7 +462,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ilovetv"
|
name = "ilovetv"
|
||||||
version = "1.1.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ilovetv"
|
name = "ilovetv"
|
||||||
authors = ["Love Billenius <lovebillenius@disroot.org>"]
|
version = "1.0.0"
|
||||||
version = "1.1.0"
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
# I Love TV
|
# Iptvnator
|
||||||
|
|
||||||
An iptvclient that is capable of parsing and reading m3uplaylists of essentially arbitrary sizes _blazingly fast_
|
An iptvclient that is capable of parsing and reading m3uplaylists of essentially arbitrary sizes _blazingly fast_
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
Just clone the repo and run `cargo build --release` to compile the project. Then put it in your `$PATH` or make a shortcut to the binary (target/release/ilovetv)
|
Just clone the repo and run `cargo build --release` to compile the project. Then put it in your `$PATH` or make a shortcut to the binary (target/release/iptvnator_rs)
|
||||||
You will need to install mpv, and have it in your path, otherwise it wont work
|
You will need to install mpv, and have it in your path, otherwise it wont work
|
||||||
|
|
||||||
## Left to do
|
## Left to do
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
error, fmt,
|
fmt,
|
||||||
fs::{File, OpenOptions},
|
fs::File,
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,44 +31,7 @@ impl DualWriter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> u64 {
|
pub fn get_string(self) -> Result<String, String> {
|
||||||
match self {
|
|
||||||
DualWriter::File(f) => f.metadata().and_then(|x| Ok(x.len())).unwrap_or_else(|e| {
|
|
||||||
println!("Could not get metadata from file {:?}", e);
|
|
||||||
0
|
|
||||||
}),
|
|
||||||
DualWriter::Buffer(buf) => buf.len() as u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Option<&str>> for DualWriter {
|
|
||||||
type Error = io::Error;
|
|
||||||
|
|
||||||
fn try_from(file_name: Option<&str>) -> Result<Self, Self::Error> {
|
|
||||||
Ok(if let Some(file_name) = file_name {
|
|
||||||
let file = match OpenOptions::new().append(true).open(&file_name) {
|
|
||||||
Ok(f) => f,
|
|
||||||
Err(e) => {
|
|
||||||
let e = e as io::Error;
|
|
||||||
if e.kind() == io::ErrorKind::NotFound {
|
|
||||||
File::create(&file_name)?
|
|
||||||
} else {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Self::File(file)
|
|
||||||
} else {
|
|
||||||
Self::Buffer(Vec::<u8>::new())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryInto<String> for DualWriter {
|
|
||||||
type Error = String;
|
|
||||||
|
|
||||||
fn try_into(self) -> Result<String, Self::Error> {
|
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Buffer(buffer) => {
|
Self::Buffer(buffer) => {
|
||||||
String::from_utf8(buffer).or(Err("Failed to decode buffer".to_owned()))?
|
String::from_utf8(buffer).or(Err("Failed to decode buffer".to_owned()))?
|
||||||
@ -84,30 +47,41 @@ impl TryInto<String> for DualWriter {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new(file_name: Option<&str>) -> Result<Self, io::Error> {
|
||||||
|
Ok(if let Some(file_name) = file_name {
|
||||||
|
Self::File(File::create(&file_name)?)
|
||||||
|
} else {
|
||||||
|
Self::Buffer(Vec::<u8>::new())
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn download_with_progress(
|
pub async fn download_with_progress(
|
||||||
link: &str,
|
link: &str,
|
||||||
file_name: Option<&str>,
|
file_name: Option<&str>,
|
||||||
) -> Result<DualWriter, Box<dyn error::Error>> {
|
) -> Result<DualWriter, String> {
|
||||||
let mut dual_writer: DualWriter = file_name.try_into()?;
|
let mut dual_writer = DualWriter::new(file_name).or(Err("Failed to create file".to_owned()))?;
|
||||||
|
|
||||||
let client = Client::builder().gzip(true).deflate(true).build()?;
|
let client = Client::builder()
|
||||||
let builder = client
|
.gzip(true)
|
||||||
|
.deflate(true)
|
||||||
|
.build()
|
||||||
|
.or(Err("Failed to create client".to_owned()))?;
|
||||||
|
let resp = client
|
||||||
.get(link)
|
.get(link)
|
||||||
.header("Range", format!("bytes={}-", dual_writer.len()));
|
.send()
|
||||||
|
.await
|
||||||
let resp = builder.send().await?;
|
.or(Err("Failed to connect server".to_owned()))?;
|
||||||
|
let content_length = if let Some(got_content_length) = resp.content_length() {
|
||||||
let content_length = resp.content_length().unwrap_or_default();
|
got_content_length
|
||||||
if content_length == 0 {
|
} else {
|
||||||
println!("File was already downloaded");
|
panic!("Could not retrive content length from server. {:?}", &resp);
|
||||||
return Ok(dual_writer);
|
};
|
||||||
}
|
|
||||||
|
|
||||||
let progress_bar = ProgressBar::new(content_length);
|
let progress_bar = ProgressBar::new(content_length);
|
||||||
progress_bar.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})").unwrap()
|
progress_bar.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})").unwrap()
|
||||||
.with_key("eta", |state: &ProgressState, w: &mut dyn fmt::Write| write!(w, "{:.1}m", state.eta().as_secs_f64() / 60.0 ).unwrap())
|
.with_key("eta", |state: &ProgressState, w: &mut dyn fmt::Write| write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap())
|
||||||
.progress_chars("#>-"));
|
.progress_chars("#>-"));
|
||||||
|
|
||||||
let mut downloaded: u64 = 0;
|
let mut downloaded: u64 = 0;
|
||||||
@ -116,7 +90,9 @@ pub async fn download_with_progress(
|
|||||||
while let Some(item) = stream.next().await {
|
while let Some(item) = stream.next().await {
|
||||||
let bytes = item.unwrap();
|
let bytes = item.unwrap();
|
||||||
downloaded = cmp::min(downloaded + (bytes.len() as u64), content_length);
|
downloaded = cmp::min(downloaded + (bytes.len() as u64), content_length);
|
||||||
dual_writer.write(bytes)?;
|
dual_writer
|
||||||
|
.write(bytes)
|
||||||
|
.or(Err("Failed to write to file".to_owned()))?;
|
||||||
|
|
||||||
progress_bar.set_position(downloaded);
|
progress_bar.set_position(downloaded);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use crate::GetM3u8;
|
use crate::GetM3u8;
|
||||||
use crate::{
|
use crate::{
|
||||||
get_mut_ref,
|
|
||||||
parser::{Parser, WatchedFind},
|
parser::{Parser, WatchedFind},
|
||||||
Configuration, OfflineParser, OnlineParser, Playlist, MAX_TRIES,
|
Configuration, OfflineParser, OnlineParser, Playlist, MAX_TRIES,
|
||||||
};
|
};
|
||||||
@ -31,11 +30,6 @@ impl GrandMother {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn demote_to_offline(&mut self) {
|
|
||||||
let offline_mother = GrandMother::new_offline(self.config.clone());
|
|
||||||
(self.parser, self.playlist) = (offline_mother.parser, offline_mother.playlist);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn promote_to_online(&mut self) -> Result<(), Error> {
|
pub async fn promote_to_online(&mut self) -> Result<(), Error> {
|
||||||
let online_mother = GrandMother::new_online(self.config.clone()).await?;
|
let online_mother = GrandMother::new_online(self.config.clone()).await?;
|
||||||
(self.parser, self.playlist) = (online_mother.parser, online_mother.playlist);
|
(self.parser, self.playlist) = (online_mother.parser, online_mother.playlist);
|
||||||
@ -53,7 +47,8 @@ impl GrandMother {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn refresh_dirty(&self) -> Result<(), Error> {
|
pub async fn refresh_dirty(&self) -> Result<(), Error> {
|
||||||
unsafe { get_mut_ref(self) }.refresh().await
|
let ptr = self as *const Self as *mut Self;
|
||||||
|
unsafe { &mut *ptr }.refresh().await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn refresh(&mut self) -> Result<(), Error> {
|
pub async fn refresh(&mut self) -> Result<(), Error> {
|
||||||
@ -75,15 +70,19 @@ impl GrandMother {
|
|||||||
println!("Retrying {}/{}", counter, MAX_TRIES);
|
println!("Retrying {}/{}", counter, MAX_TRIES);
|
||||||
};
|
};
|
||||||
|
|
||||||
let watched_links = self.parser.get_watched_links();
|
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 = Box::new(OnlineParser::new(&content, &watched_links).await);
|
self.parser = Box::new(OnlineParser::new(&content, &watched_links).await);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_watched(&self) {
|
pub fn save_watched(&self) {
|
||||||
let watched_items = self.parser.get_watched_links();
|
let watched_items = self.parser.get_watched();
|
||||||
|
|
||||||
let resp = fs::write(
|
let resp = fs::write(
|
||||||
&self.config.seen_links_path,
|
&self.config.seen_links_path,
|
||||||
|
11
src/lib.rs
11
src/lib.rs
@ -8,10 +8,7 @@ mod opt;
|
|||||||
pub mod parser;
|
pub mod parser;
|
||||||
mod playlist;
|
mod playlist;
|
||||||
|
|
||||||
use std::{
|
use std::{io::{stdin, stdout, Stdin, StdoutLock, Write}, rc::Rc};
|
||||||
io::{stdin, stdout, Stdin, StdoutLock, Write},
|
|
||||||
rc::Rc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
pub use config::Configuration;
|
pub use config::Configuration;
|
||||||
@ -68,10 +65,10 @@ pub async fn get_gm(
|
|||||||
mode: Mode,
|
mode: Mode,
|
||||||
readline: &mut Readline<'_>,
|
readline: &mut Readline<'_>,
|
||||||
config: Rc<Configuration>,
|
config: Rc<Configuration>,
|
||||||
) -> Result<(GrandMother, bool), String> {
|
) -> Result<GrandMother, String> {
|
||||||
match mode {
|
match mode {
|
||||||
Mode::Online => Ok((GrandMother::new_online(config).await?, true)),
|
Mode::Online => GrandMother::new_online(config).await,
|
||||||
Mode::Offline => Ok((GrandMother::new_offline(config), false)),
|
Mode::Offline => Ok(GrandMother::new_offline(config)),
|
||||||
Mode::Ask => loop {
|
Mode::Ask => loop {
|
||||||
let input = readline
|
let input = readline
|
||||||
.input("Online/Offline mode? [1/2] ")
|
.input("Online/Offline mode? [1/2] ")
|
||||||
|
42
src/main.rs
42
src/main.rs
@ -31,7 +31,7 @@ async fn main() {
|
|||||||
" {} is to make entries availibe for offline use later on in the program",
|
" {} is to make entries availibe for offline use later on in the program",
|
||||||
"o".bold()
|
"o".bold()
|
||||||
),
|
),
|
||||||
format!(" {} is to switch between modes (toggle)", "m".bold()),
|
format!(" {} is to switch to online mode", "m".bold()),
|
||||||
format!(" {} is to perform a new search", "s".bold()),
|
format!(" {} is to perform a new search", "s".bold()),
|
||||||
format!(" {} is to select all", "a".bold()),
|
format!(" {} is to select all", "a".bold()),
|
||||||
format!(" {} is to toggtle fullscreen for mpv", "f".bold()),
|
format!(" {} is to toggtle fullscreen for mpv", "f".bold()),
|
||||||
@ -44,10 +44,15 @@ async fn main() {
|
|||||||
.iter()
|
.iter()
|
||||||
.for_each(|s| println!("{}", &s));
|
.for_each(|s| println!("{}", &s));
|
||||||
|
|
||||||
|
// 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 mpv_fs = false;
|
||||||
let mut search_result: Option<Rc<Vec<&M3u8>>> = None;
|
let mut search_result: Option<Rc<Vec<&M3u8>>> = None;
|
||||||
let mut readline = Readline::new();
|
let mut readline = Readline::new();
|
||||||
let (gm, mut in_online) = get_gm(
|
let gm = get_gm(
|
||||||
opt.mode,
|
opt.mode,
|
||||||
&mut readline,
|
&mut readline,
|
||||||
Rc::new(Configuration::new().expect("Failed to write to configfile")),
|
Rc::new(Configuration::new().expect("Failed to write to configfile")),
|
||||||
@ -100,19 +105,10 @@ async fn main() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
"m" => {
|
"m" => {
|
||||||
if in_online {
|
let result = unsafe { get_mut_ref(&gm) }.promote_to_online().await;
|
||||||
unsafe { get_mut_ref(&gm) }.demote_to_offline();
|
if let Err(e) = result {
|
||||||
println!("Switched to offline mode");
|
println!("Failed to switch to onlinemode {:?}", e);
|
||||||
} else {
|
|
||||||
let result = unsafe { get_mut_ref(&gm) }.promote_to_online().await;
|
|
||||||
if let Err(e) = result {
|
|
||||||
println!("Failed to switch to onlinemode {:?}", e);
|
|
||||||
} else {
|
|
||||||
println!("Switched to online mode");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
in_online = !in_online;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -182,22 +178,7 @@ async fn main() {
|
|||||||
ask_which_to_download(&mut readline, &search_result.as_ref().unwrap());
|
ask_which_to_download(&mut readline, &search_result.as_ref().unwrap());
|
||||||
|
|
||||||
for to_download in download_selections.iter() {
|
for to_download in download_selections.iter() {
|
||||||
let file_ending = to_download
|
let path = gm.config.data_dir.join(&to_download.name);
|
||||||
.name
|
|
||||||
.rfind(".")
|
|
||||||
.map(|dot_idx| {
|
|
||||||
if dot_idx < 6 {
|
|
||||||
&to_download.name[dot_idx..]
|
|
||||||
} else {
|
|
||||||
".mkv"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| ".mkv");
|
|
||||||
|
|
||||||
let path = gm
|
|
||||||
.config
|
|
||||||
.data_dir
|
|
||||||
.join(format!("{}{}", &to_download.name, file_ending));
|
|
||||||
let path = Rc::new(path.to_string_lossy().to_string());
|
let path = Rc::new(path.to_string_lossy().to_string());
|
||||||
download_m3u8(to_download, Some(&path)).await;
|
download_m3u8(to_download, Some(&path)).await;
|
||||||
let data_entry = OfflineEntry::new((*to_download).clone(), path);
|
let data_entry = OfflineEntry::new((*to_download).clone(), path);
|
||||||
@ -233,6 +214,7 @@ async fn main() {
|
|||||||
println!("Not possible to refresh playlist while in offlinemode");
|
println!("Not possible to refresh playlist while in offlinemode");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
stream(to_play, &*path_link, mpv_fs);
|
stream(to_play, &*path_link, mpv_fs);
|
||||||
gm.save_watched();
|
gm.save_watched();
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,12 @@ use crate::{m3u8::M3u8, Configuration, GetM3u8, GetPlayPath, OfflineEntry};
|
|||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct OfflineParser {
|
pub struct OfflineParser {
|
||||||
offline_entries: Rc<Vec<OfflineEntry>>,
|
m3u8_items: Rc<Vec<OfflineEntry>>,
|
||||||
}
|
}
|
||||||
impl OfflineParser {
|
impl OfflineParser {
|
||||||
pub fn new(config: &Configuration) -> Self {
|
pub fn new(config: &Configuration) -> Self {
|
||||||
Self {
|
Self {
|
||||||
offline_entries: config.offlinefile_content.clone(),
|
m3u8_items: config.offlinefile_content.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -20,13 +20,13 @@ impl Deref for OfflineParser {
|
|||||||
type Target = Vec<OfflineEntry>;
|
type Target = Vec<OfflineEntry>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&*self.offline_entries
|
&*self.m3u8_items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetPlayPath for OfflineParser {
|
impl GetPlayPath for OfflineParser {
|
||||||
fn get_path_to_play(&self, link: Rc<String>) -> Result<Rc<String>, String> {
|
fn get_path_to_play(&self, link: Rc<String>) -> Result<Rc<String>, String> {
|
||||||
for offline_entry in &*self.offline_entries {
|
for offline_entry in &*self.m3u8_items {
|
||||||
if *offline_entry.link == *link {
|
if *offline_entry.link == *link {
|
||||||
return Ok(offline_entry.path.clone());
|
return Ok(offline_entry.path.clone());
|
||||||
}
|
}
|
||||||
@ -37,8 +37,6 @@ impl GetPlayPath for OfflineParser {
|
|||||||
|
|
||||||
impl GetM3u8 for OfflineParser {
|
impl GetM3u8 for OfflineParser {
|
||||||
fn get_m3u8(&self) -> Vec<&M3u8> {
|
fn get_m3u8(&self) -> Vec<&M3u8> {
|
||||||
let mut items: Vec<&M3u8> = self.offline_entries.iter().map(|x| &**x).collect();
|
self.m3u8_items.iter().map(|x| &**x).collect()
|
||||||
items.sort_by_key(|x| &x.link);
|
|
||||||
items
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ pub trait GetM3u8 {
|
|||||||
|
|
||||||
pub trait WatchedFind {
|
pub trait WatchedFind {
|
||||||
fn find(&self, name: &str) -> Vec<&M3u8>;
|
fn find(&self, name: &str) -> Vec<&M3u8>;
|
||||||
fn get_watched_links(&self) -> Vec<Rc<String>>;
|
fn get_watched(&self) -> Vec<&M3u8>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + GetM3u8> WatchedFind for T {
|
impl<T: ?Sized + GetM3u8> WatchedFind for T {
|
||||||
@ -20,12 +20,8 @@ impl<T: ?Sized + GetM3u8> WatchedFind for T {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_watched_links(&self) -> Vec<Rc<String>> {
|
fn get_watched(&self) -> Vec<&M3u8> {
|
||||||
self.get_m3u8()
|
self.get_m3u8().into_iter().filter(|x| x.watched).collect()
|
||||||
.into_iter()
|
|
||||||
.filter(|x| x.watched)
|
|
||||||
.map(|x| x.link.clone())
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub trait GetPlayPath {
|
pub trait GetPlayPath {
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use std::{fs, ops::Deref, path::PathBuf, rc::Rc};
|
use std::{fs, ops::Deref, path::PathBuf, rc::Rc};
|
||||||
|
|
||||||
use crate::{download_with_progress, MAX_TRIES};
|
use crate::{download_with_progress, downloader::DualWriter, MAX_TRIES};
|
||||||
|
|
||||||
type Error = String;
|
|
||||||
|
|
||||||
pub struct Playlist {
|
pub struct Playlist {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
@ -33,23 +31,24 @@ impl Playlist {
|
|||||||
fs::metadata(&*self.path_to_playlist)
|
fs::metadata(&*self.path_to_playlist)
|
||||||
.and_then(|metadata| {
|
.and_then(|metadata| {
|
||||||
Ok({
|
Ok({
|
||||||
metadata
|
let seconds = metadata.modified()?;
|
||||||
.modified()?
|
seconds
|
||||||
.elapsed()
|
.elapsed()
|
||||||
.map(|x| x.as_secs() > 60 * 60 * 24 * 3)
|
.expect("Failed to get systemtime")
|
||||||
.unwrap_or_else(|_| {
|
.as_secs()
|
||||||
println!("Could not get systemtime, trying to download new file");
|
> 60 * 60 * 24 * 3
|
||||||
true
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|_| {
|
.map_or_else(
|
||||||
println!("Could not find a saved playlist, Downloading a new one");
|
|_| {
|
||||||
false
|
println!("Could not find playlist-file, Downloading a new one");
|
||||||
})
|
false
|
||||||
|
},
|
||||||
|
|x| x,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_saved_or_download(&self) -> Result<String, Error> {
|
pub async fn get_saved_or_download(&self) -> Result<String, String> {
|
||||||
let content = if let Some(content) = self.get_saved() {
|
let content = if let Some(content) = self.get_saved() {
|
||||||
content
|
content
|
||||||
} else {
|
} else {
|
||||||
@ -67,7 +66,7 @@ impl Playlist {
|
|||||||
Ok(content)
|
Ok(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn download(&self) -> Result<String, Error> {
|
pub async fn download(&self) -> Result<String, String> {
|
||||||
let mut counter: u8 = 0;
|
let mut counter: u8 = 0;
|
||||||
loop {
|
loop {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
@ -80,9 +79,8 @@ impl Playlist {
|
|||||||
|
|
||||||
let downloaded = download_with_progress(&url, None)
|
let downloaded = download_with_progress(&url, None)
|
||||||
.await
|
.await
|
||||||
.map(TryInto::try_into);
|
.and_then(DualWriter::get_string);
|
||||||
|
if let Ok(content) = downloaded {
|
||||||
if let Ok(Ok(content)) = downloaded {
|
|
||||||
break Ok(content);
|
break Ok(content);
|
||||||
} else if counter > MAX_TRIES {
|
} else if counter > MAX_TRIES {
|
||||||
break Err("Failed to download playlist".to_owned());
|
break Err("Failed to download playlist".to_owned());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user