Implement indexing for remote sources

This commit is contained in:
Agatha Lovelace 2022-09-24 14:15:51 +02:00
parent 644403c2f3
commit ad29628af7
Signed by: sorceress
GPG Key ID: 11BBCFC65FC9F401
6 changed files with 316 additions and 16 deletions

220
Cargo.lock generated
View File

@ -560,6 +560,8 @@ dependencies = [
"mime",
"mime_guess",
"paris",
"reqwest",
"rmp-serde",
"sea-orm",
"sea-orm-migration",
"sea-query",
@ -622,6 +624,12 @@ dependencies = [
"spin",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
@ -801,6 +809,25 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "h2"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -861,6 +888,77 @@ dependencies = [
"digest",
]
[[package]]
name = "http"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-body"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
dependencies = [
"bytes",
"http",
"pin-project-lite",
]
[[package]]
name = "httparse"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
[[package]]
name = "httpdate"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "hyper"
version = "0.14.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]]
name = "idna"
version = "0.2.3"
@ -891,6 +989,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "ipnet"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
[[package]]
name = "is_ci"
version = "1.1.1"
@ -1563,6 +1667,65 @@ dependencies = [
"winapi",
]
[[package]]
name = "reqwest"
version = "0.11.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"hyper",
"hyper-tls",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-native-tls",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
]
[[package]]
name = "rmp"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f"
dependencies = [
"byteorder",
"num-traits",
"paste",
]
[[package]]
name = "rmp-serde"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25786b0d276110195fa3d6f3f31299900cf71dfbd6c28450f3f58a0e7f7a347e"
dependencies = [
"byteorder",
"rmp",
"serde",
]
[[package]]
name = "rust_decimal"
version = "1.25.0"
@ -1829,6 +1992,18 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sha2"
version = "0.10.2"
@ -2368,6 +2543,20 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
"tracing",
]
[[package]]
name = "toml"
version = "0.5.9"
@ -2377,6 +2566,12 @@ dependencies = [
"serde",
]
[[package]]
name = "tower-service"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tracing"
version = "0.1.36"
@ -2440,6 +2635,12 @@ dependencies = [
"tracing-log",
]
[[package]]
name = "try-lock"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "typenum"
version = "1.15.0"
@ -2570,6 +2771,16 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "want"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
dependencies = [
"log",
"try-lock",
]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
@ -2740,3 +2951,12 @@ name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]

View File

