Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
gilcu3 committed Aug 12, 2024
2 parents 0d159b1 + 0184b0d commit 7be76a3
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 34 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ features stated below, while learning `rust` at the same time.
### Planned changes

- [x] Option to avoid marking as read when playing
- [ ] Syncing with the gpodder API
- [x] Syncing with the gpodder API
- [x] Add playing queue
- [ ] Internal Player using [rodio](https://github.com/RustAudio/rodio)
- [x] Show key bindings in a bar on the bottom
Expand All @@ -45,11 +45,13 @@ features stated below, while learning `rust` at the same time.
- [x] Make queue persistent
- [x] Fix crash when playing from queue
- [ ] Create lock file to prevent several instances of hullcaster
- [ ] Add option to play next from queue automatically
- [ ] Add history of episode actions
- [ ] Avoid repeated elements in queue
- [ ] Add option to play next from queue automatically, makes sense only after internal player is implemented
- [ ] Add history of episode actions. It seems that `AntennaPod` does not do this.
- [x] Avoid repeated elements in queue
- [ ] Fix bug where queue actions are not persistent, hard to reproduce
- [ ] Fix gpodder test, it should use local files
- [ ] Fix gpodder test, it should use local files or local server
- [x] Add panel for unplayed episodes across podcasts
- [x] Fix bug in two-column state, going to/from queue from/to episode/unplayed panel does not work.

## Installing hullcaster

Expand Down Expand Up @@ -173,10 +175,10 @@ filled in with the default value specified in those comments.
| x | Delete downloaded file |
| Shift+X | Delete all downloaded files |
| r | Remove selected feed from list |
| Shift+R | Remove all feeds from list |
| 1 | Toggle played/unplayed filter |
| 2 | Toggle downloaded/not downloaded filter |
| e | Push episode in queue |
| u | Show/hide Unread list of episodes |

**Note:** Actions can be mapped to more than one key (e.g., "Enter" and "p" both
play an episode), but a single key may not do more than one action (e.g., you
Expand Down
2 changes: 2 additions & 0 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ enqueue = [ "e" ]
help = [ "?" ]
quit = [ "q" ]

unplayed_list = [ "u" ]


[colors]

Expand Down
2 changes: 2 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ pub struct KeybindingsFromToml {
pub enqueue: Option<Vec<String>>,
pub help: Option<Vec<String>>,
pub quit: Option<Vec<String>>,
pub unplayed_list: Option<Vec<String>>,
}

/// A temporary struct used to deserialize colors data from the TOML
Expand Down Expand Up @@ -182,6 +183,7 @@ impl Config {
enqueue: None,
help: None,
quit: None,
unplayed_list: None,
};

let colors = AppColorsFromToml {
Expand Down
4 changes: 4 additions & 0 deletions src/keymap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub enum UserAction {

Help,
Quit,

UnplayedList,
}

/// Wrapper around a hash map that keeps track of all keybindings. Multiple
Expand Down Expand Up @@ -103,6 +105,7 @@ impl Keybindings {
(config.enqueue, UserAction::Enqueue),
(config.help, UserAction::Help),
(config.quit, UserAction::Quit),
(config.unplayed_list, UserAction::UnplayedList),
];

let mut keymap = Self::default();
Expand Down Expand Up @@ -189,6 +192,7 @@ impl Keybindings {
(UserAction::Enqueue, vec!["e".to_string()]),
(UserAction::Help, vec!["?".to_string()]),
(UserAction::Quit, vec!["q".to_string()]),
(UserAction::UnplayedList, vec!["u".to_string()]),
]
}
}
Expand Down
22 changes: 20 additions & 2 deletions src/main_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use crate::play_file;
use crate::threadpool::Threadpool;
use crate::types::*;
use crate::ui::{Ui, UiMsg};
use crate::utils::{audio_duration, current_time_ms, evaluate_in_shell, resolve_redirection};
use crate::utils::{
audio_duration, current_time_ms, evaluate_in_shell, get_unplayed_episodes, resolve_redirection,
};

/// Enum used for communicating with other threads.
#[allow(clippy::enum_variant_names)]
Expand All @@ -31,14 +33,15 @@ pub enum MainMessage {
}

/// Main application controller, holding all of the main application
/// state and mechanisms for communicatingg with the rest of the app.
/// state and mechanisms for communicating with the rest of the app.
pub struct MainController {
config: Arc<Config>,
db: Database,
threadpool: Threadpool,
sync_agent: Option<GpodderController>,
podcasts: LockVec<Podcast>,
queue: LockVec<Episode>,
unplayed: LockVec<Episode>,
filters: Filters,
sync_counter: usize,
sync_tracker: Vec<SyncResult>,
Expand Down Expand Up @@ -99,12 +102,17 @@ impl MainController {
res
});

let unplayed_items = get_unplayed_episodes(&podcast_list);
unplayed_items.sort();
unplayed_items.reverse();

// set up UI in new thread
let tx_ui_to_main = mpsc::Sender::clone(&tx_to_main);
let ui_thread = Ui::spawn(
config.clone(),
podcast_list.clone(),
queue_items.clone(),
unplayed_items.clone(),
rx_from_main,
tx_ui_to_main,
);
Expand All @@ -116,6 +124,7 @@ impl MainController {
sync_agent,
podcasts: podcast_list,
queue: queue_items,
unplayed: unplayed_items,
filters: Filters::default(),
ui_thread,
sync_counter: 0,
Expand Down Expand Up @@ -668,6 +677,15 @@ impl MainController {
changed = true;
episode.played = played;
}
if episode.played && self.unplayed.contains_key(ep_id) {
self.unplayed.remove(ep_id);
changed = true;
} else if !episode.played && !self.unplayed.contains_key(ep_id) {
self.unplayed.push(episode.clone());
self.unplayed.sort();
self.unplayed.reverse();
changed = true;
}
self.db.set_played_status(ep_id, played).ok()?;
(episode.duration, episode.url.clone(), podcast.url.clone())
};
Expand Down
34 changes: 32 additions & 2 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl Ord for Podcast {
/// is metadata, but if the episode has been downloaded to the local
/// machine, the filepath will be included here as well. `played`
/// indicates whether the podcast has been marked as played or unplayed.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Episode {
pub id: i64,
pub pod_id: i64,
Expand Down Expand Up @@ -131,6 +131,18 @@ impl Episode {
}
}

impl Ord for Episode {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.pubdate.cmp(&other.pubdate)
}
}

