Skip to content

Commit

Permalink
Prepare v0.1.0.
Browse files Browse the repository at this point in the history
  • Loading branch information
tkem committed Apr 7, 2015
1 parent 245b94a commit 5a37a03
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 61 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Expand Up @@ -5,4 +5,4 @@
.tox/
MANIFEST
build/
dist/
dist/
2 changes: 1 addition & 1 deletion CHANGES.rst
@@ -1,4 +1,4 @@
0.1.0 UNRELEASED
0.1.0 2015-04-07
----------------

- Initial release.
45 changes: 32 additions & 13 deletions README.rst
Expand Up @@ -2,32 +2,40 @@ Mopidy-dLeyna
========================================================================

Mopidy-dLeyna is a Mopidy_ extension that lets you play music from
Digital Media Servers.
DLNA_ Digital Media Servers using the dLeyna_ D-Bus interface.

This project is in a *very* early prototyping, so you better stay away
from it for now...

Dependencies
------------------------------------------------------------------------

- D-Bus Python bindings, such as the package ``python-dbus`` in
Ubuntu/Debian.

- The ``dleyna-server`` package available in Ubuntu 14.04 and Debian
"jessie". For other platforms, please see the dLeyna `installation
instructions <https://github.com/01org/dleyna-server>`.


Installation
============
------------------------------------------------------------------------

Mopidy-Local-dLeyna can be installed using pip_ by running::
Mopidy-dLeyna can be installed using pip_ by running::

pip install Mopidy-Local-dLeyna
pip install Mopidy-dLeyna


Configuration
=============
------------------------------------------------------------------------

Before starting Mopidy, you must add configuration for Mopidy-dLeyna
to your Mopidy configuration file::
Note that configuration settings for this extension are still subject
to change::

[dleyna]
# TODO: Add example of extension config
[dleyna]
enabled = true


Project resources
=================
------------------------------------------------------------------------

.. image:: https://img.shields.io/pypi/v/Mopidy-dLeyna.svg?style=flat
:target: https://pypi.python.org/pypi/Mopidy-dLeyna/
Expand Down Expand Up @@ -61,10 +69,18 @@ Licensed under the `Apache License, Version 2.0`_.
Known Bugs and Limitations
------------------------------------------------------------------------

TBD.
Currently, only the browse interface is implemented, so searching will
return no results from Digital Media Servers.

Browsing through this extension may crash at least some versions of
minidlna_ (a.k.a ReadyMedia, the NAS daemon provided with many NETGEAR
routers). The reasons for this, and possible workarounds, remain to
be investigated.


.. _Mopidy: http://www.mopidy.com/
.. _DLNA: http://www.dlna.org/
.. _dLeyna: http://01.org/dleyna

.. _pip: https://pip.pypa.io/en/latest/

Expand All @@ -73,3 +89,6 @@ TBD.
.. _Change Log: https://github.com/tkem/mopidy-dleyna/blob/master/CHANGES.rst

.. _Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0


.. _minidlna: http://sourceforge.net/projects/minidlna/
16 changes: 15 additions & 1 deletion mopidy_dleyna/__init__.py
Expand Up @@ -3,8 +3,9 @@
import logging
import os

from mopidy import config, ext
from mopidy import config, exceptions, ext

from .dleyna import MANAGER_IFACE, SERVER_BUS_NAME, SERVER_ROOT_PATH

__version__ = '0.1.0'

Expand All @@ -28,3 +29,16 @@ def get_config_schema(self):
def setup(self, registry):
from .backend import dLeynaBackend
registry.add('backend', dLeynaBackend)

def validate_environment(self):
try:
import dbus
except ImportError as e:
raise exceptions.ExtensionError('dbus library not found', e)
try:
bus = dbus.SessionBus()
obj = bus.get_object(SERVER_BUS_NAME, SERVER_ROOT_PATH)
mgr = dbus.Interface(obj, MANAGER_IFACE)
logger.info('Running dleyna-server %s', mgr.GetVersion())
except Exception as e:
raise exceptions.ExtensionError('dleyna-server not found', e)
6 changes: 4 additions & 2 deletions mopidy_dleyna/backend.py
@@ -1,11 +1,12 @@
from __future__ import unicode_literals

import pykka

from mopidy import backend

import pykka

from . import Extension
from .library import dLeynaLibraryProvider
from .playback import dLeynaPlaybackProvider


class dLeynaBackend(pykka.ThreadingActor, backend.Backend):
Expand All @@ -15,3 +16,4 @@ class dLeynaBackend(pykka.ThreadingActor, backend.Backend):
def __init__(self, config, audio):
super(dLeynaBackend, self).__init__()
self.library = dLeynaLibraryProvider(config, self)
self.playback = dLeynaPlaybackProvider(audio, self)
11 changes: 11 additions & 0 deletions mopidy_dleyna/dleyna.py
@@ -0,0 +1,11 @@
from __future__ import absolute_import, unicode_literals

MANAGER_IFACE = 'com.intel.dLeynaServer.Manager'

MEDIA_CONTAINER_IFACE = 'org.gnome.UPnP.MediaContainer2'

MEDIA_ITEM_IFACE = 'org.gnome.UPnP.MediaItem2'

SERVER_BUS_NAME = 'com.intel.dleyna-server'

SERVER_ROOT_PATH = '/com/intel/dLeynaServer'
95 changes: 60 additions & 35 deletions mopidy_dleyna/library.py
@@ -1,58 +1,83 @@
from __future__ import unicode_literals

