Add folder source to import

This commit is contained in:
Elias Projahn 2021-01-29 17:51:44 +01:00
parent 030eccf253
commit 357a4a4429
8 changed files with 156 additions and 7 deletions

View file

@ -3,6 +3,7 @@ use anyhow::{anyhow, bail, Result};
use async_trait::async_trait;
use discid::DiscId;
use futures_channel::oneshot;
use gettextrs::gettext;
use gstreamer::prelude::*;
use gstreamer::{Element, ElementFactory, Pipeline};
use once_cell::sync::OnceCell;
@ -45,6 +46,8 @@ impl DiscSource {
let tmp_dir = Self::create_tmp_dir()?;
for number in first_track..=last_track {
let name = gettext!("Track {}", number);
let file_name = format!("track_{:02}.flac", number);
let mut path = tmp_dir.clone();
@ -52,6 +55,7 @@ impl DiscSource {
let track = SourceTrack {
number,
name,
path,
};

View file

@ -0,0 +1,90 @@
use super::source::{Source, SourceTrack};
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures_channel::oneshot;
use once_cell::sync::OnceCell;
use std::path::{Path, PathBuf};
use std::thread;
/// A folder outside of the music library that contains tracks to import.
#[derive(Clone, Debug)]
pub struct FolderSource {
/// The path to the folder.
path: PathBuf,
/// The tracks within the folder.
tracks: OnceCell<Vec<SourceTrack>>,
}
impl FolderSource {
/// Create a new folder source.
pub fn new(path: PathBuf) -> Self {
Self {
path,
tracks: OnceCell::new(),
}
}
/// Load the contents of the folder as tracks.
fn load_priv(path: &Path) -> Result<Vec<SourceTrack>> {
let mut tracks = Vec::new();
let mut number = 1;
for entry in std::fs::read_dir(path)? {
let entry = entry?;
if entry.file_type()?.is_file() {
let name = entry
.file_name()
.into_string()
.or_else(|_| Err(anyhow!("Failed to convert OsString to String!")))?;
let path = entry.path();
let track = SourceTrack {
number,
name,
path,
};
tracks.push(track);
number += 1;
}
}
Ok(tracks)
}
}
#[async_trait]
impl Source for FolderSource {
async fn load(&self) -> Result<()> {
let (sender, receiver) = oneshot::channel();
let path = self.path.clone();
thread::spawn(move || {
let result = Self::load_priv(&path);
sender.send(result).unwrap();
});
let tracks = receiver.await??;
self.tracks.set(tracks);
Ok(())
}
fn tracks(&self) -> Option<&[SourceTrack]> {
match self.tracks.get() {
Some(tracks) => Some(tracks.as_slice()),
None => None,
}
}
fn discid(&self) -> Option<String> {
None
}
async fn copy(&self) -> Result<()> {
Ok(())
}
}

View file

@ -1,4 +1,5 @@
mod disc_source;
mod folder_source;
mod medium_editor;
mod source;
mod source_selector;

View file

@ -27,6 +27,10 @@ pub struct SourceTrack {
/// the track numbers start.
pub number: u32,
/// A human readable identifier for the track. This will be used to
/// present the track for selection.
pub name: String,
/// The path to the file where the corresponding audio file is. This file
/// is only required to exist, once the source's copy method has finished.
/// This will not be the actual file within the user's music library, but

View file

@ -1,12 +1,15 @@
use super::medium_editor::MediumEditor;
use super::disc_source::DiscSource;
use super::folder_source::FolderSource;
use super::source::Source;
use crate::backend::Backend;
use crate::widgets::{Navigator, NavigatorScreen};
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use std::cell::RefCell;
use std::path::PathBuf;
use std::rc::Rc;
/// A dialog for starting to import music.
@ -49,6 +52,55 @@ impl SourceSelector {
}
}));
folder_button.connect_clicked(clone!(@strong this => move |_| {
let window = this.navigator.borrow().clone().unwrap().window.clone();
let dialog = gtk::FileChooserDialog::new(
Some(&gettext("Select folder")),
Some(&window),
gtk::FileChooserAction::SelectFolder,
&[
(&gettext("Cancel"), gtk::ResponseType::Cancel),
(&gettext("Select"), gtk::ResponseType::Accept),
]);
dialog.connect_response(clone!(@strong this => move |dialog, response| {
this.stack.set_visible_child_name("loading");
dialog.hide();
if let gtk::ResponseType::Accept = response {
if let Some(file) = dialog.get_file() {
if let Some(path) = file.get_path() {
let context = glib::MainContext::default();
let clone = this.clone();
context.spawn_local(async move {
let folder = FolderSource::new(PathBuf::from(path));
match folder.load().await {
Ok(_) => {
let navigator = clone.navigator.borrow().clone();
if let Some(navigator) = navigator {
let source = Rc::new(Box::new(folder) as Box<dyn Source>);
let editor = MediumEditor::new(clone.backend.clone(), source);
navigator.push(editor);
}
clone.info_bar.set_revealed(false);
clone.stack.set_visible_child_name("start");
}
Err(_) => {
// TODO: Present error.
clone.info_bar.set_revealed(true);
clone.stack.set_visible_child_name("start");
}
}
});
}
}
}
}));
dialog.show();
}));
disc_button.connect_clicked(clone!(@strong this => move |_| {
this.stack.set_visible_child_name("loading");
@ -69,6 +121,7 @@ impl SourceSelector {
clone.stack.set_visible_child_name("start");
}
Err(_) => {
// TODO: Present error.
clone.info_bar.set_revealed(true);
clone.stack.set_visible_child_name("start");
}

View file

@ -87,13 +87,11 @@ impl TrackSelector {
}
}));
let title = format!("Track {}", track.number);
let row = libadwaita::ActionRow::new();
row.add_prefix(&check);
row.set_activatable_widget(Some(&check));
row.set_activatable(true);
row.set_title(Some(&title));
row.set_title(Some(&track.name));
track_list.append(&row);
}

View file

@ -175,9 +175,7 @@ impl TrackSetEditor {
};
let tracks = this.source.tracks().unwrap();
let number = tracks[track.track_source].number;
let subtitle = format!("Track {}", number);
let track_name = &tracks[track.track_source].name;
let edit_image = gtk::Image::from_icon_name(Some("document-edit-symbolic"));
let edit_button = gtk::Button::new();
@ -188,7 +186,7 @@ impl TrackSetEditor {
let row = libadwaita::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&title));
row.set_subtitle(Some(&subtitle));
row.set_subtitle(Some(track_name));
row.add_suffix(&edit_button);
row.set_activatable_widget(Some(&edit_button));

View file

@ -66,6 +66,7 @@ sources = files(
'editors/work_part.rs',
'editors/work_section.rs',
'import/disc_source.rs',
'import/folder_source.rs',
'import/medium_editor.rs',
'import/mod.rs',
'import/source.rs',