impl PartialOrd for Episode {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}

impl Menuable for Episode {
/// Returns the database ID for the episode.
fn get_id(&self) -> i64 {
Expand Down Expand Up @@ -301,7 +313,12 @@ impl<T: Clone + Menuable> LockVec<T> {

pub fn get(&self, id: i64) -> Option<T> {
let borrowed = self.borrow_map();
return borrowed.get(&id).cloned();
borrowed.get(&id).cloned()
}

pub fn contains_key(&self, id: i64) -> bool {
let borrowed = self.borrow_map();
borrowed.contains_key(&id)
}

/// Lock the LockVec hashmap for reading/writing.
Expand Down Expand Up @@ -460,6 +477,19 @@ impl LockVec<Podcast> {
}
}

impl LockVec<Episode> {
pub fn sort(&self) {
self.borrow_order().sort();

self.borrow_filtered_order().sort();
}

pub fn reverse(&self) {
self.borrow_order().reverse();
self.borrow_filtered_order().reverse();
}
}

/// Overarching Message enum that allows multiple threads to communicate
/// back to the main thread with a single enum type.
#[derive(Debug)]
Expand Down
23 changes: 18 additions & 5 deletions src/ui/details_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ pub struct Details {
pub description: Option<String>,
pub author: Option<String>,
pub last_checked: Option<DateTime<Utc>>,
pub title: Option<String>,
pub episode_title: Option<String>,
pub podcast_title: Option<String>,
}

#[derive(Debug)]
Expand Down Expand Up @@ -139,11 +140,23 @@ impl DetailsPanel {

let mut anything = false;

// title
if let Some(title) = &details.title {
// podcast title
if let Some(title) = &details.podcast_title {
anything = true;
self.content
.push(DetailsLine::Line("Title:".to_owned(), Some(italic)));
.push(DetailsLine::Line("Podcast:".to_owned(), Some(italic)));
let wrapper = textwrap::wrap(title, num_cols);
for line in wrapper {
self.content.push(DetailsLine::Line(line.to_string(), None));
}
self.content.push(DetailsLine::Blank); // blank line
}

// episode title
if let Some(title) = &details.episode_title {
anything = true;
self.content
.push(DetailsLine::Line("Episode:".to_owned(), Some(italic)));
let wrapper = textwrap::wrap(title, num_cols);
for line in wrapper {
self.content.push(DetailsLine::Line(line.to_string(), None));
Expand All @@ -160,7 +173,7 @@ impl DetailsPanel {
));
}

// author
// last checked time
if let Some(last_checked) = details.last_checked {
anything = true;
self.content.push(DetailsLine::KeyValueLine(
Expand Down
1 change: 1 addition & 0 deletions src/ui/keybindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ impl KeybindingsWin {
(UserAction::AddFeed, "Add podcast"),
(UserAction::Enqueue, "Enqueue"),
(UserAction::Remove, "Remove"),
(UserAction::UnplayedList, "Show/Hide Unplayed"),
];
let mut cur_length = -3;
let mut key_strs = Vec::new();
Expand Down
Loading

0 comments on commit 7be76a3

Please sign in to comment.