import dbus
import logging

import dbus

from mopidy import backend
from mopidy.models import Ref
from mopidy.models import Album, Artist, Ref, Track

from .dleyna import MANAGER_IFACE, MEDIA_CONTAINER_IFACE
from .dleyna import SERVER_BUS_NAME, SERVER_ROOT_PATH

logger = logging.getLogger(__name__)

_BROWSE_FILTER = ['DisplayName', 'Path', 'Type']


def _item_to_track(props):
return Track(
name=props.get('DisplayName'),
uri='dleyna:'+props.get('Path'),
artists=[Artist(name=props.get('Artist'))],
album=Album(name=props.get('Album')),
date=props.get('Date'),
genre=props.get('Genre'),
length=int(props.get('Duration', 0)*1000),
track_no=props.get('TrackNumber')
)


class dLeynaLibraryProvider(backend.LibraryProvider):

root_directory = Ref.directory(
uri='dleyna:',
name='DLNA Servers'
name='DLNA Media Servers'
)

def __init__(self, config, backend):
super(dLeynaLibraryProvider, self).__init__(backend)
bus = dbus.SessionBus()
self.__manager = dbus.Interface(
bus.get_object(
'com.intel.dleyna-server',
'/com/intel/dLeynaServer'
),
'com.intel.dLeynaServer.Manager'
)
self._propsIF = dbus.Interface(bus.get_object(
'com.intel.dleyna-server',
'/com/intel/dLeynaServer'),
'org.freedesktop.DBus.Properties')

logger.info('manager %r' % self.__manager)
self.__bus = dbus.SessionBus()

def browse(self, uri):
bus = dbus.SessionBus()
_, _, path = uri.partition(':')

refs = []
if uri == 'dleyna:':
for path in self.__manager.GetServers():
logger.info('server: %r' % path)
if path:
container = self.__get_object(path, MEDIA_CONTAINER_IFACE)
for obj in container.ListChildren(0, 0, _BROWSE_FILTER):
name = obj.get('DisplayName', obj['Path'])
uri = 'dleyna:' + obj['Path']
if obj['Type'] == 'container':
# TODO: album, playlist, ...
refs.append(Ref.directory(name=name, uri=uri))
else:
refs.append(Ref.track(name=name, uri=uri))
else:
manager = self.__get_object(SERVER_ROOT_PATH, MANAGER_IFACE)
for path in manager.GetServers():
props = self.__get_object(path, dbus.PROPERTIES_IFACE)
try:
props = dbus.Interface(
bus.get_object(
'com.intel.dleyna-server',
path
),
'org.freedesktop.DBus.Properties'
)
try:
name = props.Get('', 'FriendlyName')
except Exception:
name = props.Get('', 'DisplayName')
refs.append(Ref.directory(name=name, uri='dleyna:'+path))
except dbus.exceptions.DBusException as e:
logger.error('error %r' % e)
name = props.Get('', 'FriendlyName')
except Exception:
name = props.Get('', 'DisplayName')
refs.append(Ref.directory(name=name, uri='dleyna:'+path))
return refs

def lookup(self, uri):
_, _, path = uri.partition(':')
props = self.__get_object(path, dbus.PROPERTIES_IFACE)
if props.Get('', 'Type') == 'music':
return [_item_to_track(props.GetAll(''))]
else:
return [] # TODO: lookup containers, etc.

def refresh(self, uri=None):
self.__get_object(SERVER_ROOT_PATH, MANAGER_IFACE).Rescan()

def __get_object(self, path, iface=None):
obj = self.__bus.get_object(SERVER_BUS_NAME, path)
if iface:
return dbus.Interface(obj, iface)
else:
return obj
21 changes: 21 additions & 0 deletions mopidy_dleyna/playback.py
@@ -0,0 +1,21 @@
from __future__ import unicode_literals

import dbus

from mopidy import backend

from .dleyna import MEDIA_ITEM_IFACE, SERVER_BUS_NAME


class dLeynaPlaybackProvider(backend.PlaybackProvider):

def __init__(self, audio, backend):
super(dLeynaPlaybackProvider, self).__init__(audio, backend)
self.__bus = dbus.SessionBus()

def translate_uri(self, uri):
_, _, path = uri.partition(':')
obj = self.__bus.get_object(SERVER_BUS_NAME, path)
props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
urls = props.Get(MEDIA_ITEM_IFACE, 'URLs')
return urls[0]
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -18,7 +18,7 @@ def get_version(filename):
license='Apache License, Version 2.0',
author='Thomas Kemmer',
author_email='tkemmer@computer.org',
description='Mopidy extension for playing music from Digital Media Servers',
description='Mopidy extension for playing music from Digital Media Servers', # noqa
long_description=open('README.rst').read(),
packages=find_packages(exclude=['tests', 'tests.*']),
zip_safe=False,
Expand Down
8 changes: 1 addition & 7 deletions tests/test_extension.py
Expand Up @@ -5,21 +5,15 @@

def test_get_default_config():
ext = Extension()

config = ext.get_default_config()

assert '[dleyna]' in config
assert 'enabled = true' in config


def test_get_config_schema():
ext = Extension()

schema = ext.get_config_schema()

# TODO Test the content of your config schema
#assert 'username' in schema
#assert 'password' in schema
assert 'enabled' in schema


# TODO Write more tests

0 comments on commit 5a37a03

Please sign in to comment.