Add --disable-audio-cache startup parameter (#204)

Disable caching of downloaded audio files at runtime. Comes in handy when running librespot on a small device with SD card or other small storage.
This commit is contained in:
michaelherger 2017-06-30 08:43:11 +02:00 committed by Paul Liétar
parent c070f6b0db
commit 67deb07250
3 changed files with 13 additions and 108 deletions

View file

@ -1,101 +0,0 @@
use std::path::PathBuf;
use std::io::Read;
use std::fs::File;
use util::{SpotifyId, FileId, ReadSeek, mkdir_existing};
use authentication::Credentials;
use audio_key::AudioKey;
use super::Cache;
pub struct DefaultCache {
environment: lmdb::Environment,
root: PathBuf,
}
impl DefaultCache {
pub fn new(location: PathBuf) -> Result<DefaultCache, ()> {
let env = lmdb::EnvBuilder::new().max_dbs(5).open(&location.join("db"), 0o755).unwrap();
mkdir_existing(&location).unwrap();
mkdir_existing(&location.join("files")).unwrap();
Ok(DefaultCache {
environment: env,
root: location
})
}
fn audio_keys(&self) -> MdbResult<lmdb::DbHandle> {
self.environment.create_db("audio-keys", lmdb::DbFlags::empty())
}
fn file_path(&self, file: FileId) -> PathBuf {
let name = file.to_base16();
self.root.join("files").join(&name[0..2]).join(&name[2..])
}
fn credentials_path(&self) -> PathBuf {
self.root.join("credentials.json")
}
}
impl Cache for DefaultCache {
fn get_audio_key(&self, track: SpotifyId, file: FileId) -> Option<AudioKey> {
let reader = self.environment.get_reader().unwrap();
let handle = self.audio_keys().unwrap();
let db = reader.bind(&handle);
let mut key = Vec::new();
key.extend_from_slice(&track.to_raw());
key.extend_from_slice(&file.0);
let value : Option<Vec<_>> = db.get(&key).ok();
value.and_then(|value| if value.len() == 16 {
let mut result = [0u8; 16];
result.clone_from_slice(&value);
Some(AudioKey(result))
} else {
None
})
}
fn put_audio_key(&self, track: SpotifyId, file: FileId, audio_key: AudioKey) {
let xact = self.environment.new_transaction().unwrap();
let handle = self.audio_keys().unwrap();
{
let db = xact.bind(&handle);
let mut key = Vec::new();
key.extend_from_slice(&track.to_raw());
key.extend_from_slice(&file.0);
db.set(&key, &audio_key.0.as_ref()).unwrap();
}
xact.commit().unwrap();
}
fn get_credentials(&self) -> Option<Credentials> {
let path = self.credentials_path();
Credentials::from_file(path)
}
fn put_credentials(&self, cred: &Credentials) {
let path = self.credentials_path();
cred.save_to_file(&path);
}
fn get_file(&self, file: FileId) -> Option<Box<ReadSeek>> {
File::open(self.file_path(file)).ok().map(|f| Box::new(f) as Box<ReadSeek>)
}
fn put_file(&self, file: FileId, contents: &mut Read) {
let path = self.file_path(file);
mkdir_existing(path.parent().unwrap()).unwrap();
let mut cache_file = File::create(path).unwrap();
::std::io::copy(contents, &mut cache_file).unwrap();
}
}

8
src/cache/mod.rs vendored
View file

@ -8,15 +8,17 @@ use authentication::Credentials;
#[derive(Clone)] #[derive(Clone)]
pub struct Cache { pub struct Cache {
root: PathBuf, root: PathBuf,
use_audio_cache: bool,
} }
impl Cache { impl Cache {
pub fn new(location: PathBuf) -> Cache { pub fn new(location: PathBuf, use_audio_cache: bool) -> Cache {
mkdir_existing(&location).unwrap(); mkdir_existing(&location).unwrap();
mkdir_existing(&location.join("files")).unwrap(); mkdir_existing(&location.join("files")).unwrap();
Cache { Cache {
root: location root: location,
use_audio_cache: use_audio_cache
} }
} }
} }
@ -48,6 +50,7 @@ impl Cache {
} }
pub fn save_file(&self, file: FileId, contents: &mut Read) { pub fn save_file(&self, file: FileId, contents: &mut Read) {
if self.use_audio_cache {
let path = self.file_path(file); let path = self.file_path(file);
mkdir_existing(path.parent().unwrap()).unwrap(); mkdir_existing(path.parent().unwrap()).unwrap();
@ -55,4 +58,5 @@ impl Cache {
let mut cache_file = File::create(path).unwrap(); let mut cache_file = File::create(path).unwrap();
::std::io::copy(contents, &mut cache_file).unwrap(); ::std::io::copy(contents, &mut cache_file).unwrap();
} }
}
} }

View file

@ -86,6 +86,7 @@ struct Setup {
fn setup(args: &[String]) -> Setup { fn setup(args: &[String]) -> Setup {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE") opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
.optflag("", "disable-audio-cache", "Disable caching of the audio data.")
.reqopt("n", "name", "Device name", "NAME") .reqopt("n", "name", "Device name", "NAME")
.optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE") .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE")
.optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM") .optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
@ -133,9 +134,10 @@ fn setup(args: &[String]) -> Setup {
let name = matches.opt_str("name").unwrap(); let name = matches.opt_str("name").unwrap();
let device_id = librespot::session::device_id(&name); let device_id = librespot::session::device_id(&name);
let use_audio_cache = !matches.opt_present("disable-audio-cache");
let cache = matches.opt_str("c").map(|cache_location| { let cache = matches.opt_str("c").map(|cache_location| {
Cache::new(PathBuf::from(cache_location)) Cache::new(PathBuf::from(cache_location), use_audio_cache)
}); });
let cached_credentials = cache.as_ref().and_then(Cache::credentials); let cached_credentials = cache.as_ref().and_then(Cache::credentials);