Daemonize the musicbrainz thread #217
@ -94,13 +94,13 @@ fn with<Database: IDatabase + 'static, Library: ILibrary + 'static>(
|
|||||||
let listener = EventListener::new(listener_sender);
|
let listener = EventListener::new(listener_sender);
|
||||||
let handler = EventHandler::new(channel.receiver());
|
let handler = EventHandler::new(channel.receiver());
|
||||||
|
|
||||||
let mb_request_channel = JobChannel::new();
|
let mb_job_channel = JobChannel::new();
|
||||||
thread::spawn(|| MusicBrainzDaemon::run(musicbrainz, mb_request_channel.receiver, app_sender));
|
|
||||||
|
|
||||||
let app = App::new(music_hoard, mb_request_channel.sender);
|
let app = App::new(music_hoard, mb_job_channel.sender());
|
||||||
let ui = Ui;
|
let ui = Ui;
|
||||||
|
|
||||||
// Run the TUI application.
|
// Run the TUI application.
|
||||||
|
thread::spawn(|| MusicBrainzDaemon::run(musicbrainz, mb_job_channel.receiver(), app_sender));
|
||||||
Tui::run(terminal, app, ui, handler, listener).expect("failed to run tui");
|
Tui::run(terminal, app, ui, handler, listener).expect("failed to run tui");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ use crate::tui::{
|
|||||||
AppPublicState, AppState, IAppEventFetch, IAppInteractFetch, MatchStateInfo,
|
AppPublicState, AppState, IAppEventFetch, IAppInteractFetch, MatchStateInfo,
|
||||||
},
|
},
|
||||||
lib::{
|
lib::{
|
||||||
external::musicbrainz::daemon::{ApiParams, Job, JobInstance, JobPriority, ResultSender},
|
external::musicbrainz::daemon::{MbParams, ResultSender},
|
||||||
interface::musicbrainz::api::Error as MbError,
|
interface::musicbrainz::api::Error as MbError,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -80,21 +80,18 @@ impl AppMachine<FetchState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn submit_fetch_job(inner: &AppInner, artist: &Artist, tx: ResultSender) {
|
fn submit_fetch_job(inner: &AppInner, artist: &Artist, tx: ResultSender) {
|
||||||
let mut queue = VecDeque::new();
|
let queue = match artist.meta.musicbrainz {
|
||||||
match artist.meta.musicbrainz {
|
|
||||||
Some(ref arid) => {
|
Some(ref arid) => {
|
||||||
let arid = arid.mbid();
|
let arid = arid.mbid();
|
||||||
for album in artist.albums.iter() {
|
artist
|
||||||
let meta = &album.meta;
|
.albums
|
||||||
queue.push_back(ApiParams::search_release_group(arid.clone(), meta.clone()));
|
.iter()
|
||||||
|
.map(|album| MbParams::search_release_group(arid.clone(), album.meta.clone()))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
None => VecDeque::from([MbParams::search_artist(artist.meta.clone())]),
|
||||||
None => {
|
};
|
||||||
queue.push_back(ApiParams::search_artist(artist.meta.clone()));
|
inner.musicbrainz.submit_background_job(tx, queue);
|
||||||
}
|
|
||||||
}
|
|
||||||
let job = Job::new(JobPriority::Background, JobInstance::new(tx, queue));
|
|
||||||
inner.musicbrainz.send(job);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,14 +8,12 @@ mod match_state;
|
|||||||
mod reload_state;
|
mod reload_state;
|
||||||
mod search_state;
|
mod search_state;
|
||||||
|
|
||||||
use std::sync::mpsc;
|
|
||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
app::{
|
app::{
|
||||||
selection::Selection, AppMode, AppPublic, AppPublicInner, AppPublicState, AppState, IApp,
|
selection::Selection, AppMode, AppPublic, AppPublicInner, AppPublicState, AppState, IApp,
|
||||||
IAppAccess, IAppBase, IAppState,
|
IAppAccess, IAppBase, IAppState,
|
||||||
},
|
},
|
||||||
lib::{external::musicbrainz::daemon::Job, IMusicHoard},
|
lib::{external::musicbrainz::daemon::IMbJobSender, IMusicHoard},
|
||||||
};
|
};
|
||||||
|
|
||||||
use browse_state::BrowseState;
|
use browse_state::BrowseState;
|
||||||
@ -48,7 +46,7 @@ pub struct AppMachine<STATE> {
|
|||||||
pub struct AppInner {
|
pub struct AppInner {
|
||||||
running: bool,
|
running: bool,
|
||||||
music_hoard: Box<dyn IMusicHoard>,
|
music_hoard: Box<dyn IMusicHoard>,
|
||||||
musicbrainz: mpsc::Sender<Job>,
|
musicbrainz: Box<dyn IMbJobSender>,
|
||||||
selection: Selection,
|
selection: Selection,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,9 +81,9 @@ macro_rules! app_field_mut {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new<MH: IMusicHoard + 'static>(
|
pub fn new<MH: IMusicHoard + 'static, MB: IMbJobSender + 'static>(
|
||||||
mut music_hoard: MH,
|
mut music_hoard: MH,
|
||||||
musicbrainz: mpsc::Sender<Job>,
|
musicbrainz: MB,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let init_result = Self::init(&mut music_hoard);
|
let init_result = Self::init(&mut music_hoard);
|
||||||
let inner = AppInner::new(music_hoard, musicbrainz);
|
let inner = AppInner::new(music_hoard, musicbrainz);
|
||||||
@ -171,12 +169,15 @@ impl IAppAccess for App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AppInner {
|
impl AppInner {
|
||||||
pub fn new<MH: IMusicHoard + 'static>(music_hoard: MH, musicbrainz: mpsc::Sender<Job>) -> Self {
|
pub fn new<MH: IMusicHoard + 'static, MB: IMbJobSender + 'static>(
|
||||||
|
music_hoard: MH,
|
||||||
|
musicbrainz: MB,
|
||||||
|
) -> Self {
|
||||||
let selection = Selection::new(music_hoard.get_collection());
|
let selection = Selection::new(music_hoard.get_collection());
|
||||||
AppInner {
|
AppInner {
|
||||||
running: true,
|
running: true,
|
||||||
music_hoard: Box::new(music_hoard),
|
music_hoard: Box::new(music_hoard),
|
||||||
musicbrainz,
|
musicbrainz: Box::new(musicbrainz),
|
||||||
selection,
|
selection,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
103
src/tui/lib/external/musicbrainz/daemon/mod.rs
vendored
103
src/tui/lib/external/musicbrainz/daemon/mod.rs
vendored
@ -10,7 +10,7 @@ use crate::tui::{
|
|||||||
|
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
EventChannelDisconnected,
|
EventChannelDisconnected,
|
||||||
RequestChannelDisconnected,
|
JobChannelDisconnected,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MusicBrainzDaemon {
|
pub struct MusicBrainzDaemon {
|
||||||
@ -30,7 +30,7 @@ struct JobQueue {
|
|||||||
background_queue: VecDeque<JobInstance>,
|
background_queue: VecDeque<JobInstance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Job {
|
struct Job {
|
||||||
priority: JobPriority,
|
priority: JobPriority,
|
||||||
instance: JobInstance,
|
instance: JobInstance,
|
||||||
}
|
}
|
||||||
@ -41,15 +41,15 @@ impl Job {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum JobPriority {
|
enum JobPriority {
|
||||||
Foreground,
|
Foreground,
|
||||||
Background,
|
Background,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ResultSender = mpsc::Sender<Result<MatchStateInfo, ApiError>>;
|
pub type ResultSender = mpsc::Sender<Result<MatchStateInfo, ApiError>>;
|
||||||
pub struct JobInstance {
|
struct JobInstance {
|
||||||
result_sender: ResultSender,
|
result_sender: ResultSender,
|
||||||
call_queue: VecDeque<ApiParams>,
|
requests: VecDeque<MbParams>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum JobInstanceStatus {
|
enum JobInstanceStatus {
|
||||||
@ -63,25 +63,25 @@ enum JobInstanceError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl JobInstance {
|
impl JobInstance {
|
||||||
pub fn new(result_sender: ResultSender, call_queue: VecDeque<ApiParams>) -> Self {
|
fn new(result_sender: ResultSender, requests: VecDeque<MbParams>) -> Self {
|
||||||
JobInstance {
|
JobInstance {
|
||||||
result_sender,
|
result_sender,
|
||||||
call_queue,
|
requests,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ApiParams {
|
pub enum MbParams {
|
||||||
Search(SearchParams),
|
Search(SearchParams),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApiParams {
|
impl MbParams {
|
||||||
pub fn search_artist(artist: ArtistMeta) -> Self {
|
pub fn search_artist(artist: ArtistMeta) -> Self {
|
||||||
ApiParams::Search(SearchParams::Artist(SearchArtistParams { artist }))
|
MbParams::Search(SearchParams::Artist(SearchArtistParams { artist }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_release_group(arid: Mbid, album: AlbumMeta) -> Self {
|
pub fn search_release_group(arid: Mbid, album: AlbumMeta) -> Self {
|
||||||
ApiParams::Search(SearchParams::ReleaseGroup(SearchReleaseGroupParams {
|
MbParams::Search(SearchParams::ReleaseGroup(SearchReleaseGroupParams {
|
||||||
arid,
|
arid,
|
||||||
album,
|
album,
|
||||||
}))
|
}))
|
||||||
@ -103,8 +103,16 @@ pub struct SearchReleaseGroupParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct JobChannel {
|
pub struct JobChannel {
|
||||||
pub receiver: mpsc::Receiver<Job>,
|
sender: mpsc::Sender<Job>,
|
||||||
pub sender: mpsc::Sender<Job>,
|
receiver: mpsc::Receiver<Job>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct JobSender {
|
||||||
|
sender: mpsc::Sender<Job>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct JobReceiver {
|
||||||
|
receiver: mpsc::Receiver<Job>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JobChannel {
|
impl JobChannel {
|
||||||
@ -112,17 +120,68 @@ impl JobChannel {
|
|||||||
let (sender, receiver) = mpsc::channel();
|
let (sender, receiver) = mpsc::channel();
|
||||||
JobChannel { receiver, sender }
|
JobChannel { receiver, sender }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sender(&self) -> JobSender {
|
||||||
|
JobSender {
|
||||||
|
sender: self.sender.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receiver(self) -> JobReceiver {
|
||||||
|
JobReceiver {
|
||||||
|
receiver: self.receiver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IMbJobSender {
|
||||||
|
fn submit_background_job(
|
||||||
|
&self,
|
||||||
|
result_sender: ResultSender,
|
||||||
|
requests: VecDeque<MbParams>,
|
||||||
|
) -> Result<(), Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IMbJobSender for JobSender {
|
||||||
|
fn submit_background_job(
|
||||||
|
&self,
|
||||||
|
result_sender: ResultSender,
|
||||||
|
requests: VecDeque<MbParams>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.send_background_job(result_sender, requests)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JobSender {
|
||||||
|
fn send_background_job(
|
||||||
|
&self,
|
||||||
|
result_sender: ResultSender,
|
||||||
|
requests: VecDeque<MbParams>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.send_job(JobPriority::Background, result_sender, requests)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_job(
|
||||||
|
&self,
|
||||||
|
priority: JobPriority,
|
||||||
|
result_sender: ResultSender,
|
||||||
|
requests: VecDeque<MbParams>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let instance = JobInstance::new(result_sender, requests);
|
||||||
|
let job = Job::new(priority, instance);
|
||||||
|
self.sender.send(job).or(Err(Error::JobChannelDisconnected))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MusicBrainzDaemon {
|
impl MusicBrainzDaemon {
|
||||||
pub fn run<MB: IMusicBrainz + 'static, ES: IFetchCompleteEventSender + 'static>(
|
pub fn run<MB: IMusicBrainz + 'static, ES: IFetchCompleteEventSender + 'static>(
|
||||||
musicbrainz: MB,
|
musicbrainz: MB,
|
||||||
job_receiver: mpsc::Receiver<Job>,
|
job_receiver: JobReceiver,
|
||||||
event_sender: ES,
|
event_sender: ES,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let daemon = MusicBrainzDaemon {
|
let daemon = MusicBrainzDaemon {
|
||||||
musicbrainz: Box::new(musicbrainz),
|
musicbrainz: Box::new(musicbrainz),
|
||||||
job_receiver,
|
job_receiver: job_receiver.receiver,
|
||||||
job_queue: JobQueue::new(),
|
job_queue: JobQueue::new(),
|
||||||
event_sender: Box::new(event_sender),
|
event_sender: Box::new(event_sender),
|
||||||
};
|
};
|
||||||
@ -155,7 +214,7 @@ impl MusicBrainzDaemon {
|
|||||||
Ok(job) => self.job_queue.push_back(job),
|
Ok(job) => self.job_queue.push_back(job),
|
||||||
Err(mpsc::TryRecvError::Empty) => return Ok(()),
|
Err(mpsc::TryRecvError::Empty) => return Ok(()),
|
||||||
Err(mpsc::TryRecvError::Disconnected) => {
|
Err(mpsc::TryRecvError::Disconnected) => {
|
||||||
return Err(Error::RequestChannelDisconnected);
|
return Err(Error::JobChannelDisconnected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,7 +243,7 @@ impl MusicBrainzDaemon {
|
|||||||
|
|
||||||
match self.job_receiver.recv() {
|
match self.job_receiver.recv() {
|
||||||
Ok(job) => self.job_queue.push_back(job),
|
Ok(job) => self.job_queue.push_back(job),
|
||||||
Err(mpsc::RecvError) => return Err(Error::RequestChannelDisconnected),
|
Err(mpsc::RecvError) => return Err(Error::JobChannelDisconnected),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -197,12 +256,12 @@ impl JobInstance {
|
|||||||
musicbrainz: &mut Box<dyn IMusicBrainz>,
|
musicbrainz: &mut Box<dyn IMusicBrainz>,
|
||||||
event_sender: &mut Box<dyn IFetchCompleteEventSender>,
|
event_sender: &mut Box<dyn IFetchCompleteEventSender>,
|
||||||
) -> Result<JobInstanceStatus, JobInstanceError> {
|
) -> Result<JobInstanceStatus, JobInstanceError> {
|
||||||
// self.call_queue can be empty if the caller submits an empty job.
|
// self.requests can be empty if the caller submits an empty job.
|
||||||
if let Some(params) = self.call_queue.pop_front() {
|
if let Some(params) = self.requests.pop_front() {
|
||||||
self.execute(musicbrainz, event_sender, params)?
|
self.execute(musicbrainz, event_sender, params)?
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.call_queue.is_empty() {
|
if self.requests.is_empty() {
|
||||||
Ok(JobInstanceStatus::Complete)
|
Ok(JobInstanceStatus::Complete)
|
||||||
} else {
|
} else {
|
||||||
Ok(JobInstanceStatus::Continue)
|
Ok(JobInstanceStatus::Continue)
|
||||||
@ -213,10 +272,10 @@ impl JobInstance {
|
|||||||
&mut self,
|
&mut self,
|
||||||
musicbrainz: &mut Box<dyn IMusicBrainz>,
|
musicbrainz: &mut Box<dyn IMusicBrainz>,
|
||||||
event_sender: &mut Box<dyn IFetchCompleteEventSender>,
|
event_sender: &mut Box<dyn IFetchCompleteEventSender>,
|
||||||
api_params: ApiParams,
|
api_params: MbParams,
|
||||||
) -> Result<(), JobInstanceError> {
|
) -> Result<(), JobInstanceError> {
|
||||||
match api_params {
|
match api_params {
|
||||||
ApiParams::Search(search) => match search {
|
MbParams::Search(search) => match search {
|
||||||
SearchParams::Artist(params) => {
|
SearchParams::Artist(params) => {
|
||||||
let result = musicbrainz.search_artist(¶ms.artist);
|
let result = musicbrainz.search_artist(¶ms.artist);
|
||||||
let result = result.map(|list| MatchStateInfo::artist(params.artist, list));
|
let result = result.map(|list| MatchStateInfo::artist(params.artist, list));
|
||||||
|
Loading…
Reference in New Issue
Block a user