Use SimplePlugin framework

This commit is contained in:
gordielachance 2016-10-02 20:10:40 +02:00
parent f16f234679
commit ac9160ea2c
6 changed files with 651 additions and 536 deletions

View File

@ -1,5 +1,8 @@
# Changelog # Changelog
## v2.0.2
* Use SimplePlugin framework (http://romanvm.github.io/script.module.simpleplugin/index.html)
## v2.0.1 ## v2.0.1
* New setting 'albums_per_page' * New setting 'albums_per_page'
* New menu structure * New menu structure

View File

@ -8,7 +8,7 @@ Kodi plugin to stream music from Subsonic.
## Installation ## Installation
* Navigate to your `.kodi/addons/` folder * Navigate to your `.kodi/addons/` folder
* Clone this repository: `git clone https://github.com/basilfx/plugin.audio.subsonic.git` * Clone this repository: `git clone https://github.com/gordielachance/plugin.audio.subsonic.git`
* (Re)start Kodi. * (Re)start Kodi.
## License ## License

514
addon.py
View File

@ -1,514 +0,0 @@
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()
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.apiversion = addon.getSetting("apiversion")
self.insecure = addon.getSetting("insecure") == "true"
self.legacyauth = addon.getSetting("legacyauth") == "true"
self.albums_per_page = int(addon.getSetting("albums_per_page"))
self.tracks_per_page = int(addon.getSetting("tracks_per_page"))
self.bitrate = int(addon.getSetting("bitrate"))
self.transcode_format = addon.getSetting("transcode_format")
# Create connection
self.connection = libsonic_extra.SubsonicClient(
self.url, self.username, self.password, self.apiversion, self.insecure, self.legacyauth)
def build_url(self, query):
"""
Create URL for page.
"""
parts = list(urlparse.urlparse(self.addon_url))
parts[4] = urllib.urlencode(query)
return urlparse.urlunparse(parts)
def route(self):
"""
Map a Kodi request to certain action. This takes the `mode' query
parameter and executed the function in this instance with that name.
"""
mode = self.addon_args.get("mode", ["main_menu"])[0]
if not mode.startswith("_"):
getattr(self, mode)()
def add_track(self, track, show_artist=False):
"""
Display one track in the list.
"""
url = self.connection.streamUrl(
sid=track["id"], maxBitRate=self.bitrate,
tformat=self.transcode_format)
# Create list item
if show_artist:
title = "%s - %s" % (
track.get("artist", "<Unknown>"),
track.get("title", "<Unknown>"))
else:
title = track.get("title", "<Unknown>")
# Create item
li = xbmcgui.ListItem(title)
# Handle cover art
if "coverArt" in track:
cover_art_url = self.connection.getCoverArtUrl(track["coverArt"])
li.setIconImage(cover_art_url)
li.setThumbnailImage(cover_art_url)
li.setProperty("fanart_image", cover_art_url)
# Handle metadata
li.setProperty("IsPlayable", "true")
li.setMimeType(track.get("contentType"))
li.setInfo(type="Music", infoLabels={
"Artist": track.get("artist"),
"Title": track.get("title"),
"Year": track.get("year"),
"Duration": track.get("duration"),
"Genre": track.get("genre"),
"TrackNumber": track.get("track")})
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=url, listitem=li)
def add_album(self, album, show_artist=False):
"""
Display one album in the list.
"""
url = self.build_url({
"mode": "track_list",
"album_id": album["id"]})
# Create list item
if show_artist:
title = "%s - %s" % (
album.get("artist", "<Unknown>"),
album.get("name", "<Unknown>"))
else:
title = album.get("name", "<Unknown>")
# Add year if applicable
if album.get("year"):
title = "%s [%d]" % (title, album.get("year"))
# Create item
li = xbmcgui.ListItem()
li.setLabel(title)
# Handle cover art
if "coverArt" in album:
cover_art_url = self.connection.getCoverArtUrl(album["coverArt"])
li.setIconImage(cover_art_url)
li.setThumbnailImage(cover_art_url)
li.setProperty("fanart_image", cover_art_url)
# Handle metadata
li.setInfo(type="music", infoLabels={
"Artist": album.get("artist"),
"Album": album.get("name"),
"Year": album.get("year")})
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
def main_menu(self):
"""
Display main menu.
"""
menu = [
{"mode": "list_artists", "foldername": "Artists"},
{"mode": "menu_albums", "foldername": "Albums"},
{"mode": "list_playlists", "foldername": "Playlists"},
{"mode": "menu_tracks", "foldername": "Tracks"}
]
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 menu_tracks(self):
"""
Display main menu.
"""
menu = [
{"mode": "list_tracks_starred", "foldername": "Starred tracks"},
{"mode": "list_tracks_random_genre", "foldername": "Random tracks by genre"},
{"mode": "list_tracks_random_year", "foldername": "Random tracks 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 menu_albums(self):
"""
Display main menu.
"""
menu = [
{"mode": "list_albums_newest", "foldername": "Newest albums", "page":1},
{"mode": "list_albums_frequent", "foldername": "Most played albums", "page":1},
{"mode": "list_albums_recent", "foldername": "Recently played albums", "page":1},
{"mode": "list_albums_genre", "foldername": "Albums by Genre"}
]
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 menu_link(self):
mode = self.addon_args["mode"][0]
menu_item = {"mode": "main_menu", "foldername": "Back to Menu"}
menu_item_url = self.build_url(menu_item)
menu_item_li = xbmcgui.ListItem(menu_item["foldername"])
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=menu_item_url, listitem=menu_item_li, isFolder=True)
def next_page_link(self,page):
page += 1
title = "Next page (%s)" % (page)
mode = self.addon_args["mode"][0]
menu_item = {"mode": mode, "foldername": title, "page":page}
menu_item_url = self.build_url(menu_item)
menu_item_li = xbmcgui.ListItem(menu_item["foldername"])
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=menu_item_url, listitem=menu_item_li, isFolder=True)
def list_playlists(self):
"""
Display playlists.
"""
for playlist in self.connection.walk_playlists():
cover_art_url = self.connection.getCoverArtUrl(
playlist["coverArt"])
url = self.build_url({
"mode": "list_playlist_songs", "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 list_playlist_songs(self):
"""
Display playlist tracks.
"""
playlist_id = self.addon_args["playlist_id"][0]
xbmcplugin.setContent(self.addon_handle, "songs")
for track in self.connection.walk_playlist(playlist_id):
self.add_track(track, show_artist=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def list_albums_genre(self):
"""
Display albums by genre list.
"""
genres = self.connection.walk_genres()
genres_sorted = sorted(genres, key=lambda k: k['value'])
for genre in genres_sorted:
try:
genre_slug = genre["value"]
genre_name = genre_slug.replace(';',' / ')
genre_count = int(genre["albumCount"])
if genre_count == 0:
continue
genre_name += ' (' + str(genre_count) + ')'
url = self.build_url({
"mode": 'album_list_genre',
"foldername": genre_slug.encode("utf-8")})
li = xbmcgui.ListItem(genre_name)
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
except:
pass
xbmcplugin.endOfDirectory(self.addon_handle)
def list_albums_newest(self):
"""
Display newest album list.
"""
page = int(self.addon_args["page"][0])
size = self.albums_per_page
offset = size * ( page -1 )
xbmcplugin.setContent(self.addon_handle,"albums")
for album in self.connection.walk_albums(ltype='newest',size=size,offset=offset):
self.add_album(album, show_artist=True)
self.next_page_link(page)
self.menu_link()
xbmcplugin.endOfDirectory(self.addon_handle)
def list_albums_frequent(self):
"""
Display most played albums list.
"""
page = int(self.addon_args["page"][0])
size = self.albums_per_page
offset = size * ( page -1 )
xbmcplugin.setContent(self.addon_handle,"albums")
for album in self.connection.walk_albums(ltype='frequent',size=size,offset=offset):
self.add_album(album, show_artist=True)
self.next_page_link(page)
self.menu_link()
xbmcplugin.endOfDirectory(self.addon_handle)
def list_albums_recent(self):
"""
Display recently played album list.
"""
page = int(self.addon_args["page"][0])
size = self.albums_per_page
offset = size * ( page -1 )
xbmcplugin.setContent(self.addon_handle,"albums")
for album in self.connection.walk_albums(ltype='recent',size=size):
self.add_album(album, show_artist=True)
self.next_page_link(page)
self.menu_link()
xbmcplugin.endOfDirectory(self.addon_handle)
def album_list_genre(self):
"""
Display album list by genre menu.
"""
genre = self.addon_args["foldername"][0].decode("utf-8")
size = self.albums_per_page
xbmcplugin.setContent(self.addon_handle,"albums")
for album in self.connection.walk_albums(ltype='byGenre',size=size,genre=genre):
self.add_album(album, show_artist=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def list_artists(self):
"""
Display artist list
"""
xbmcplugin.setContent(self.addon_handle, "artists")
for artist in self.connection.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.endOfDirectory(self.addon_handle)
def album_list(self):
"""
Display list of albums for certain artist.
"""
artist_id = self.addon_args["artist_id"][0]
xbmcplugin.setContent(self.addon_handle,"albums")
for album in self.connection.walk_artist(artist_id):
self.add_album(album)
xbmcplugin.addSortMethod(
self.addon_handle, xbmcplugin.SORT_METHOD_UNSORTED)
xbmcplugin.addSortMethod(
self.addon_handle, xbmcplugin.SORT_METHOD_ALBUM)
xbmcplugin.addSortMethod(
self.addon_handle, xbmcplugin.SORT_METHOD_ARTIST)
xbmcplugin.addSortMethod(
self.addon_handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR)
xbmcplugin.endOfDirectory(self.addon_handle)
def track_list(self):
"""
Display track list.
"""
album_id = self.addon_args["album_id"][0]
xbmcplugin.setContent(self.addon_handle, "songs")
for track in self.connection.walk_album(album_id):
self.add_track(track)
xbmcplugin.endOfDirectory(self.addon_handle)
def list_tracks_starred(self):
"""
Display starred songs.
"""
xbmcplugin.setContent(self.addon_handle, "songs")
for starred in self.connection.walk_tracks_starred():
self.add_track(starred, show_artist=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def list_tracks_random_genre(self):
"""
Display random genre list.
"""
for genre in self.connection.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")
xbmcplugin.setContent(self.addon_handle, "songs")
for track in self.connection.walk_tracks_random(
size=self.tracks_per_page, genre=genre):
self.add_track(track, show_artist=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def list_tracks_random_year(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)
xbmcplugin.setContent(self.addon_handle, "songs")
for track in self.connection.walk_tracks_random(
size=self.tracks_per_page, from_year=from_year, to_year=to_year):
self.add_track(track, show_artist=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def main():
"""
Entry point for this plugin. Instantiates a new plugin object and runs the
action that is given.
"""
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 within Kodi.
if __name__ == "__main__":
main()

View File

@ -1,9 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.audio.subsonic" name="Subsonic" version="2.0.1" provider-name="BasilFX,grosbouff,lrusak"> <addon id="plugin.audio.subsonic" name="Subsonic" version="2.0.2" provider-name="BasilFX,grosbouff,lrusak">
<requires> <requires>
<import addon="xbmc.python" version="2.19.0"/> <import addon="xbmc.python" version="2.14.0"/>
<import addon="script.module.dateutil" version="2.4.2"/>
<import addon="script.module.simpleplugin" version="2.0.1"/>
</requires> </requires>
<extension point="xbmc.python.pluginsource" library="addon.py"> <extension point="xbmc.python.pluginsource" library="main.py">
<provides>audio</provides> <provides>audio</provides>
</extension> </extension>
<extension point="xbmc.addon.metadata"> <extension point="xbmc.addon.metadata">
@ -15,7 +18,7 @@
<license>MIT</license> <license>MIT</license>
<forum></forum> <forum></forum>
<website>http://www.subsonic.org</website> <website>http://www.subsonic.org</website>
<source>https://github.com/basilfx/plugin.audio.subsonic</source> <source>https://github.com/gordielachance/plugin.audio.subsonic</source>
<email></email> <email></email>
</extension> </extension>
</addon> </addon>

613
main.py Normal file
View File

@ -0,0 +1,613 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Module: main
# Author: Roman V. M.
# Created on: 28.11.2014
# License: GPL v.3 https://www.gnu.org/copyleft/gpl.html
from simpleplugin import Plugin
from simpleplugin import Addon
import os
import json
from datetime import datetime
import dateutil.parser
# Make sure library folder is on the path
sys.path.append(xbmc.translatePath(
os.path.join(Addon().addon.getAddonInfo('path'), 'lib')))
# Create plugin instance
plugin = Plugin()
# initialize_gettext
#_ = plugin.initialize_gettext() //https://github.com/romanvm/script.module.simpleplugin/issues/1
connection = None
cache_minutes = int(Addon().get_setting('cache_minutes'))
import libsonic_extra
def popup(text, time=5000, image=None):
title = Addon().addon.getAddonInfo('name')
icon = Addon().addon.getAddonInfo('icon')
xbmc.executebuiltin('Notification(%s, %s, %d, %s)' % (title, text,
time, icon))
def get_connection():
global connection
if connection is None:
# Create connection
try:
connection = libsonic_extra.SubsonicClient(
Addon().get_setting('subsonic_url'),
Addon().get_setting('username'),
Addon().get_setting('password'),
Addon().get_setting('apiversion'),
Addon().get_setting('insecure') == 'true',
Addon().get_setting('legacyauth') == 'true',
)
connected = connection.ping()
except:
pass
if connected is False:
popup('Connection error')
return False
return connection
def menu_root(params):
# get connection
connection = get_connection()
listing = []
menus = {
'artists': {
'name': 'Artists',
'callback': 'list_artists',
'thumb': None
},
'albums': {
'name': 'Albums',
'callback': 'menu_albums',
'thumb': None
},
'tracks': {
'name': 'Tracks',
'callback': 'menu_tracks',
'thumb': None
},
'playlists': {
'name': 'Playlists',
'callback': 'list_playlists',
'thumb': None
}
}
# Iterate through categories
for mid in menus:
# image
if 'thumb' in menus[mid]:
thumb = menus[mid]['thumb']
listing.append({
'label': menus[mid]['name'],
'thumb': thumb, # Item thumbnail
'fanart': thumb, # Item thumbnail
'url': plugin.get_url(
action=menus[mid]['callback'],
mid=mid
)
}) # Item label
return plugin.create_listing(
listing,
#succeeded = True, #if False Kodi wont open a new listing and stays on the current level.
#update_listing = False, #if True, Kodi wont open a sub-listing but refresh the current one.
#cache_to_disk = True, #cache this view to disk.
#sort_methods = None, #he list of integer constants representing virtual folder sort methods.
#view_mode = None, #a numeric code for a skin view mode. View mode codes are different in different skins except for 50 (basic listing).
#content = None #string - current plugin content, e.g. movies or episodes.
)
def menu_albums(params):
# get connection
connection = get_connection()
listing = []
menus = {
'newest': {
'name': 'Newest albums',
'thumb': None,
'args': {"ltype": "newest"}
},
'frequent': {
'name': 'Most played albums',
'thumb': None,
'args': {"ltype": "frequent"}
},
'recent': {
'name': 'Recently played albums',
'thumb': None,
'args': {"ltype": "recent"}
},
'random': {
'name': 'Random albums',
'thumb': None,
'args': {"ltype": "random"}
}
}
# Iterate through categories
for mid in menus:
menu = menus.get(mid)
# image
if 'thumb' in menu:
thumb = menu.get('thumb')
listing.append({
'label': menu.get('name'),
'thumb': menu.get('thumb'), # Item thumbnail
'fanart': menu.get('thumb'), # Item thumbnail
'url': plugin.get_url(
action= 'list_albums',
page= 1,
query_args= json.dumps(menu.get('args'))
)
}) # Item label
return plugin.create_listing(
listing,
#succeeded = True, #if False Kodi wont open a new listing and stays on the current level.
#update_listing = False, #if True, Kodi wont open a sub-listing but refresh the current one.
#cache_to_disk = True, #cache this view to disk.
#sort_methods = None, #he list of integer constants representing virtual folder sort methods.
#view_mode = None, #a numeric code for a skin view mode. View mode codes are different in different skins except for 50 (basic listing).
#content = None #string - current plugin content, e.g. movies or episodes.
)
def menu_tracks(params):
# get connection
connection = get_connection()
listing = []
menus = {
'starred': {
'name': 'Starred tracks',
'thumb': None,
'starred': True
}
}
# Iterate through categories
for mid in menus:
menu = menus.get(mid)
# image
if 'thumb' in menu:
thumb = menu.get('thumb')
listing.append({
'label': menu.get('name'),
'thumb': menu.get('thumb'), # Item thumbnail
'fanart': menu.get('thumb'), # Item thumbnail
'url': plugin.get_url(
action= 'list_tracks',
starred= menu.get('starred')
)
}) # Item label
return plugin.create_listing(
listing,
#succeeded = True, #if False Kodi wont open a new listing and stays on the current level.
#update_listing = False, #if True, Kodi wont open a sub-listing but refresh the current one.
#cache_to_disk = True, #cache this view to disk.
#sort_methods = None, #he list of integer constants representing virtual folder sort methods.
#view_mode = None, #a numeric code for a skin view mode. View mode codes are different in different skins except for 50 (basic listing).
#content = None #string - current plugin content, e.g. movies or episodes.
)
@plugin.cached(cache_minutes) #if cache is enabled, cache data for the following function
def list_artists(params):
# get connection
connection = get_connection()
listing = []
# Get items
items = connection.walk_artists()
# Iterate through items
for item in items:
listing.append({
'label': item['name'],
'thumb': connection.getCoverArtUrl(item.get('id')),
'fanart': connection.getCoverArtUrl(item.get('id')),
'url': plugin.get_url(action='list_artist_albums',
artist_id=item.get('id')
),
'info': {
'music': { ##http://romanvm.github.io/Kodistubs/_autosummary/xbmcgui.html#xbmcgui.ListItem.setInfo
'count': item.get('albumCount'),
'artist': item.get('name')
}
}
})
# Sort methods - List of integer constants representing virtual folder sort methods. - see SortFileItem.h from Kodi core
sortable_by = (
0, #SORT_METHOD_NONE
11, #SORT_METHOD_ARTIST
40 #SORT_METHOD_UNSORTED
)
return plugin.create_listing(
listing,
#succeeded = True, #if False Kodi wont open a new listing and stays on the current level.
#update_listing = False, #if True, Kodi wont open a sub-listing but refresh the current one.
cache_to_disk = True, #cache this view to disk.
sort_methods = sortable_by, #he list of integer constants representing virtual folder sort methods.
#view_mode = None, #a numeric code for a skin view mode. View mode codes are different in different skins except for 50 (basic listing).
content = 'artists' #string - current plugin content, e.g. movies or episodes.
)
@plugin.cached(cache_minutes) #if cache is enabled, cache data for the following function
def list_albums(params):
listing = []
# get connection
connection = get_connection()
query_args_json = params['query_args']
query_args = json.loads(query_args_json)
#size
albums_per_page = int(Addon().get_setting('albums_per_page'))
query_args["size"] = albums_per_page
#offset
offset = int(params.get('page')) - 1;
if offset > 0:
query_args["offset"] = offset * albums_per_page
#TO FIX this test is for pagination
#query_args["fromYear"] = 2016
#query_args["toYear"] = 2016
#query_args["ltype"] = 'byYear'
#debug
query_args_json = json.dumps(query_args)
plugin.log('list_albums with args:' + query_args_json);
#popup(json.dumps(query_args_json))
#Get items
items = connection.walk_albums(**query_args)
# Iterate through items
for item in items:
album = get_album_entry(item, params)
listing.append(album)
# Root menu
link_root = navigate_root()
listing.append(link_root)
# Pagination if we've not reached the end of the lsit
# if type(items) != type(True): TO FIX
link_next = navigate_next(params)
listing.append(link_next)
# Sort methods - List of integer constants representing virtual folder sort methods. - see SortFileItem.h from Kodi core
sortable_by = (
0, #SORT_METHOD_NONE
1, #SORT_METHOD_LABEL
#3, #SORT_METHOD_DATE
11, #SORT_METHOD_ARTIST
#14, #SORT_METHOD_ALBUM
18, #SORT_METHOD_YEAR
#21 #SORT_METHOD_DATEADDED
40 #SORT_METHOD_UNSORTED
)
return plugin.create_listing(
listing,
#succeeded = True, #if False Kodi wont open a new listing and stays on the current level.
#update_listing = False, #if True, Kodi wont open a sub-listing but refresh the current one.
cache_to_disk = True, #cache this view to disk.
sort_methods = sortable_by,
#view_mode = None, #a numeric code for a skin view mode. View mode codes are different in different skins except for 50 (basic listing).
content = 'albums' #string - current plugin content, e.g. movies or episodes.
)
@plugin.cached(cache_minutes) #if cache is enabled, cache data for the following function
def list_artist_albums(params):
# get connection
connection = get_connection()
listing = []
# Get items
artist_id = params['artist_id']
params['hide_artist'] = True
items = connection.walk_artist(artist_id)
# Iterate through items
for item in items:
album = get_album_entry(item, params)
listing.append(album)
return plugin.create_listing(
listing,
#succeeded = True, #if False Kodi wont open a new listing and stays on the current level.
#update_listing = False, #if True, Kodi wont open a sub-listing but refresh the current one.
cache_to_disk = True, #cache this view to disk.
#sort_methods = None, #he list of integer constants representing virtual folder sort methods.
#view_mode = None, #a numeric code for a skin view mode. View mode codes are different in different skins except for 50 (basic listing).
content = 'albums' #string - current plugin content, e.g. movies or episodes.
)
def get_album_entry(item, params):
# name
if 'hide_artist' in params:
title = item.get('name', '<Unknown>')
else:
title = '%s - %s' % (item.get('artist', '<Unknown>'),
item.get('name', '<Unknown>'))
return {
'label': title,
'thumb': item.get('coverArt'),
'fanart': item.get('coverArt'),
'url': plugin.get_url(
action= 'list_tracks',
album_id= item.get('id'),
hide_artist= item.get('hide_artist')
),
'info': {
'music': { ##http://romanvm.github.io/Kodistubs/_autosummary/xbmcgui.html#xbmcgui.ListItem.setInfo
'count': item.get('songCount'),
'date': convert_date_from_iso8601(item.get('created')), #date added
'duration': item.get('duration'),
'artist': item.get('artist'),
'album': item.get('name'),
'year': item.get('year')
}
},
}
@plugin.cached(cache_minutes) #if cache is enabled, cache data for the following function
def list_tracks(params):
listing = []
# get connection
connection = get_connection()
# Album
if 'album_id' in params:
items = connection.walk_album(params['album_id'])
# Playlist
if 'playlist_id' in params:
items = connection.walk_playlist(params['playlist_id'])
#TO FIX
#tracknumber = 0
#for item in items:
# tracknumber += 1
# items[item]['tracknumber'] = tracknumber
# Starred
if 'starred' in params:
items = connection.walk_tracks_starred()
# Iterate through items
key = 0;
for item in items:
track = get_track_entry(item,params)
listing.append(track)
key +=1
# Root menu
link_root = navigate_root()
listing.append(link_root)
# Pagination if we've not reached the end of the lsit
# if type(items) != type(True): TO FIX
#link_next = navigate_next(params)
#listing.append(link_next)
# Sort methods - List of integer constants representing virtual folder sort methods. - see SortFileItem.h from Kodi core
sortable_by = (
0, #SORT_METHOD_NONE
1, #SORT_METHOD_LABEL
#3, #SORT_METHOD_DATE
7, #SORT_METHOD_TRACKNUM
11, #SORT_METHOD_ARTIST
#14,#SORT_METHOD_ALBUM
18, #SORT_METHOD_YEAR
#21 #SORT_METHOD_DATEADDED
40 #SORT_METHOD_UNSORTED
)
return plugin.create_listing(
listing,
#succeeded = True, #if False Kodi wont open a new listing and stays on the current level.
#update_listing = False, #if True, Kodi wont open a sub-listing but refresh the current one.
#cache_to_disk = True, #cache this view to disk.
sort_methods = sortable_by, #he list of integer constants representing virtual folder sort methods.
#view_mode = None, #a numeric code for a skin view mode. View mode codes are different in different skins except for 50 (basic listing).
content = 'songs' #string - current plugin content, e.g. movies or episodes.
)
def get_track_entry(item,params):
# name
if 'hide_artist' in params:
title = item.get('title', '<Unknown>')
else:
title = '%s - %s' % (
item.get('artist', '<Unknown>'),
item.get('title', '<Unknown>')
)
return {
'label': title,
'thumb': item.get('coverArt'),
'fanart': item.get('coverArt'),
'url': plugin.get_url(
action='play_track',
id=item.get('id')
),
'is_playable': True,
'mime': item.get("contentType"),
'info': {'music': { #http://romanvm.github.io/Kodistubs/_autosummary/xbmcgui.html#xbmcgui.ListItem.setInfo
'title': item.get('title'),
'album': item.get('album'),
'artist': item.get('artist'),
'tracknumber': item.get('tracknumber'),
'year': item.get('year'),
'genre': item.get('genre'),
'size': item.get('size'),
'duration': item.get('duration')
}},
}
def play_track(params):
id = params['id']
plugin.log('play_track #' + id);
# get connection
connection = get_connection()
url = connection.streamUrl(sid=id,
maxBitRate=Addon().get_setting('bitrate_streaming'),
tformat=Addon().get_setting('transcode_format_streaming')
)
return url
def navigate_next(params):
page = int(params['page'])
page += 1
title = "Next page (%d)" % (page)
return {
'label': title,
'url': plugin.get_url(
action= params['action'],
page= page,
query_args= params['query_args']
)
}
def navigate_root():
return {
'label': "Back to menu",
'url': plugin.get_url(action='root')
}
#converts a date string from eg. '2012-04-17T19:53:44' to eg. '17.04.2012'
def convert_date_from_iso8601(iso8601):
date_obj = dateutil.parser.parse(iso8601)
return date_obj.strftime('%d.%m.%Y')
@plugin.cached(cache_minutes) #if cache is enabled, cache data for the following function
def list_playlists(params):
# get connection
connection = get_connection()
listing = []
# Get items
items = connection.walk_playlists()
# Iterate through items
for item in items:
listing.append({
'label': item['name'],
'thumb': connection.getCoverArtUrl(item.get('id')),
'fanart': connection.getCoverArtUrl(item.get('id')),
'url': plugin.get_url(action='list_tracks',
playlist_id=item.get('id'),
),
'info': {'music': { ##http://romanvm.github.io/Kodistubs/_autosummary/xbmcgui.html#xbmcgui.ListItem.setInfo
'title': item.get('name'),
'count': item.get('songCount'),
'duration': item.get('duration'),
'date': convert_date_from_iso8601(item.get('created'))
}}
})
return plugin.create_listing(
listing,
#succeeded = True, #if False Kodi wont open a new listing and stays on the current level.
#update_listing = False, #if True, Kodi wont open a sub-listing but refresh the current one.
#cache_to_disk = True, #cache this view to disk.
#sort_methods = None, #he list of integer constants representing virtual folder sort methods.
#view_mode = None, #a numeric code for a skin view mode. View mode codes are different in different skins except for 50 (basic listing).
#content = None #string - current plugin content, e.g. movies or episodes.
)
# Start plugin from within Kodi.
if __name__ == "__main__":
# Map actions
# Note that we map callable objects without brackets ()
plugin.actions['root'] = menu_root # - optional if we have a function named 'root'
plugin.actions['menu_albums'] = menu_albums
plugin.actions['list_artists'] = list_artists
plugin.actions['list_artist_albums'] = list_artist_albums
plugin.actions['list_albums'] = list_albums
plugin.actions['menu_tracks'] = menu_tracks
plugin.actions['list_tracks'] = list_tracks
plugin.actions['play_track'] = play_track
plugin.actions['list_playlists'] = list_playlists
plugin.run()

View File

@ -1,17 +1,27 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings> <settings>
<category label="Server"> <!-- GENERAL -->
<category label="General">
<setting label="Server" type="lsep" />
<setting id="subsonic_url" type="text" label="Server URL" default="http://demo.subsonic.org"/> <setting id="subsonic_url" type="text" label="Server URL" default="http://demo.subsonic.org"/>
<setting id="username" type="text" label="Username" default="guest3"/> <setting id="username" type="text" label="Username" default="guest3"/>
<setting id="password" type="text" option="hidden" label="Password" default="guest"/> <setting id="password" type="text" option="hidden" label="Password" default="guest"/>
<setting id="apiversion" type="labelenum" label="API version" values="1.11.0|1.12.0|1.13.0|1.14.0" default="1.13.0"/> <setting label="Display" type="lsep" />
<setting id="insecure" type="bool" label="Allow self signed certificates" default="false" />
<setting id="legacyauth" type="bool" label="Use pre-1.13.0 API version authentication" default="false"/>
<setting type="sep" />
<setting id="albums_per_page" type="labelenum" label="Albums per page" default="50" values="10|25|50|100|250|500"/> <setting id="albums_per_page" type="labelenum" label="Albums per page" default="50" values="10|25|50|100|250|500"/>
<setting id="tracks_per_page" type="labelenum" label="Tracks per page (ignored in albums & playlists)" default="100" values="10|25|50|100|250|500"/> <setting id="tracks_per_page" type="labelenum" label="Tracks per page (ignored in albums & playlists)" default="100" values="10|25|50|100|250|500"/>
<setting type="sep" /> <setting label="Streaming" type="lsep" />
<setting id="transcode_format" type="labelenum" label="Transcode format" values="mp3|raw|flv|ogg"/> <setting id="transcode_format_streaming" type="labelenum" label="Transcode 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"/> <setting id="bitrate_streaming" type="labelenum" label="Bitrate" values="320|256|224|192|160|128|112|96|80|64|56|48|40|32"/>
</category>
<!-- ADVANCED -->
<category label="Advanced Settings">
<setting label="Server" type="lsep" />
<setting id="apiversion" type="labelenum" label="API version" values="1.11.0|1.12.0|1.13.0|1.14.0" default="1.13.0"/>
<setting id="insecure" type="bool" label="Allow self signed certificates" default="false" />
<setting label="Cache" type="lsep" />
<setting id="cache_minutes" type="labelenum" label="Cache datas time (in minutes)" default="30" values="1|15|30|60|120|180|720|1440"/>
<setting label="Debug" type="lsep" />
<setting label="Enable Debug mode" type="bool" id="debug" default="false" />
</category> </category>
</settings> </settings>