@ -14,6 +14,8 @@ miette = { version = "5.2.0", features = ["fancy"] }
mime = "0.3.16"
mime_guess = "2.0.4"
paris = { version = "1.5.13", features = ["macros"] }
reqwest = "0.11.12"
rmp-serde = "1.1.0"
sea-orm = { version = "0.9.1", features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros"] }
sea-orm-migration = "^0.9.0"
sea-query = "0.26.2"

View File

@ -5,14 +5,17 @@ use std::{
path::Path,
};
use crate::backend::utils::get_auth_source;
use super::{
config::{Config, Source, SourceKind},
model::library,
model::{library, library::Column},
};
use adler::Adler32;
use lofty::{read_from_path, Accessor, AudioFile};
use miette::{miette, IntoDiagnostic, Result};
use paris::{success, warn};
use reqwest::Client;
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, QuerySelect, Set};
use symphonia::{
core::{
@ -116,7 +119,7 @@ pub async fn index_source(source: Source, mode: IndexMode, db: &DatabaseConnecti
library::Entity::insert(song)
.on_conflict(
sea_query::OnConflict::column(library::Column::Filename)
sea_query::OnConflict::column(Column::Hash)
.do_nothing()
.to_owned(),
)
@ -126,7 +129,54 @@ pub async fn index_source(source: Source, mode: IndexMode, db: &DatabaseConnecti
}
}
SourceKind::Remote { address } => {
unimplemented!();
let (username, password) = get_auth_source(source.id)?;
let client = Client::new();
let index = client
.get(format!("{address}/"))
.basic_auth(username, Some(password))
.send()
.await
.into_diagnostic()?
.bytes()
.await
.into_diagnostic()?;
// Deserialize messagepack into a library model
let parsed: Vec<library::Model> = rmp_serde::from_slice(&index).into_diagnostic()?;
// Use all fields except for id and source_id
let songs: Vec<_> = parsed
.into_iter()
.map(|v| {
return library::ActiveModel {
path: Set(v.path),
filename: Set(v.filename),
source_id: Set(source.id.into()), // Use local source id, not remote
hash: Set(v.hash),
artist: Set(v.artist),
album_artist: Set(v.album_artist),
name: Set(v.name),
album: Set(v.album),
genres: Set(v.genres),
track: Set(v.track),
year: Set(v.year),
duration: Set(v.duration),
..Default::default()
};
})
.collect();
library::Entity::insert_many(songs)
.on_conflict(
sea_query::OnConflict::column(Column::Hash)
.do_nothing()
.to_owned(),
)
.exec(db)
.await
.into_diagnostic()?;
}
}

View File

@ -19,12 +19,7 @@ impl MigrationTrait for Migration {
.primary_key(),
)
.col(ColumnDef::new(Song::Path).string().not_null())
.col(
ColumnDef::new(Song::Filename)
.string()
.not_null()
.unique_key(),
)
.col(ColumnDef::new(Song::Filename).string().not_null())
.col(ColumnDef::new(Song::SourceId).integer().not_null())
.col(ColumnDef::new(Song::Hash).integer().not_null().unique_key())
.col(ColumnDef::new(Song::Artist).string())

View File

@ -12,16 +12,24 @@ use migrator::Migrator;
use paris::success;
use sea_orm_migration::prelude::*;
use self::{config::Config, utils::config_dir};
use self::{
config::Config,
utils::{cache_dir, config_dir},
};
/// Create the necessary files on first run
pub fn create_app_data() -> Result<()> {
let path = config_dir().ok_or(miette!("Configuration directory does not exist"))?;
let config_path = config_dir().ok_or(miette!("Configuration directory does not exist"))?;
// Create Eleanor's config directory
create_dir_all(&path).into_diagnostic()?;
create_dir_all(&config_path).into_diagnostic()?;
File::create(&path.join("eleanor.db")).into_diagnostic()?;
let cache_path = cache_dir().ok_or(miette!("Configuration directory does not exist"))?;
// Create Eleanor's cache directory
create_dir_all(&cache_path).into_diagnostic()?;
File::create(&config_path.join("eleanor.db")).into_diagnostic()?;
Config::write_config(&Default::default())?;
success!("Created configuration file");

View File

@ -1,11 +1,10 @@
use miette::{miette, Result};
use std::path::PathBuf;
use miette::{miette, IntoDiagnostic, Result};
use std::{fs::File, io::Write, path::PathBuf};
pub fn config_dir() -> Option<PathBuf> {
dirs::config_dir().map(|v| v.join("eleanor"))
}
#[allow(dead_code)]
pub fn cache_dir() -> Option<PathBuf> {
dirs::cache_dir().map(|v| v.join("eleanor"))
}
@ -16,3 +15,29 @@ pub fn is_first_run() -> Result<bool> {
Ok(!path.exists())
}
/// Stores credentials for a remote source
pub fn store_auth_source(username: String, password: String, source: u8) -> Result<()> {
let contents = rmp_serde::to_vec(&(username, password)).into_diagnostic()?;
let path = cache_dir()
.ok_or(miette!("Cache directory does not exist"))?
.join(format!("{source}.auth"));
File::create(path)
.and_then(|mut v| v.write_all(&contents))
.into_diagnostic()
}
/// Returns the stored credentials for a remote source
pub fn get_auth_source(source: u8) -> Result<(String, String)> {
let path = cache_dir()
.ok_or(miette!("Cache directory does not exist"))?
.join(format!("{source}.auth"));
let file = std::fs::read(path).into_diagnostic()?;
let contents = rmp_serde::from_slice(&file).into_diagnostic()?;
Ok(contents)
}