Merge branch 'feature/gsettings-integration' into 'master'
Integrate gsettings into application See merge request alatiera/Hammond!23
This commit is contained in:
commit
a0d55417cd
@ -39,17 +39,15 @@ fn download_checker() -> Result<(), DataError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Delete watched `episodes` that have exceded their liftime after played.
|
/// Delete watched `episodes` that have exceded their liftime after played.
|
||||||
fn played_cleaner() -> Result<(), DataError> {
|
fn played_cleaner(cleanup_date: DateTime<Utc>) -> Result<(), DataError> {
|
||||||
let mut episodes = dbqueries::get_played_cleaner_episodes()?;
|
let mut episodes = dbqueries::get_played_cleaner_episodes()?;
|
||||||
|
let now_utc = cleanup_date.timestamp() as i32;
|
||||||
|
|
||||||
let now_utc = Utc::now().timestamp() as i32;
|
|
||||||
episodes
|
episodes
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.filter(|ep| ep.local_uri().is_some() && ep.played().is_some())
|
.filter(|ep| ep.local_uri().is_some() && ep.played().is_some())
|
||||||
.for_each(|ep| {
|
.for_each(|ep| {
|
||||||
// TODO: expose a config and a user set option.
|
let limit = ep.played().unwrap();
|
||||||
// Chnage the test too when exposed
|
|
||||||
let limit = ep.played().unwrap() + 172_800; // add 2days in seconds
|
|
||||||
if now_utc > limit {
|
if now_utc > limit {
|
||||||
if let Err(err) = delete_local_content(ep) {
|
if let Err(err) = delete_local_content(ep) {
|
||||||
error!("Error while trying to delete file: {:?}", ep.local_uri());
|
error!("Error while trying to delete file: {:?}", ep.local_uri());
|
||||||
@ -92,10 +90,10 @@ fn delete_local_content(ep: &mut EpisodeCleanerQuery) -> Result<(), DataError> {
|
|||||||
///
|
///
|
||||||
/// Runs a cleaner for played Episode's that are pass the lifetime limit and
|
/// Runs a cleaner for played Episode's that are pass the lifetime limit and
|
||||||
/// scheduled for removal.
|
/// scheduled for removal.
|
||||||
pub fn checkup() -> Result<(), DataError> {
|
pub fn checkup(cleanup_date: DateTime<Utc>) -> Result<(), DataError> {
|
||||||
info!("Running database checks.");
|
info!("Running database checks.");
|
||||||
download_checker()?;
|
download_checker()?;
|
||||||
played_cleaner()?;
|
played_cleaner(cleanup_date)?;
|
||||||
info!("Checks completed.");
|
info!("Checks completed.");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -182,6 +180,7 @@ mod tests {
|
|||||||
|
|
||||||
use self::tempdir::TempDir;
|
use self::tempdir::TempDir;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use chrono::Duration;
|
||||||
|
|
||||||
use database::truncate_db;
|
use database::truncate_db;
|
||||||
use models::NewEpisodeBuilder;
|
use models::NewEpisodeBuilder;
|
||||||
@ -261,15 +260,14 @@ mod tests {
|
|||||||
fn test_played_cleaner_expired() {
|
fn test_played_cleaner_expired() {
|
||||||
let _tmp_dir = helper_db();
|
let _tmp_dir = helper_db();
|
||||||
let mut episode = dbqueries::get_episode_from_pk("foo_bar", 0).unwrap();
|
let mut episode = dbqueries::get_episode_from_pk("foo_bar", 0).unwrap();
|
||||||
let now_utc = Utc::now().timestamp() as i32;
|
let cleanup_date = Utc::now() - Duration::seconds(1000);
|
||||||
// let limit = now_utc - 172_800;
|
let epoch = cleanup_date.timestamp() as i32 - 1;
|
||||||
let epoch = now_utc - 200_000;
|
|
||||||
episode.set_played(Some(epoch));
|
episode.set_played(Some(epoch));
|
||||||
episode.save().unwrap();
|
episode.save().unwrap();
|
||||||
let valid_path = episode.local_uri().unwrap().to_owned();
|
let valid_path = episode.local_uri().unwrap().to_owned();
|
||||||
|
|
||||||
// This should delete the file
|
// This should delete the file
|
||||||
played_cleaner().unwrap();
|
played_cleaner(cleanup_date).unwrap();
|
||||||
assert_eq!(Path::new(&valid_path).exists(), false);
|
assert_eq!(Path::new(&valid_path).exists(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,15 +275,14 @@ mod tests {
|
|||||||
fn test_played_cleaner_none() {
|
fn test_played_cleaner_none() {
|
||||||
let _tmp_dir = helper_db();
|
let _tmp_dir = helper_db();
|
||||||
let mut episode = dbqueries::get_episode_from_pk("foo_bar", 0).unwrap();
|
let mut episode = dbqueries::get_episode_from_pk("foo_bar", 0).unwrap();
|
||||||
let now_utc = Utc::now().timestamp() as i32;
|
let cleanup_date = Utc::now() - Duration::seconds(1000);
|
||||||
// limit = 172_800;
|
let epoch = cleanup_date.timestamp() as i32 + 1;
|
||||||
let epoch = now_utc - 20_000;
|
|
||||||
episode.set_played(Some(epoch));
|
episode.set_played(Some(epoch));
|
||||||
episode.save().unwrap();
|
episode.save().unwrap();
|
||||||
let valid_path = episode.local_uri().unwrap().to_owned();
|
let valid_path = episode.local_uri().unwrap().to_owned();
|
||||||
|
|
||||||
// This should not delete the file
|
// This should not delete the file
|
||||||
played_cleaner().unwrap();
|
played_cleaner(cleanup_date).unwrap();
|
||||||
assert_eq!(Path::new(&valid_path).exists(), true);
|
assert_eq!(Path::new(&valid_path).exists(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,6 @@ chrono = "0.4.0"
|
|||||||
dissolve = "0.2.2"
|
dissolve = "0.2.2"
|
||||||
gdk = "0.7.0"
|
gdk = "0.7.0"
|
||||||
gdk-pixbuf = "0.3.0"
|
gdk-pixbuf = "0.3.0"
|
||||||
gio = "0.3.0"
|
|
||||||
glib = "0.4.1"
|
glib = "0.4.1"
|
||||||
humansize = "1.1.0"
|
humansize = "1.1.0"
|
||||||
lazy_static = "1.0.0"
|
lazy_static = "1.0.0"
|
||||||
@ -31,6 +30,10 @@ serde_json = "1.0.11"
|
|||||||
features = ["v3_22"]
|
features = ["v3_22"]
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
|
||||||
|
[dependencies.gio]
|
||||||
|
features = ["v2_50"]
|
||||||
|
version = "0.3.0"
|
||||||
|
|
||||||
[dependencies.hammond-data]
|
[dependencies.hammond-data]
|
||||||
path = "../hammond-data"
|
path = "../hammond-data"
|
||||||
|
|
||||||
|
|||||||
@ -15,16 +15,16 @@
|
|||||||
<summary>Enable or disable dark theme</summary>
|
<summary>Enable or disable dark theme</summary>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
<key name="auto-refresh" type="b">
|
<key name="refresh-interval" type="b">
|
||||||
<default>true</default>
|
<default>true</default>
|
||||||
<summary>Whether to periodically refresh content</summary>
|
<summary>Whether to periodically refresh content</summary>
|
||||||
</key>
|
</key>
|
||||||
<key name="auto-refresh-time" type="i">
|
<key name="refresh-interval-time" type="i">
|
||||||
<range min="1" max="100"/>
|
<range min="1" max="100"/>
|
||||||
<default>1</default>
|
<default>1</default>
|
||||||
<summary>How many periods of time to wait between automatic refreshes</summary>
|
<summary>How many periods of time to wait between automatic refreshes</summary>
|
||||||
</key>
|
</key>
|
||||||
<key name="auto-refresh-period" enum="org.gnome.Hammond.timePeriods">
|
<key name="refresh-interval-period" enum="org.gnome.Hammond.timePeriods">
|
||||||
<default>'hours'</default>
|
<default>'hours'</default>
|
||||||
<summary>What period of time to wait between automatic refreshes</summary>
|
<summary>What period of time to wait between automatic refreshes</summary>
|
||||||
</key>
|
</key>
|
||||||
@ -33,16 +33,12 @@
|
|||||||
<summary>Whether to refresh content after startup</summary>
|
<summary>Whether to refresh content after startup</summary>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
<key name="auto-cleanup" type="b">
|
<key name="cleanup-age-time" type="i">
|
||||||
<default>true</default>
|
|
||||||
<summary>Whether to periodically cleanup content</summary>
|
|
||||||
</key>
|
|
||||||
<key name="auto-cleanup-time" type="i">
|
|
||||||
<range min="1" max="100"/>
|
<range min="1" max="100"/>
|
||||||
<default>2</default>
|
<default>2</default>
|
||||||
<summary>How many periods of time to wait between automatic cleanups</summary>
|
<summary>How many periods of time to wait between automatic cleanups</summary>
|
||||||
</key>
|
</key>
|
||||||
<key name="auto-cleanup-period" enum="org.gnome.Hammond.timePeriods">
|
<key name="cleanup-age-period" enum="org.gnome.Hammond.timePeriods">
|
||||||
<default>'days'</default>
|
<default>'days'</default>
|
||||||
<summary>What period of time to wait between automatic cleanups</summary>
|
<summary>What period of time to wait between automatic cleanups</summary>
|
||||||
</key>
|
</key>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
#![allow(new_without_default)]
|
#![allow(new_without_default)]
|
||||||
|
|
||||||
use gio::{ApplicationExt, ApplicationExtManual, ApplicationFlags};
|
use gio::{ApplicationExt, ApplicationExtManual, ApplicationFlags, Settings, SettingsExt};
|
||||||
use glib;
|
use glib;
|
||||||
use gtk;
|
use gtk;
|
||||||
|
use gtk::SettingsExt as GtkSettingsExt;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
||||||
use hammond_data::{Podcast, Source};
|
use hammond_data::{Podcast, Source};
|
||||||
use hammond_data::utils::checkup;
|
|
||||||
|
|
||||||
use appnotif::*;
|
use appnotif::*;
|
||||||
use headerbar::Header;
|
use headerbar::Header;
|
||||||
@ -47,6 +47,7 @@ pub struct App {
|
|||||||
content: Arc<Content>,
|
content: Arc<Content>,
|
||||||
receiver: Receiver<Action>,
|
receiver: Receiver<Action>,
|
||||||
sender: Sender<Action>,
|
sender: Sender<Action>,
|
||||||
|
settings: Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
@ -84,6 +85,8 @@ impl App {
|
|||||||
// Add the overlay to the main window
|
// Add the overlay to the main window
|
||||||
window.add(&overlay);
|
window.add(&overlay);
|
||||||
|
|
||||||
|
let settings = Settings::new("org.gnome.Hammond");
|
||||||
|
|
||||||
App {
|
App {
|
||||||
app_instance: application,
|
app_instance: application,
|
||||||
window,
|
window,
|
||||||
@ -92,33 +95,52 @@ impl App {
|
|||||||
content,
|
content,
|
||||||
receiver,
|
receiver,
|
||||||
sender,
|
sender,
|
||||||
|
settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_timed_callbacks(&self) {
|
fn setup_timed_callbacks(&self) {
|
||||||
let sender = self.sender.clone();
|
self.setup_dark_theme();
|
||||||
|
self.setup_refresh_on_startup();
|
||||||
|
self.setup_auto_refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_dark_theme(&self) {
|
||||||
|
let settings = gtk::Settings::get_default().unwrap();
|
||||||
|
let enabled = self.settings.get_boolean("dark-theme");
|
||||||
|
|
||||||
|
settings.set_property_gtk_application_prefer_dark_theme(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_refresh_on_startup(&self) {
|
||||||
// Update the feeds right after the Application is initialized.
|
// Update the feeds right after the Application is initialized.
|
||||||
gtk::timeout_add_seconds(2, move || {
|
if self.settings.get_boolean("refresh-on-startup") {
|
||||||
utils::refresh_feed_wrapper(None, sender.clone());
|
let cleanup_date = utils::get_cleanup_date(&self.settings);
|
||||||
glib::Continue(false)
|
let sender = self.sender.clone();
|
||||||
});
|
|
||||||
|
|
||||||
|
info!("Refresh on startup.");
|
||||||
|
|
||||||
|
utils::cleanup(cleanup_date);
|
||||||
|
|
||||||
|
gtk::timeout_add_seconds(2, move || {
|
||||||
|
utils::refresh(None, sender.clone());
|
||||||
|
|
||||||
|
glib::Continue(false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_auto_refresh(&self) {
|
||||||
|
let refresh_interval = utils::get_refresh_interval(&self.settings).num_seconds() as u32;
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
// Auto-updater, runs every hour.
|
|
||||||
// TODO: expose the interval in which it run to a user setting.
|
info!("Auto-refresh every {:?} seconds.", refresh_interval);
|
||||||
gtk::timeout_add_seconds(3600, move || {
|
|
||||||
utils::refresh_feed_wrapper(None, sender.clone());
|
gtk::timeout_add_seconds(refresh_interval, move || {
|
||||||
|
utils::refresh(None, sender.clone());
|
||||||
|
|
||||||
glib::Continue(true)
|
glib::Continue(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Run a database checkup once the application is initialized.
|
|
||||||
gtk::timeout_add(300, || {
|
|
||||||
if let Err(err) = checkup() {
|
|
||||||
error!("Check up failed: {}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
glib::Continue(false)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self) {
|
pub fn run(self) {
|
||||||
@ -137,9 +159,9 @@ impl App {
|
|||||||
match receiver.recv_timeout(Duration::from_millis(10)) {
|
match receiver.recv_timeout(Duration::from_millis(10)) {
|
||||||
Ok(Action::UpdateSources(source)) => {
|
Ok(Action::UpdateSources(source)) => {
|
||||||
if let Some(s) = source {
|
if let Some(s) = source {
|
||||||
utils::refresh_feed_wrapper(Some(vec![s]), sender.clone());
|
utils::refresh(Some(vec![s]), sender.clone());
|
||||||
} else {
|
} else {
|
||||||
utils::refresh_feed_wrapper(None, sender.clone());
|
utils::refresh(None, sender.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Action::RefreshAllViews) => content.update(),
|
Ok(Action::RefreshAllViews) => content.update(),
|
||||||
|
|||||||
@ -89,11 +89,5 @@ fn main() {
|
|||||||
600,
|
600,
|
||||||
);
|
);
|
||||||
|
|
||||||
// This set's the app to dark mode.
|
|
||||||
// It wiil be in the user's preference later.
|
|
||||||
// Uncomment it to run with the dark theme variant.
|
|
||||||
// let settings = gtk::Settings::get_default().unwrap();
|
|
||||||
// settings.set_property_gtk_application_prefer_dark_theme(true);
|
|
||||||
|
|
||||||
App::new().run();
|
App::new().run();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use gdk_pixbuf::Pixbuf;
|
use gdk_pixbuf::Pixbuf;
|
||||||
|
use gio::{Settings, SettingsExt};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use reqwest;
|
use reqwest;
|
||||||
use send_cell::SendCell;
|
use send_cell::SendCell;
|
||||||
@ -11,6 +12,7 @@ use serde_json::Value;
|
|||||||
use hammond_data::{PodcastCoverQuery, Source};
|
use hammond_data::{PodcastCoverQuery, Source};
|
||||||
use hammond_data::dbqueries;
|
use hammond_data::dbqueries;
|
||||||
use hammond_data::pipeline;
|
use hammond_data::pipeline;
|
||||||
|
use hammond_data::utils::checkup;
|
||||||
use hammond_downloader::downloader;
|
use hammond_downloader::downloader;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -20,13 +22,37 @@ use std::thread;
|
|||||||
|
|
||||||
use app::Action;
|
use app::Action;
|
||||||
|
|
||||||
pub fn refresh_feed_wrapper(source: Option<Vec<Source>>, sender: Sender<Action>) {
|
use chrono::Duration;
|
||||||
|
use chrono::prelude::*;
|
||||||
|
|
||||||
|
pub fn cleanup(cleanup_date: DateTime<Utc>) {
|
||||||
|
if let Err(err) = checkup(cleanup_date) {
|
||||||
|
error!("Check up failed: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh(source: Option<Vec<Source>>, sender: Sender<Action>) {
|
||||||
if let Err(err) = refresh_feed(source, sender) {
|
if let Err(err) = refresh_feed(source, sender) {
|
||||||
error!("An error occured while trying to update the feeds.");
|
error!("An error occured while trying to update the feeds.");
|
||||||
error!("Error: {}", err);
|
error!("Error: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_refresh_interval(settings: &Settings) -> Duration {
|
||||||
|
let time = settings.get_int("refresh-interval-time") as i64;
|
||||||
|
let period = settings.get_string("refresh-interval-period").unwrap();
|
||||||
|
|
||||||
|
time_period_to_duration(time, period.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cleanup_date(settings: &Settings) -> DateTime<Utc> {
|
||||||
|
let time = settings.get_int("cleanup-age-time") as i64;
|
||||||
|
let period = settings.get_string("cleanup-age-period").unwrap();
|
||||||
|
let duration = time_period_to_duration(time, period.as_str());
|
||||||
|
|
||||||
|
Utc::now() - duration
|
||||||
|
}
|
||||||
|
|
||||||
/// Update the rss feed(s) originating from `source`.
|
/// Update the rss feed(s) originating from `source`.
|
||||||
/// If `source` is None, Fetches all the `Source` entries in the database and updates them.
|
/// If `source` is None, Fetches all the `Source` entries in the database and updates them.
|
||||||
/// When It's done,it queues up a `RefreshViews` action.
|
/// When It's done,it queues up a `RefreshViews` action.
|
||||||
@ -138,12 +164,40 @@ fn lookup_id(id: u32) -> Result<String, Error> {
|
|||||||
Ok(feedurl.into())
|
Ok(feedurl.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn time_period_to_duration(time: i64, period: &str) -> Duration {
|
||||||
|
match period {
|
||||||
|
"weeks" => Duration::weeks(time),
|
||||||
|
"days" => Duration::days(time),
|
||||||
|
"hours" => Duration::hours(time),
|
||||||
|
"minutes" => Duration::minutes(time),
|
||||||
|
_ => Duration::seconds(time),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use hammond_data::Source;
|
use hammond_data::Source;
|
||||||
use hammond_data::dbqueries;
|
use hammond_data::dbqueries;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_time_period_to_duration() {
|
||||||
|
let time = 2;
|
||||||
|
let week = 604800 * time;
|
||||||
|
let day = 86400 * time;
|
||||||
|
let hour = 3600 * time;
|
||||||
|
let minute = 60 * time;
|
||||||
|
|
||||||
|
assert_eq!(week, time_period_to_duration(time, "weeks").num_seconds());
|
||||||
|
assert_eq!(day, time_period_to_duration(time, "days").num_seconds());
|
||||||
|
assert_eq!(hour, time_period_to_duration(time, "hours").num_seconds());
|
||||||
|
assert_eq!(
|
||||||
|
minute,
|
||||||
|
time_period_to_duration(time, "minutes").num_seconds()
|
||||||
|
);
|
||||||
|
assert_eq!(time, time_period_to_duration(time, "seconds").num_seconds());
|
||||||
|
}
|
||||||
|
|
||||||
#[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.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user