summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs13
-rw-r--r--src/cmd/sync.rs5
-rw-r--r--src/lastfm/mod.rs49
-rw-r--r--src/main.rs1
-rw-r--r--src/paths.rs7
5 files changed, 55 insertions, 20 deletions
diff --git a/src/cli.rs b/src/cli.rs
index d296fea..0c140e7 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -7,7 +7,6 @@ pub enum Command {
pub struct Options {
pub command: Command,
pub username: Option<String>,
- pub api_key: Option<String>,
}
pub fn get_options() -> failure::Fallible<Options> {
@@ -17,20 +16,9 @@ pub fn get_options() -> failure::Fallible<Options> {
.about("Updates the local copy of track data from last.fm")
.arg(
clap::Arg::with_name("username")
- .short("u")
- .long("username")
- .value_name("USERNAME")
.required(true)
.help("last.fm username to fetch tracks for")
)
- .arg(
- clap::Arg::with_name("api-key")
- .short("k")
- .long("api-key")
- .value_name("API_KEY")
- .required(true)
- .help("last.fm api key")
- )
)
.get_matches();
@@ -43,6 +31,5 @@ pub fn get_options() -> failure::Fallible<Options> {
Ok(Options {
command: command,
username: sub_matches.value_of("username").map(|s| s.to_string()),
- api_key: sub_matches.value_of("api-key").map(|s| s.to_string()),
})
}
diff --git a/src/cmd/sync.rs b/src/cmd/sync.rs
index 65b48b7..9dfb11f 100644
--- a/src/cmd/sync.rs
+++ b/src/cmd/sync.rs
@@ -4,11 +4,10 @@ use lastfm;
use paths;
pub fn run(opts: &cli::Options) -> failure::Fallible<()> {
- let db = db::DB::new(&paths::dbpath()?)?;
+ let db = db::DB::new(&paths::db_path()?)?;
let lastfm = lastfm::LastFMClient::new(
- opts.api_key.as_ref().unwrap(),
opts.username.as_ref().unwrap()
- );
+ )?;
let from = db.most_recent_timestamp()?.map(|x| x + 1);
let to_fetch = lastfm.track_count(from)?;
diff --git a/src/lastfm/mod.rs b/src/lastfm/mod.rs
index fc2eb5b..b5888cc 100644
--- a/src/lastfm/mod.rs
+++ b/src/lastfm/mod.rs
@@ -1,3 +1,8 @@
+use paths;
+
+use failure::Fail;
+use std::io::{Read, Write};
+
mod api_types;
const API_ROOT: &'static str = "https://ws.audioscrobbler.com/2.0/";
@@ -98,12 +103,12 @@ impl<'a> Iterator for Tracks<'a> {
}
impl LastFMClient {
- pub fn new(api_key: &str, user: &str) -> LastFMClient {
- LastFMClient {
+ pub fn new(user: &str) -> failure::Fallible<LastFMClient> {
+ Ok(LastFMClient {
client: reqwest::Client::new(),
- api_key: api_key.to_string(),
+ api_key: find_api_key()?,
user: user.to_string(),
- }
+ })
}
pub fn track_count(&self, from: Option<i64>) -> failure::Fallible<u64> {
@@ -151,3 +156,39 @@ impl LastFMClient {
}
}
}
+
+fn find_api_key() -> failure::Fallible<String> {
+ let api_key_path = paths::api_key_path()
+ .map_err(|e| e.context("failed to determine api key path"))?;
+ let api_key = if api_key_path.exists() {
+ let mut api_key = String::new();
+ let mut f = std::fs::File::open(&api_key_path)
+ .map_err(|e| {
+ e.context(format!("failed to open {}", api_key_path.display()))
+ })?;
+ f.read_to_string(&mut api_key)
+ .map_err(|e| {
+ e.context(format!("failed to read from {}", api_key_path.display()))
+ })?;
+ api_key
+ }
+ else {
+ let api_key = rpassword::prompt_password_stderr(
+ &format!(
+ "last.fm api key (will be stored in {}): ",
+ api_key_path.display()
+ )
+ )?;
+ std::fs::create_dir_all(api_key_path.parent().unwrap())?;
+ let mut f = std::fs::File::create(&api_key_path)
+ .map_err(|e| {
+ e.context(format!("failed to open {}", api_key_path.display()))
+ })?;
+ f.write_all(api_key.as_bytes())
+ .map_err(|e| {
+ e.context(format!("failed to write to {}", api_key_path.display()))
+ })?;
+ api_key
+ };
+ Ok(api_key.trim_end().to_string())
+}
diff --git a/src/main.rs b/src/main.rs
index 2518e55..d7d80fd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,6 +5,7 @@ extern crate directories;
extern crate failure;
extern crate indicatif;
extern crate reqwest;
+extern crate rpassword;
extern crate rusqlite;
extern crate serde;
extern crate serde_json;
diff --git a/src/paths.rs b/src/paths.rs
index a3f1eb8..badb783 100644
--- a/src/paths.rs
+++ b/src/paths.rs
@@ -4,3 +4,10 @@ pub fn db_path() -> failure::Fallible<std::path::PathBuf> {
.data_dir()
.join("tracks.sqlite"))
}
+
+pub fn api_key_path() -> failure::Fallible<std::path::PathBuf> {
+ Ok(directories::ProjectDirs::from("", "", "lastfm-query")
+ .ok_or_else(|| failure::err_msg("couldn't determine config directory"))?
+ .config_dir()
+ .join("lastfm-api-key"))
+}