mirror of
https://github.com/agersant/polaris
synced 2024-12-11 13:56:24 +00:00
Proper error handling in file indexing
No longer unwrap like there's no tomorrow
This commit is contained in:
parent
c02b971984
commit
0a729184e4
2 changed files with 141 additions and 136 deletions
|
@ -8,6 +8,7 @@ use iron::status::Status;
|
||||||
use lewton;
|
use lewton;
|
||||||
use metaflac;
|
use metaflac;
|
||||||
use regex;
|
use regex;
|
||||||
|
use sqlite;
|
||||||
use std;
|
use std;
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
|
@ -20,6 +21,7 @@ error_chain! {
|
||||||
Image(image::ImageError);
|
Image(image::ImageError);
|
||||||
Io(std::io::Error);
|
Io(std::io::Error);
|
||||||
Regex(regex::Error);
|
Regex(regex::Error);
|
||||||
|
SQLite(sqlite::Error);
|
||||||
Vorbis(lewton::VorbisError);
|
Vorbis(lewton::VorbisError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
275
src/index.rs
275
src/index.rs
|
@ -95,22 +95,20 @@ struct IndexBuilder<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> IndexBuilder<'db> {
|
impl<'db> IndexBuilder<'db> {
|
||||||
fn new(db: &Connection) -> IndexBuilder {
|
fn new(db: &Connection) -> Result<IndexBuilder> {
|
||||||
let mut queue = Vec::new();
|
let mut queue = Vec::new();
|
||||||
queue.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);
|
queue.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);
|
||||||
IndexBuilder {
|
Ok(IndexBuilder {
|
||||||
queue: queue,
|
queue: queue,
|
||||||
db: db,
|
db: db,
|
||||||
insert_directory:
|
insert_directory:
|
||||||
db.prepare("INSERT OR REPLACE INTO directories (path, parent, artwork, year, \
|
db.prepare("INSERT OR REPLACE INTO directories (path, parent, artwork, year, \
|
||||||
artist, album) VALUES (?, ?, ?, ?, ?, ?)")
|
artist, album) VALUES (?, ?, ?, ?, ?, ?)")?,
|
||||||
.unwrap(),
|
|
||||||
insert_song:
|
insert_song:
|
||||||
db.prepare("INSERT OR REPLACE INTO songs (path, parent, disc_number, track_number, \
|
db.prepare("INSERT OR REPLACE INTO songs (path, parent, disc_number, track_number, \
|
||||||
title, year, album_artist, artist, album, artwork) VALUES (?, ?, ?, ?, \
|
title, year, album_artist, artist, album, artwork) VALUES (?, ?, ?, ?, \
|
||||||
?, ?, ?, ?, ?, ?)")
|
?, ?, ?, ?, ?, ?)")?,
|
||||||
.unwrap(),
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_parent(path: &str) -> Option<String> {
|
fn get_parent(path: &str) -> Option<String> {
|
||||||
|
@ -123,63 +121,56 @@ impl<'db> IndexBuilder<'db> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) {
|
fn flush(&mut self) -> Result<()> {
|
||||||
self.db.execute("BEGIN TRANSACTION").ok();
|
self.db.execute("BEGIN TRANSACTION")?;
|
||||||
while let Some(file) = self.queue.pop() {
|
while let Some(file) = self.queue.pop() {
|
||||||
match file {
|
match file {
|
||||||
|
|
||||||
// Insert directory
|
// Insert directory
|
||||||
CollectionFile::Directory(directory) => {
|
CollectionFile::Directory(directory) => {
|
||||||
let parent = IndexBuilder::get_parent(directory.path.as_str());
|
let parent = IndexBuilder::get_parent(directory.path.as_str());
|
||||||
self.insert_directory.reset().ok();
|
self.insert_directory.reset()?;
|
||||||
self.insert_directory.bind(1, &Value::String(directory.path)).unwrap();
|
self.insert_directory.bind(1, &Value::String(directory.path))?;
|
||||||
self.insert_directory.bind(2, &string_option_to_value(parent)).unwrap();
|
self.insert_directory.bind(2, &string_option_to_value(parent))?;
|
||||||
self.insert_directory
|
self.insert_directory
|
||||||
.bind(3, &string_option_to_value(directory.artwork))
|
.bind(3, &string_option_to_value(directory.artwork))?;
|
||||||
.unwrap();
|
self.insert_directory.bind(4, &i32_option_to_value(directory.year))?;
|
||||||
self.insert_directory.bind(4, &i32_option_to_value(directory.year)).unwrap();
|
|
||||||
self.insert_directory
|
self.insert_directory
|
||||||
.bind(5, &string_option_to_value(directory.artist))
|
.bind(5, &string_option_to_value(directory.artist))?;
|
||||||
.unwrap();
|
|
||||||
self.insert_directory
|
self.insert_directory
|
||||||
.bind(6, &string_option_to_value(directory.album))
|
.bind(6, &string_option_to_value(directory.album))?;
|
||||||
.unwrap();
|
self.insert_directory.next()?;
|
||||||
self.insert_directory.next().ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert song
|
// Insert song
|
||||||
CollectionFile::Song(song) => {
|
CollectionFile::Song(song) => {
|
||||||
let parent = IndexBuilder::get_parent(song.path.as_str());
|
let parent = IndexBuilder::get_parent(song.path.as_str());
|
||||||
self.insert_song.reset().ok();
|
self.insert_song.reset()?;
|
||||||
self.insert_song.bind(1, &Value::String(song.path)).unwrap();
|
self.insert_song.bind(1, &Value::String(song.path))?;
|
||||||
self.insert_song.bind(2, &string_option_to_value(parent)).unwrap();
|
self.insert_song.bind(2, &string_option_to_value(parent))?;
|
||||||
self.insert_song.bind(3, &u32_option_to_value(song.disc_number)).unwrap();
|
self.insert_song.bind(3, &u32_option_to_value(song.disc_number))?;
|
||||||
self.insert_song.bind(4, &u32_option_to_value(song.track_number)).unwrap();
|
self.insert_song.bind(4, &u32_option_to_value(song.track_number))?;
|
||||||
self.insert_song.bind(5, &string_option_to_value(song.title)).unwrap();
|
self.insert_song.bind(5, &string_option_to_value(song.title))?;
|
||||||
self.insert_song.bind(6, &i32_option_to_value(song.year)).unwrap();
|
self.insert_song.bind(6, &i32_option_to_value(song.year))?;
|
||||||
self.insert_song.bind(7, &string_option_to_value(song.album_artist)).unwrap();
|
self.insert_song.bind(7, &string_option_to_value(song.album_artist))?;
|
||||||
self.insert_song.bind(8, &string_option_to_value(song.artist)).unwrap();
|
self.insert_song.bind(8, &string_option_to_value(song.artist))?;
|
||||||
self.insert_song.bind(9, &string_option_to_value(song.album)).unwrap();
|
self.insert_song.bind(9, &string_option_to_value(song.album))?;
|
||||||
self.insert_song.bind(10, &string_option_to_value(song.artwork)).unwrap();
|
self.insert_song.bind(10, &string_option_to_value(song.artwork))?;
|
||||||
self.insert_song.next().ok();
|
self.insert_song.next()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.db.execute("END TRANSACTION").ok();
|
self.db.execute("END TRANSACTION")?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push(&mut self, file: CollectionFile) {
|
fn push(&mut self, file: CollectionFile) -> Result<()> {
|
||||||
if self.queue.len() == self.queue.capacity() {
|
if self.queue.len() == self.queue.capacity() {
|
||||||
self.flush();
|
self.flush()?;
|
||||||
}
|
}
|
||||||
self.queue.push(file);
|
self.queue.push(file);
|
||||||
}
|
Ok(())
|
||||||
}
|
|
||||||
|
|
||||||
impl<'db> Drop for IndexBuilder<'db> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,18 +191,18 @@ impl Index {
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
// Migration
|
// Migration
|
||||||
} else {
|
} else {
|
||||||
index.init();
|
index.init()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(index)
|
Ok(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self) {
|
fn init(&self) -> Result<()> {
|
||||||
|
|
||||||
println!("Initializing index database");
|
println!("Initializing index database");
|
||||||
|
|
||||||
let db = self.connect();
|
let db = self.connect()?;
|
||||||
db.execute("PRAGMA synchronous = NORMAL").unwrap();
|
db.execute("PRAGMA synchronous = NORMAL")?;
|
||||||
db.execute("
|
db.execute("
|
||||||
|
|
||||||
CREATE TABLE version
|
CREATE TABLE version
|
||||||
|
@ -254,62 +245,68 @@ impl Index {
|
||||||
, UNIQUE(path)
|
, UNIQUE(path)
|
||||||
);
|
);
|
||||||
|
|
||||||
")
|
")?;
|
||||||
.unwrap();
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect(&self) -> Connection {
|
fn connect(&self) -> Result<Connection> {
|
||||||
let mut db = sqlite::open(self.path.clone()).unwrap();
|
let mut db = sqlite::open(self.path.clone())?;
|
||||||
db.set_busy_timeout(INDEX_LOCK_TIMEOUT).ok();
|
db.set_busy_timeout(INDEX_LOCK_TIMEOUT)?;
|
||||||
db
|
Ok(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_index(&self, db: &Connection) {
|
fn update_index(&self, db: &Connection) -> Result<()> {
|
||||||
let start = time::Instant::now();
|
let start = time::Instant::now();
|
||||||
println!("Beginning library index update");
|
println!("Beginning library index update");
|
||||||
self.clean(db);
|
self.clean(db)?;
|
||||||
self.populate(db);
|
self.populate(db)?;
|
||||||
println!("Library index update took {} seconds",
|
println!("Library index update took {} seconds",
|
||||||
start.elapsed().as_secs());
|
start.elapsed().as_secs());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean(&self, db: &Connection) {
|
fn clean(&self, db: &Connection) -> Result<()> {
|
||||||
{
|
{
|
||||||
let mut select = db.prepare("SELECT path FROM songs").unwrap();
|
let mut select = db.prepare("SELECT path FROM songs")?;
|
||||||
let mut delete = db.prepare("DELETE FROM songs WHERE path = ?").unwrap();
|
let mut delete = db.prepare("DELETE FROM songs WHERE path = ?")?;
|
||||||
while let State::Row = select.next().unwrap() {
|
while let Ok(State::Row) = select.next() {
|
||||||
let path_string: String = select.read(0).unwrap();
|
let path_string: String = select.read(0)?;
|
||||||
let path = Path::new(path_string.as_str());
|
let path = Path::new(path_string.as_str());
|
||||||
if !path.exists() || self.vfs.real_to_virtual(path).is_err() {
|
if !path.exists() || self.vfs.real_to_virtual(path).is_err() {
|
||||||
delete.reset().ok();
|
delete.reset()?;
|
||||||
delete.bind(1, &Value::String(path_string.to_owned())).ok();
|
delete.bind(1, &Value::String(path_string.to_owned()))?;
|
||||||
delete.next().ok();
|
delete.next()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut select = db.prepare("SELECT path FROM directories").unwrap();
|
let mut select = db.prepare("SELECT path FROM directories")?;
|
||||||
let mut delete = db.prepare("DELETE FROM directories WHERE path = ?").unwrap();
|
let mut delete = db.prepare("DELETE FROM directories WHERE path = ?")?;
|
||||||
while let State::Row = select.next().unwrap() {
|
while let Ok(State::Row) = select.next() {
|
||||||
let path_string: String = select.read(0).unwrap();
|
let path_string: String = select.read(0)?;
|
||||||
let path = Path::new(path_string.as_str());
|
let path = Path::new(path_string.as_str());
|
||||||
if !path.exists() || self.vfs.real_to_virtual(path).is_err() {
|
if !path.exists() || self.vfs.real_to_virtual(path).is_err() {
|
||||||
delete.reset().ok();
|
delete.reset()?;
|
||||||
delete.bind(1, &Value::String(path_string.to_owned())).ok();
|
delete.bind(1, &Value::String(path_string.to_owned()))?;
|
||||||
delete.next().ok();
|
delete.next()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate(&self, db: &Connection) {
|
fn populate(&self, db: &Connection) -> Result<()> {
|
||||||
let vfs = self.vfs.deref();
|
let vfs = self.vfs.deref();
|
||||||
let mount_points = vfs.get_mount_points();
|
let mount_points = vfs.get_mount_points();
|
||||||
let mut builder = IndexBuilder::new(db);
|
let mut builder = IndexBuilder::new(db)?;
|
||||||
for (_, target) in mount_points {
|
for (_, target) in mount_points {
|
||||||
self.populate_directory(&mut builder, target.as_path());
|
self.populate_directory(&mut builder, target.as_path())?;
|
||||||
}
|
}
|
||||||
|
builder.flush()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_artwork(&self, dir: &Path) -> Option<String> {
|
fn get_artwork(&self, dir: &Path) -> Option<String> {
|
||||||
|
@ -333,15 +330,12 @@ impl Index {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate_directory(&self, builder: &mut IndexBuilder, path: &Path) {
|
fn populate_directory(&self, builder: &mut IndexBuilder, path: &Path) -> Result<()> {
|
||||||
|
|
||||||
// Find artwork
|
// Find artwork
|
||||||
let artwork = self.get_artwork(path);
|
let artwork = self.get_artwork(path);
|
||||||
|
|
||||||
let path_string = match path.to_str() {
|
let path_string = path.to_str().ok_or("Invalid directory path")?;
|
||||||
Some(p) => p,
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut directory_album = None;
|
let mut directory_album = None;
|
||||||
let mut directory_year = None;
|
let mut directory_year = None;
|
||||||
|
@ -359,7 +353,7 @@ impl Index {
|
||||||
};
|
};
|
||||||
|
|
||||||
if file_path.is_dir() {
|
if file_path.is_dir() {
|
||||||
self.populate_directory(builder, file_path.as_path());
|
self.populate_directory(builder, file_path.as_path())?;
|
||||||
} else {
|
} else {
|
||||||
if let Some(file_path_string) = file_path.to_str() {
|
if let Some(file_path_string) = file_path.to_str() {
|
||||||
if let Ok(tags) = metadata::read(file_path.as_path()) {
|
if let Ok(tags) = metadata::read(file_path.as_path()) {
|
||||||
|
@ -372,19 +366,18 @@ impl Index {
|
||||||
if tags.album.is_some() {
|
if tags.album.is_some() {
|
||||||
inconsistent_directory_album |= directory_album.is_some() &&
|
inconsistent_directory_album |= directory_album.is_some() &&
|
||||||
directory_album != tags.album;
|
directory_album != tags.album;
|
||||||
directory_album = Some(tags.album.as_ref().unwrap().clone());
|
directory_album = tags.album.as_ref().map(|a| a.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if tags.album_artist.is_some() {
|
if tags.album_artist.is_some() {
|
||||||
inconsistent_directory_artist |= directory_artist.is_some() &&
|
inconsistent_directory_artist |= directory_artist.is_some() &&
|
||||||
directory_artist !=
|
directory_artist !=
|
||||||
tags.album_artist;
|
tags.album_artist;
|
||||||
directory_artist =
|
directory_artist = tags.album_artist.as_ref().map(|a| a.clone());
|
||||||
Some(tags.album_artist.as_ref().unwrap().clone());
|
|
||||||
} else if tags.artist.is_some() {
|
} else if tags.artist.is_some() {
|
||||||
inconsistent_directory_artist |= directory_artist.is_some() &&
|
inconsistent_directory_artist |= directory_artist.is_some() &&
|
||||||
directory_artist != tags.artist;
|
directory_artist != tags.artist;
|
||||||
directory_artist = Some(tags.artist.as_ref().unwrap().clone());
|
directory_artist = tags.artist.as_ref().map(|a| a.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let song = Song {
|
let song = Song {
|
||||||
|
@ -399,7 +392,7 @@ impl Index {
|
||||||
artwork: artwork.as_ref().map(|s| s.to_owned()),
|
artwork: artwork.as_ref().map(|s| s.to_owned()),
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.push(CollectionFile::Song(song));
|
builder.push(CollectionFile::Song(song))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -424,47 +417,55 @@ impl Index {
|
||||||
artist: directory_artist,
|
artist: directory_artist,
|
||||||
year: directory_year,
|
year: directory_year,
|
||||||
};
|
};
|
||||||
builder.push(CollectionFile::Directory(directory));
|
builder.push(CollectionFile::Directory(directory))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&self) {
|
pub fn run(&self) {
|
||||||
loop {
|
loop {
|
||||||
{
|
if let Ok(db) = self.connect() {
|
||||||
let db = self.connect();
|
if let Err(e) = self.update_index(&db) {
|
||||||
self.update_index(&db);
|
println!("Error while updating index: {}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
thread::sleep(time::Duration::from_secs(self.sleep_duration));
|
thread::sleep(time::Duration::from_secs(self.sleep_duration));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_songs(&self, select: &mut Statement) -> Vec<Song> {
|
fn select_songs(&self, select: &mut Statement) -> Result<Vec<Song>> {
|
||||||
|
|
||||||
let mut output = Vec::new();
|
let mut output = Vec::new();
|
||||||
|
|
||||||
while let State::Row = select.next().unwrap() {
|
while let State::Row = select.next()? {
|
||||||
|
|
||||||
let song_path: String = select.read(0).unwrap();
|
let song_path: String = select.read(0)?;
|
||||||
let disc_number: Value = select.read(1).unwrap();
|
let disc_number: Value = select.read(1)?;
|
||||||
let track_number: Value = select.read(2).unwrap();
|
let track_number: Value = select.read(2)?;
|
||||||
let title: Value = select.read(3).unwrap();
|
let title: Value = select.read(3)?;
|
||||||
let year: Value = select.read(4).unwrap();
|
let year: Value = select.read(4)?;
|
||||||
let album_artist: Value = select.read(5).unwrap();
|
let album_artist: Value = select.read(5)?;
|
||||||
let artist: Value = select.read(6).unwrap();
|
let artist: Value = select.read(6)?;
|
||||||
let album: Value = select.read(7).unwrap();
|
let album: Value = select.read(7)?;
|
||||||
let artwork: Value = select.read(8).unwrap();
|
let artwork: Value = select.read(8)?;
|
||||||
|
|
||||||
let song_path = Path::new(song_path.as_str());
|
let song_path = Path::new(song_path.as_str());
|
||||||
let song_path = match self.vfs.real_to_virtual(song_path) {
|
let song_path = match self.vfs.real_to_virtual(song_path) {
|
||||||
Ok(p) => p,
|
Ok(p) => p.to_str().ok_or("Invalid song path")?.to_owned(),
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let artwork = artwork.as_string()
|
let artwork_path = artwork.as_string().map(|p| Path::new(p));
|
||||||
.map(|p| Path::new(p))
|
let mut artwork = None;
|
||||||
.and_then(|p| self.vfs.real_to_virtual(p).ok());
|
if let Some(artwork_path) = artwork_path {
|
||||||
|
artwork = match self.vfs.real_to_virtual(artwork_path) {
|
||||||
|
Ok(p) => Some(p.to_str().ok_or("Invalid song artwork path")?.to_owned()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let song = Song {
|
let song = Song {
|
||||||
path: song_path.to_str().unwrap().to_owned(),
|
path: song_path,
|
||||||
disc_number: disc_number.as_integer().map(|n| n as u32),
|
disc_number: disc_number.as_integer().map(|n| n as u32),
|
||||||
track_number: track_number.as_integer().map(|n| n as u32),
|
track_number: track_number.as_integer().map(|n| n as u32),
|
||||||
title: title.as_string().map(|s| s.to_owned()),
|
title: title.as_string().map(|s| s.to_owned()),
|
||||||
|
@ -472,47 +473,51 @@ impl Index {
|
||||||
album_artist: album_artist.as_string().map(|s| s.to_owned()),
|
album_artist: album_artist.as_string().map(|s| s.to_owned()),
|
||||||
artist: artist.as_string().map(|s| s.to_owned()),
|
artist: artist.as_string().map(|s| s.to_owned()),
|
||||||
album: album.as_string().map(|s| s.to_owned()),
|
album: album.as_string().map(|s| s.to_owned()),
|
||||||
artwork: artwork.map(|p| p.to_str().unwrap().to_owned()),
|
artwork: artwork,
|
||||||
};
|
};
|
||||||
output.push(song);
|
output.push(song);
|
||||||
}
|
}
|
||||||
|
|
||||||
output
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List sub-directories within a directory
|
// List sub-directories within a directory
|
||||||
fn browse_directories(&self, real_path: &Path) -> Vec<CollectionFile> {
|
fn browse_directories(&self, real_path: &Path) -> Result<Vec<CollectionFile>> {
|
||||||
let db = self.connect();
|
let db = self.connect()?;
|
||||||
let mut output = Vec::new();
|
let mut output = Vec::new();
|
||||||
|
|
||||||
let path_string = real_path.to_string_lossy();
|
let path_string = real_path.to_string_lossy();
|
||||||
let mut select =
|
let mut select =
|
||||||
db.prepare("SELECT path, artwork, year, artist, album FROM directories WHERE \
|
db.prepare("SELECT path, artwork, year, artist, album FROM directories WHERE \
|
||||||
parent = ? ORDER BY path COLLATE NOCASE ASC")
|
parent = ? ORDER BY path COLLATE NOCASE ASC")?;
|
||||||
.unwrap();
|
select.bind(1, &Value::String(path_string.deref().to_owned()))?;
|
||||||
select.bind(1, &Value::String(path_string.deref().to_owned())).unwrap();
|
|
||||||
|
|
||||||
while let State::Row = select.next().unwrap() {
|
while let State::Row = select.next()? {
|
||||||
|
|
||||||
let directory_value: String = select.read(0).unwrap();
|
let directory_value: String = select.read(0)?;
|
||||||
let artwork_path: Value = select.read(1).unwrap();
|
let artwork_path: Value = select.read(1)?;
|
||||||
let year: Value = select.read(2).unwrap();
|
let year: Value = select.read(2)?;
|
||||||
let artist: Value = select.read(3).unwrap();
|
let artist: Value = select.read(3)?;
|
||||||
let album: Value = select.read(4).unwrap();
|
let album: Value = select.read(4)?;
|
||||||
|
|
||||||
let directory_path = Path::new(directory_value.as_str());
|
let directory_path = Path::new(directory_value.as_str());
|
||||||
let directory_path = match self.vfs.real_to_virtual(directory_path) {
|
let directory_path = match self.vfs.real_to_virtual(directory_path) {
|
||||||
Ok(p) => p,
|
Ok(p) => p.to_str().ok_or("Invalid directory path")?.to_owned(),
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let artwork_path = artwork_path.as_string()
|
let artwork_path = artwork_path.as_string().map(|p| Path::new(p));
|
||||||
.map(|p| Path::new(p))
|
let mut artwork = None;
|
||||||
.and_then(|p| self.vfs.real_to_virtual(p).ok());
|
if let Some(artwork_path) = artwork_path {
|
||||||
|
artwork = match self.vfs.real_to_virtual(artwork_path) {
|
||||||
|
Ok(p) => Some(p.to_str().ok_or("Invalid directory artwork path")?.to_owned()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let directory = Directory {
|
let directory = Directory {
|
||||||
path: directory_path.to_str().unwrap().to_owned(),
|
path: directory_path,
|
||||||
artwork: artwork_path.map(|p| p.to_str().unwrap().to_owned()),
|
artwork: artwork,
|
||||||
year: year.as_integer().map(|n| n as i32),
|
year: year.as_integer().map(|n| n as i32),
|
||||||
artist: artist.as_string().map(|s| s.to_owned()),
|
artist: artist.as_string().map(|s| s.to_owned()),
|
||||||
album: album.as_string().map(|s| s.to_owned()),
|
album: album.as_string().map(|s| s.to_owned()),
|
||||||
|
@ -520,20 +525,19 @@ impl Index {
|
||||||
output.push(CollectionFile::Directory(directory));
|
output.push(CollectionFile::Directory(directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
output
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List songs within a directory
|
// List songs within a directory
|
||||||
fn browse_songs(&self, real_path: &Path) -> Vec<CollectionFile> {
|
fn browse_songs(&self, real_path: &Path) -> Result<Vec<CollectionFile>> {
|
||||||
let db = self.connect();
|
let db = self.connect()?;
|
||||||
let path_string = real_path.to_string_lossy();
|
let path_string = real_path.to_string_lossy();
|
||||||
let mut select =
|
let mut select =
|
||||||
db.prepare("SELECT path, disc_number, track_number, title, year, album_artist, \
|
db.prepare("SELECT path, disc_number, track_number, title, year, album_artist, \
|
||||||
artist, album, artwork FROM songs WHERE parent = ? ORDER BY path \
|
artist, album, artwork FROM songs WHERE parent = ? ORDER BY path \
|
||||||
COLLATE NOCASE ASC")
|
COLLATE NOCASE ASC")?;
|
||||||
.unwrap();
|
select.bind(1, &Value::String(path_string.deref().to_owned()))?;
|
||||||
select.bind(1, &Value::String(path_string.deref().to_owned())).unwrap();
|
Ok(self.select_songs(&mut select)?.into_iter().map(|s| CollectionFile::Song(s)).collect())
|
||||||
self.select_songs(&mut select).into_iter().map(|s| CollectionFile::Song(s)).collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn browse(&self, virtual_path: &Path) -> Result<Vec<CollectionFile>> {
|
pub fn browse(&self, virtual_path: &Path) -> Result<Vec<CollectionFile>> {
|
||||||
|
@ -556,8 +560,8 @@ impl Index {
|
||||||
// Browse sub-directory
|
// Browse sub-directory
|
||||||
} else {
|
} else {
|
||||||
let real_path = self.vfs.virtual_to_real(virtual_path)?;
|
let real_path = self.vfs.virtual_to_real(virtual_path)?;
|
||||||
let directories = self.browse_directories(real_path.as_path());
|
let directories = self.browse_directories(real_path.as_path())?;
|
||||||
let songs = self.browse_songs(real_path.as_path());
|
let songs = self.browse_songs(real_path.as_path())?;
|
||||||
output.extend(directories);
|
output.extend(directories);
|
||||||
output.extend(songs);
|
output.extend(songs);
|
||||||
}
|
}
|
||||||
|
@ -566,15 +570,14 @@ impl Index {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flatten(&self, virtual_path: &Path) -> Result<Vec<Song>> {
|
pub fn flatten(&self, virtual_path: &Path) -> Result<Vec<Song>> {
|
||||||
let db = self.connect();
|
let db = self.connect()?;
|
||||||
let real_path = self.vfs.virtual_to_real(virtual_path)?;
|
let real_path = self.vfs.virtual_to_real(virtual_path)?;
|
||||||
let path_string = real_path.to_string_lossy().into_owned() + "%";
|
let path_string = real_path.to_string_lossy().into_owned() + "%";
|
||||||
let mut select =
|
let mut select =
|
||||||
db.prepare("SELECT path, disc_number, track_number, title, year, album_artist, \
|
db.prepare("SELECT path, disc_number, track_number, title, year, album_artist, \
|
||||||
artist, album, artwork FROM songs WHERE path LIKE ? ORDER BY path \
|
artist, album, artwork FROM songs WHERE path LIKE ? ORDER BY path \
|
||||||
COLLATE NOCASE ASC")
|
COLLATE NOCASE ASC")?;
|
||||||
.unwrap();
|
select.bind(1, &Value::String(path_string.deref().to_owned()))?;
|
||||||
select.bind(1, &Value::String(path_string.deref().to_owned())).unwrap();
|
self.select_songs(&mut select)
|
||||||
Ok(self.select_songs(&mut select))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue