Initial version of plugin, based on https://github.com/DarkAllMan/SubKodi.
This commit is contained in:
parent
ff86dfa49f
commit
2c4ac7b4c8
|
@ -0,0 +1,414 @@
|
|||
import os
|
||||
import sys
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
import xbmcaddon
|
||||
import xbmcplugin
|
||||
|
||||
# Make sure library folder is on the path
|
||||
addon = xbmcaddon.Addon("plugin.audio.subsonic")
|
||||
sys.path.append(xbmc.translatePath(os.path.join(
|
||||
addon.getAddonInfo("path"), "lib")))
|
||||
|
||||
import libsonic_extra
|
||||
|
||||
|
||||
class Plugin(object):
|
||||
"""
|
||||
Plugin container
|
||||
"""
|
||||
|
||||
def __init__(self, addon_url, addon_handle, addon_args):
|
||||
self.addon_url = addon_url
|
||||
self.addon_handle = addon_handle
|
||||
self.addon_args = addon_args
|
||||
|
||||
# Retrieve plugin settings
|
||||
self.url = addon.getSetting("subsonic_url")
|
||||
self.username = addon.getSetting("username")
|
||||
self.password = addon.getSetting("password")
|
||||
|
||||
self.random_count = addon.getSetting("random_count")
|
||||
self.bitrate = addon.getSetting("bitrate")
|
||||
self.trans_format = addon.getSetting("trans_format")
|
||||
|
||||
# Create connection
|
||||
self.connection = libsonic_extra.Connection(
|
||||
self.url, self.username, self.password)
|
||||
|
||||
def build_url(self, query):
|
||||
"""
|
||||
"""
|
||||
|
||||
parts = list(urlparse.urlparse(self.addon_url))
|
||||
parts[4] = urllib.urlencode(query)
|
||||
|
||||
return urlparse.urlunparse(parts)
|
||||
|
||||
def walk_genres(self):
|
||||
"""
|
||||
Request Subsonic's genres list and iterate each item.
|
||||
"""
|
||||
|
||||
response = self.connection.getGenres()
|
||||
|
||||
for genre in response["genres"]["genre"]:
|
||||
yield genre
|
||||
|
||||
def walk_artists(self):
|
||||
"""
|
||||
Request SubSonic's index and iterate each item.
|
||||
"""
|
||||
|
||||
response = self.connection.getArtists()
|
||||
|
||||
for index in response["artists"]["index"]:
|
||||
for artist in index["artist"]:
|
||||
yield artist
|
||||
|
||||
def walk_album_list2_genre(self, genre):
|
||||
"""
|
||||
"""
|
||||
|
||||
offset = 0
|
||||
|
||||
while True:
|
||||
response = self.connection.getAlbumList2(
|
||||
ltype="byGenre", genre=genre, size=500, offset=offset)
|
||||
|
||||
if not response["albumList2"]["album"]:
|
||||
break
|
||||
|
||||
for album in response["albumList2"]["album"]:
|
||||
yield album
|
||||
|
||||
offset += 500
|
||||
|
||||
def walk_album(self, album_id):
|
||||
"""
|
||||
Request Album and iterate each song.
|
||||
"""
|
||||
|
||||
response = self.connection.getAlbum(album_id)
|
||||
|
||||
for song in response["album"]["song"]:
|
||||
yield song
|
||||
|
||||
def walk_playlists(self):
|
||||
"""
|
||||
Request SubSonic's playlists and iterate over each item.
|
||||
"""
|
||||
|
||||
response = self.connection.getPlaylists()
|
||||
|
||||
for child in response["playlists"]["playlist"]:
|
||||
yield child
|
||||
|
||||
def walk_playlist(self, playlist_id):
|
||||
"""
|
||||
Request SubSonic's playlist items and iterate over each item.
|
||||
"""
|
||||
|
||||
response = self.connection.getPlaylist(playlist_id)
|
||||
|
||||
for order, child in enumerate(response["playlist"]["entry"], start=1):
|
||||
child["order"] = order
|
||||
yield child
|
||||
|
||||
def walk_directory(self, directory_id):
|
||||
"""
|
||||
Request a SubSonic music directory and iterate over each item.
|
||||
"""
|
||||
|
||||
response = self.connection.getMusicDirectory(directory_id)
|
||||
|
||||
for child in response["directory"]["child"]:
|
||||
if child.get("isDir"):
|
||||
for child in self.walk_directory(child["id"]):
|
||||
yield child
|
||||
else:
|
||||
yield child
|
||||
|
||||
def walk_artist(self, artist_id):
|
||||
"""
|
||||
Request a SubSonic artist and iterate over each album.
|
||||
"""
|
||||
|
||||
response = self.connection.getArtist(artist_id)
|
||||
|
||||
for child in response["artist"]["album"]:
|
||||
yield child
|
||||
|
||||
def walk_random_songs(self, size, genre=None, from_year=None,
|
||||
to_year=None):
|
||||
"""
|
||||
"""
|
||||
|
||||
response = self.connection.getRandomSongs(
|
||||
size=size, genre=genre, fromYear=from_year, toYear=to_year)
|
||||
|
||||
for song in response["randomSongs"]["song"]:
|
||||
song["id"] = int(song["id"])
|
||||
|
||||
yield song
|
||||
|
||||
def route(self):
|
||||
mode = self.addon_args.get("mode", ["main_page"])[0]
|
||||
getattr(self, mode)()
|
||||
|
||||
def add_track(self, track, show_artist=False):
|
||||
"""
|
||||
"""
|
||||
|
||||
cover_art_url = self.connection.getCoverArtUrl(track["id"])
|
||||
url = self.connection.streamUrl(
|
||||
sid=track["id"], maxBitRate=self.bitrate,
|
||||
tformat=self.trans_format)
|
||||
|
||||
if show_artist:
|
||||
li = xbmcgui.ListItem(track["artist"] + " - " + track["title"])
|
||||
else:
|
||||
li = xbmcgui.ListItem(track["title"])
|
||||
|
||||
li.setIconImage(cover_art_url)
|
||||
li.setThumbnailImage(cover_art_url)
|
||||
li.setProperty("fanart_image", cover_art_url)
|
||||
li.setProperty("IsPlayable", "true")
|
||||
li.setInfo(type="Music", infoLabels={
|
||||
"Artist": track["artist"],
|
||||
"Title": track["title"],
|
||||
"Year": track.get("year"),
|
||||
"Duration": track.get("duration"),
|
||||
"Genre": track.get("genre")})
|
||||
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=self.addon_handle, url=url, listitem=li)
|
||||
|
||||
def add_album(self, album, show_artist=False):
|
||||
cover_art_url = self.connection.getCoverArtUrl(album["id"])
|
||||
url = self.build_url({
|
||||
"mode": "track_list",
|
||||
"album_id": album["id"]})
|
||||
|
||||
if show_artist:
|
||||
li = xbmcgui.ListItem(album["artist"] + " - " + album["name"])
|
||||
else:
|
||||
li = xbmcgui.ListItem(album["name"])
|
||||
|
||||
li.setIconImage(cover_art_url)
|
||||
li.setThumbnailImage(cover_art_url)
|
||||
li.setProperty("fanart_image", cover_art_url)
|
||||
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
|
||||
|
||||
def main_page(self):
|
||||
"""
|
||||
Display main menu.
|
||||
"""
|
||||
|
||||
menu = [
|
||||
{"mode": "playlists_list", "foldername": "Playlists"},
|
||||
{"mode": "artist_list", "foldername": "Artists"},
|
||||
{"mode": "genre_list", "foldername": "Genres"},
|
||||
{"mode": "random_list", "foldername": "Random songs"}]
|
||||
|
||||
for entry in menu:
|
||||
url = self.build_url(entry)
|
||||
|
||||
li = xbmcgui.ListItem(entry["foldername"])
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
|
||||
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
def playlists_list(self):
|
||||
"""
|
||||
Display playlists.
|
||||
"""
|
||||
|
||||
for playlist in self.walk_playlists():
|
||||
cover_art_url = self.connection.getCoverArtUrl(
|
||||
playlist["coverArt"])
|
||||
url = self.build_url({
|
||||
"mode": "playlist_list", "playlist_id": playlist["id"]})
|
||||
|
||||
li = xbmcgui.ListItem(playlist["name"], iconImage=cover_art_url)
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
|
||||
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
def playlist_list(self):
|
||||
"""
|
||||
Display playlist tracks.
|
||||
"""
|
||||
|
||||
playlist_id = self.addon_args["playlist_id"][0]
|
||||
|
||||
for track in self.walk_playlist(playlist_id):
|
||||
self.add_track(track, show_artist=True)
|
||||
|
||||
xbmcplugin.setContent(self.addon_handle, "songs")
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
def genre_list(self):
|
||||
"""
|
||||
Display list of genres menu.
|
||||
"""
|
||||
|
||||
for genre in self.walk_genres():
|
||||
url = self.build_url({
|
||||
"mode": "albums_by_genre_list",
|
||||
"foldername": genre["value"].encode("utf-8")})
|
||||
|
||||
li = xbmcgui.ListItem(genre["value"])
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
|
||||
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
def albums_by_genre_list(self):
|
||||
"""
|
||||
Display album list by genre menu.
|
||||
"""
|
||||
|
||||
genre = self.addon_args["foldername"][0].decode("utf-8")
|
||||
|
||||
for album in self.walk_album_list2_genre(genre):
|
||||
self.add_album(album, show_artist=True)
|
||||
|
||||
xbmcplugin.setContent(self.addon_handle, "albums")
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
def artist_list(self):
|
||||
"""
|
||||
Display artist list
|
||||
"""
|
||||
|
||||
for artist in self.walk_artists():
|
||||
cover_art_url = self.connection.getCoverArtUrl(artist["id"])
|
||||
url = self.build_url({
|
||||
"mode": "album_list",
|
||||
"artist_id": artist["id"]})
|
||||
|
||||
li = xbmcgui.ListItem(artist["name"])
|
||||
li.setIconImage(cover_art_url)
|
||||
li.setThumbnailImage(cover_art_url)
|
||||
li.setProperty("fanart_image", cover_art_url)
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
|
||||
|
||||
xbmcplugin.setContent(self.addon_handle, "artists")
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
def album_list(self):
|
||||
"""
|
||||
Display list of albums for certain artist.
|
||||
"""
|
||||
|
||||
artist_id = self.addon_args["artist_id"][0]
|
||||
|
||||
for album in self.walk_artist(artist_id):
|
||||
self.add_album(album)
|
||||
|
||||
xbmcplugin.setContent(self.addon_handle, "albums")
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
def track_list(self):
|
||||
"""
|
||||
Display track list.
|
||||
"""
|
||||
|
||||
album_id = self.addon_args["album_id"][0]
|
||||
|
||||
for track in self.walk_album(album_id):
|
||||
self.add_track(track)
|
||||
|
||||
xbmcplugin.setContent(self.addon_handle, "songs")
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
def random_list(self):
|
||||
"""
|
||||
Display random options.
|
||||
"""
|
||||
|
||||
menu = [
|
||||
{"mode": "random_by_genre_list", "foldername": "By genre"},
|
||||
{"mode": "random_by_year_list", "foldername": "By year"}]
|
||||
|
||||
for entry in menu:
|
||||
url = self.build_url(entry)
|
||||
|
||||
li = xbmcgui.ListItem(entry["foldername"])
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
|
||||
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
def random_by_genre_list(self):
|
||||
"""
|
||||
Display random genre list.
|
||||
"""
|
||||
|
||||
for genre in self.walk_genres():
|
||||
url = self.build_url({
|
||||
"mode": "random_by_genre_track_list",
|
||||
"foldername": genre["value"].encode("utf-8")})
|
||||
|
||||
li = xbmcgui.ListItem(genre["value"])
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
|
||||
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
def random_by_genre_track_list(self):
|
||||
"""
|
||||
Display random tracks by genre
|
||||
"""
|
||||
|
||||
genre = self.addon_args["foldername"][0].decode("utf-8")
|
||||
|
||||
for track in self.walk_random_songs(
|
||||
size=self.random_count, genre=genre):
|
||||
self.add_track(track, show_artist=True)
|
||||
|
||||
xbmcplugin.setContent(self.addon_handle, "songs")
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
def random_by_year_list(self):
|
||||
"""
|
||||
Display random tracks by year.
|
||||
"""
|
||||
|
||||
from_year = xbmcgui.Dialog().input(
|
||||
"From year", type=xbmcgui.INPUT_NUMERIC)
|
||||
to_year = xbmcgui.Dialog().input(
|
||||
"To year", type=xbmcgui.INPUT_NUMERIC)
|
||||
|
||||
for track in self.walk_random_songs(
|
||||
size=self.random_count, from_year=from_year, to_year=to_year):
|
||||
self.add_track(track, show_artist=True)
|
||||
|
||||
xbmcplugin.setContent(self.addon_handle, "songs")
|
||||
xbmcplugin.endOfDirectory(self.addon_handle)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Entry point for this plugin.
|
||||
"""
|
||||
|
||||
addon_url = sys.argv[0]
|
||||
addon_handle = int(sys.argv[1])
|
||||
addon_args = urlparse.parse_qs(sys.argv[2][1:])
|
||||
|
||||
# Route request to action
|
||||
Plugin(addon_url, addon_handle, addon_args).route()
|
||||
|
||||
# Start plugin from Kodi
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<addon id="plugin.audio.subsonic" name="Subsonic" version="1.0.0" provider-name="xsteadfastx">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="2.19.0"/>
|
||||
</requires>
|
||||
<extension point="xbmc.python.pluginsource" library="addon.py">
|
||||
<provides>audio</provides>
|
||||
</extension>
|
||||
<extension point="xbmc.addon.metadata">
|
||||
<summary lang="en">Subsonic music addon for Kodi.</summary>
|
||||
<description lang="en">Subsonic music addon for Kodi.</description>
|
||||
<disclaimer lang="en"></disclaimer>
|
||||
<language></language>
|
||||
<platform>all</platform>
|
||||
<license>MIT</license>
|
||||
<forum></forum>
|
||||
<website></website>
|
||||
<email></email>
|
||||
<source></source>
|
||||
</extension>
|
||||
</addon>
|
Binary file not shown.
After Width: | Height: | Size: 117 KiB |
|
@ -0,0 +1,32 @@
|
|||
"""
|
||||
This file is part of py-sonic.
|
||||
|
||||
py-sonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
py-sonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with py-sonic. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
For information on method calls, see 'pydoc libsonic.connection'
|
||||
|
||||
----------
|
||||
Basic example:
|
||||
----------
|
||||
|
||||
import libsonic
|
||||
|
||||
conn = libsonic.Connection('http://localhost' , 'admin' , 'password')
|
||||
print conn.ping()
|
||||
|
||||
"""
|
||||
|
||||
from connection import *
|
||||
|
||||
__version__ = '0.3.3'
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,59 @@
|
|||
"""
|
||||
This file is part of py-sonic.
|
||||
|
||||
py-sonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
py-sonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with py-sonic. If not, see <http://www.gnu.org/licenses/>
|
||||
"""
|
||||
|
||||
class SonicError(Exception):
|
||||
pass
|
||||
|
||||
class ParameterError(SonicError):
|
||||
pass
|
||||
|
||||
class VersionError(SonicError):
|
||||
pass
|
||||
|
||||
class CredentialError(SonicError):
|
||||
pass
|
||||
|
||||
class AuthError(SonicError):
|
||||
pass
|
||||
|
||||
class LicenseError(SonicError):
|
||||
pass
|
||||
|
||||
class DataNotFoundError(SonicError):
|
||||
pass
|
||||
|
||||
class ArgumentError(SonicError):
|
||||
pass
|
||||
|
||||
# This maps the error code numbers from the Subsonic server to their
|
||||
# appropriate Exceptions
|
||||
ERR_CODE_MAP = {
|
||||
0: SonicError ,
|
||||
10: ParameterError ,
|
||||
20: VersionError ,
|
||||
30: VersionError ,
|
||||
40: CredentialError ,
|
||||
50: AuthError ,
|
||||
60: LicenseError ,
|
||||
70: DataNotFoundError ,
|
||||
}
|
||||
|
||||
def getExcByCode(code):
|
||||
code = int(code)
|
||||
if code in ERR_CODE_MAP:
|
||||
return ERR_CODE_MAP[code]
|
||||
return SonicError
|
|
@ -0,0 +1,231 @@
|
|||
import urllib
|
||||
import urlparse
|
||||
import libsonic
|
||||
|
||||
|
||||
def force_dict(value):
|
||||
"""
|
||||
Coerce the input value to a dict.
|
||||
"""
|
||||
|
||||
if type(value) == dict:
|
||||
return value
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
def force_list(value):
|
||||
"""
|
||||
Coerce the input value to a list.
|
||||
|
||||
If `value` is `None`, return an empty list. If it is a single value, create
|
||||
a new list with that element on index 0.
|
||||
|
||||
:param value: Input value to coerce.
|
||||
:return: Value as list.
|
||||
:rtype: list
|
||||
"""
|
||||
|
||||
if value is None:
|
||||
return []
|
||||
elif type(value) == list:
|
||||
return value
|
||||
else:
|
||||
return [value]
|
||||
|
||||
|
||||
class Connection(libsonic.Connection):
|
||||
"""
|
||||
Extend `libsonic.Connection` with new features and fix a few issues.
|
||||
|
||||
- Add library name property.
|
||||
- Parse URL for host and port for constructor.
|
||||
- Make sure API results are of of uniform type.
|
||||
|
||||
:param str name: Name of connection.
|
||||
:param str url: Full URL (including protocol) of SubSonic server.
|
||||
:param str username: Username of server.
|
||||
:param str password: Password of server.
|
||||
"""
|
||||
|
||||
def __init__(self, url, username, password):
|
||||
self._intercept_url = False
|
||||
|
||||
# Parse SubSonic URL
|
||||
parts = urlparse.urlparse(url)
|
||||
scheme = parts.scheme or "http"
|
||||
|
||||
# Make sure there is hostname
|
||||
if not parts.hostname:
|
||||
raise ValueError("Expected hostname for URL: %s" % url)
|
||||
|
||||
# Validate scheme
|
||||
if scheme not in ("http", "https"):
|
||||
raise ValueError("Unexpected scheme '%s' for URL: %s" % (
|
||||
scheme, url))
|
||||
|
||||
# Pick a default port
|
||||
host = "%s://%s" % (scheme, parts.hostname)
|
||||
port = parts.port or {"http": 80, "https": 443}[scheme]
|
||||
|
||||
# Invoke original constructor
|
||||
super(Connection, self).__init__(host, username, password, port=port)
|
||||
|
||||
def getArtists(self, *args, **kwargs):
|
||||
"""
|
||||
"""
|
||||
|
||||
def _artists_iterator(artists):
|
||||
for artist in force_list(artists):
|
||||
artist["id"] = int(artist["id"])
|
||||
yield artist
|
||||
|
||||
def _index_iterator(index):
|
||||
for index in force_list(index):
|
||||
index["artist"] = list(_artists_iterator(index.get("artist")))
|
||||
yield index
|
||||
|
||||
response = super(Connection, self).getArtists(*args, **kwargs)
|
||||
response["artists"] = response.get("artists", {})
|
||||
response["artists"]["index"] = list(
|
||||
_index_iterator(response["artists"].get("index")))
|
||||
|
||||
return response
|
||||
|
||||
def getPlaylists(self, *args, **kwargs):
|
||||
"""
|
||||
"""
|
||||
|
||||
def _playlists_iterator(playlists):
|
||||
for playlist in force_list(playlists):
|
||||
playlist["id"] = int(playlist["id"])
|
||||
yield playlist
|
||||
|
||||
response = super(Connection, self).getPlaylists(*args, **kwargs)
|
||||
response["playlists"]["playlist"] = list(
|
||||
_playlists_iterator(response["playlists"].get("playlist")))
|
||||
|
||||
return response
|
||||
|
||||
def getPlaylist(self, *args, **kwargs):
|
||||
"""
|
||||
"""
|
||||
|
||||
def _entries_iterator(entries):
|
||||
for entry in force_list(entries):
|
||||
entry["id"] = int(entry["id"])
|
||||
yield entry
|
||||
|
||||
response = super(Connection, self).getPlaylist(*args, **kwargs)
|
||||
response["playlist"]["entry"] = list(
|
||||
_entries_iterator(response["playlist"].get("entry")))
|
||||
|
||||
return response
|
||||
|
||||
def getArtist(self, *args, **kwargs):
|
||||
"""
|
||||
"""
|
||||
|
||||
def _albums_iterator(albums):
|
||||
for album in force_list(albums):
|
||||
album["id"] = int(album["id"])
|
||||
yield album
|
||||
|
||||
response = super(Connection, self).getArtist(*args, **kwargs)
|
||||
response["artist"]["album"] = list(
|
||||
_albums_iterator(response["artist"].get("album")))
|
||||
|
||||
return response
|
||||
|
||||
def getAlbum(self, *args, **kwargs):
|
||||
""
|
||||
""
|
||||
|
||||
def _songs_iterator(songs):
|
||||
for song in force_list(songs):
|
||||
song["id"] = int(song["id"])
|
||||
yield song
|
||||
|
||||
response = super(Connection, self).getAlbum(*args, **kwargs)
|
||||
response["album"]["song"] = list(
|
||||
_songs_iterator(response["album"].get("song")))
|
||||
|
||||
return response
|
||||
|
||||
def getAlbumList2(self, *args, **kwargs):
|
||||
""
|
||||
""
|
||||
|
||||
def _album_iterator(albums):
|
||||
for album in force_list(albums):
|
||||
album["id"] = int(album["id"])
|
||||
yield album
|
||||
|
||||
response = super(Connection, self).getAlbumList2(*args, **kwargs)
|
||||
response["albumList2"]["album"] = list(
|
||||
_album_iterator(response["albumList2"].get("album")))
|
||||
|
||||
return response
|
||||
|
||||
def getMusicDirectory(self, *args, **kwargs):
|
||||
"""
|
||||
"""
|
||||
|
||||
def _children_iterator(children):
|
||||
for child in force_list(children):
|
||||
child["id"] = int(child["id"])
|
||||
|
||||
if "parent" in child:
|
||||
child["parent"] = int(child["parent"])
|
||||
if "coverArt" in child:
|
||||
child["coverArt"] = int(child["coverArt"])
|
||||
if "artistId" in child:
|
||||
child["artistId"] = int(child["artistId"])
|
||||
if "albumId" in child:
|
||||
child["albumId"] = int(child["albumId"])
|
||||
|
||||
yield child
|
||||
|
||||
response = super(Connection, self).getMusicDirectory(*args, **kwargs)
|
||||
response["directory"]["child"] = list(
|
||||
_children_iterator(response["directory"].get("child")))
|
||||
|
||||
return response
|
||||
|
||||
def getCoverArtUrl(self, *args, **kwargs):
|
||||
"""
|
||||
Return an URL to the cover art.
|
||||
"""
|
||||
|
||||
self._intercept_url = True
|
||||
url = self.getCoverArt(*args, **kwargs)
|
||||
self._intercept_url = False
|
||||
|
||||
return url
|
||||
|
||||
def streamUrl(self, *args, **kwargs):
|
||||
"""
|
||||
Return an URL to the file to stream.
|
||||
"""
|
||||
|
||||
self._intercept_url = True
|
||||
url = self.stream(*args, **kwargs)
|
||||
self._intercept_url = False
|
||||
|
||||
return url
|
||||
|
||||
def _doBinReq(self, *args, **kwargs):
|
||||
"""
|
||||
Intercept request URL.
|
||||
"""
|
||||
|
||||
if self._intercept_url:
|
||||
parts = list(urlparse.urlparse(
|
||||
args[0].get_full_url() + "?" + args[0].data))
|
||||
parts[4] = dict(urlparse.parse_qsl(parts[4]))
|
||||
parts[4].update({"u": self.username, "p": self.password})
|
||||
parts[4] = urllib.urlencode(parts[4])
|
||||
|
||||
return urlparse.urlunparse(parts)
|
||||
else:
|
||||
return super(Connection, self)._doBinReq(*args, **kwargs)
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<settings>
|
||||
<setting id="subsonic_url" type="text" label="URL" default="http://demo.subsonic.org"/>
|
||||
<setting id="username" type="text" label="Username" default="guest3"/>
|
||||
<setting id="password" type="text" option="hidden" label="Password" default="guest"/>
|
||||
<setting id="random_count" type="labelenum" label="Random songs" values="10|15|20|25"/>
|
||||
<setting id="format" type="labelenum" label="Format" values="mp3|raw|flv|ogg"/>
|
||||
<setting id="bitrate" type="labelenum" label="Bitrate" values="320|256|224|192|160|128|112|96|80|64|56|48|40|32"/>
|
||||
</settings>
|
Loading…
Reference in New Issue