From d9210dcaef464f092288244bd1dca23d829da56c Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Sun, 8 Jan 2017 11:09:54 +0100 Subject: [PATCH 01/13] new 'Browse' top menu to list media folders --- main.py | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index 036b674..4c70a10 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,6 @@ import os import xbmcaddon -lang = xbmcaddon.Addon() import xbmcplugin import xbmcgui import json @@ -16,7 +15,7 @@ import shutil import dateutil.parser from datetime import datetime - +lang = xbmcaddon.Addon() # Add the /lib folder to sys sys.path.append(xbmc.translatePath(os.path.join(xbmcaddon.Addon("plugin.audio.subsonic").getAddonInfo("path"), "lib"))) @@ -101,6 +100,11 @@ def root(params): 'name': lang.getLocalizedString(30022), 'callback': 'list_playlists', 'thumb': None + }, + 'folders': { + 'name': 'Browse', + 'callback': 'list_folders', + 'thumb': None } } @@ -801,10 +805,40 @@ def list_playlists(params): #content = None #string - current plugin content, e.g. ‘movies’ or ‘episodes’. ) +@plugin.action() +def list_folders(params): + # get connection + connection = get_connection() + + if connection is False: + return + + listing = [] + + # Get items + items = connection.walk_folders() + + # Iterate through items + for item in items: + entry = { + 'label': item.get('name'), + 'url': plugin.get_url( + action= 'list_indexes', + folder_id= item.get('id'), + menu_id= params.get('menu_id') + + ) + } + listing.append(entry) + + return plugin.create_listing( + listing + ) + def get_entry_playlist(item,params): image = connection.getCoverArtUrl(item.get('coverArt')) return { - 'label': item['name'], + 'label': item.get('name'), 'thumb': image, 'fanart': image, 'url': plugin.get_url( From 7d26333ce01f487972d40ba79902e9ec5ae73fa4 Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Sun, 8 Jan 2017 11:11:05 +0100 Subject: [PATCH 02/13] WIP : list_indexes function (not working yet) --- lib/libsonic_extra/__init__.py | 10 ++++++++-- main.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/lib/libsonic_extra/__init__.py b/lib/libsonic_extra/__init__.py index 29a45df..eca9231 100644 --- a/lib/libsonic_extra/__init__.py +++ b/lib/libsonic_extra/__init__.py @@ -296,12 +296,12 @@ class SubsonicClient(libsonic.Connection): else: return super(SubsonicClient, self)._doBinReq(*args, **kwargs) - def walk_index(self): + def walk_index(self, folder_id=None): """ Request Subsonic's index and iterate each item. """ - response = self.getIndexes() + response = self.getIndexes(folder_id) for index in response["indexes"]["index"]: for index in index["artist"]: @@ -334,6 +334,12 @@ class SubsonicClient(libsonic.Connection): for child in response["playlist"]["entry"]: yield child + + def walk_folders(self): + response = self.getMusicFolders() + + for child in response["musicFolders"]["musicFolder"]: + yield child def walk_directory(self, directory_id): """ diff --git a/main.py b/main.py index 4c70a10..253a839 100644 --- a/main.py +++ b/main.py @@ -805,6 +805,37 @@ def list_playlists(params): #content = None #string - current plugin content, e.g. ‘movies’ or ‘episodes’. ) +@plugin.action() +def list_indexes(params): + # get connection + connection = get_connection() + + if connection is False: + return + + listing = [] + + # Get items + folder_id = params.get('folder_id') + items = connection.walk_index(folder_id) + + plugin.log('list_indexes:') + # Iterate through items + for item in items: + entry = { + 'label': item.get('name'), + 'url': plugin.get_url( + action= 'list_albums', + artist_id= item.get('id'), + menu_id= params.get('menu_id') + ) + } + listing.append(entry) + + return plugin.create_listing( + listing + ) + @plugin.action() def list_folders(params): # get connection From 3ed7b231cc82f0a4a51d53d8cc6ad16a83e6338c Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Sun, 8 Jan 2017 11:35:53 +0100 Subject: [PATCH 03/13] if there is only one media folder, return list_indexes() directly --- main.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 253a839..f5e1d9f 100644 --- a/main.py +++ b/main.py @@ -861,10 +861,12 @@ def list_folders(params): ) } listing.append(entry) - - return plugin.create_listing( - listing - ) + + if len(listing) == 1: + plugin.log('One single Media Folder found; do return listing from list_indexes()...') + return list_indexes(params) + else: + return plugin.create_listing(listing) def get_entry_playlist(item,params): image = connection.getCoverArtUrl(item.get('coverArt')) From c6e9cc92e8d844f094a0e9e82965b484c52d08af Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Mon, 9 Jan 2017 01:02:37 +0100 Subject: [PATCH 04/13] Browse/Library menus (file structure vs ID3 tags) --- CHANGELOG.md | 1 + lib/libsonic_extra/__init__.py | 17 ++-- main.py | 125 +++++++++++++++++--------- resources/language/English/strings.po | 2 +- resources/language/French/strings.po | 6 +- resources/language/German/strings.po | 4 +- 6 files changed, 95 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a74a05..5a20202 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## v2.0.X Released XXX +* Browse/Library menus (file structure vs ID3 tags) * Added multilanguage support ## v2.0.5 diff --git a/lib/libsonic_extra/__init__.py b/lib/libsonic_extra/__init__.py index eca9231..3749fb5 100644 --- a/lib/libsonic_extra/__init__.py +++ b/lib/libsonic_extra/__init__.py @@ -140,6 +140,7 @@ class SubsonicClient(libsonic.Connection): def getArtists(self, *args, **kwargs): """ + (ID3 tags) Improve the getArtists method. Ensures IDs are integers. """ @@ -162,6 +163,7 @@ class SubsonicClient(libsonic.Connection): def getArtist(self, *args, **kwargs): """ + (ID3 tags) Improve the getArtist method. Ensures IDs are integers. """ @@ -209,6 +211,7 @@ class SubsonicClient(libsonic.Connection): def getAlbum(self, *args, **kwargs): """ + (ID3 tags) Improve the getAlbum method. Ensures the IDs are real integers. """ @@ -304,16 +307,9 @@ class SubsonicClient(libsonic.Connection): response = self.getIndexes(folder_id) for index in response["indexes"]["index"]: - for index in index["artist"]: - for item in self.walk_directory(index["id"]): - yield item + for artist in index["artist"]: + yield artist - for child in response["indexes"]["child"]: - if child.get("isDir"): - for child in self.walk_directory(child["id"]): - yield child - else: - yield child def walk_playlists(self): """ @@ -367,6 +363,7 @@ class SubsonicClient(libsonic.Connection): def walk_artists(self): """ + (ID3 tags) Request all artists and iterate over each item. """ @@ -409,7 +406,7 @@ class SubsonicClient(libsonic.Connection): def walk_album(self, album_id): """ - Request an alum and iterate over each item. + Request an album and iterate over each item. """ response = self.getAlbum(album_id) diff --git a/main.py b/main.py index f5e1d9f..471bdc1 100644 --- a/main.py +++ b/main.py @@ -81,9 +81,14 @@ def root(params): listing = [] menus = { - 'artists': { + 'folders': { + 'name': 'Browse', + 'callback': 'browse_folders', + 'thumb': None + }, + 'library': { 'name': lang.getLocalizedString(30019), - 'callback': 'list_artists', + 'callback': 'browse_library', 'thumb': None }, 'albums': { @@ -100,11 +105,6 @@ def root(params): 'name': lang.getLocalizedString(30022), 'callback': 'list_playlists', 'thumb': None - }, - 'folders': { - 'name': 'Browse', - 'callback': 'list_folders', - 'thumb': None } } @@ -131,7 +131,7 @@ def root(params): #succeeded = True, #if False Kodi won’t open a new listing and stays on the current level. #update_listing = False, #if True, Kodi won’t 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. + 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’. ) @@ -256,7 +256,7 @@ def menu_tracks(params): @plugin.action() #@plugin.cached(cachetime) #if cache is enabled, cache data for the following function -def list_artists(params): +def browse_library(params): # get connection connection = get_connection() @@ -806,38 +806,7 @@ def list_playlists(params): ) @plugin.action() -def list_indexes(params): - # get connection - connection = get_connection() - - if connection is False: - return - - listing = [] - - # Get items - folder_id = params.get('folder_id') - items = connection.walk_index(folder_id) - - plugin.log('list_indexes:') - # Iterate through items - for item in items: - entry = { - 'label': item.get('name'), - 'url': plugin.get_url( - action= 'list_albums', - artist_id= item.get('id'), - menu_id= params.get('menu_id') - ) - } - listing.append(entry) - - return plugin.create_listing( - listing - ) - -@plugin.action() -def list_folders(params): +def browse_folders(params): # get connection connection = get_connection() @@ -854,7 +823,7 @@ def list_folders(params): entry = { 'label': item.get('name'), 'url': plugin.get_url( - action= 'list_indexes', + action= 'browse_indexes', folder_id= item.get('id'), menu_id= params.get('menu_id') @@ -863,11 +832,79 @@ def list_folders(params): listing.append(entry) if len(listing) == 1: - plugin.log('One single Media Folder found; do return listing from list_indexes()...') - return list_indexes(params) + plugin.log('One single Media Folder found; do return listing from browse_indexes()...') + return browse_indexes(params) else: return plugin.create_listing(listing) +@plugin.action() +def browse_indexes(params): + # get connection + connection = get_connection() + + if connection is False: + return + + listing = [] + + # Get items + # optional folder ID + folder_id = params.get('folder_id') + items = connection.walk_index(folder_id) + + # Iterate through items + for item in items: + entry = { + 'label': item.get('name'), + 'url': plugin.get_url( + action= 'list_directory', + id= item.get('id'), + menu_id= params.get('menu_id') + ) + } + listing.append(entry) + + return plugin.create_listing( + listing + ) + +@plugin.action() +def list_directory(params): + # get connection + connection = get_connection() + + if connection is False: + return + + listing = [] + + # Get items + id = params.get('id') + items = connection.walk_directory(id) + + # Iterate through items + for item in items: + + #is a directory + if (item.get('isDir')==True): + entry = { + 'label': item.get('title'), + 'url': plugin.get_url( + action= 'list_directory', + id= item.get('id'), + menu_id= params.get('menu_id') + ) + } + else: + entry = get_entry_track(item,params) + + + listing.append(entry) + + return plugin.create_listing( + listing + ) + def get_entry_playlist(item,params): image = connection.getCoverArtUrl(item.get('coverArt')) return { diff --git a/resources/language/English/strings.po b/resources/language/English/strings.po index 6c4bdfe..51a129d 100644 --- a/resources/language/English/strings.po +++ b/resources/language/English/strings.po @@ -81,7 +81,7 @@ msgid "Cache datas time" msgstr "" msgctxt "#30019" -msgid "Artists" +msgid "Library" msgstr "" msgctxt "#30020" diff --git a/resources/language/French/strings.po b/resources/language/French/strings.po index 76d90aa..72c780d 100644 --- a/resources/language/French/strings.po +++ b/resources/language/French/strings.po @@ -46,7 +46,7 @@ msgstr "Télécharger" msgctxt "#30009" msgid "Download folder" -msgstr "Télécharger le répertoire" +msgstr "Répertoire de téléchargement" msgctxt "#30010" msgid "Streaming" @@ -81,8 +81,8 @@ msgid "Cache datas time" msgstr "Durée du cache pour les données" msgctxt "#30019" -msgid "Artists" -msgstr "Artistes" +msgid "Library" +msgstr "Bibliothèque" msgctxt "#30020" msgid "Albums" diff --git a/resources/language/German/strings.po b/resources/language/German/strings.po index 69dc43b..9f5650a 100644 --- a/resources/language/German/strings.po +++ b/resources/language/German/strings.po @@ -81,8 +81,8 @@ msgid "Cache datas time" msgstr "Speicher Daten Zeit" msgctxt "#30019" -msgid "Artists" -msgstr "Künstler" +msgid "Library" +msgstr "Bibliothek" msgctxt "#30020" msgid "Albums" From d9a18d41e3d3d120f7e62af4c467c819a9634d85 Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Mon, 9 Jan 2017 10:45:12 +0100 Subject: [PATCH 05/13] reorganize code --- lib/libsonic_extra/__init__.py | 3 + main.py | 900 +++++++++++++++++---------------- 2 files changed, 458 insertions(+), 445 deletions(-) diff --git a/lib/libsonic_extra/__init__.py b/lib/libsonic_extra/__init__.py index 3749fb5..606c678 100644 --- a/lib/libsonic_extra/__init__.py +++ b/lib/libsonic_extra/__init__.py @@ -375,6 +375,7 @@ class SubsonicClient(libsonic.Connection): def walk_genres(self): """ + (ID3 tags) Request all genres and iterate over each item. """ @@ -385,6 +386,7 @@ class SubsonicClient(libsonic.Connection): def walk_albums(self, ltype, size=None, fromYear=None,toYear=None, genre=None, offset=None): """ + (ID3 tags) Request all albums for a given genre and iterate over each album. """ @@ -406,6 +408,7 @@ class SubsonicClient(libsonic.Connection): def walk_album(self, album_id): """ + (ID3 tags) Request an album and iterate over each item. """ diff --git a/main.py b/main.py index 471bdc1..34f0171 100644 --- a/main.py +++ b/main.py @@ -255,8 +255,114 @@ def menu_tracks(params): ) @plugin.action() -#@plugin.cached(cachetime) #if cache is enabled, cache data for the following function +#@plugin.cached(cachetime) # cache (in minutes) +def browse_folders(params): + # get connection + connection = get_connection() + + if connection is False: + return + + listing = [] + + # Get items + items = connection.walk_folders() + + # Iterate through items + for item in items: + entry = { + 'label': item.get('name'), + 'url': plugin.get_url( + action= 'browse_indexes', + folder_id= item.get('id'), + menu_id= params.get('menu_id') + + ) + } + listing.append(entry) + + if len(listing) == 1: + plugin.log('One single Media Folder found; do return listing from browse_indexes()...') + return browse_indexes(params) + else: + return plugin.create_listing(listing) + +@plugin.action() +#@plugin.cached(cachetime) # cache (in minutes) +def browse_indexes(params): + # get connection + connection = get_connection() + + if connection is False: + return + + listing = [] + + # Get items + # optional folder ID + folder_id = params.get('folder_id') + items = connection.walk_index(folder_id) + + # Iterate through items + for item in items: + entry = { + 'label': item.get('name'), + 'url': plugin.get_url( + action= 'list_directory', + id= item.get('id'), + menu_id= params.get('menu_id') + ) + } + listing.append(entry) + + return plugin.create_listing( + listing + ) + +@plugin.action() +#@plugin.cached(cachetime) # cache (in minutes) +def list_directory(params): + # get connection + connection = get_connection() + + if connection is False: + return + + listing = [] + + # Get items + id = params.get('id') + items = connection.walk_directory(id) + + # Iterate through items + for item in items: + + #is a directory + if (item.get('isDir')==True): + entry = { + 'label': item.get('title'), + 'url': plugin.get_url( + action= 'list_directory', + id= item.get('id'), + menu_id= params.get('menu_id') + ) + } + else: + entry = get_entry_track(item,params) + + + listing.append(entry) + + return plugin.create_listing( + listing + ) + +@plugin.action() +#@plugin.cached(cachetime) # cache (in minutes) def browse_library(params): + """ + List artists from the library (ID3 tags) + """ # get connection connection = get_connection() @@ -295,29 +401,14 @@ def browse_library(params): content = 'artists' #string - current plugin content, e.g. ‘movies’ or ‘episodes’. ) -def get_entry_artist(item,params): - image = connection.getCoverArtUrl(item.get('coverArt')) - return { - 'label': get_starred_label(item.get('id'),item.get('name')), - 'thumb': image, - 'fanart': image, - 'url': plugin.get_url( - action= 'list_albums', - artist_id= item.get('id'), - menu_id= params.get('menu_id') - ), - 'info': { - 'music': { ##http://romanvm.github.io/Kodistubs/_autosummary/xbmcgui.html#xbmcgui.ListItem.setInfo - 'count': item.get('albumCount'), - 'artist': item.get('name') - } - } - } - @plugin.action() -#@plugin.cached(cachetime) #if cache is enabled, cache data for the following function +#@plugin.cached(cachetime) #cache (in minutes) def list_albums(params): + """ + List albums from the library (ID3 tags) + """ + listing = [] # get connection @@ -387,125 +478,8 @@ def list_albums(params): content = 'albums' #string - current plugin content, e.g. ‘movies’ or ‘episodes’. ) -def get_entry_album(item, params): - - image = connection.getCoverArtUrl(item.get('coverArt')) - - entry = { - 'label': get_entry_album_label(item,params.get('hide_artist',False)), - 'thumb': image, - 'fanart': image, - 'url': plugin.get_url( - action= 'list_tracks', - album_id= item.get('id'), - hide_artist= item.get('hide_artist'), - menu_id= params.get('menu_id') - ), - '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') - } - } - } - - #context menu actions - context_actions = [] - - if can_star('album',item.get('id')): - action_star = context_action_star('album',item.get('id')) - context_actions.append(action_star) - - if can_download('album',item.get('id')): - action_download = context_action_download('album',item.get('id')) - context_actions.append(action_download) - - if len(context_actions) > 0: - entry['context_menu'] = context_actions - - return entry - -#sort method for list types -#https://github.com/xbmc/xbmc/blob/master/xbmc/SortFileItem.h -#TO FIX _DATE or _DATEADDED ? -def get_sort_methods(type,params): - - #TO FIX - #actually it seems possible to 'restore' the default sorting (by labels) - #so our starred items don't get colorized. - #so do not sort stuff - return [] - - sortable = [ - xbmcplugin.SORT_METHOD_NONE, - xbmcplugin.SORT_METHOD_LABEL, - xbmcplugin.SORT_METHOD_UNSORTED - ] - - if type is 'artists': - - artists = [ - xbmcplugin.SORT_METHOD_ARTIST - ] - - sortable = sortable + artists - - elif type is 'albums': - - albums = [ - xbmcplugin.SORT_METHOD_ALBUM, - xbmcplugin.SORT_METHOD_DURATION, - xbmcplugin.SORT_METHOD_DATE, - #xbmcplugin.SORT_METHOD_YEAR - ] - - if not params.get('hide_artist',False): - albums.append(xbmcplugin.SORT_METHOD_ARTIST) - - sortable = sortable + albums - - elif type is 'tracks': - - tracks = [ - xbmcplugin.SORT_METHOD_TITLE, - xbmcplugin.SORT_METHOD_ALBUM, - xbmcplugin.SORT_METHOD_TRACKNUM, - #xbmcplugin.SORT_METHOD_YEAR, - xbmcplugin.SORT_METHOD_GENRE, - xbmcplugin.SORT_METHOD_SIZE, - xbmcplugin.SORT_METHOD_DURATION, - xbmcplugin.SORT_METHOD_DATE, - xbmcplugin.SORT_METHOD_BITRATE - ] - - if not params.get('hide_artist',False): - tracks.append(xbmcplugin.SORT_METHOD_ARTIST) - - if params.get('playlist_id',False): - xbmcplugin.SORT_METHOD_PLAYLIST_ORDER, - - - sortable = sortable + tracks - - elif type is 'playlists': - - playlists = [ - xbmcplugin.SORT_METHOD_TITLE, - xbmcplugin.SORT_METHOD_DURATION, - xbmcplugin.SORT_METHOD_DATE - ] - - sortable = sortable + playlists - - return sortable - - @plugin.action() -#@plugin.cached(cachetime) #if cache is enabled, cache data for the following function +#@plugin.cached(cachetime) #cache (in minutes) def list_tracks(params): menu_id = params.get('menu_id') @@ -611,171 +585,8 @@ def list_tracks(params): #run this function every time we get starred items. #ids can be a single ID or a list #using a set makes sure that IDs will be unique. -def stars_cache_update(ids,remove=False): - - #get existing cache set - starred = stars_cache_get() - - #make sure this is a list - if not isinstance(ids, list): - ids = [ids] - - #abord if empty - if len(ids) == 0: - return - - #parse items - for item_id in ids: - item_id = int(item_id) - if not remove: - starred.add(item_id) - else: - starred.remove(item_id) - - #store them - with plugin.get_storage() as storage: - storage['starred_ids'] = starred - - plugin.log('stars_cache_update:') - plugin.log(starred) - - -def stars_cache_get(): - with plugin.get_storage() as storage: - starred = storage.get('starred_ids',set()) - - plugin.log('stars_cache_get:') - plugin.log(starred) - return starred - -def is_starred(id): - starred = stars_cache_get() - id = int(id) - if id in starred: - return True - else: - return False - -def get_entry_track(item,params): - - menu_id = params.get('menu_id') - image = connection.getCoverArtUrl(item.get('coverArt')) - - entry = { - 'label': get_entry_track_label(item,params.get('hide_artist')), - 'thumb': image, - 'fanart': image, - 'url': plugin.get_url( - action= 'play_track', - id= item.get('id'), - menu_id= menu_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'), - 'date': item.get('created') - } - } - } - - #context menu actions - context_actions = [] - - if can_star('track',item.get('id')): - action_star = context_action_star('track',item.get('id')) - context_actions.append(action_star) - - if can_download('track',item.get('id')): - action_download = context_action_download('track',item.get('id')) - context_actions.append(action_download) - - if len(context_actions) > 0: - entry['context_menu'] = context_actions - - - return entry - -def get_starred_label(id,label): - if is_starred(id): - label = '[COLOR=FF00FF00]%s[/COLOR]' % label - return label - -def get_entry_track_label(item,hide_artist = False): - if hide_artist: - label = item.get('title', '') - else: - label = '%s - %s' % ( - item.get('artist', ''), - item.get('title', '') - ) - - return get_starred_label(item.get('id'),label) - -def get_entry_album_label(item,hide_artist = False): - if hide_artist: - label = item.get('name', '') - else: - label = '%s - %s' % (item.get('artist', ''), - item.get('name', '')) - return get_starred_label(item.get('id'),label) - - @plugin.action() -def play_track(params): - - id = params['id'] - plugin.log('play_track #' + id); - - # get connection - connection = get_connection() - - if connection is False: - return - - 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.get('page',1)) - page += 1 - - title = lang.getLocalizedString(30029) +"(%d)" % (page) - - return { - 'label': title, - 'url': plugin.get_url( - action= params.get('action',None), - page= page, - query_args= params.get('query_args',None) - ) - } - -def navigate_root(): - return { - 'label': lang.getLocalizedString(30030), - '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.action() -#@plugin.cached(cachetime) #if cache is enabled, cache data for the following function +#@plugin.cached(cachetime) #cache (in minutes) def list_playlists(params): # get connection @@ -806,126 +617,24 @@ def list_playlists(params): ) @plugin.action() -def browse_folders(params): +def play_track(params): + + id = params['id'] + plugin.log('play_track #' + id); + # get connection connection = get_connection() if connection is False: return - listing = [] - - # Get items - items = connection.walk_folders() - - # Iterate through items - for item in items: - entry = { - 'label': item.get('name'), - 'url': plugin.get_url( - action= 'browse_indexes', - folder_id= item.get('id'), - menu_id= params.get('menu_id') - - ) - } - listing.append(entry) - - if len(listing) == 1: - plugin.log('One single Media Folder found; do return listing from browse_indexes()...') - return browse_indexes(params) - else: - return plugin.create_listing(listing) - -@plugin.action() -def browse_indexes(params): - # get connection - connection = get_connection() - - if connection is False: - return - - listing = [] - - # Get items - # optional folder ID - folder_id = params.get('folder_id') - items = connection.walk_index(folder_id) - - # Iterate through items - for item in items: - entry = { - 'label': item.get('name'), - 'url': plugin.get_url( - action= 'list_directory', - id= item.get('id'), - menu_id= params.get('menu_id') - ) - } - listing.append(entry) - - return plugin.create_listing( - listing + url = connection.streamUrl(sid=id, + maxBitRate=Addon().get_setting('bitrate_streaming'), + tformat=Addon().get_setting('transcode_format_streaming') ) -@plugin.action() -def list_directory(params): - # get connection - connection = get_connection() - - if connection is False: - return + return url - listing = [] - - # Get items - id = params.get('id') - items = connection.walk_directory(id) - - # Iterate through items - for item in items: - - #is a directory - if (item.get('isDir')==True): - entry = { - 'label': item.get('title'), - 'url': plugin.get_url( - action= 'list_directory', - id= item.get('id'), - menu_id= params.get('menu_id') - ) - } - else: - entry = get_entry_track(item,params) - - - listing.append(entry) - - return plugin.create_listing( - listing - ) - -def get_entry_playlist(item,params): - image = connection.getCoverArtUrl(item.get('coverArt')) - return { - 'label': item.get('name'), - 'thumb': image, - 'fanart': image, - 'url': plugin.get_url( - action= 'list_tracks', - playlist_id= item.get('id'), - menu_id= params.get('menu_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')) - }} - } - -#star (or unstar) an item @plugin.action() def star_item(params): @@ -1001,6 +710,338 @@ def star_item(params): +@plugin.action() +def download_item(params): + + id= params.get('id'); #can be single or lists of IDs + type= params.get('type'); + + #validate path + download_folder = Addon().get_setting('download_folder') + + if not download_folder: + popup("Please set a directory for your downloads") + plugin.log_error("No directory set for downloads") + + #validate capability + if not can_download(type,id): + return; + + if type == 'track': + did_action = download_tracks(id) + elif type == 'album': + did_action = download_album(id) + + if did_action: + plugin.log('Downloaded %s #%s' % (type,id)) + popup('Item has been downloaded!') + + else: + plugin.log_error('Unable to downloaded %s #%s' % (type,id)) + + return did_action + +def get_entry_playlist(item,params): + image = connection.getCoverArtUrl(item.get('coverArt')) + return { + 'label': item.get('name'), + 'thumb': image, + 'fanart': image, + 'url': plugin.get_url( + action= 'list_tracks', + playlist_id= item.get('id'), + menu_id= params.get('menu_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')) + }} + } + +#star (or unstar) an item +def get_entry_artist(item,params): + image = connection.getCoverArtUrl(item.get('coverArt')) + return { + 'label': get_starred_label(item.get('id'),item.get('name')), + 'thumb': image, + 'fanart': image, + 'url': plugin.get_url( + action= 'list_albums', + artist_id= item.get('id'), + menu_id= params.get('menu_id') + ), + 'info': { + 'music': { ##http://romanvm.github.io/Kodistubs/_autosummary/xbmcgui.html#xbmcgui.ListItem.setInfo + 'count': item.get('albumCount'), + 'artist': item.get('name') + } + } + } + +def get_entry_album(item, params): + + image = connection.getCoverArtUrl(item.get('coverArt')) + + entry = { + 'label': get_entry_album_label(item,params.get('hide_artist',False)), + 'thumb': image, + 'fanart': image, + 'url': plugin.get_url( + action= 'list_tracks', + album_id= item.get('id'), + hide_artist= item.get('hide_artist'), + menu_id= params.get('menu_id') + ), + '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') + } + } + } + + #context menu actions + context_actions = [] + + if can_star('album',item.get('id')): + action_star = context_action_star('album',item.get('id')) + context_actions.append(action_star) + + if can_download('album',item.get('id')): + action_download = context_action_download('album',item.get('id')) + context_actions.append(action_download) + + if len(context_actions) > 0: + entry['context_menu'] = context_actions + + return entry + +#sort method for list types +#https://github.com/xbmc/xbmc/blob/master/xbmc/SortFileItem.h +#TO FIX _DATE or _DATEADDED ? +def get_entry_track(item,params): + + menu_id = params.get('menu_id') + image = connection.getCoverArtUrl(item.get('coverArt')) + + entry = { + 'label': get_entry_track_label(item,params.get('hide_artist')), + 'thumb': image, + 'fanart': image, + 'url': plugin.get_url( + action= 'play_track', + id= item.get('id'), + menu_id= menu_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'), + 'date': item.get('created') + } + } + } + + #context menu actions + context_actions = [] + + if can_star('track',item.get('id')): + action_star = context_action_star('track',item.get('id')) + context_actions.append(action_star) + + if can_download('track',item.get('id')): + action_download = context_action_download('track',item.get('id')) + context_actions.append(action_download) + + if len(context_actions) > 0: + entry['context_menu'] = context_actions + + + return entry + +def get_starred_label(id,label): + if is_starred(id): + label = '[COLOR=FF00FF00]%s[/COLOR]' % label + return label + +def get_entry_track_label(item,hide_artist = False): + if hide_artist: + label = item.get('title', '') + else: + label = '%s - %s' % ( + item.get('artist', ''), + item.get('title', '') + ) + + return get_starred_label(item.get('id'),label) + +def get_entry_album_label(item,hide_artist = False): + if hide_artist: + label = item.get('name', '') + else: + label = '%s - %s' % (item.get('artist', ''), + item.get('name', '')) + return get_starred_label(item.get('id'),label) + + +def get_sort_methods(type,params): + + #TO FIX + #actually it seems possible to 'restore' the default sorting (by labels) + #so our starred items don't get colorized. + #so do not sort stuff + return [] + + sortable = [ + xbmcplugin.SORT_METHOD_NONE, + xbmcplugin.SORT_METHOD_LABEL, + xbmcplugin.SORT_METHOD_UNSORTED + ] + + if type is 'artists': + + artists = [ + xbmcplugin.SORT_METHOD_ARTIST + ] + + sortable = sortable + artists + + elif type is 'albums': + + albums = [ + xbmcplugin.SORT_METHOD_ALBUM, + xbmcplugin.SORT_METHOD_DURATION, + xbmcplugin.SORT_METHOD_DATE, + #xbmcplugin.SORT_METHOD_YEAR + ] + + if not params.get('hide_artist',False): + albums.append(xbmcplugin.SORT_METHOD_ARTIST) + + sortable = sortable + albums + + elif type is 'tracks': + + tracks = [ + xbmcplugin.SORT_METHOD_TITLE, + xbmcplugin.SORT_METHOD_ALBUM, + xbmcplugin.SORT_METHOD_TRACKNUM, + #xbmcplugin.SORT_METHOD_YEAR, + xbmcplugin.SORT_METHOD_GENRE, + xbmcplugin.SORT_METHOD_SIZE, + xbmcplugin.SORT_METHOD_DURATION, + xbmcplugin.SORT_METHOD_DATE, + xbmcplugin.SORT_METHOD_BITRATE + ] + + if not params.get('hide_artist',False): + tracks.append(xbmcplugin.SORT_METHOD_ARTIST) + + if params.get('playlist_id',False): + xbmcplugin.SORT_METHOD_PLAYLIST_ORDER, + + + sortable = sortable + tracks + + elif type is 'playlists': + + playlists = [ + xbmcplugin.SORT_METHOD_TITLE, + xbmcplugin.SORT_METHOD_DURATION, + xbmcplugin.SORT_METHOD_DATE + ] + + sortable = sortable + playlists + + return sortable + + +def stars_cache_update(ids,remove=False): + + #get existing cache set + starred = stars_cache_get() + + #make sure this is a list + if not isinstance(ids, list): + ids = [ids] + + #abord if empty + if len(ids) == 0: + return + + #parse items + for item_id in ids: + item_id = int(item_id) + if not remove: + starred.add(item_id) + else: + starred.remove(item_id) + + #store them + with plugin.get_storage() as storage: + storage['starred_ids'] = starred + + plugin.log('stars_cache_update:') + plugin.log(starred) + + +def stars_cache_get(): + with plugin.get_storage() as storage: + starred = storage.get('starred_ids',set()) + + plugin.log('stars_cache_get:') + plugin.log(starred) + return starred + +def is_starred(id): + starred = stars_cache_get() + id = int(id) + if id in starred: + return True + else: + return False + +def navigate_next(params): + + page = int(params.get('page',1)) + page += 1 + + title = lang.getLocalizedString(30029) +"(%d)" % (page) + + return { + 'label': title, + 'url': plugin.get_url( + action= params.get('action',None), + page= page, + query_args= params.get('query_args',None) + ) + } + +def navigate_root(): + return { + 'label': lang.getLocalizedString(30030), + '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') + def context_action_star(type,id): starred = is_starred(id) @@ -1060,37 +1101,6 @@ def can_download(type,id = None): elif type == 'album': return True -@plugin.action() -def download_item(params): - - id= params.get('id'); #can be single or lists of IDs - type= params.get('type'); - - #validate path - download_folder = Addon().get_setting('download_folder') - - if not download_folder: - popup("Please set a directory for your downloads") - plugin.log_error("No directory set for downloads") - - #validate capability - if not can_download(type,id): - return; - - if type == 'track': - did_action = download_tracks(id) - elif type == 'album': - did_action = download_album(id) - - if did_action: - plugin.log('Downloaded %s #%s' % (type,id)) - popup('Item has been downloaded!') - - else: - plugin.log_error('Unable to downloaded %s #%s' % (type,id)) - - return did_action - def download_tracks(ids): #popup is fired before, in download_item From ebf82b821890d35a7cb050a2f2323cf9b9a5a2ab Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Mon, 9 Jan 2017 10:46:11 +0100 Subject: [PATCH 06/13] fixes for localized strings --- main.py | 6 +++--- resources/language/English/strings.po | 14 +++++++++++++- resources/language/French/strings.po | 16 ++++++++++++++-- resources/language/German/strings.po | 15 +++++++++++++-- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/main.py b/main.py index 34f0171..f4ca09e 100644 --- a/main.py +++ b/main.py @@ -82,7 +82,7 @@ def root(params): menus = { 'folders': { - 'name': 'Browse', + 'name': lang.getLocalizedString(30038), 'callback': 'browse_folders', 'thumb': None }, @@ -215,11 +215,11 @@ def menu_tracks(params): menus = { 'tracks_starred': { - 'name': 'Starred tracks', + 'name': lang.getLocalizedString(30036), 'thumb': None }, 'tracks_random': { - 'name': 'Random tracks', + 'name': lang.getLocalizedString(30037), 'thumb': None } } diff --git a/resources/language/English/strings.po b/resources/language/English/strings.po index 51a129d..540d226 100644 --- a/resources/language/English/strings.po +++ b/resources/language/English/strings.po @@ -73,7 +73,7 @@ msgid "Allow self signed certificates" msgstr "" msgctxt "#30017" -msgid "Cache (in minutes) - not yet implemented" +msgid "Cache (in minutes)" msgstr "" msgctxt "#30018" @@ -140,3 +140,15 @@ msgstr "" msgctxt "#30035" msgid "Download" msgstr "" + +msgctxt "#30036" +msgid "Starred tracks" +msgstr "" + +msgctxt "#30037" +msgid "Random tracks" +msgstr "" + +msgctxt "#30038" +msgid "Browse" +msgstr "" diff --git a/resources/language/French/strings.po b/resources/language/French/strings.po index 72c780d..cb2c2cf 100644 --- a/resources/language/French/strings.po +++ b/resources/language/French/strings.po @@ -73,8 +73,8 @@ msgid "Allow self signed certificates" msgstr "Autoriser les certificats auto-signés" msgctxt "#30017" -msgid "Cache (in minutes) - not yet implemented" -msgstr "Cache (en minutes) - pas encore implémenté" +msgid "Cache (in minutes)" +msgstr "Cache (en minutes)" msgctxt "#30018" msgid "Cache datas time" @@ -139,3 +139,15 @@ msgstr "Retirer des favoris Subsonic" msgctxt "#30035" msgid "Download" msgstr "Télécharger" + +msgctxt "#30036" +msgid "Starred tracks" +msgstr "Pistes favorites" + +msgctxt "#30037" +msgid "Random tracks" +msgstr "Pistes au hasard" + +msgctxt "#30038" +msgid "Parcourir" +msgstr "" diff --git a/resources/language/German/strings.po b/resources/language/German/strings.po index 9f5650a..0b4dbe8 100644 --- a/resources/language/German/strings.po +++ b/resources/language/German/strings.po @@ -73,8 +73,8 @@ msgid "Allow self signed certificates" msgstr "Erlaube eigensignierte Zertifikate" msgctxt "#30017" -msgid "Cache (in minutes) - not yet implemented" -msgstr "Speicher (in Minuten) - noch nicht eingebaut" +msgid "Cache (in minutes)" +msgstr "Speicher (in Minuten)" msgctxt "#30018" msgid "Cache datas time" @@ -140,3 +140,14 @@ msgctxt "#30035" msgid "Download" msgstr "Herunterladen" +msgctxt "#30036" +msgid "Starred tracks" +msgstr "Lieblings lieder" + +msgctxt "#30037" +msgid "Random tracks" +msgstr "Zufällig lieder" + +msgctxt "#30038" +msgid "Parcourir" +msgstr "Durchsuchen" From 8c2ffe006c36124e78d576e263582ca4dc109a68 Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Mon, 9 Jan 2017 10:46:24 +0100 Subject: [PATCH 07/13] localized strings : settings typo --- resources/settings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/settings.xml b/resources/settings.xml index eb8b1fa..5f3affc 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -20,9 +20,9 @@ - - - + + + From 3a0efadebb6886e356ed8aa0db5f233468401647 Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Mon, 9 Jan 2017 10:48:30 +0100 Subject: [PATCH 08/13] use Addon().get_localized_string (simpleplugin) --- main.py | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/main.py b/main.py index f4ca09e..c82b923 100644 --- a/main.py +++ b/main.py @@ -15,8 +15,6 @@ import shutil import dateutil.parser from datetime import datetime -lang = xbmcaddon.Addon() - # Add the /lib folder to sys sys.path.append(xbmc.translatePath(os.path.join(xbmcaddon.Addon("plugin.audio.subsonic").getAddonInfo("path"), "lib"))) @@ -82,27 +80,27 @@ def root(params): menus = { 'folders': { - 'name': lang.getLocalizedString(30038), + 'name': Addon().get_localized_string(30038), 'callback': 'browse_folders', 'thumb': None }, 'library': { - 'name': lang.getLocalizedString(30019), + 'name': Addon().get_localized_string(30019), 'callback': 'browse_library', 'thumb': None }, 'albums': { - 'name': lang.getLocalizedString(30020), + 'name': Addon().get_localized_string(30020), 'callback': 'menu_albums', 'thumb': None }, 'tracks': { - 'name': lang.getLocalizedString(30021), + 'name': Addon().get_localized_string(30021), 'callback': 'menu_tracks', 'thumb': None }, 'playlists': { - 'name': lang.getLocalizedString(30022), + 'name': Addon().get_localized_string(30022), 'callback': 'list_playlists', 'thumb': None } @@ -149,22 +147,22 @@ def menu_albums(params): menus = { 'albums_newest': { - 'name': lang.getLocalizedString(30023), + 'name': Addon().get_localized_string(30023), 'thumb': None, 'args': {"ltype": "newest"} }, 'albums_frequent': { - 'name': lang.getLocalizedString(30024), + 'name': Addon().get_localized_string(30024), 'thumb': None, 'args': {"ltype": "frequent"} }, 'albums_recent': { - 'name': lang.getLocalizedString(30025), + 'name': Addon().get_localized_string(30025), 'thumb': None, 'args': {"ltype": "recent"} }, 'albums_random': { - 'name': lang.getLocalizedString(30026), + 'name': Addon().get_localized_string(30026), 'thumb': None, 'args': {"ltype": "random"} } @@ -215,11 +213,11 @@ def menu_tracks(params): menus = { 'tracks_starred': { - 'name': lang.getLocalizedString(30036), + 'name': Addon().get_localized_string(30036), 'thumb': None }, 'tracks_random': { - 'name': lang.getLocalizedString(30037), + 'name': Addon().get_localized_string(30037), 'thumb': None } } @@ -687,10 +685,10 @@ def star_item(params): if did_action: if unstar: - message = lang.getLocalizedString(30031) + message = Addon().get_localized_string(30031) plugin.log('Unstarred %s #%s' % (type,json.dumps(ids))) else: #star - message = lang.getLocalizedString(30032) + message = Addon().get_localized_string(30032) plugin.log('Starred %s #%s' % (type,json.dumps(ids))) stars_cache_update(ids,unstar) @@ -1020,7 +1018,7 @@ def navigate_next(params): page = int(params.get('page',1)) page += 1 - title = lang.getLocalizedString(30029) +"(%d)" % (page) + title = Addon().get_localized_string(30029) +"(%d)" % (page) return { 'label': title, @@ -1033,7 +1031,7 @@ def navigate_next(params): def navigate_root(): return { - 'label': lang.getLocalizedString(30030), + 'label': Addon().get_localized_string(30030), 'url': plugin.get_url(action='root') } @@ -1048,7 +1046,7 @@ def context_action_star(type,id): if not starred: - label = lang.getLocalizedString(30033) + label = Addon().get_localized_string(30033) else: @@ -1056,7 +1054,7 @@ def context_action_star(type,id): #so we don't have to fetch the starred status for each item #(since it is not available into the XML response from the server) - label = lang.getLocalizedString(30034) + label = Addon().get_localized_string(30034) return ( label, @@ -1085,7 +1083,7 @@ def can_star(type,ids = None): def context_action_download(type,id): - label = lang.getLocalizedString(30035) + label = Addon().get_localized_string(30035) return ( label, From 0344163cd5e63ac419bfe052a8466ec86f0716dd Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Mon, 9 Jan 2017 10:50:00 +0100 Subject: [PATCH 09/13] typo --- resources/language/French/strings.po | 4 ++-- resources/language/German/strings.po | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/language/French/strings.po b/resources/language/French/strings.po index cb2c2cf..f18855e 100644 --- a/resources/language/French/strings.po +++ b/resources/language/French/strings.po @@ -149,5 +149,5 @@ msgid "Random tracks" msgstr "Pistes au hasard" msgctxt "#30038" -msgid "Parcourir" -msgstr "" +msgid "Browse" +msgstr "Parcourir" diff --git a/resources/language/German/strings.po b/resources/language/German/strings.po index 0b4dbe8..da3b3d2 100644 --- a/resources/language/German/strings.po +++ b/resources/language/German/strings.po @@ -149,5 +149,5 @@ msgid "Random tracks" msgstr "Zufällig lieder" msgctxt "#30038" -msgid "Parcourir" +msgid "Browse" msgstr "Durchsuchen" From 290fa837b301c7e65030f559234ec854db3cd51c Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Mon, 9 Jan 2017 11:01:41 +0100 Subject: [PATCH 10/13] simpleplugin 2.1.0 --- lib/simpleplugin/__init__.py | 2 +- lib/simpleplugin/simpleplugin.py | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/simpleplugin/__init__.py b/lib/simpleplugin/__init__.py index 346c0ab..dbe08eb 100644 --- a/lib/simpleplugin/__init__.py +++ b/lib/simpleplugin/__init__.py @@ -1,4 +1,4 @@ -#v2.0.1 +#v2.1.0 #https://github.com/romanvm/script.module.simpleplugin/releases from simpleplugin import * \ No newline at end of file diff --git a/lib/simpleplugin/simpleplugin.py b/lib/simpleplugin/simpleplugin.py index 8fc6975..fdb227c 100644 --- a/lib/simpleplugin/simpleplugin.py +++ b/lib/simpleplugin/simpleplugin.py @@ -267,6 +267,16 @@ class Addon(object): """ return self._configdir + @property + def version(self): + """ + Addon version + + :return: addon version + :rtype: str + """ + return self._addon.getAddonInfo('version') + def get_localized_string(self, id_): """ Get localized UI string @@ -274,7 +284,7 @@ class Addon(object): :param id_: UI string ID :type id_: int :return: UI string in the current language - :rtype: unicode + :rtype: str """ return self._addon.getLocalizedString(id_).encode('utf-8') @@ -327,7 +337,7 @@ class Addon(object): value = str(value) self._addon.setSetting(id_, value) - def log(self, message, level=0): + def log(self, message, level=xbmc.LOGDEBUG): """ Add message to Kodi log starting with Addon ID @@ -339,7 +349,7 @@ class Addon(object): """ if isinstance(message, unicode): message = message.encode('utf-8') - xbmc.log('{0}: {1}'.format(self.id, message), level) + xbmc.log('{0} [v.{1}]: {2}'.format(self.id, self.version, message), level) def log_notice(self, message): """ @@ -879,7 +889,6 @@ class Plugin(Addon): self.log_debug('Creating listing from {0}'.format(str(context))) if context.content is not None: xbmcplugin.setContent(self._handle, context.content) # This must be at the beginning - listing = [] for item in context.listing: is_folder = item.get('is_folder', True) if item.get('list_item') is not None: @@ -889,8 +898,7 @@ class Plugin(Addon): if item.get('is_playable'): list_item.setProperty('IsPlayable', 'true') is_folder = False - listing.append((item['url'], list_item, is_folder)) - xbmcplugin.addDirectoryItems(self._handle, listing, len(listing)) + xbmcplugin.addDirectoryItem(self._handle, item['url'], list_item, is_folder) if context.sort_methods is not None: [xbmcplugin.addSortMethod(self._handle, method) for method in context.sort_methods] xbmcplugin.endOfDirectory(self._handle, @@ -912,4 +920,4 @@ class Plugin(Addon): list_item = xbmcgui.ListItem(path=context.path) else: list_item = self.create_list_item(context.play_item) - xbmcplugin.setResolvedUrl(self._handle, context.succeeded, list_item) + xbmcplugin.setResolvedUrl(self._handle, context.succeeded, list_item) \ No newline at end of file From 2482e56fdc294db18c09653cd3a794a107ee134c Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Mon, 9 Jan 2017 11:36:23 +0100 Subject: [PATCH 11/13] minor --- main.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/main.py b/main.py index c82b923..808172a 100644 --- a/main.py +++ b/main.py @@ -525,7 +525,6 @@ def list_tracks(params): # tracknumber += 1 # items[item]['tracknumber'] = tracknumber - # Starred # Starred elif menu_id == 'tracks_starred': generator = connection.walk_tracks_starred() @@ -571,12 +570,12 @@ def list_tracks(params): return plugin.create_listing( listing, - #succeeded = True, #if False Kodi won’t open a new listing and stays on the current level. - #update_listing = False, #if True, Kodi won’t open a sub-listing but refresh the current one. - #cache_to_disk = True, #cache this view to disk. - sort_methods=get_sort_methods('tracks',params), - #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’. + #succeeded = True, #if False Kodi won’t open a new listing and stays on the current level. + #update_listing = False, #if True, Kodi won’t open a sub-listing but refresh the current one. + #cache_to_disk = True, #cache this view to disk. + sort_methods= get_sort_methods('tracks',params), + #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’. ) #stars (persistent) cache is used to know what context action (star/unstar) we should display. @@ -821,9 +820,6 @@ def get_entry_album(item, params): return entry -#sort method for list types -#https://github.com/xbmc/xbmc/blob/master/xbmc/SortFileItem.h -#TO FIX _DATE or _DATEADDED ? def get_entry_track(item,params): menu_id = params.get('menu_id') @@ -897,11 +893,15 @@ def get_entry_album_label(item,hide_artist = False): def get_sort_methods(type,params): + #sort method for list types + #https://github.com/xbmc/xbmc/blob/master/xbmc/SortFileItem.h + #TO FIX _DATE or _DATEADDED ? #TO FIX #actually it seems possible to 'restore' the default sorting (by labels) #so our starred items don't get colorized. #so do not sort stuff + #see http://forum.kodi.tv/showthread.php?tid=293037 return [] sortable = [ From 60ebe928b005f6d43de4c5dbc6c1a6a761d80e35 Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Tue, 10 Jan 2017 18:36:34 +0100 Subject: [PATCH 12/13] minor --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a20202..6293b2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## v2.0.X Released XXX +* Upgrade to simpleplugin 2.1.0 * Browse/Library menus (file structure vs ID3 tags) * Added multilanguage support From e6b00afc278c236e1ecbd7e19f85582687793399 Mon Sep 17 00:00:00 2001 From: rickybiscus Date: Sat, 14 Jan 2017 12:38:50 +0100 Subject: [PATCH 13/13] version update --- CHANGELOG.md | 4 ++-- addon.xml | 2 +- main.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6293b2c..700b753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog -## v2.0.X -Released XXX +## v2.0.6 +Released 14 January 2017 * Upgrade to simpleplugin 2.1.0 * Browse/Library menus (file structure vs ID3 tags) * Added multilanguage support diff --git a/addon.xml b/addon.xml index c1ab10c..a093482 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + diff --git a/main.py b/main.py index 808172a..c17511f 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,7 @@ # Module: main # Author: G.Breant -# Created on: 04.10.2016 +# Created on: 14 January 2017 # License: GPL v.3 https://www.gnu.org/copyleft/gpl.html import os