|
@ -4,22 +4,67 @@ |
|
|
"""Song management.""" |
|
|
"""Song management.""" |
|
|
|
|
|
|
|
|
from unidecode import unidecode |
|
|
from unidecode import unidecode |
|
|
|
|
|
import errno |
|
|
import hashlib |
|
|
import hashlib |
|
|
import os |
|
|
import os |
|
|
import re |
|
|
import re |
|
|
|
|
|
|
|
|
try: |
|
|
try: |
|
|
import cPickle as pickle |
|
|
import cPickle as pickle |
|
|
except ImportError: |
|
|
except ImportError: |
|
|
import pickle |
|
|
import pickle |
|
|
|
|
|
|
|
|
from patacrep.authors import processauthors |
|
|
from patacrep.authors import processauthors |
|
|
from patacrep.plastex import parsetex |
|
|
from patacrep.plastex import parsetex |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def cached_name(filename): |
|
|
def cached_name(datadir, filename): |
|
|
"""Return the filename of the cache version of the file.""" |
|
|
"""Return the filename of the cache version of the file.""" |
|
|
return filename + ".cache" |
|
|
fullpath = os.path.join(datadir, '.cache', filename) |
|
|
|
|
|
directory = os.path.dirname(fullpath) |
|
|
|
|
|
try: |
|
|
|
|
|
os.makedirs(directory) |
|
|
|
|
|
except OSError as error: |
|
|
|
|
|
if error.errno == errno.EEXIST and os.path.isdir(directory): |
|
|
|
|
|
pass |
|
|
|
|
|
else: |
|
|
|
|
|
raise |
|
|
|
|
|
return fullpath |
|
|
|
|
|
|
|
|
|
|
|
class DataSubpath(object): |
|
|
|
|
|
"""A path divided in two path: a datadir, and its subpath. |
|
|
|
|
|
|
|
|
|
|
|
- This object can represent either a file or directory. |
|
|
|
|
|
- If the datadir part is the empty string, it means that the represented |
|
|
|
|
|
path does not belong to a datadir. |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, datadir, subpath): |
|
|
|
|
|
if os.path.isabs(subpath): |
|
|
|
|
|
self.datadir = "" |
|
|
|
|
|
else: |
|
|
|
|
|
self.datadir = datadir |
|
|
|
|
|
self.subpath = subpath |
|
|
|
|
|
|
|
|
|
|
|
def __str__(self): |
|
|
|
|
|
return os.path.join(self.datadir, self.subpath) |
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
def fullpath(self): |
|
|
|
|
|
"""Return the full path represented by self.""" |
|
|
|
|
|
return os.path.join(self.datadir, self.subpath) |
|
|
|
|
|
|
|
|
|
|
|
def clone(self): |
|
|
|
|
|
"""Return a cloned object.""" |
|
|
|
|
|
return DataSubpath(self.datadir, self.subpath) |
|
|
|
|
|
|
|
|
|
|
|
def join(self, path): |
|
|
|
|
|
"""Join "path" argument to self path. |
|
|
|
|
|
|
|
|
|
|
|
Return self for commodity. |
|
|
|
|
|
""" |
|
|
|
|
|
self.subpath = os.path.join(self.subpath, path) |
|
|
|
|
|
return self |
|
|
|
|
|
|
|
|
# pylint: disable=too-few-public-methods, too-many-instance-attributes |
|
|
# pylint: disable=too-few-public-methods, too-many-instance-attributes |
|
|
class Song(object): |
|
|
class Song(object): |
|
@ -34,28 +79,36 @@ class Song(object): |
|
|
"titles", |
|
|
"titles", |
|
|
"unprefixed_titles", |
|
|
"unprefixed_titles", |
|
|
"args", |
|
|
"args", |
|
|
"path", |
|
|
"datadir", |
|
|
|
|
|
"fullpath", |
|
|
|
|
|
"subpath", |
|
|
"languages", |
|
|
"languages", |
|
|
"authors", |
|
|
"authors", |
|
|
"_filehash", |
|
|
"_filehash", |
|
|
"_version", |
|
|
"_version", |
|
|
] |
|
|
] |
|
|
|
|
|
|
|
|
def __init__(self, filename, config): |
|
|
def __init__(self, datadir, subpath, config): |
|
|
self._filehash = hashlib.md5(open(filename, 'rb').read()).hexdigest() |
|
|
self.fullpath = os.path.join(datadir, subpath) |
|
|
if os.path.exists(cached_name(filename)): |
|
|
if datadir: |
|
|
cached = pickle.load(open(cached_name(filename), 'rb')) |
|
|
# Only songs in datadirs are cached |
|
|
if ( |
|
|
self._filehash = hashlib.md5( |
|
|
cached['_filehash'] == self._filehash |
|
|
open(self.fullpath, 'rb').read() |
|
|
and cached['_version'] == self.CACHE_VERSION |
|
|
).hexdigest() |
|
|
): |
|
|
if os.path.exists(cached_name(datadir, subpath)): |
|
|
for attribute in self.cached_attributes: |
|
|
cached = pickle.load(open(cached_name(datadir, subpath), 'rb')) |
|
|
setattr(self, attribute, cached[attribute]) |
|
|
if ( |
|
|
return |
|
|
cached['_filehash'] == self._filehash |
|
|
|
|
|
and cached['_version'] == self.CACHE_VERSION |
|
|
|
|
|
): |
|
|
|
|
|
for attribute in self.cached_attributes: |
|
|
|
|
|
setattr(self, attribute, cached[attribute]) |
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
# Data extraction from the song with plastex |
|
|
# Data extraction from the song with plastex |
|
|
data = parsetex(filename) |
|
|
data = parsetex(self.fullpath) |
|
|
self.titles = data['titles'] |
|
|
self.titles = data['titles'] |
|
|
|
|
|
self.datadir = datadir |
|
|
self.unprefixed_titles = [ |
|
|
self.unprefixed_titles = [ |
|
|
unprefixed_title( |
|
|
unprefixed_title( |
|
|
unidecode(unicode(title, "utf-8")), |
|
|
unidecode(unicode(title, "utf-8")), |
|
@ -65,7 +118,7 @@ class Song(object): |
|
|
in self.titles |
|
|
in self.titles |
|
|
] |
|
|
] |
|
|
self.args = data['args'] |
|
|
self.args = data['args'] |
|
|
self.path = filename |
|
|
self.subpath = subpath |
|
|
self.languages = data['languages'] |
|
|
self.languages = data['languages'] |
|
|
if "by" in self.args.keys(): |
|
|
if "by" in self.args.keys(): |
|
|
self.authors = processauthors( |
|
|
self.authors = processauthors( |
|
@ -79,14 +132,18 @@ class Song(object): |
|
|
self._write_cache() |
|
|
self._write_cache() |
|
|
|
|
|
|
|
|
def _write_cache(self): |
|
|
def _write_cache(self): |
|
|
"""Write a dumbed down version of self to the cache.""" |
|
|
"""If relevant, write a dumbed down version of self to the cache.""" |
|
|
cached = {} |
|
|
if self.datadir: |
|
|
for attribute in self.cached_attributes: |
|
|
cached = {} |
|
|
cached[attribute] = getattr(self, attribute) |
|
|
for attribute in self.cached_attributes: |
|
|
pickle.dump(cached, open(cached_name(self.path), 'wb')) |
|
|
cached[attribute] = getattr(self, attribute) |
|
|
|
|
|
pickle.dump( |
|
|
|
|
|
cached, |
|
|
|
|
|
open(cached_name(self.datadir, self.subpath), 'wb'), |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
def __repr__(self): |
|
|
def __repr__(self): |
|
|
return repr((self.titles, self.args, self.path)) |
|
|
return repr((self.titles, self.args, self.fullpath)) |
|
|
|
|
|
|
|
|
def unprefixed_title(title, prefixes): |
|
|
def unprefixed_title(title, prefixes): |
|
|
"""Remove the first prefix of the list in the beginning of title (if any). |
|
|
"""Remove the first prefix of the list in the beginning of title (if any). |
|
|