summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xfeeding_example.py10
-rw-r--r--mpdrast/cache.py82
-rw-r--r--[-rwxr-xr-x]mpdrast/client.py46
3 files changed, 114 insertions, 24 deletions
diff --git a/feeding_example.py b/feeding_example.py
index 404af27..d9bde2b 100755
--- a/feeding_example.py
+++ b/feeding_example.py
@@ -12,10 +12,14 @@ m.connect_from_env(mpd_host, mpd_port)
m.wait_for_update()
# get all "final" directories
-m.update_final_dirs("")
+nb_dirs = len(m.get_final_dirs())
+print "We have found %s albums." % nb_dirs
# if the playlist is empty, add one random album
-if m.is_playlist_hungry(1):
- m.add(m.get_random_dir())
+while m.is_playlist_hungry(100) and nb_dirs:
+ rdir = m.get_random_dir()
+ m.add(rdir)
+ print "Added %s" % rdir
print m.status()
+print m.stats()
diff --git a/mpdrast/cache.py b/mpdrast/cache.py
new file mode 100644
index 0000000..67ddc0f
--- /dev/null
+++ b/mpdrast/cache.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+
+import pickle
+from os.path import join, expanduser, exists, getmtime, isdir
+from os import makedirs
+from hashlib import sha1
+
+USER_DIR = join(expanduser('~'), '.mpdrast')
+
+def cache(name):
+ def decorator(fn):
+ def wrapper(*args, **kwargs):
+ _updated = kwargs["_updated"]
+ del kwargs["_updated"]
+
+ if kwargs.has_key("_hash"):
+ _hash = kwargs["_hash"]
+ del kwargs["_hash"]
+ else:
+ _hash = uhash()
+
+ cache_dir = join(USER_DIR, "cache", name)
+ if not isdir(cache_dir):
+ makedirs(cache_dir)
+
+ cache_file = join(cache_dir, _hash)
+ if not exists(cache_file) or \
+ getmtime(cache_file) < _updated:
+ data = fn(*args, **kwargs)
+ with open(cache_file, 'w') as handle:
+ pickle.dump(data, handle)
+ else:
+ with open(cache_file, 'r') as handle:
+ data = pickle.load(handle)
+
+ return data
+
+ return wrapper
+
+ return decorator
+
+def uhash(*args):
+ return sha1(pickle.dumps(args)).hexdigest()
+
+def test():
+ from tempfile import mkdtemp
+ from time import time
+
+ # TODO we should clean up after, also… it's kinda ugly
+ globals()["USER_DIR"] = mkdtemp(prefix='mpdrast')
+ print "Testing in %s." % USER_DIR
+
+ we_should_be_here = False
+ testval = "plop"
+
+ @cache("test")
+ def f1():
+ assert we_should_be_here
+ return testval
+
+ @cache("test2")
+ def f2(testparam):
+ assert we_should_be_here
+ return testparam
+
+ # Handling of the updated parameter
+ we_should_be_here = True
+ assert "plop" == f1(_updated=1)
+ we_should_be_here = False
+ testval = "plop2"
+ assert "plop" == f1(_updated=1)
+ we_should_be_here = True
+ assert "plop2" == f1(_updated=time()+42)
+
+ # Different cache for different parameters
+ we_should_be_here = True
+ assert "plap" == f2(_updated=1, _hash=uhash("plap"), testparam="plap")
+ we_should_be_here = False
+ assert "plap" == f2(_updated=1, _hash=uhash("plap"), testparam="plap")
+ we_should_be_here = True
+ assert "plip" == f2(_updated=1, _hash=uhash("plip"), testparam="plip")
+
diff --git a/mpdrast/client.py b/mpdrast/client.py
index fbf471a..974350c 100755..100644
--- a/mpdrast/client.py
+++ b/mpdrast/client.py
@@ -4,11 +4,11 @@ import time
import random
import mpdrast.process as process
+from mpdrast.cache import cache, uhash
class MPDrastClient(mpd.MPDClient):
def __init__(self):
mpd.MPDClient.__init__(self)
- self.final_dirs = []
def connect_from_env(self, host, port):
@@ -34,34 +34,38 @@ class MPDrastClient(mpd.MPDClient):
self.password(password)
- def update_final_dirs(self, path="", first=True):
+ def get_final_dirs(self, root=""):
"""
- Compute a list of directory containing only files.
+ Get a list of directory containing only files.
They can be considered as full albums. Remember that MPD indexes
only music; if a directory has subdirectories of non-music files,
it will not prevent the directory from being added (which is good).
- If first is True, the function considers that it represents the root,
- and will clear the existing list of "final dirs". If not, it will
- append to the list. The function is recursive, hence this paratemer.
- There should be no need to call it with first=False manually.
+ The root path can be any subdirectory of the database, any
+ directory not in the path will be ignored.
- The root path can be any subdirectory of the database, any directory
- not in the path will be ignored.
+ The list is cached, it will be repopulated after a database update.
"""
- if first:
- self.final_dirs = []
+ _updated = int(self.stats()["db_update"])
+ _hash = uhash(root)
+ return self._get_final_dirs(_updated=_updated, _hash=_hash, root=root)
- items = self.lsinfo(path)
- if first and not items:
- raise Exception("database is empty")
- files, dirs = process.get_files_and_dirs_from_db(items)
- if len(files) and len(dirs) == 0:
- self.final_dirs.append(path)
- else:
- for dir in dirs:
- self.update_final_dirs(dir, False)
+ @cache("final_dirs")
+ def _get_final_dirs(self, root):
+ final_dirs = []
+ def update_final_dirs(path=""):
+ items = self.lsinfo(path)
+
+ files, directories = process.get_files_and_dirs_from_db(items)
+ if len(files) and len(directories) == 0:
+ final_dirs.append(path)
+ else:
+ for directory in directories:
+ update_final_dirs(directory)
+
+ update_final_dirs(root)
+ return final_dirs
def wait_for_update(self):
@@ -73,7 +77,7 @@ class MPDrastClient(mpd.MPDClient):
def get_random_dir(self):
- return random.choice(self.final_dirs)
+ return random.choice(self.get_final_dirs())
def is_playlist_hungry(self, hungriness=100):