local: Split out library reading and writting

- Create $XDG_DATA_DIR/mopidy/local in the local extension's validate env.
- Make sure we handle bad data causing ValueError in JSON decoding
- Initializing empty file causes more harm than good as it just leads to a
  ValueError. Switched to doing write_library(json_file, {})
- Helpers have been updated to be library oriented, not track. This paves the
  way for having {tracks: {uri: ...}, artist: {uri: ...}, ...} type
  denormalized data.
This commit is contained in:
Thomas Adamcik 2013-12-04 21:33:04 +01:00
parent 9c2d38e989
commit ad53a067ae
3 changed files with 46 additions and 35 deletions

View File

@ -1,9 +1,13 @@
from __future__ import unicode_literals
import logging
import os
import mopidy
from mopidy import config, ext
from mopidy.utils import encoding, path
logger = logging.getLogger('mopidy.backends.local')
class Extension(ext.Extension):
@ -27,7 +31,11 @@ class Extension(ext.Extension):
return schema
def validate_environment(self):
pass
try:
path.get_or_create_dir(b'$XDG_DATA_DIR/mopidy/local')
except EnvironmentError as error:
error = encoding.locale_decode(error)
logger.warning('Could not create local data dir: %s', error)
def get_backend_classes(self):
from .actor import LocalBackend

View File

@ -1,13 +1,14 @@
from __future__ import unicode_literals
import logging
import os
import pykka
from mopidy.backends import base
from mopidy.utils import encoding, path
from mopidy.utils import encoding
from .library import LocalJsonLibraryProvider
from . import library
logger = logging.getLogger('mopidy.backends.local.json')
@ -17,14 +18,13 @@ class LocalJsonBackend(pykka.ThreadingActor, base.Backend):
super(LocalJsonBackend, self).__init__()
self.config = config
self.check_dirs_and_files()
self.library = LocalJsonLibraryProvider(backend=self)
self.library = library.LocalJsonLibraryProvider(backend=self)
self.uri_schemes = ['local']
def check_dirs_and_files(self):
try:
path.get_or_create_file(self.config['local-json']['json_file'])
except EnvironmentError as error:
logger.warning(
'Could not create empty json file: %s',
encoding.locale_decode(error))
if not os.path.exists(config['local-json']['json_file']):
try:
library.write_library(config['local-json']['json_file'], {})
logger.info('Created empty local JSON library.')
except EnvironmentError as error:
error = encoding.locale_decode(error)
logger.warning('Could not create local library: %s', error)

View File

@ -14,13 +14,31 @@ from mopidy.backends.local import search
logger = logging.getLogger('mopidy.backends.local.json')
def _load_tracks(json_file):
def load_library(json_file):
try:
with gzip.open(json_file, 'rb') as fp:
result = json.load(fp, object_hook=models.model_json_decoder)
except IOError:
return []
return result.get('tracks', [])
return json.load(fp, object_hook=models.model_json_decoder)
except (IOError, ValueError) as e:
logger.warning('Loading JSON local library failed: %s', e)
return {}
def write_library(json_file, data):
data['version'] = mopidy.__version__
directory, basename = os.path.split(json_file)
# TODO: cleanup directory/basename.* files.
tmp = tempfile.NamedTemporaryFile(
prefix=basename + '.', dir=directory, delete=False)
try:
with gzip.GzipFile(fileobj=tmp, mode='wb') as fp:
json.dump(data, fp, cls=models.ModelJSONEncoder,
indent=2, separators=(',', ': '))
os.rename(tmp.name, json_file)
finally:
if os.path.exists(tmp.name):
os.remove(tmp.name)
class LocalJsonLibraryProvider(base.BaseLibraryProvider):
@ -36,7 +54,7 @@ class LocalJsonLibraryProvider(base.BaseLibraryProvider):
'Loading local tracks from %s using %s',
self._media_dir, self._json_file)
tracks = _load_tracks(self._json_file)
tracks = load_library(self._json_file).get('tracks', [])
uris_to_remove = set(self._uri_mapping)
for track in tracks:
@ -75,7 +93,7 @@ class LocalJsonLibraryUpdateProvider(base.BaseLibraryProvider):
self._json_file = config['local-json']['json_file']
def load(self):
for track in _load_tracks(self._json_file):
for track in load_library(self._json_file).get('tracks', []):
self._tracks[track.uri] = track
return self._tracks.values()
@ -87,19 +105,4 @@ class LocalJsonLibraryUpdateProvider(base.BaseLibraryProvider):
del self._tracks[uri]
def commit(self):
directory, basename = os.path.split(self._json_file)
# TODO: cleanup directory/basename.* files.
tmp = tempfile.NamedTemporaryFile(
prefix=basename + '.', dir=directory, delete=False)
try:
with gzip.GzipFile(fileobj=tmp, mode='wb') as fp:
data = {'version': mopidy.__version__,
'tracks': self._tracks.values()}
json.dump(data, fp, cls=models.ModelJSONEncoder,
indent=2, separators=(',', ': '))
os.rename(tmp.name, self._json_file)
finally:
if os.path.exists(tmp.name):
os.remove(tmp.name)
write_library(self._json_file, {'tracks': self._tracks.values()})