Merge branch 'master' into 33-downloader-re-work
This commit is contained in:
@@ -15,6 +15,10 @@ use hammond_downloader::downloader;
|
||||
|
||||
use app::DOWNLOADS_MANAGER;
|
||||
|
||||
use app::Action;
|
||||
|
||||
use std::thread;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -71,24 +75,30 @@ impl Default for EpisodeWidget {
|
||||
}
|
||||
|
||||
impl EpisodeWidget {
|
||||
pub fn new(episode: &mut EpisodeWidgetQuery) -> EpisodeWidget {
|
||||
pub fn new(episode: &mut EpisodeWidgetQuery, sender: Sender<Action>) -> EpisodeWidget {
|
||||
let widget = EpisodeWidget::default();
|
||||
widget.init(episode);
|
||||
widget.init(episode, sender);
|
||||
widget
|
||||
}
|
||||
|
||||
// TODO: calculate lenght.
|
||||
// TODO: wire the progress_bar to the downloader.
|
||||
// TODO: wire the cancel button.
|
||||
fn init(&self, episode: &mut EpisodeWidgetQuery) {
|
||||
self.title.set_xalign(0.0);
|
||||
self.title.set_text(episode.title());
|
||||
fn init(&self, episode: &mut EpisodeWidgetQuery, sender: Sender<Action>) {
|
||||
// Set the title label state.
|
||||
self.set_title(episode);
|
||||
|
||||
if episode.played().is_some() {
|
||||
self.title
|
||||
.get_style_context()
|
||||
.map(|c| c.add_class("dim-label"));
|
||||
}
|
||||
// Set the size label.
|
||||
self.set_size(episode.length());
|
||||
|
||||
// Set the duaration label.
|
||||
self.set_duration(episode.duration());
|
||||
|
||||
// Set the date label.
|
||||
self.set_date(episode.epoch());
|
||||
|
||||
// Show or hide the play/delete/download buttons upon widget initialization.
|
||||
self.show_buttons(episode.local_uri());
|
||||
|
||||
{
|
||||
let m = DOWNLOADS_MANAGER.lock().unwrap();
|
||||
@@ -98,6 +108,77 @@ impl EpisodeWidget {
|
||||
};
|
||||
}
|
||||
|
||||
let title = &self.title;
|
||||
self.play
|
||||
.connect_clicked(clone!(episode, title, sender => move |_| {
|
||||
let mut episode = episode.clone();
|
||||
on_play_bttn_clicked(episode.rowid());
|
||||
if episode.set_played_now().is_ok() {
|
||||
title
|
||||
.get_style_context()
|
||||
.map(|c| c.add_class("dim-label"));
|
||||
sender.send(Action::RefreshEpisodesViewBGR).unwrap();
|
||||
};
|
||||
}));
|
||||
|
||||
let cancel = &self.cancel;
|
||||
let progress = self.progress.clone();
|
||||
self.download
|
||||
.connect_clicked(clone!(episode, cancel, progress, sender => move |dl| {
|
||||
on_download_clicked(
|
||||
&mut episode.clone(),
|
||||
dl,
|
||||
&cancel,
|
||||
progress.clone(),
|
||||
sender.clone()
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
/// Show or hide the play/delete/download buttons upon widget initialization.
|
||||
fn show_buttons(&self, local_uri: Option<&str>) {
|
||||
if local_uri.is_some() && Path::new(local_uri.unwrap()).exists() {
|
||||
self.download.hide();
|
||||
self.play.show();
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine the title state.
|
||||
fn set_title(&self, episode: &EpisodeWidgetQuery) {
|
||||
self.title.set_xalign(0.0);
|
||||
self.title.set_text(episode.title());
|
||||
|
||||
// Grey out the title if the episode is played.
|
||||
if episode.played().is_some() {
|
||||
self.title
|
||||
.get_style_context()
|
||||
.map(|c| c.add_class("dim-label"));
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the date label depending on the current time.
|
||||
fn set_date(&self, epoch: i32) {
|
||||
let now = Utc::now();
|
||||
let date = Utc.timestamp(i64::from(epoch), 0);
|
||||
if now.year() == date.year() {
|
||||
self.date.set_text(&date.format("%e %b").to_string().trim());
|
||||
} else {
|
||||
self.date
|
||||
.set_text(&date.format("%e %b %Y").to_string().trim());
|
||||
};
|
||||
}
|
||||
|
||||
/// Set the duration label.
|
||||
fn set_duration(&self, seconds: Option<i32>) {
|
||||
if let Some(secs) = seconds {
|
||||
self.duration.set_text(&format!("{} min", secs / 60));
|
||||
self.duration.show();
|
||||
self.separator1.show();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Episode label dependings on its size
|
||||
fn set_size(&self, bytes: Option<i32>) {
|
||||
// Declare a custom humansize option struct
|
||||
// See: https://docs.rs/humansize/1.0.2/humansize/file_size_opts/struct.FileSizeOpts.html
|
||||
let custom_options = size_opts::FileSizeOpts {
|
||||
@@ -112,7 +193,7 @@ impl EpisodeWidget {
|
||||
allow_negative: false,
|
||||
};
|
||||
|
||||
if let Some(size) = episode.length() {
|
||||
if let Some(size) = bytes {
|
||||
if size != 0 {
|
||||
let s = size.file_size(custom_options);
|
||||
if let Ok(s) = s {
|
||||
@@ -122,51 +203,6 @@ impl EpisodeWidget {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(secs) = episode.duration() {
|
||||
self.duration.set_text(&format!("{} min", secs / 60));
|
||||
self.duration.show();
|
||||
self.separator1.show();
|
||||
};
|
||||
|
||||
let now = Utc::now();
|
||||
let date = Utc.timestamp(i64::from(episode.epoch()), 0);
|
||||
if now.year() == date.year() {
|
||||
self.date.set_text(&date.format("%e %b").to_string());
|
||||
} else {
|
||||
self.date.set_text(&date.format("%e %b %Y").to_string());
|
||||
};
|
||||
|
||||
// Show or hide the play/delete/download buttons upon widget initialization.
|
||||
let local_uri = episode.local_uri();
|
||||
if local_uri.is_some() && Path::new(local_uri.unwrap()).exists() {
|
||||
self.download.hide();
|
||||
self.play.show();
|
||||
}
|
||||
|
||||
let title = &self.title;
|
||||
self.play
|
||||
.connect_clicked(clone!(episode, title => move |_| {
|
||||
let mut episode = episode.clone();
|
||||
on_play_bttn_clicked(episode.rowid());
|
||||
if episode.set_played_now().is_ok() {
|
||||
title
|
||||
.get_style_context()
|
||||
.map(|c| c.add_class("dim-label"));
|
||||
};
|
||||
}));
|
||||
|
||||
let cancel = &self.cancel;
|
||||
let progress = self.progress.clone();
|
||||
self.download
|
||||
.connect_clicked(clone!(episode, cancel, progress => move |dl| {
|
||||
on_download_clicked(
|
||||
&episode,
|
||||
dl,
|
||||
&cancel,
|
||||
progress.clone()
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
fn show_progess_bar(&self) {
|
||||
@@ -187,6 +223,7 @@ fn on_download_clicked(
|
||||
download_bttn: >k::Button,
|
||||
cancel_bttn: >k::Button,
|
||||
progress_bar: gtk::ProgressBar,
|
||||
sender: Sender<Action>,
|
||||
) {
|
||||
let progress = progress_bar.clone();
|
||||
|
||||
@@ -206,6 +243,7 @@ fn on_download_clicked(
|
||||
let man = DOWNLOADS_MANAGER.lock().unwrap();
|
||||
man.add(ep.rowid(), &download_fold);
|
||||
}
|
||||
sender.send(Action::RefreshEpisodesViewBGR).unwrap();
|
||||
}
|
||||
|
||||
fn on_play_bttn_clicked(episode_id: i32) {
|
||||
@@ -240,13 +278,13 @@ fn on_play_bttn_clicked(episode_id: i32) {
|
||||
// };
|
||||
// }
|
||||
|
||||
pub fn episodes_listbox(pd: &Podcast) -> Result<gtk::ListBox> {
|
||||
pub fn episodes_listbox(pd: &Podcast, sender: Sender<Action>) -> Result<gtk::ListBox> {
|
||||
let mut episodes = dbqueries::get_pd_episodeswidgets(pd)?;
|
||||
|
||||
let list = gtk::ListBox::new();
|
||||
|
||||
episodes.iter_mut().for_each(|ep| {
|
||||
let widget = EpisodeWidget::new(ep);
|
||||
let widget = EpisodeWidget::new(ep, sender.clone());
|
||||
list.add(&widget.container);
|
||||
});
|
||||
|
||||
|
||||
@@ -12,14 +12,17 @@ use hammond_downloader::downloader;
|
||||
use widgets::episode::episodes_listbox;
|
||||
use utils::get_pixbuf_from_path;
|
||||
use content::ShowStack;
|
||||
use headerbar::Header;
|
||||
use app::Action;
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::fs;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ShowWidget {
|
||||
pub container: gtk::Box,
|
||||
scrolled_window: gtk::ScrolledWindow,
|
||||
cover: gtk::Image,
|
||||
description: gtk::Label,
|
||||
link: gtk::Button,
|
||||
@@ -32,6 +35,7 @@ impl Default for ShowWidget {
|
||||
fn default() -> Self {
|
||||
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/show_widget.ui");
|
||||
let container: gtk::Box = builder.get_object("container").unwrap();
|
||||
let scrolled_window: gtk::ScrolledWindow = builder.get_object("scrolled_window").unwrap();
|
||||
let episodes: gtk::Frame = builder.get_object("episodes").unwrap();
|
||||
|
||||
let cover: gtk::Image = builder.get_object("cover").unwrap();
|
||||
@@ -42,6 +46,7 @@ impl Default for ShowWidget {
|
||||
|
||||
ShowWidget {
|
||||
container,
|
||||
scrolled_window,
|
||||
cover,
|
||||
description,
|
||||
unsub,
|
||||
@@ -53,67 +58,95 @@ impl Default for ShowWidget {
|
||||
}
|
||||
|
||||
impl ShowWidget {
|
||||
pub fn new(shows: Rc<ShowStack>, header: Rc<Header>, pd: &Podcast) -> ShowWidget {
|
||||
pub fn new(shows: Arc<ShowStack>, pd: &Podcast, sender: Sender<Action>) -> ShowWidget {
|
||||
let pdw = ShowWidget::default();
|
||||
pdw.init(shows, header, pd);
|
||||
pdw.init(shows, pd, sender);
|
||||
pdw
|
||||
}
|
||||
|
||||
pub fn init(&self, shows: Rc<ShowStack>, header: Rc<Header>, pd: &Podcast) {
|
||||
pub fn init(&self, shows: Arc<ShowStack>, pd: &Podcast, sender: Sender<Action>) {
|
||||
// Hacky workaround so the pd.id() can be retrieved from the `ShowStack`.
|
||||
WidgetExt::set_name(&self.container, &pd.id().to_string());
|
||||
|
||||
self.unsub.connect_clicked(clone!(shows, pd => move |bttn| {
|
||||
on_unsub_button_clicked(shows.clone(), &pd, bttn);
|
||||
header.switch_to_normal();
|
||||
self.unsub
|
||||
.connect_clicked(clone!(shows, pd, sender => move |bttn| {
|
||||
on_unsub_button_clicked(shows.clone(), &pd, bttn, sender.clone());
|
||||
sender.send(Action::HeaderBarNormal).unwrap();
|
||||
}));
|
||||
|
||||
let listbox = episodes_listbox(pd);
|
||||
if let Ok(l) = listbox {
|
||||
self.episodes.add(&l);
|
||||
}
|
||||
|
||||
// TODO: Temporary solution until we render html urls/bold/italic probably with markup.
|
||||
let desc = dissolve::strip_html_tags(pd.description()).join(" ");
|
||||
self.description.set_text(&replace_extra_spaces(&desc));
|
||||
|
||||
let img = get_pixbuf_from_path(&pd.clone().into(), 128);
|
||||
if let Some(i) = img {
|
||||
self.cover.set_from_pixbuf(&i);
|
||||
}
|
||||
self.setup_listbox(pd, sender.clone());
|
||||
self.set_cover(pd);
|
||||
self.set_description(pd.description());
|
||||
|
||||
let link = pd.link().to_owned();
|
||||
self.link.set_tooltip_text(Some(link.as_str()));
|
||||
self.link.connect_clicked(move |_| {
|
||||
info!("Opening link: {}", &link);
|
||||
let _ = open::that(&link);
|
||||
});
|
||||
}
|
||||
|
||||
// self.played.connect_clicked(clone!(shows, pd => move |_| {
|
||||
// on_played_button_clicked(shows.clone(), &pd);
|
||||
// }));
|
||||
/// Populate the listbox with the shows episodes.
|
||||
fn setup_listbox(&self, pd: &Podcast, sender: Sender<Action>) {
|
||||
let listbox = episodes_listbox(pd, sender.clone());
|
||||
if let Ok(l) = listbox {
|
||||
self.episodes.add(&l);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the show cover.
|
||||
fn set_cover(&self, pd: &Podcast) {
|
||||
let img = get_pixbuf_from_path(&pd.clone().into(), 128);
|
||||
if let Some(i) = img {
|
||||
self.cover.set_from_pixbuf(&i);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the descripton text.
|
||||
fn set_description(&self, text: &str) {
|
||||
// TODO: Temporary solution until we render html urls/bold/italic probably with markup.
|
||||
let desc = dissolve::strip_html_tags(text).join(" ");
|
||||
self.description.set_text(&replace_extra_spaces(&desc));
|
||||
}
|
||||
|
||||
/// Set scrolled window vertical adjustment.
|
||||
pub fn set_vadjustment(&self, vadjustment: >k::Adjustment) {
|
||||
self.scrolled_window.set_vadjustment(vadjustment)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_unsub_button_clicked(shows: Rc<ShowStack>, pd: &Podcast, unsub_button: >k::Button) {
|
||||
let res = dbqueries::remove_feed(pd);
|
||||
if res.is_ok() {
|
||||
info!("{} was removed succesfully.", pd.title());
|
||||
// hack to get away without properly checking for none.
|
||||
// if pressed twice would panic.
|
||||
unsub_button.hide();
|
||||
fn on_unsub_button_clicked(
|
||||
shows: Arc<ShowStack>,
|
||||
pd: &Podcast,
|
||||
unsub_button: >k::Button,
|
||||
sender: Sender<Action>,
|
||||
) {
|
||||
// hack to get away without properly checking for none.
|
||||
// if pressed twice would panic.
|
||||
unsub_button.hide();
|
||||
// Spawn a thread so it won't block the ui.
|
||||
thread::spawn(clone!(pd => move || {
|
||||
let res = dbqueries::remove_feed(&pd);
|
||||
if res.is_ok() {
|
||||
info!("{} was removed succesfully.", pd.title());
|
||||
|
||||
let dl_fold = downloader::get_download_folder(pd.title());
|
||||
if let Ok(fold) = dl_fold {
|
||||
let res3 = fs::remove_dir_all(&fold);
|
||||
if res3.is_ok() {
|
||||
info!("All the content at, {} was removed succesfully", &fold);
|
||||
}
|
||||
};
|
||||
}
|
||||
let dl_fold = downloader::get_download_folder(pd.title());
|
||||
if let Ok(fold) = dl_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();
|
||||
// Queue a refresh after the switch to avoid blocking the db.
|
||||
sender.send(Action::RefreshViews).unwrap();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn on_played_button_clicked(shows: Rc<ShowStack>, pd: &Podcast) {
|
||||
fn on_played_button_clicked(shows: Arc<ShowStack>, pd: &Podcast) {
|
||||
let _ = dbqueries::update_none_to_played_now(pd);
|
||||
|
||||
shows.update_widget();
|
||||
|
||||
Reference in New Issue
Block a user