Implement indexing for remote sources
This commit is contained in:
parent
644403c2f3
commit
ad29628af7
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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()?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue