Merge branch 'master' into 33-downloader-re-work
This commit is contained in:
commit
955845110b
@ -1,3 +1,5 @@
|
|||||||
|
//! Database Setup. This is only public to help with some unit tests.
|
||||||
|
|
||||||
use r2d2_diesel::ConnectionManager;
|
use r2d2_diesel::ConnectionManager;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use r2d2;
|
use r2d2;
|
||||||
@ -35,7 +37,7 @@ lazy_static! {
|
|||||||
static ref DB_PATH: PathBuf = TEMPDIR.path().join("hammond.db");
|
static ref DB_PATH: PathBuf = TEMPDIR.path().join("hammond.db");
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this should not be public
|
/// Get an r2d2 SqliteConnection.
|
||||||
pub fn connection() -> Pool {
|
pub fn connection() -> Pool {
|
||||||
POOL.clone()
|
POOL.clone()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,8 +56,6 @@ pub mod utils;
|
|||||||
pub mod feed;
|
pub mod feed;
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
// FIXME: this should not be public
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod database;
|
pub mod database;
|
||||||
pub(crate) mod models;
|
pub(crate) mod models;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|||||||
@ -645,6 +645,7 @@ impl<'a> Source {
|
|||||||
// TODO: Refactor into TryInto once it lands on stable.
|
// TODO: Refactor into TryInto once it lands on stable.
|
||||||
pub fn into_feed(mut self, ignore_etags: bool) -> Result<Feed> {
|
pub fn into_feed(mut self, ignore_etags: bool) -> Result<Feed> {
|
||||||
use reqwest::header::{EntityTag, Headers, HttpDate, IfModifiedSince, IfNoneMatch};
|
use reqwest::header::{EntityTag, Headers, HttpDate, IfModifiedSince, IfNoneMatch};
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
let mut headers = Headers::new();
|
let mut headers = Headers::new();
|
||||||
|
|
||||||
@ -670,12 +671,26 @@ impl<'a> Source {
|
|||||||
self.update_etag(&req)?;
|
self.update_etag(&req)?;
|
||||||
|
|
||||||
// TODO match on more stuff
|
// TODO match on more stuff
|
||||||
// 301: Permanent redirect of the url
|
// 301: Moved Permanently
|
||||||
// 302: Temporary redirect of the url
|
|
||||||
// 304: Up to date Feed, checked with the Etag
|
// 304: Up to date Feed, checked with the Etag
|
||||||
|
// 307: Temporary redirect of the url
|
||||||
|
// 308: Permanent redirect of the url
|
||||||
|
// 401: Unathorized
|
||||||
|
// 403: Forbidden
|
||||||
|
// 408: Timeout
|
||||||
// 410: Feed deleted
|
// 410: Feed deleted
|
||||||
match req.status() {
|
match req.status() {
|
||||||
reqwest::StatusCode::NotModified => bail!("304, skipping.."),
|
StatusCode::NotModified => bail!("304: skipping.."),
|
||||||
|
StatusCode::TemporaryRedirect => debug!("307: Temporary Redirect."),
|
||||||
|
// TODO: Change the source uri to the new one
|
||||||
|
StatusCode::MovedPermanently | StatusCode::PermanentRedirect => {
|
||||||
|
warn!("Feed was moved permanently.")
|
||||||
|
}
|
||||||
|
StatusCode::Unauthorized => bail!("401: Unauthorized."),
|
||||||
|
StatusCode::Forbidden => bail!("403: Forbidden."),
|
||||||
|
StatusCode::NotFound => bail!("404: Not found."),
|
||||||
|
StatusCode::RequestTimeout => bail!("408: Request Timeout."),
|
||||||
|
StatusCode::Gone => bail!("410: Feed was deleted."),
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -123,10 +123,70 @@ fn parse_itunes_duration(item: &Item) -> Option<i32> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use rss::Channel;
|
use rss;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_itunes_duration() {
|
||||||
|
use rss::extension::itunes::ITunesItemExtensionBuilder;
|
||||||
|
|
||||||
|
// Input is a String<Int>
|
||||||
|
let extension = ITunesItemExtensionBuilder::default()
|
||||||
|
.duration(Some("3370".into()))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let item = rss::ItemBuilder::default()
|
||||||
|
.itunes_ext(Some(extension))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(parse_itunes_duration(&item), Some(3370));
|
||||||
|
|
||||||
|
// Input is a String<M:SS>
|
||||||
|
let extension = ITunesItemExtensionBuilder::default()
|
||||||
|
.duration(Some("6:10".into()))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let item = rss::ItemBuilder::default()
|
||||||
|
.itunes_ext(Some(extension))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(parse_itunes_duration(&item), Some(370));
|
||||||
|
|
||||||
|
// Input is a String<MM:SS>
|
||||||
|
let extension = ITunesItemExtensionBuilder::default()
|
||||||
|
.duration(Some("56:10".into()))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let item = rss::ItemBuilder::default()
|
||||||
|
.itunes_ext(Some(extension))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(parse_itunes_duration(&item), Some(3370));
|
||||||
|
|
||||||
|
// Input is a String<H:MM:SS>
|
||||||
|
let extension = ITunesItemExtensionBuilder::default()
|
||||||
|
.duration(Some("1:56:10".into()))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let item = rss::ItemBuilder::default()
|
||||||
|
.itunes_ext(Some(extension))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(parse_itunes_duration(&item), Some(6970));
|
||||||
|
|
||||||
|
// Input is a String<HH:MM:SS>
|
||||||
|
let extension = ITunesItemExtensionBuilder::default()
|
||||||
|
.duration(Some("01:56:10".into()))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let item = rss::ItemBuilder::default()
|
||||||
|
.itunes_ext(Some(extension))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(parse_itunes_duration(&item), Some(6970));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_new_podcast_intercepted() {
|
fn test_new_podcast_intercepted() {
|
||||||
let file = File::open("tests/feeds/Intercepted.xml").unwrap();
|
let file = File::open("tests/feeds/Intercepted.xml").unwrap();
|
||||||
|
|||||||
@ -8,11 +8,13 @@ use itertools::Itertools;
|
|||||||
|
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use dbqueries;
|
use dbqueries;
|
||||||
use models::queryables::EpisodeCleanerQuery;
|
use models::queryables::{EpisodeCleanerQuery, Podcast};
|
||||||
|
use xdg_dirs::DL_DIR;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
|
/// Scan downloaded `episode` entries that might have broken `local_uri`s and set them to `None`.
|
||||||
fn download_checker() -> Result<()> {
|
fn download_checker() -> Result<()> {
|
||||||
let episodes = dbqueries::get_downloaded_episodes()?;
|
let episodes = dbqueries::get_downloaded_episodes()?;
|
||||||
|
|
||||||
@ -30,6 +32,7 @@ fn download_checker() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete watched `episodes` that have exceded their liftime after played.
|
||||||
fn played_cleaner() -> Result<()> {
|
fn played_cleaner() -> Result<()> {
|
||||||
let mut episodes = dbqueries::get_played_cleaner_episodes()?;
|
let mut episodes = dbqueries::get_played_cleaner_episodes()?;
|
||||||
|
|
||||||
@ -54,7 +57,7 @@ fn played_cleaner() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check `ep.local_uri` field and delete the file it points to.
|
/// Check `ep.local_uri` field and delete the file it points to.
|
||||||
pub fn delete_local_content(ep: &mut EpisodeCleanerQuery) -> Result<()> {
|
fn delete_local_content(ep: &mut EpisodeCleanerQuery) -> Result<()> {
|
||||||
if ep.local_uri().is_some() {
|
if ep.local_uri().is_some() {
|
||||||
let uri = ep.local_uri().unwrap().to_owned();
|
let uri = ep.local_uri().unwrap().to_owned();
|
||||||
if Path::new(&uri).exists() {
|
if Path::new(&uri).exists() {
|
||||||
@ -119,6 +122,31 @@ pub fn replace_extra_spaces(s: &str) -> String {
|
|||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the URI of a Podcast Downloads given it's title.
|
||||||
|
pub fn get_download_folder(pd_title: &str) -> Result<String> {
|
||||||
|
// It might be better to make it a hash of the title or the podcast rowid
|
||||||
|
let download_fold = format!("{}/{}", DL_DIR.to_str().unwrap(), pd_title);
|
||||||
|
|
||||||
|
// Create the folder
|
||||||
|
fs::DirBuilder::new()
|
||||||
|
.recursive(true)
|
||||||
|
.create(&download_fold)?;
|
||||||
|
Ok(download_fold)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all the entries associated with the given show from the database,
|
||||||
|
/// and deletes all of the downloaded content.
|
||||||
|
/// TODO: Write Tests
|
||||||
|
pub fn delete_show(pd: &Podcast) -> Result<()> {
|
||||||
|
dbqueries::remove_feed(&pd)?;
|
||||||
|
info!("{} was removed succesfully.", pd.title());
|
||||||
|
|
||||||
|
let fold = get_download_folder(pd.title())?;
|
||||||
|
fs::remove_dir_all(&fold)?;
|
||||||
|
info!("All the content at, {} was removed succesfully", &fold);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
@ -277,4 +305,11 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(replace_extra_spaces(&bad_txt), valid_txt);
|
assert_eq!(replace_extra_spaces(&bad_txt), valid_txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_dl_folder() {
|
||||||
|
let foo_ = format!("{}/{}", DL_DIR.to_str().unwrap(), "foo");
|
||||||
|
assert_eq!(get_download_folder("foo").unwrap(), foo_);
|
||||||
|
let _ = fs::remove_dir_all(foo_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use hammond_data::{EpisodeWidgetQuery, PodcastCoverQuery};
|
use hammond_data::{EpisodeWidgetQuery, PodcastCoverQuery};
|
||||||
use hammond_data::xdg_dirs::{DL_DIR, HAMMOND_CACHE};
|
use hammond_data::xdg_dirs::HAMMOND_CACHE;
|
||||||
|
|
||||||
// TODO: Replace path that are of type &str with std::path.
|
// TODO: Replace path that are of type &str with std::path.
|
||||||
// TODO: Have a convention/document absolute/relative paths, if they should end with / or not.
|
// TODO: Have a convention/document absolute/relative paths, if they should end with / or not.
|
||||||
@ -129,15 +129,6 @@ fn save_io(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_download_folder(pd_title: &str) -> Result<String> {
|
|
||||||
// It might be better to make it a hash of the title
|
|
||||||
let download_fold = format!("{}/{}", DL_DIR.to_str().unwrap(), pd_title);
|
|
||||||
|
|
||||||
// Create the folder
|
|
||||||
DirBuilder::new().recursive(true).create(&download_fold)?;
|
|
||||||
Ok(download_fold)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Refactor
|
// TODO: Refactor
|
||||||
pub fn get_episode(
|
pub fn get_episode(
|
||||||
ep: &mut EpisodeWidgetQuery,
|
ep: &mut EpisodeWidgetQuery,
|
||||||
@ -228,15 +219,6 @@ mod tests {
|
|||||||
use hammond_data::dbqueries;
|
use hammond_data::dbqueries;
|
||||||
use diesel::associations::Identifiable;
|
use diesel::associations::Identifiable;
|
||||||
|
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_dl_folder() {
|
|
||||||
let foo_ = format!("{}/{}", DL_DIR.to_str().unwrap(), "foo");
|
|
||||||
assert_eq!(get_download_folder("foo").unwrap(), foo_);
|
|
||||||
let _ = fs::remove_dir_all(foo_);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// This test inserts an rss feed to your `XDG_DATA/hammond/hammond.db` so we make it explicit
|
// This test inserts an rss feed to your `XDG_DATA/hammond/hammond.db` so we make it explicit
|
||||||
// to run it.
|
// to run it.
|
||||||
|
|||||||
@ -174,21 +174,30 @@ impl ShowStack {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
debug!("Name: {:?}", WidgetExt::get_name(&old));
|
debug!("Name: {:?}", WidgetExt::get_name(&old));
|
||||||
|
|
||||||
let scrolled_window = old.get_children()
|
|
||||||
.first()
|
|
||||||
// This is guaranted to exist based on the show_widget.ui file.
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
.downcast::<gtk::ScrolledWindow>()
|
|
||||||
// This is guaranted based on the show_widget.ui file.
|
|
||||||
.unwrap();
|
|
||||||
debug!("Name: {:?}", WidgetExt::get_name(&scrolled_window));
|
|
||||||
|
|
||||||
let new = ShowWidget::new(Arc::new(self.clone()), pd, self.sender.clone());
|
let new = ShowWidget::new(Arc::new(self.clone()), pd, self.sender.clone());
|
||||||
// Copy the vertical scrollbar adjustment from the old view into the new one.
|
// Each composite ShowWidget is a gtkBox with the Podcast.id encoded in the gtk::Widget
|
||||||
scrolled_window
|
// name. It's a hack since we can't yet subclass GObject easily.
|
||||||
.get_vadjustment()
|
let oldid = WidgetExt::get_name(&old);
|
||||||
.map(|x| new.set_vadjustment(&x));
|
let newid = WidgetExt::get_name(&new.container);
|
||||||
|
debug!("Old widget Name: {:?}\nNew widget Name: {:?}", oldid, newid);
|
||||||
|
|
||||||
|
// Only copy the old scrollbar if both widget's represent the same podcast.
|
||||||
|
if newid == oldid {
|
||||||
|
let scrolled_window = old.get_children()
|
||||||
|
.first()
|
||||||
|
// This is guaranted to exist based on the show_widget.ui file.
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
.downcast::<gtk::ScrolledWindow>()
|
||||||
|
// This is guaranted based on the show_widget.ui file.
|
||||||
|
.unwrap();
|
||||||
|
debug!("Name: {:?}", WidgetExt::get_name(&scrolled_window));
|
||||||
|
|
||||||
|
// Copy the vertical scrollbar adjustment from the old view into the new one.
|
||||||
|
scrolled_window
|
||||||
|
.get_vadjustment()
|
||||||
|
.map(|x| new.set_vadjustment(&x));
|
||||||
|
}
|
||||||
|
|
||||||
self.stack.remove(&old);
|
self.stack.remove(&old);
|
||||||
self.stack.add_named(&new.container, "widget");
|
self.stack.add_named(&new.container, "widget");
|
||||||
|
|||||||
@ -106,11 +106,10 @@ pub fn add(id: i32, directory: &str, sender: Sender<Action>) {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use hammond_downloader::downloader;
|
|
||||||
|
|
||||||
use diesel::Identifiable;
|
use diesel::Identifiable;
|
||||||
|
|
||||||
use hammond_data::database;
|
use hammond_data::database;
|
||||||
|
use hammond_data::utils::get_download_folder;
|
||||||
use hammond_data::feed::*;
|
use hammond_data::feed::*;
|
||||||
use hammond_data::{Episode, Source};
|
use hammond_data::{Episode, Source};
|
||||||
use hammond_data::dbqueries;
|
use hammond_data::dbqueries;
|
||||||
@ -148,7 +147,7 @@ mod tests {
|
|||||||
|
|
||||||
let (sender, _rx) = channel();
|
let (sender, _rx) = channel();
|
||||||
|
|
||||||
let download_fold = downloader::get_download_folder(&pd.title()).unwrap();
|
let download_fold = get_download_folder(&pd.title()).unwrap();
|
||||||
add(episode.rowid(), download_fold.as_str(), sender);
|
add(episode.rowid(), download_fold.as_str(), sender);
|
||||||
|
|
||||||
// Give it soem time to download the file
|
// Give it soem time to download the file
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use hammond_downloader::downloader;
|
|||||||
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use headerbar::Header;
|
use headerbar::Header;
|
||||||
@ -22,11 +22,9 @@ pub fn refresh_feed(headerbar: Arc<Header>, source: Option<Vec<Source>>, sender:
|
|||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
if let Some(s) = source {
|
if let Some(s) = source {
|
||||||
feed::index_loop(s);
|
feed::index_loop(s);
|
||||||
} else {
|
} else if let Err(err) = feed::index_all() {
|
||||||
if let Err(err) = feed::index_all() {
|
error!("Error While trying to update the database.");
|
||||||
error!("Error While trying to update the database.");
|
error!("Error msg: {}", err);
|
||||||
error!("Error msg: {}", err);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sender.send(Action::HeaderBarHideUpdateIndicator).unwrap();
|
sender.send(Action::HeaderBarHideUpdateIndicator).unwrap();
|
||||||
@ -35,8 +33,8 @@ pub fn refresh_feed(headerbar: Arc<Header>, source: Option<Vec<Source>>, sender:
|
|||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CACHED_PIXBUFS: Mutex<HashMap<(i32, u32), Mutex<SendCell<Pixbuf>>>> = {
|
static ref CACHED_PIXBUFS: RwLock<HashMap<(i32, u32), Mutex<SendCell<Pixbuf>>>> = {
|
||||||
Mutex::new(HashMap::new())
|
RwLock::new(HashMap::new())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +46,8 @@ lazy_static! {
|
|||||||
// Also lazy_static requires Sync trait, so that's what the mutexes are.
|
// Also lazy_static requires Sync trait, so that's what the mutexes are.
|
||||||
// TODO: maybe use something that would just scale to requested size?
|
// TODO: maybe use something that would just scale to requested size?
|
||||||
pub fn get_pixbuf_from_path(pd: &PodcastCoverQuery, size: u32) -> Option<Pixbuf> {
|
pub fn get_pixbuf_from_path(pd: &PodcastCoverQuery, size: u32) -> Option<Pixbuf> {
|
||||||
let mut hashmap = CACHED_PIXBUFS.lock().unwrap();
|
|
||||||
{
|
{
|
||||||
|
let hashmap = CACHED_PIXBUFS.read().unwrap();
|
||||||
let res = hashmap.get(&(pd.id(), size));
|
let res = hashmap.get(&(pd.id(), size));
|
||||||
if let Some(px) = res {
|
if let Some(px) = res {
|
||||||
let m = px.lock().unwrap();
|
let m = px.lock().unwrap();
|
||||||
@ -60,6 +58,7 @@ pub fn get_pixbuf_from_path(pd: &PodcastCoverQuery, size: u32) -> Option<Pixbuf>
|
|||||||
let img_path = downloader::cache_image(pd)?;
|
let img_path = downloader::cache_image(pd)?;
|
||||||
let px = Pixbuf::new_from_file_at_scale(&img_path, size as i32, size as i32, true).ok();
|
let px = Pixbuf::new_from_file_at_scale(&img_path, size as i32, size as i32, true).ok();
|
||||||
if let Some(px) = px {
|
if let Some(px) = px {
|
||||||
|
let mut hashmap = CACHED_PIXBUFS.write().unwrap();
|
||||||
hashmap.insert((pd.id(), size), Mutex::new(SendCell::new(px.clone())));
|
hashmap.insert((pd.id(), size), Mutex::new(SendCell::new(px.clone())));
|
||||||
return Some(px);
|
return Some(px);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,9 +9,8 @@ use humansize::{file_size_opts as size_opts, FileSize};
|
|||||||
|
|
||||||
use hammond_data::dbqueries;
|
use hammond_data::dbqueries;
|
||||||
use hammond_data::{EpisodeWidgetQuery, Podcast};
|
use hammond_data::{EpisodeWidgetQuery, Podcast};
|
||||||
// use hammond_data::utils::*;
|
use hammond_data::utils::get_download_folder;
|
||||||
use hammond_data::errors::*;
|
use hammond_data::errors::*;
|
||||||
use hammond_downloader::downloader;
|
|
||||||
|
|
||||||
use app::Action;
|
use app::Action;
|
||||||
use manager;
|
use manager;
|
||||||
@ -94,6 +93,10 @@ impl Default for EpisodeWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref NOW: DateTime<Utc> = Utc::now();
|
||||||
|
}
|
||||||
|
|
||||||
impl EpisodeWidget {
|
impl EpisodeWidget {
|
||||||
pub fn new(episode: &mut EpisodeWidgetQuery, sender: Sender<Action>) -> EpisodeWidget {
|
pub fn new(episode: &mut EpisodeWidgetQuery, sender: Sender<Action>) -> EpisodeWidget {
|
||||||
let widget = EpisodeWidget::default();
|
let widget = EpisodeWidget::default();
|
||||||
@ -166,18 +169,21 @@ impl EpisodeWidget {
|
|||||||
|
|
||||||
/// Set the date label depending on the current time.
|
/// Set the date label depending on the current time.
|
||||||
fn set_date(&self, epoch: i32) {
|
fn set_date(&self, epoch: i32) {
|
||||||
let now = Utc::now();
|
|
||||||
let date = Utc.timestamp(i64::from(epoch), 0);
|
let date = Utc.timestamp(i64::from(epoch), 0);
|
||||||
if now.year() == date.year() {
|
if NOW.year() == date.year() {
|
||||||
self.date.set_text(&date.format("%e %b").to_string().trim());
|
self.date.set_text(date.format("%e %b").to_string().trim());
|
||||||
} else {
|
} else {
|
||||||
self.date
|
self.date
|
||||||
.set_text(&date.format("%e %b %Y").to_string().trim());
|
.set_text(date.format("%e %b %Y").to_string().trim());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the duration label.
|
/// Set the duration label.
|
||||||
fn set_duration(&self, seconds: Option<i32>) {
|
fn set_duration(&self, seconds: Option<i32>) {
|
||||||
|
if (seconds == Some(0)) || seconds.is_none() {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(secs) = seconds {
|
if let Some(secs) = seconds {
|
||||||
self.duration.set_text(&format!("{} min", secs / 60));
|
self.duration.set_text(&format!("{} min", secs / 60));
|
||||||
self.duration.show();
|
self.duration.show();
|
||||||
@ -241,7 +247,7 @@ impl EpisodeWidget {
|
|||||||
fn on_download_clicked(ep: &EpisodeWidgetQuery, sender: Sender<Action>) {
|
fn on_download_clicked(ep: &EpisodeWidgetQuery, sender: Sender<Action>) {
|
||||||
let download_fold = dbqueries::get_podcast_from_id(ep.podcast_id())
|
let download_fold = dbqueries::get_podcast_from_id(ep.podcast_id())
|
||||||
.ok()
|
.ok()
|
||||||
.map(|pd| downloader::get_download_folder(&pd.title().to_owned()).ok())
|
.map(|pd| get_download_folder(&pd.title().to_owned()).ok())
|
||||||
.and_then(|x| x);
|
.and_then(|x| x);
|
||||||
|
|
||||||
// Start a new download.
|
// Start a new download.
|
||||||
|
|||||||
@ -6,8 +6,7 @@ use dissolve;
|
|||||||
|
|
||||||
use hammond_data::dbqueries;
|
use hammond_data::dbqueries;
|
||||||
use hammond_data::Podcast;
|
use hammond_data::Podcast;
|
||||||
use hammond_data::utils::replace_extra_spaces;
|
use hammond_data::utils::{delete_show, replace_extra_spaces};
|
||||||
use hammond_downloader::downloader;
|
|
||||||
|
|
||||||
use widgets::episode::episodes_listbox;
|
use widgets::episode::episodes_listbox;
|
||||||
use utils::get_pixbuf_from_path;
|
use utils::get_pixbuf_from_path;
|
||||||
@ -17,7 +16,6 @@ use app::Action;
|
|||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ShowWidget {
|
pub struct ShowWidget {
|
||||||
@ -123,17 +121,10 @@ fn on_unsub_button_clicked(
|
|||||||
unsub_button.hide();
|
unsub_button.hide();
|
||||||
// Spawn a thread so it won't block the ui.
|
// Spawn a thread so it won't block the ui.
|
||||||
thread::spawn(clone!(pd => move || {
|
thread::spawn(clone!(pd => move || {
|
||||||
dbqueries::remove_feed(&pd).ok().map(|_| {
|
if let Err(err) = delete_show(&pd) {
|
||||||
info!("{} was removed succesfully.", pd.title());
|
error!("Something went wrong trying to remove {}", pd.title());
|
||||||
|
error!("Error: {}", err);
|
||||||
downloader::get_download_folder(pd.title()).ok().map(|fold| {
|
}
|
||||||
let res3 = fs::remove_dir_all(&fold);
|
|
||||||
// TODO: Show errors?
|
|
||||||
if res3.is_ok() {
|
|
||||||
info!("All the content at, {} was removed succesfully", &fold);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
}));
|
||||||
shows.switch_podcasts_animated();
|
shows.switch_podcasts_animated();
|
||||||
sender.send(Action::HeaderBarNormal).unwrap();
|
sender.send(Action::HeaderBarNormal).unwrap();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user