2016-10-02 20:10:40 +02:00
|
|
|
|
#!/usr/bin/python
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
# Module: main
|
2016-10-04 02:20:17 +02:00
|
|
|
|
# Author: G.Breant
|
2017-01-14 12:38:50 +01:00
|
|
|
|
# Created on: 14 January 2017
|
2016-10-02 20:10:40 +02:00
|
|
|
|
# License: GPL v.3 https://www.gnu.org/copyleft/gpl.html
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
import xbmcvfs
|
2016-10-04 11:15:12 +02:00
|
|
|
|
import os
|
|
|
|
|
import xbmcaddon
|
2016-10-04 14:47:01 +02:00
|
|
|
|
import xbmcplugin
|
2016-10-04 01:33:22 +02:00
|
|
|
|
import xbmcgui
|
2016-10-02 20:10:40 +02:00
|
|
|
|
import json
|
2016-10-03 21:18:43 +02:00
|
|
|
|
import shutil
|
2016-10-04 01:33:22 +02:00
|
|
|
|
import dateutil.parser
|
|
|
|
|
from datetime import datetime
|
2021-06-22 08:35:39 +02:00
|
|
|
|
from collections import MutableMapping, namedtuple
|
2016-10-04 01:33:22 +02:00
|
|
|
|
|
2016-10-04 11:15:12 +02:00
|
|
|
|
# Add the /lib folder to sys
|
2021-06-22 08:35:39 +02:00
|
|
|
|
sys.path.append(xbmcvfs.translatePath(os.path.join(xbmcaddon.Addon("plugin.audio.subsonic").getAddonInfo("path"), "lib")))
|
2016-10-04 01:33:22 +02:00
|
|
|
|
|
2016-12-28 14:33:26 +01:00
|
|
|
|
|
2021-06-23 01:56:28 +02:00
|
|
|
|
import libsonic#Removed libsonic_extra
|
2016-10-04 16:56:36 +02:00
|
|
|
|
|
2016-10-04 11:15:12 +02:00
|
|
|
|
from simpleplugin import Plugin
|
|
|
|
|
from simpleplugin import Addon
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
# Create plugin instance
|
|
|
|
|
plugin = Plugin()
|
|
|
|
|
|
|
|
|
|
# initialize_gettext
|
2016-10-02 22:20:10 +02:00
|
|
|
|
#_ = plugin.initialize_gettext()
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
connection = None
|
2016-10-04 02:19:56 +02:00
|
|
|
|
cachetime = int(Addon().get_setting('cachetime'))
|
2021-06-28 06:06:59 +02:00
|
|
|
|
local_starred = set({})
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
ListContext = namedtuple('ListContext', ['listing', 'succeeded','update_listing', 'cache_to_disk','sort_methods', 'view_mode','content', 'category'])
|
|
|
|
|
PlayContext = namedtuple('PlayContext', ['path', 'play_item', 'succeeded'])
|
2016-10-02 20:10:40 +02:00
|
|
|
|
def popup(text, time=5000, image=None):
|
2016-10-02 22:24:03 +02:00
|
|
|
|
title = plugin.addon.getAddonInfo('name')
|
|
|
|
|
icon = plugin.addon.getAddonInfo('icon')
|
2016-10-02 20:10:40 +02:00
|
|
|
|
xbmc.executebuiltin('Notification(%s, %s, %d, %s)' % (title, text,
|
|
|
|
|
time, icon))
|
|
|
|
|
|
|
|
|
|
def get_connection():
|
|
|
|
|
global connection
|
|
|
|
|
|
2021-06-23 01:56:28 +02:00
|
|
|
|
if connection==None:
|
|
|
|
|
connected = False
|
2021-06-28 06:55:56 +02:00
|
|
|
|
# Create connection
|
2016-10-02 20:10:40 +02:00
|
|
|
|
try:
|
2021-06-22 08:35:39 +02:00
|
|
|
|
connection = libsonic.Connection(
|
|
|
|
|
baseUrl=Addon().get_setting('subsonic_url'),
|
|
|
|
|
username=Addon().get_setting('username', convert=False),
|
|
|
|
|
password=Addon().get_setting('password', convert=False),
|
2021-06-28 06:55:56 +02:00
|
|
|
|
port=Addon().get_setting('port'),
|
2021-06-22 08:35:39 +02:00
|
|
|
|
apiVersion=Addon().get_setting('apiversion'),
|
2021-06-28 06:55:56 +02:00
|
|
|
|
insecure=Addon().get_setting('insecure'),
|
|
|
|
|
legacyAuth=Addon().get_setting('legacyauth'),
|
|
|
|
|
useGET=Addon().get_setting('useget'),
|
|
|
|
|
)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
connected = connection.ping()
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connected==False:
|
2016-10-02 20:10:40 +02:00
|
|
|
|
popup('Connection error')
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
return connection
|
|
|
|
|
|
2016-10-02 22:20:10 +02:00
|
|
|
|
@plugin.action()
|
|
|
|
|
def root(params):
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2016-10-03 00:48:57 +02:00
|
|
|
|
return
|
|
|
|
|
|
2016-10-02 20:10:40 +02:00
|
|
|
|
listing = []
|
|
|
|
|
|
|
|
|
|
menus = {
|
2017-01-09 01:02:37 +01:00
|
|
|
|
'folders': {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'name': Addon().get_localized_string(30038),
|
2017-01-09 01:02:37 +01:00
|
|
|
|
'callback': 'browse_folders',
|
|
|
|
|
'thumb': None
|
|
|
|
|
},
|
|
|
|
|
'library': {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'name': Addon().get_localized_string(30019),
|
2017-01-09 01:02:37 +01:00
|
|
|
|
'callback': 'browse_library',
|
2016-10-02 20:10:40 +02:00
|
|
|
|
'thumb': None
|
|
|
|
|
},
|
|
|
|
|
'albums': {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'name': Addon().get_localized_string(30020),
|
2016-10-02 20:10:40 +02:00
|
|
|
|
'callback': 'menu_albums',
|
|
|
|
|
'thumb': None
|
|
|
|
|
},
|
|
|
|
|
'tracks': {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'name': Addon().get_localized_string(30021),
|
2016-10-02 20:10:40 +02:00
|
|
|
|
'callback': 'menu_tracks',
|
|
|
|
|
'thumb': None
|
|
|
|
|
},
|
|
|
|
|
'playlists': {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'name': Addon().get_localized_string(30022),
|
2016-10-02 20:10:40 +02:00
|
|
|
|
'callback': 'list_playlists',
|
|
|
|
|
'thumb': None
|
2017-04-02 15:06:49 +02:00
|
|
|
|
},
|
|
|
|
|
'search': {
|
|
|
|
|
'name': Addon().get_localized_string(30039),
|
|
|
|
|
'callback': 'search',
|
|
|
|
|
'thumb': None
|
|
|
|
|
},
|
2016-10-02 20:10:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 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'],
|
2016-10-03 03:07:19 +02:00
|
|
|
|
menu_id=mid
|
2016-10-02 20:10:40 +02:00
|
|
|
|
)
|
|
|
|
|
}) # Item label
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
add_directory_items(create_listing(
|
2016-10-02 20:10:40 +02:00
|
|
|
|
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.
|
2017-01-09 01:02:37 +01:00
|
|
|
|
sort_methods = None, #he list of integer constants representing virtual folder sort methods.
|
2016-10-02 20:10:40 +02:00
|
|
|
|
#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’.
|
2021-06-22 08:35:39 +02:00
|
|
|
|
))
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2016-10-02 22:20:10 +02:00
|
|
|
|
@plugin.action()
|
2016-10-02 20:10:40 +02:00
|
|
|
|
def menu_albums(params):
|
|
|
|
|
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2016-10-03 00:48:57 +02:00
|
|
|
|
return
|
|
|
|
|
|
2016-10-02 20:10:40 +02:00
|
|
|
|
listing = []
|
|
|
|
|
|
|
|
|
|
menus = {
|
2016-10-03 03:07:19 +02:00
|
|
|
|
'albums_newest': {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'name': Addon().get_localized_string(30023),
|
2016-10-02 20:10:40 +02:00
|
|
|
|
'thumb': None,
|
|
|
|
|
'args': {"ltype": "newest"}
|
|
|
|
|
},
|
2016-10-03 03:07:19 +02:00
|
|
|
|
'albums_frequent': {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'name': Addon().get_localized_string(30024),
|
2016-10-02 20:10:40 +02:00
|
|
|
|
'thumb': None,
|
|
|
|
|
'args': {"ltype": "frequent"}
|
|
|
|
|
},
|
2016-10-03 03:07:19 +02:00
|
|
|
|
'albums_recent': {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'name': Addon().get_localized_string(30025),
|
2016-10-02 20:10:40 +02:00
|
|
|
|
'thumb': None,
|
|
|
|
|
'args': {"ltype": "recent"}
|
|
|
|
|
},
|
2016-10-03 03:07:19 +02:00
|
|
|
|
'albums_random': {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'name': Addon().get_localized_string(30026),
|
2016-10-02 20:10:40 +02:00
|
|
|
|
'thumb': None,
|
|
|
|
|
'args': {"ltype": "random"}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Iterate through categories
|
|
|
|
|
|
2016-10-03 03:07:19 +02:00
|
|
|
|
for menu_id in menus:
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2016-10-03 03:07:19 +02:00
|
|
|
|
menu = menus.get(menu_id)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
# 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,
|
2016-10-03 03:07:19 +02:00
|
|
|
|
query_args= json.dumps(menu.get('args')),
|
|
|
|
|
menu_id= menu_id
|
2016-10-02 20:10:40 +02:00
|
|
|
|
)
|
|
|
|
|
}) # Item label
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
add_directory_items(create_listing(
|
2016-10-02 20:10:40 +02:00
|
|
|
|
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 = 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’.
|
2021-06-22 08:35:39 +02:00
|
|
|
|
))
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2016-10-02 22:20:10 +02:00
|
|
|
|
@plugin.action()
|
2016-10-02 20:10:40 +02:00
|
|
|
|
def menu_tracks(params):
|
|
|
|
|
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2016-10-03 00:48:57 +02:00
|
|
|
|
return
|
|
|
|
|
|
2016-10-02 20:10:40 +02:00
|
|
|
|
listing = []
|
|
|
|
|
|
|
|
|
|
menus = {
|
2016-10-03 03:07:19 +02:00
|
|
|
|
'tracks_starred': {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'name': Addon().get_localized_string(30036),
|
2016-10-04 12:45:01 +02:00
|
|
|
|
'thumb': None
|
2016-10-04 02:52:42 +02:00
|
|
|
|
},
|
|
|
|
|
'tracks_random': {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'name': Addon().get_localized_string(30037),
|
2016-10-04 02:52:42 +02:00
|
|
|
|
'thumb': None
|
2016-10-02 20:10:40 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Iterate through categories
|
|
|
|
|
|
2016-10-03 03:07:19 +02:00
|
|
|
|
for menu_id in menus:
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2016-10-03 03:07:19 +02:00
|
|
|
|
menu = menus.get(menu_id)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
# 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(
|
2016-10-03 15:57:52 +02:00
|
|
|
|
action= 'list_tracks',
|
2016-10-04 12:45:01 +02:00
|
|
|
|
menu_id= menu_id
|
2016-10-03 03:07:19 +02:00
|
|
|
|
)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
}) # Item label
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
add_directory_items(create_listing(
|
2016-10-02 20:10:40 +02:00
|
|
|
|
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 = 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’.
|
2021-06-22 08:35:39 +02:00
|
|
|
|
))
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2016-10-02 22:20:10 +02:00
|
|
|
|
@plugin.action()
|
2017-01-09 10:45:12 +01:00
|
|
|
|
#@plugin.cached(cachetime) # cache (in minutes)
|
|
|
|
|
def browse_folders(params):
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2017-01-09 10:45:12 +01:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
listing = []
|
|
|
|
|
|
|
|
|
|
# Get items
|
2021-06-22 08:35:39 +02:00
|
|
|
|
items = walk_folders()
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
# 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:
|
2021-06-22 08:35:39 +02:00
|
|
|
|
add_directory_items(create_listing(listing))
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
@plugin.action()
|
|
|
|
|
#@plugin.cached(cachetime) # cache (in minutes)
|
|
|
|
|
def browse_indexes(params):
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2017-01-09 10:45:12 +01:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
listing = []
|
|
|
|
|
|
|
|
|
|
# Get items
|
|
|
|
|
# optional folder ID
|
|
|
|
|
folder_id = params.get('folder_id')
|
2021-06-22 08:35:39 +02:00
|
|
|
|
items = walk_index(folder_id)
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
add_directory_items(create_listing(
|
2017-01-09 10:45:12 +01:00
|
|
|
|
listing
|
2021-06-22 08:35:39 +02:00
|
|
|
|
))
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
@plugin.action()
|
|
|
|
|
#@plugin.cached(cachetime) # cache (in minutes)
|
|
|
|
|
def list_directory(params):
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2017-01-09 10:45:12 +01:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
listing = []
|
|
|
|
|
|
|
|
|
|
# Get items
|
|
|
|
|
id = params.get('id')
|
2021-06-22 08:35:39 +02:00
|
|
|
|
items = walk_directory(id)
|
2021-06-28 06:06:59 +02:00
|
|
|
|
|
2017-01-09 10:45:12 +01:00
|
|
|
|
# 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)
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
add_directory_items(create_listing(
|
2017-01-09 10:45:12 +01:00
|
|
|
|
listing
|
2021-06-22 08:35:39 +02:00
|
|
|
|
))
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
@plugin.action()
|
|
|
|
|
#@plugin.cached(cachetime) # cache (in minutes)
|
2017-01-09 01:02:37 +01:00
|
|
|
|
def browse_library(params):
|
2017-01-09 10:45:12 +01:00
|
|
|
|
"""
|
|
|
|
|
List artists from the library (ID3 tags)
|
|
|
|
|
"""
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
2016-10-03 00:48:57 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2016-10-03 00:48:57 +02:00
|
|
|
|
return
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
listing = []
|
|
|
|
|
|
|
|
|
|
# Get items
|
2021-06-22 08:35:39 +02:00
|
|
|
|
items = walk_artists()
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
# Iterate through items
|
|
|
|
|
|
|
|
|
|
for item in items:
|
2016-10-04 14:47:01 +02:00
|
|
|
|
entry = get_entry_artist(item,params)
|
2016-10-03 03:07:19 +02:00
|
|
|
|
|
|
|
|
|
#context menu actions
|
|
|
|
|
context_actions = []
|
|
|
|
|
if can_star('artist',item.get('id')):
|
2016-10-04 12:45:01 +02:00
|
|
|
|
action_star = context_action_star('artist',item.get('id'))
|
2016-10-03 03:07:19 +02:00
|
|
|
|
context_actions.append(action_star)
|
|
|
|
|
|
|
|
|
|
if len(context_actions) > 0:
|
|
|
|
|
entry['context_menu'] = context_actions
|
|
|
|
|
|
|
|
|
|
listing.append(entry)
|
2016-10-04 14:47:01 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
add_directory_items(create_listing(
|
2016-10-02 20:10:40 +02:00
|
|
|
|
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.
|
2016-10-04 14:47:01 +02:00
|
|
|
|
sort_methods = get_sort_methods('artists',params), #he list of integer constants representing virtual folder sort methods.
|
2016-10-02 20:10:40 +02:00
|
|
|
|
#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’.
|
2021-06-22 08:35:39 +02:00
|
|
|
|
))
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2016-10-02 22:20:10 +02:00
|
|
|
|
@plugin.action()
|
2017-01-09 10:45:12 +01:00
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
def list_albums(params):
|
|
|
|
|
|
2017-01-09 10:45:12 +01:00
|
|
|
|
"""
|
|
|
|
|
List albums from the library (ID3 tags)
|
|
|
|
|
"""
|
|
|
|
|
|
2016-10-02 20:10:40 +02:00
|
|
|
|
listing = []
|
|
|
|
|
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
2016-10-03 00:48:57 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2016-10-03 00:48:57 +02:00
|
|
|
|
return
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2016-10-04 02:52:42 +02:00
|
|
|
|
#query
|
|
|
|
|
query_args = {}
|
|
|
|
|
try:
|
|
|
|
|
query_args_json = params['query_args']
|
|
|
|
|
query_args = json.loads(query_args_json)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
#size
|
|
|
|
|
albums_per_page = int(Addon().get_setting('albums_per_page'))
|
|
|
|
|
query_args["size"] = albums_per_page
|
|
|
|
|
|
|
|
|
|
#offset
|
2016-10-04 02:52:42 +02:00
|
|
|
|
offset = int(params.get('page',1)) - 1;
|
2016-10-02 20:10:40 +02:00
|
|
|
|
if offset > 0:
|
|
|
|
|
query_args["offset"] = offset * albums_per_page
|
|
|
|
|
|
|
|
|
|
#debug
|
|
|
|
|
query_args_json = json.dumps(query_args)
|
|
|
|
|
plugin.log('list_albums with args:' + query_args_json);
|
|
|
|
|
|
|
|
|
|
#Get items
|
2016-10-04 15:12:06 +02:00
|
|
|
|
if 'artist_id' in params:
|
2021-06-22 08:35:39 +02:00
|
|
|
|
generator = walk_artist(params.get('artist_id'))
|
2016-10-04 15:12:06 +02:00
|
|
|
|
else:
|
2021-06-22 08:35:39 +02:00
|
|
|
|
generator = walk_albums(**query_args)
|
2016-10-04 14:57:24 +02:00
|
|
|
|
|
|
|
|
|
#make a list out of the generator so we can iterate it several times
|
|
|
|
|
items = list(generator)
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
#check if there==only one artist for this album (and then hide it)
|
2016-10-04 14:57:24 +02:00
|
|
|
|
artists = [item.get('artist',None) for item in items]
|
|
|
|
|
if len(artists) <= 1:
|
|
|
|
|
params['hide_artist'] = True
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
# Iterate through items
|
|
|
|
|
for item in items:
|
2016-10-04 14:47:01 +02:00
|
|
|
|
album = get_entry_album(item, params)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
listing.append(album)
|
|
|
|
|
|
2016-10-04 15:12:06 +02:00
|
|
|
|
# Root menu
|
2016-10-02 20:10:40 +02:00
|
|
|
|
link_root = navigate_root()
|
|
|
|
|
listing.append(link_root)
|
|
|
|
|
|
|
|
|
|
|
2016-10-04 15:12:06 +02:00
|
|
|
|
if not 'artist_id' in params:
|
|
|
|
|
# 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)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
add_directory_items(create_listing(
|
2016-10-02 20:10:40 +02:00
|
|
|
|
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.
|
2016-10-04 15:12:06 +02:00
|
|
|
|
sort_methods = get_sort_methods('albums',params),
|
2016-10-02 20:10:40 +02:00
|
|
|
|
#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’.
|
2021-06-22 08:35:39 +02:00
|
|
|
|
))
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2016-10-02 22:20:10 +02:00
|
|
|
|
@plugin.action()
|
2017-01-09 10:45:12 +01:00
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
def list_tracks(params):
|
2016-10-03 03:07:19 +02:00
|
|
|
|
|
|
|
|
|
menu_id = params.get('menu_id')
|
2016-10-02 20:10:40 +02:00
|
|
|
|
listing = []
|
|
|
|
|
|
2016-10-04 14:47:01 +02:00
|
|
|
|
|
2016-10-04 02:52:42 +02:00
|
|
|
|
#query
|
|
|
|
|
query_args = {}
|
|
|
|
|
try:
|
|
|
|
|
query_args_json = params['query_args']
|
|
|
|
|
query_args = json.loads(query_args_json)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
#size
|
|
|
|
|
tracks_per_page = int(Addon().get_setting('tracks_per_page'))
|
|
|
|
|
query_args["size"] = tracks_per_page
|
|
|
|
|
|
|
|
|
|
#offset
|
|
|
|
|
offset = int(params.get('page',1)) - 1;
|
|
|
|
|
if offset > 0:
|
|
|
|
|
query_args["offset"] = offset * tracks_per_page
|
|
|
|
|
|
|
|
|
|
#debug
|
|
|
|
|
query_args_json = json.dumps(query_args)
|
|
|
|
|
plugin.log('list_tracks with args:' + query_args_json);
|
|
|
|
|
|
2016-10-02 20:10:40 +02:00
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
2016-10-03 00:48:57 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2016-10-03 00:48:57 +02:00
|
|
|
|
return
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
# Album
|
|
|
|
|
if 'album_id' in params:
|
2021-06-22 08:35:39 +02:00
|
|
|
|
generator = walk_album(params['album_id'])
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
# Playlist
|
2016-10-04 02:52:42 +02:00
|
|
|
|
elif 'playlist_id' in params:
|
2021-06-22 08:35:39 +02:00
|
|
|
|
generator = walk_playlist(params['playlist_id'])
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
#TO FIX
|
|
|
|
|
#tracknumber = 0
|
|
|
|
|
#for item in items:
|
|
|
|
|
# tracknumber += 1
|
|
|
|
|
# items[item]['tracknumber'] = tracknumber
|
|
|
|
|
|
|
|
|
|
# Starred
|
2016-10-04 02:52:42 +02:00
|
|
|
|
elif menu_id == 'tracks_starred':
|
2021-06-22 08:35:39 +02:00
|
|
|
|
generator = walk_tracks_starred()
|
2016-10-04 12:45:01 +02:00
|
|
|
|
|
2016-10-04 02:52:42 +02:00
|
|
|
|
|
|
|
|
|
# Random
|
|
|
|
|
elif menu_id == 'tracks_random':
|
2021-06-22 08:35:39 +02:00
|
|
|
|
generator = walk_tracks_random(**query_args)
|
2016-10-04 02:52:42 +02:00
|
|
|
|
# Filters
|
|
|
|
|
#else:
|
|
|
|
|
#TO WORK
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2016-10-04 12:45:01 +02:00
|
|
|
|
#make a list out of the generator so we can iterate it several times
|
|
|
|
|
items = list(generator)
|
2016-10-04 14:57:24 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
#check if there==only one artist for this album (and then hide it)
|
2016-10-04 14:57:24 +02:00
|
|
|
|
artists = [item.get('artist',None) for item in items]
|
|
|
|
|
if len(artists) <= 1:
|
|
|
|
|
params['hide_artist'] = True
|
2016-10-04 12:45:01 +02:00
|
|
|
|
|
|
|
|
|
#update stars
|
|
|
|
|
if menu_id == 'tracks_starred':
|
|
|
|
|
ids_list = [item.get('id') for item in items]
|
|
|
|
|
stars_cache_update(ids_list)
|
2016-10-04 02:52:42 +02:00
|
|
|
|
|
2016-10-02 20:10:40 +02:00
|
|
|
|
# Iterate through items
|
|
|
|
|
key = 0;
|
|
|
|
|
for item in items:
|
2016-10-04 14:47:01 +02:00
|
|
|
|
track = get_entry_track(item,params)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
listing.append(track)
|
|
|
|
|
key +=1
|
|
|
|
|
|
|
|
|
|
# Root menu
|
2016-10-04 02:52:42 +02:00
|
|
|
|
#link_root = navigate_root()
|
|
|
|
|
#listing.append(link_root)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
# 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)
|
2016-10-04 14:47:01 +02:00
|
|
|
|
|
|
|
|
|
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
add_directory_items(create_listing(
|
2016-10-02 20:10:40 +02:00
|
|
|
|
listing,
|
2017-01-09 11:36:23 +01:00
|
|
|
|
#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’.
|
2021-06-22 08:35:39 +02:00
|
|
|
|
))
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
#stars (persistent) cache==used to know what context action (star/unstar) we should display.
|
2016-10-04 12:45:01 +02:00
|
|
|
|
#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.
|
2017-01-09 10:45:12 +01:00
|
|
|
|
@plugin.action()
|
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
|
|
|
|
def list_playlists(params):
|
2016-10-04 12:45:01 +02:00
|
|
|
|
|
2017-01-09 10:45:12 +01:00
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
2016-10-04 12:45:01 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2016-10-04 12:45:01 +02:00
|
|
|
|
return
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
listing = []
|
|
|
|
|
|
|
|
|
|
# Get items
|
2021-06-22 08:35:39 +02:00
|
|
|
|
items = walk_playlists()
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
# Iterate through items
|
|
|
|
|
|
|
|
|
|
for item in items:
|
|
|
|
|
entry = get_entry_playlist(item,params)
|
|
|
|
|
listing.append(entry)
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
add_directory_items(create_listing(
|
2017-01-09 10:45:12 +01:00
|
|
|
|
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('playlists',params), #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’.
|
2021-06-22 08:35:39 +02:00
|
|
|
|
))
|
2017-04-02 15:06:49 +02:00
|
|
|
|
@plugin.action()
|
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
|
|
|
|
def search(params):
|
|
|
|
|
|
|
|
|
|
dialog = xbmcgui.Dialog()
|
2017-04-02 17:44:14 +02:00
|
|
|
|
d = dialog.input(Addon().get_localized_string(30039), type=xbmcgui.INPUT_ALPHANUM)
|
2017-04-02 15:06:49 +02:00
|
|
|
|
if not d:
|
|
|
|
|
d = " "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2017-04-02 15:06:49 +02:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
listing = []
|
|
|
|
|
|
|
|
|
|
# Get items
|
|
|
|
|
items = connection.search2(query=d)
|
|
|
|
|
# Iterate through items
|
|
|
|
|
for item in items.get('searchResult2').get('song'):
|
|
|
|
|
entry = get_entry_track( item, params)
|
|
|
|
|
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:
|
2021-06-22 08:35:39 +02:00
|
|
|
|
add_directory_items(create_listing(listing))
|
2017-04-02 15:06:49 +02:00
|
|
|
|
|
|
|
|
|
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
@plugin.action()
|
|
|
|
|
def play_track(params):
|
|
|
|
|
|
|
|
|
|
id = params['id']
|
|
|
|
|
plugin.log('play_track #' + id);
|
|
|
|
|
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2017-01-09 10:45:12 +01:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
url = connection.streamUrl(sid=id,
|
|
|
|
|
maxBitRate=Addon().get_setting('bitrate_streaming'),
|
|
|
|
|
tformat=Addon().get_setting('transcode_format_streaming')
|
|
|
|
|
)
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
#return url
|
|
|
|
|
_set_resolved_url(resolve_url(url))
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
@plugin.action()
|
|
|
|
|
def star_item(params):
|
|
|
|
|
ids= params.get('ids'); #can be single or lists of IDs
|
|
|
|
|
unstar= params.get('unstar',False);
|
|
|
|
|
unstar = (unstar) and (unstar != 'None') and (unstar != 'False') #TO FIX better statement ?
|
|
|
|
|
type= params.get('type');
|
|
|
|
|
sids = albumIds = artistIds = None
|
|
|
|
|
|
|
|
|
|
#validate type
|
|
|
|
|
if type == 'track':
|
|
|
|
|
sids = ids
|
|
|
|
|
elif type == 'artist':
|
|
|
|
|
artistIds = ids
|
|
|
|
|
elif type == 'album':
|
|
|
|
|
albumIds = ids
|
|
|
|
|
|
|
|
|
|
#validate capability
|
|
|
|
|
if not can_star(type,ids):
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
#validate IDs
|
|
|
|
|
if (not sids and not artistIds and not albumIds):
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2017-01-09 10:45:12 +01:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
###
|
|
|
|
|
|
|
|
|
|
did_action = False
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
if unstar:
|
|
|
|
|
request = connection.unstar(sids, albumIds, artistIds)
|
2016-10-04 12:45:01 +02:00
|
|
|
|
else:
|
2017-01-09 10:45:12 +01:00
|
|
|
|
request = connection.star(sids, albumIds, artistIds)
|
|
|
|
|
if request['status'] == 'ok':
|
|
|
|
|
did_action = True
|
|
|
|
|
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
if did_action:
|
2016-10-04 12:45:01 +02:00
|
|
|
|
|
2017-01-09 10:45:12 +01:00
|
|
|
|
if unstar:
|
2017-01-09 10:48:30 +01:00
|
|
|
|
message = Addon().get_localized_string(30031)
|
2017-01-09 10:45:12 +01:00
|
|
|
|
plugin.log('Unstarred %s #%s' % (type,json.dumps(ids)))
|
|
|
|
|
else: #star
|
2017-01-09 10:48:30 +01:00
|
|
|
|
message = Addon().get_localized_string(30032)
|
2017-01-09 10:45:12 +01:00
|
|
|
|
plugin.log('Starred %s #%s' % (type,json.dumps(ids)))
|
|
|
|
|
|
|
|
|
|
stars_cache_update(ids,unstar)
|
|
|
|
|
|
|
|
|
|
popup(message)
|
|
|
|
|
|
|
|
|
|
#TO FIX clear starred lists caches ?
|
|
|
|
|
#TO FIX refresh current list after star set ?
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
if unstar:
|
|
|
|
|
plugin.log_error('Unable to unstar %s #%s' % (type,json.dumps(ids)))
|
|
|
|
|
else:
|
|
|
|
|
plugin.log_error('Unable to star %s #%s' % (type,json.dumps(ids)))
|
2016-10-04 12:45:01 +02:00
|
|
|
|
|
2021-06-28 06:06:59 +02:00
|
|
|
|
#return did_action
|
|
|
|
|
return
|
2016-10-04 12:45:01 +02:00
|
|
|
|
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
@plugin.action()
|
|
|
|
|
def download_item(params):
|
2016-10-04 12:45:01 +02:00
|
|
|
|
|
2017-01-09 10:45:12 +01:00
|
|
|
|
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")
|
2016-10-04 12:45:01 +02:00
|
|
|
|
|
2017-01-09 10:45:12 +01:00
|
|
|
|
#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!')
|
|
|
|
|
|
2016-10-04 12:45:01 +02:00
|
|
|
|
else:
|
2017-01-09 10:45:12 +01:00
|
|
|
|
plugin.log_error('Unable to downloaded %s #%s' % (type,id))
|
|
|
|
|
|
|
|
|
|
return did_action
|
2021-06-22 08:35:39 +02:00
|
|
|
|
|
2021-06-28 06:06:59 +02:00
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
2017-01-09 10:45:12 +01:00
|
|
|
|
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
|
2021-06-28 06:06:59 +02:00
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
2017-01-09 10:45:12 +01:00
|
|
|
|
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')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-28 06:06:59 +02:00
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
2017-01-09 10:45:12 +01:00
|
|
|
|
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
|
2016-10-04 12:45:01 +02:00
|
|
|
|
|
2021-06-28 06:06:59 +02:00
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
2016-10-04 14:47:01 +02:00
|
|
|
|
def get_entry_track(item,params):
|
2016-10-04 12:53:53 +02:00
|
|
|
|
|
|
|
|
|
menu_id = params.get('menu_id')
|
2016-10-15 01:33:43 +02:00
|
|
|
|
image = connection.getCoverArtUrl(item.get('coverArt'))
|
2016-10-04 12:53:53 +02:00
|
|
|
|
|
2016-10-03 03:07:19 +02:00
|
|
|
|
entry = {
|
2016-10-04 14:47:01 +02:00
|
|
|
|
'label': get_entry_track_label(item,params.get('hide_artist')),
|
2016-10-15 01:33:43 +02:00
|
|
|
|
'thumb': image,
|
|
|
|
|
'fanart': image,
|
2016-10-02 20:10:40 +02:00
|
|
|
|
'url': plugin.get_url(
|
2016-10-03 03:07:19 +02:00
|
|
|
|
action= 'play_track',
|
|
|
|
|
id= item.get('id'),
|
|
|
|
|
menu_id= menu_id
|
2016-10-02 20:10:40 +02:00
|
|
|
|
),
|
|
|
|
|
'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'),
|
2016-10-03 15:57:52 +02:00
|
|
|
|
'duration': item.get('duration'),
|
2016-10-04 14:47:01 +02:00
|
|
|
|
'date': item.get('created')
|
2016-10-03 03:07:19 +02:00
|
|
|
|
}
|
2016-10-02 20:10:40 +02:00
|
|
|
|
}
|
2016-10-03 03:07:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#context menu actions
|
|
|
|
|
context_actions = []
|
|
|
|
|
|
|
|
|
|
if can_star('track',item.get('id')):
|
2016-10-04 12:45:01 +02:00
|
|
|
|
action_star = context_action_star('track',item.get('id'))
|
2016-10-03 03:07:19 +02:00
|
|
|
|
context_actions.append(action_star)
|
|
|
|
|
|
|
|
|
|
if can_download('track',item.get('id')):
|
2016-10-03 15:57:52 +02:00
|
|
|
|
action_download = context_action_download('track',item.get('id'))
|
2016-10-03 03:07:19 +02:00
|
|
|
|
context_actions.append(action_download)
|
|
|
|
|
|
|
|
|
|
if len(context_actions) > 0:
|
|
|
|
|
entry['context_menu'] = context_actions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return entry
|
|
|
|
|
|
2021-06-28 06:06:59 +02:00
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
2016-10-04 15:12:06 +02:00
|
|
|
|
def get_starred_label(id,label):
|
|
|
|
|
if is_starred(id):
|
|
|
|
|
label = '[COLOR=FF00FF00]%s[/COLOR]' % label
|
|
|
|
|
return label
|
|
|
|
|
|
2021-06-28 06:06:59 +02:00
|
|
|
|
def is_starred(id):
|
|
|
|
|
starred = stars_cache_get()
|
|
|
|
|
id = int(id)
|
|
|
|
|
if id in starred:
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
2016-10-04 14:47:01 +02:00
|
|
|
|
def get_entry_track_label(item,hide_artist = False):
|
|
|
|
|
if hide_artist:
|
|
|
|
|
label = item.get('title', '<Unknown>')
|
|
|
|
|
else:
|
|
|
|
|
label = '%s - %s' % (
|
|
|
|
|
item.get('artist', '<Unknown>'),
|
|
|
|
|
item.get('title', '<Unknown>')
|
|
|
|
|
)
|
2016-10-04 15:12:06 +02:00
|
|
|
|
|
|
|
|
|
return get_starred_label(item.get('id'),label)
|
|
|
|
|
|
2021-06-28 06:06:59 +02:00
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
2016-10-04 15:12:06 +02:00
|
|
|
|
def get_entry_album_label(item,hide_artist = False):
|
|
|
|
|
if hide_artist:
|
|
|
|
|
label = item.get('name', '<Unknown>')
|
|
|
|
|
else:
|
|
|
|
|
label = '%s - %s' % (item.get('artist', '<Unknown>'),
|
|
|
|
|
item.get('name', '<Unknown>'))
|
|
|
|
|
return get_starred_label(item.get('id'),label)
|
2016-10-04 14:47:01 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
2017-01-09 10:45:12 +01:00
|
|
|
|
def get_sort_methods(type,params):
|
2017-01-09 11:36:23 +01:00
|
|
|
|
#sort method for list types
|
|
|
|
|
#https://github.com/xbmc/xbmc/blob/master/xbmc/SortFileItem.h
|
|
|
|
|
#TO FIX _DATE or _DATEADDED ?
|
2016-10-03 00:48:57 +02:00
|
|
|
|
|
2017-01-09 10:45:12 +01:00
|
|
|
|
#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
|
2017-01-09 11:36:23 +01:00
|
|
|
|
#see http://forum.kodi.tv/showthread.php?tid=293037
|
2017-01-09 10:45:12 +01:00
|
|
|
|
return []
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
2017-01-09 10:45:12 +01:00
|
|
|
|
sortable = [
|
|
|
|
|
xbmcplugin.SORT_METHOD_NONE,
|
|
|
|
|
xbmcplugin.SORT_METHOD_LABEL,
|
|
|
|
|
xbmcplugin.SORT_METHOD_UNSORTED
|
|
|
|
|
]
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if type=='artists':
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
artists = [
|
|
|
|
|
xbmcplugin.SORT_METHOD_ARTIST
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
sortable = sortable + artists
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
elif type=='albums':
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
elif type=='tracks':
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
elif type=='playlists':
|
2017-01-09 10:45:12 +01:00
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
#make sure this==a list
|
2017-01-09 10:45:12 +01:00
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
2021-06-28 06:55:56 +02:00
|
|
|
|
def stars_cache_get(): #Retrieving stars from cache is too slow, so load to local variable
|
2021-06-28 06:06:59 +02:00
|
|
|
|
global local_starred
|
|
|
|
|
plugin.log(len(local_starred))
|
|
|
|
|
if(len(local_starred)>0):
|
|
|
|
|
plugin.log('stars already loaded:')
|
|
|
|
|
plugin.log(local_starred)
|
|
|
|
|
return(local_starred)
|
2017-01-09 10:45:12 +01:00
|
|
|
|
else:
|
2021-06-28 06:06:59 +02:00
|
|
|
|
with plugin.get_storage() as storage:
|
|
|
|
|
local_starred = storage.get('starred_ids',set())
|
|
|
|
|
plugin.log('stars_cache_get:')
|
|
|
|
|
plugin.log(local_starred)
|
|
|
|
|
return local_starred
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
def navigate_next(params):
|
|
|
|
|
|
2016-10-04 15:12:06 +02:00
|
|
|
|
page = int(params.get('page',1))
|
2016-10-02 20:10:40 +02:00
|
|
|
|
page += 1
|
|
|
|
|
|
2017-01-09 10:48:30 +01:00
|
|
|
|
title = Addon().get_localized_string(30029) +"(%d)" % (page)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'label': title,
|
|
|
|
|
'url': plugin.get_url(
|
2016-10-04 15:12:06 +02:00
|
|
|
|
action= params.get('action',None),
|
2016-10-02 20:10:40 +02:00
|
|
|
|
page= page,
|
2016-10-04 15:12:06 +02:00
|
|
|
|
query_args= params.get('query_args',None)
|
2016-10-02 20:10:40 +02:00
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def navigate_root():
|
|
|
|
|
return {
|
2017-01-09 10:48:30 +01:00
|
|
|
|
'label': Addon().get_localized_string(30030),
|
2016-10-02 20:10:40 +02:00
|
|
|
|
'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')
|
|
|
|
|
|
2016-10-04 12:45:01 +02:00
|
|
|
|
def context_action_star(type,id):
|
2016-10-03 03:07:19 +02:00
|
|
|
|
|
2016-10-04 12:45:01 +02:00
|
|
|
|
starred = is_starred(id)
|
2016-10-03 15:57:52 +02:00
|
|
|
|
|
2016-10-04 12:45:01 +02:00
|
|
|
|
if not starred:
|
2016-10-03 15:57:52 +02:00
|
|
|
|
|
2017-01-09 10:48:30 +01:00
|
|
|
|
label = Addon().get_localized_string(30033)
|
2016-10-03 15:57:52 +02:00
|
|
|
|
|
|
|
|
|
else:
|
2016-10-03 03:07:19 +02:00
|
|
|
|
|
2016-10-03 15:57:52 +02:00
|
|
|
|
#Should be available only in the stars lists;
|
|
|
|
|
#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)
|
|
|
|
|
|
2017-01-09 10:48:30 +01:00
|
|
|
|
label = Addon().get_localized_string(30034)
|
2016-10-03 15:57:52 +02:00
|
|
|
|
|
2021-06-28 06:06:59 +02:00
|
|
|
|
xbmc.log('Context action star returning RunPlugin(%s)' % plugin.get_url(action='star_item',type=type,ids=id,unstar=starred),xbmc.LOGINFO)
|
2016-10-03 15:57:52 +02:00
|
|
|
|
return (
|
|
|
|
|
label,
|
2021-06-28 06:06:59 +02:00
|
|
|
|
'RunPlugin(%s)' % plugin.get_url(action='star_item',type=type,ids=id,unstar=starred)
|
2016-10-03 15:57:52 +02:00
|
|
|
|
)
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
#Subsonic API says this==supported for artist,tracks and albums,
|
2016-10-03 03:07:19 +02:00
|
|
|
|
#But I can see it available only for tracks on Subsonic 5.3, so disable it.
|
2016-10-03 15:57:52 +02:00
|
|
|
|
def can_star(type,ids = None):
|
|
|
|
|
|
|
|
|
|
if not ids:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
if not isinstance(ids, list) or isinstance(ids, tuple):
|
|
|
|
|
ids = [ids]
|
|
|
|
|
if len(ids) == 0:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
if type == 'track':
|
2016-10-03 03:07:19 +02:00
|
|
|
|
return True
|
2016-10-03 15:57:52 +02:00
|
|
|
|
elif type == 'artist':
|
2016-10-03 03:07:19 +02:00
|
|
|
|
return False
|
2016-10-03 15:57:52 +02:00
|
|
|
|
elif type == 'album':
|
2016-10-03 03:07:19 +02:00
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def context_action_download(type,id):
|
2017-01-03 21:14:00 +01:00
|
|
|
|
|
2017-01-09 10:48:30 +01:00
|
|
|
|
label = Addon().get_localized_string(30035)
|
2017-01-03 21:14:00 +01:00
|
|
|
|
|
2016-10-03 15:57:52 +02:00
|
|
|
|
return (
|
|
|
|
|
label,
|
2021-06-28 06:06:59 +02:00
|
|
|
|
'RunPlugin(%s)' % plugin.get_url(action='download_item',type=type,id=id)
|
2016-10-03 15:57:52 +02:00
|
|
|
|
)
|
2016-10-03 03:07:19 +02:00
|
|
|
|
|
2016-10-03 15:57:52 +02:00
|
|
|
|
def can_download(type,id = None):
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if id==None:
|
2016-10-03 03:07:19 +02:00
|
|
|
|
return False
|
|
|
|
|
|
2016-10-03 15:57:52 +02:00
|
|
|
|
if type == 'track':
|
|
|
|
|
return True
|
|
|
|
|
elif type == 'album':
|
|
|
|
|
return True
|
|
|
|
|
|
2016-10-03 21:18:43 +02:00
|
|
|
|
def download_tracks(ids):
|
2016-10-03 15:57:52 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
#popup==fired before, in download_item
|
2016-10-03 21:18:43 +02:00
|
|
|
|
download_folder = Addon().get_setting('download_folder')
|
|
|
|
|
if not download_folder:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if not ids:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
#make list
|
|
|
|
|
if not isinstance(ids, list) or isinstance(ids, tuple):
|
|
|
|
|
ids = [ids]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ids_count = len(ids)
|
|
|
|
|
|
|
|
|
|
#check if empty
|
|
|
|
|
if ids_count == 0:
|
|
|
|
|
return False
|
|
|
|
|
|
2016-10-04 01:06:52 +02:00
|
|
|
|
plugin.log('download_tracks IDs:')
|
|
|
|
|
plugin.log(json.dumps(ids))
|
|
|
|
|
|
2016-10-03 15:57:52 +02:00
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2016-10-03 15:57:52 +02:00
|
|
|
|
return
|
2016-10-04 01:06:52 +02:00
|
|
|
|
|
|
|
|
|
#progress...
|
|
|
|
|
pc_step = 100/ids_count
|
|
|
|
|
pc_progress = 0
|
|
|
|
|
ids_parsed = 0
|
|
|
|
|
progressdialog = xbmcgui.DialogProgress()
|
|
|
|
|
progressdialog.create("Downloading tracks...") #Title
|
|
|
|
|
|
2016-10-03 21:18:43 +02:00
|
|
|
|
for id in ids:
|
2016-10-04 01:06:52 +02:00
|
|
|
|
|
|
|
|
|
if (progressdialog.iscanceled()):
|
|
|
|
|
return False
|
|
|
|
|
|
2016-10-03 21:18:43 +02:00
|
|
|
|
# debug
|
|
|
|
|
plugin.log('Trying to download track #'+str(id))
|
2016-10-03 15:57:52 +02:00
|
|
|
|
|
2016-10-04 01:06:52 +02:00
|
|
|
|
# get track infos
|
2016-10-03 21:18:43 +02:00
|
|
|
|
response = connection.getSong(id);
|
2016-10-04 01:06:52 +02:00
|
|
|
|
track = response.get('song')
|
2016-10-03 21:20:55 +02:00
|
|
|
|
plugin.log('Track info :')
|
2016-10-04 01:06:52 +02:00
|
|
|
|
plugin.log(track)
|
|
|
|
|
|
|
|
|
|
# progress bar
|
|
|
|
|
pc_progress = ids_parsed * pc_step
|
2016-10-04 14:47:01 +02:00
|
|
|
|
progressdialog.update(pc_progress, 'Getting track informations...',get_entry_track_label(track))
|
2016-10-03 15:57:52 +02:00
|
|
|
|
|
2016-10-04 17:08:05 +02:00
|
|
|
|
track_path_relative = track.get("path", None).encode('utf8', 'replace') # 'Radiohead/Kid A/Idioteque.mp3'
|
2016-10-04 01:06:52 +02:00
|
|
|
|
track_path = os.path.join(download_folder, track_path_relative) # 'C:/users/.../Radiohead/Kid A/Idioteque.mp3'
|
|
|
|
|
track_directory = os.path.dirname(os.path.abspath(track_path)) # 'C:/users/.../Radiohead/Kid A'
|
2016-10-03 15:57:52 +02:00
|
|
|
|
|
2016-10-03 21:18:43 +02:00
|
|
|
|
#check if file exists
|
2016-10-04 01:06:52 +02:00
|
|
|
|
if os.path.isfile(track_path):
|
|
|
|
|
|
|
|
|
|
progressdialog.update(pc_progress, 'Track has already been downloaded!')
|
2016-10-03 21:18:43 +02:00
|
|
|
|
plugin.log("File '%s' already exists" % (id))
|
|
|
|
|
|
2016-10-04 01:06:52 +02:00
|
|
|
|
else:
|
|
|
|
|
|
|
|
|
|
progressdialog.update(pc_progress, "Downloading track...",track_path)
|
2016-10-03 21:18:43 +02:00
|
|
|
|
|
2016-10-04 01:06:52 +02:00
|
|
|
|
try:
|
|
|
|
|
#get remote file (file-object like)
|
|
|
|
|
file_obj = connection.download(id)
|
2016-10-03 21:18:43 +02:00
|
|
|
|
|
2016-10-04 01:06:52 +02:00
|
|
|
|
#create directory if it does not exists
|
|
|
|
|
if not os.path.exists(track_directory):
|
|
|
|
|
os.makedirs(track_directory)
|
2016-10-03 21:18:43 +02:00
|
|
|
|
|
2016-10-04 01:06:52 +02:00
|
|
|
|
#create blank file
|
|
|
|
|
file = open(track_path, 'a') #create a new file but don't erase the existing one if it exists
|
2016-10-03 21:18:43 +02:00
|
|
|
|
|
2016-10-04 01:06:52 +02:00
|
|
|
|
#fill blank file
|
|
|
|
|
shutil.copyfileobj(file_obj, file)
|
|
|
|
|
file.close()
|
2016-10-03 21:18:43 +02:00
|
|
|
|
|
2016-10-04 01:06:52 +02:00
|
|
|
|
except:
|
|
|
|
|
popup("Error while downloading track #%s" % (id))
|
|
|
|
|
plugin.log("Error while downloading track #%s" % (id))
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
ids_parsed += 1
|
|
|
|
|
|
|
|
|
|
progressdialog.update(100, "Done !","Enjoy !")
|
|
|
|
|
xbmc.sleep(1000)
|
|
|
|
|
progressdialog.close()
|
2016-10-03 15:57:52 +02:00
|
|
|
|
|
|
|
|
|
def download_album(id):
|
|
|
|
|
|
|
|
|
|
# get connection
|
|
|
|
|
connection = get_connection()
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if connection==False:
|
2016-10-03 15:57:52 +02:00
|
|
|
|
return
|
|
|
|
|
|
2016-10-04 01:06:52 +02:00
|
|
|
|
# get album infos
|
|
|
|
|
response = connection.getAlbum(id);
|
|
|
|
|
album = response.get('album')
|
|
|
|
|
tracks = album.get('song')
|
2016-10-03 15:57:52 +02:00
|
|
|
|
|
2016-10-04 01:06:52 +02:00
|
|
|
|
plugin.log('getAlbum:')
|
|
|
|
|
plugin.log(json.dumps(album))
|
|
|
|
|
|
|
|
|
|
ids = [] #list of track IDs
|
|
|
|
|
|
|
|
|
|
for i, track in enumerate(tracks):
|
|
|
|
|
track_id = track.get('id')
|
|
|
|
|
ids.append(track_id)
|
|
|
|
|
|
|
|
|
|
download_tracks(ids)
|
2016-10-03 15:57:52 +02:00
|
|
|
|
|
2021-06-28 06:06:59 +02:00
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
2021-06-22 08:35:39 +02:00
|
|
|
|
def create_listing(listing, succeeded=True, update_listing=False, cache_to_disk=False, sort_methods=None,view_mode=None, content=None, category=None):
|
|
|
|
|
return ListContext(listing, succeeded, update_listing, cache_to_disk,sort_methods, view_mode, content, category)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def resolve_url(path='', play_item=None, succeeded=True):
|
|
|
|
|
"""
|
|
|
|
|
Create and return a context dict to resolve a playable URL
|
|
|
|
|
:param path: the path to a playable media.
|
|
|
|
|
:type path: str or unicode
|
|
|
|
|
:param play_item: a dict of item properties as described in the class docstring.
|
|
|
|
|
It allows to set additional properties for the item being played, like graphics, metadata etc.
|
|
|
|
|
if ``play_item`` parameter==present, then ``path`` value==ignored, and the path must be set via
|
|
|
|
|
``'path'`` property of a ``play_item`` dict.
|
|
|
|
|
:type play_item: dict
|
|
|
|
|
:param succeeded: if ``False``, Kodi won't play anything
|
|
|
|
|
:type succeeded: bool
|
|
|
|
|
:return: context object containing necessary parameters
|
|
|
|
|
for Kodi to play the selected media.
|
|
|
|
|
:rtype: PlayContext
|
|
|
|
|
"""
|
|
|
|
|
return PlayContext(path, play_item, succeeded)
|
|
|
|
|
|
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
|
|
|
|
def create_list_item(item):
|
|
|
|
|
"""
|
|
|
|
|
Create an :class:`xbmcgui.ListItem` instance from an item dict
|
|
|
|
|
:param item: a dict of ListItem properties
|
|
|
|
|
:type item: dict
|
|
|
|
|
:return: ListItem instance
|
|
|
|
|
:rtype: xbmcgui.ListItem
|
|
|
|
|
"""
|
|
|
|
|
major_version = xbmc.getInfoLabel('System.BuildVersion')[:2]
|
|
|
|
|
if major_version >= '18':
|
|
|
|
|
list_item = xbmcgui.ListItem(label=item.get('label', ''),
|
|
|
|
|
label2=item.get('label2', ''),
|
|
|
|
|
path=item.get('path', ''),
|
|
|
|
|
offscreen=item.get('offscreen', False))
|
2021-06-22 10:25:52 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
art = item.get('art', {})
|
|
|
|
|
art['thumb'] = item.get('thumb', '')
|
|
|
|
|
art['icon'] = item.get('icon', '')
|
|
|
|
|
art['fanart'] = item.get('fanart', '')
|
|
|
|
|
item['art'] = art
|
|
|
|
|
cont_look = item.get('content_lookup')
|
|
|
|
|
if cont_look is not None:
|
|
|
|
|
list_item.setContentLookup(cont_look)
|
2021-06-22 08:35:39 +02:00
|
|
|
|
if item.get('art'):
|
|
|
|
|
list_item.setArt(item['art'])
|
|
|
|
|
if item.get('stream_info'):
|
2021-06-22 10:25:52 +02:00
|
|
|
|
for stream, stream_info in item['stream_info'].items():
|
2021-06-22 08:35:39 +02:00
|
|
|
|
list_item.addStreamInfo(stream, stream_info)
|
|
|
|
|
if item.get('info'):
|
2021-06-22 10:25:52 +02:00
|
|
|
|
for media, info in item['info'].items():
|
2021-06-22 08:35:39 +02:00
|
|
|
|
list_item.setInfo(media, info)
|
|
|
|
|
if item.get('context_menu') is not None:
|
|
|
|
|
list_item.addContextMenuItems(item['context_menu'])
|
|
|
|
|
if item.get('subtitles'):
|
|
|
|
|
list_item.setSubtitles(item['subtitles'])
|
|
|
|
|
if item.get('mime'):
|
|
|
|
|
list_item.setMimeType(item['mime'])
|
|
|
|
|
if item.get('properties'):
|
2021-06-22 10:25:52 +02:00
|
|
|
|
for key, value in item['properties'].items():
|
2021-06-22 08:35:39 +02:00
|
|
|
|
list_item.setProperty(key, value)
|
|
|
|
|
if major_version >= '17':
|
|
|
|
|
cast = item.get('cast')
|
|
|
|
|
if cast is not None:
|
|
|
|
|
list_item.setCast(cast)
|
|
|
|
|
db_ids = item.get('online_db_ids')
|
|
|
|
|
if db_ids is not None:
|
|
|
|
|
list_item.setUniqueIDs(db_ids)
|
|
|
|
|
ratings = item.get('ratings')
|
|
|
|
|
if ratings is not None:
|
|
|
|
|
for rating in ratings:
|
|
|
|
|
list_item.setRating(**rating)
|
|
|
|
|
return list_item
|
|
|
|
|
|
|
|
|
|
def _set_resolved_url(context):
|
|
|
|
|
|
|
|
|
|
plugin.log_debug('Resolving URL from {0}'.format(str(context)))
|
|
|
|
|
if context.play_item==None:
|
|
|
|
|
list_item = xbmcgui.ListItem(path=context.path)
|
|
|
|
|
else:
|
|
|
|
|
list_item = self.create_list_item(context.play_item)
|
|
|
|
|
xbmcplugin.setResolvedUrl(plugin.handle, context.succeeded, list_item)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#@plugin.cached(cachetime) #cache (in minutes)
|
|
|
|
|
def add_directory_items(context):
|
|
|
|
|
plugin.log_debug('Creating listing from {0}'.format(str(context)))
|
|
|
|
|
if context.category is not None:
|
|
|
|
|
xbmcplugin.setPluginCategory(plugin.handle, context.category)
|
|
|
|
|
if context.content is not None:
|
|
|
|
|
xbmcplugin.setContent(plugin.handle, context.content) # This must be at the beginning
|
|
|
|
|
for item in context.listing:
|
|
|
|
|
is_folder = item.get('is_folder', True)
|
|
|
|
|
if item.get('list_item') is not None:
|
|
|
|
|
list_item = item['list_item']
|
|
|
|
|
else:
|
|
|
|
|
list_item = create_list_item(item)
|
|
|
|
|
if item.get('is_playable'):
|
|
|
|
|
list_item.setProperty('IsPlayable', 'true')
|
|
|
|
|
is_folder = False
|
|
|
|
|
xbmcplugin.addDirectoryItem(plugin.handle, item['url'], list_item, is_folder)
|
|
|
|
|
if context.sort_methods is not None:
|
|
|
|
|
if isinstance(context.sort_methods, (int, dict)):
|
|
|
|
|
sort_methods = [context.sort_methods]
|
|
|
|
|
elif isinstance(context.sort_methods, (tuple, list)):
|
|
|
|
|
sort_methods = context.sort_methods
|
|
|
|
|
else:
|
|
|
|
|
raise TypeError(
|
|
|
|
|
'sort_methods parameter must be of int, dict, tuple or list type!')
|
|
|
|
|
for method in sort_methods:
|
|
|
|
|
if isinstance(method, int):
|
|
|
|
|
xbmcplugin.addSortMethod(plugin.handle, method)
|
|
|
|
|
elif isinstance(method, dict):
|
|
|
|
|
xbmcplugin.addSortMethod(plugin.handle, **method)
|
|
|
|
|
else:
|
|
|
|
|
raise TypeError(
|
|
|
|
|
'method parameter must be of int or dict type!')
|
|
|
|
|
|
|
|
|
|
xbmcplugin.endOfDirectory(plugin.handle,
|
|
|
|
|
context.succeeded,
|
|
|
|
|
context.update_listing,
|
|
|
|
|
context.cache_to_disk)
|
|
|
|
|
if context.view_mode is not None:
|
|
|
|
|
xbmc.executebuiltin('Container.SetViewMode({0})'.format(context.view_mode))
|
|
|
|
|
|
2021-06-22 10:25:52 +02:00
|
|
|
|
def walk_index(folder_id=None):
|
|
|
|
|
"""
|
|
|
|
|
Request Subsonic's index and iterate each item.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
response = connection.getIndexes(folder_id)
|
|
|
|
|
|
|
|
|
|
for index in response["indexes"]["index"]:
|
|
|
|
|
for artist in index["artist"]:
|
|
|
|
|
yield artist
|
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
def walk_playlists():
|
|
|
|
|
"""
|
|
|
|
|
Request Subsonic's playlists and iterate over each item.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
response = connection.getPlaylists()
|
|
|
|
|
|
|
|
|
|
for child in response["playlists"]["playlist"]:
|
|
|
|
|
yield child
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def walk_playlist(playlist_id):
|
|
|
|
|
"""
|
|
|
|
|
Request Subsonic's playlist items and iterate over each item.
|
|
|
|
|
"""
|
|
|
|
|
response = connection.getPlaylist(playlist_id)
|
|
|
|
|
|
|
|
|
|
for child in response["playlist"]["entry"]:
|
|
|
|
|
yield child
|
|
|
|
|
|
2021-06-22 10:25:52 +02:00
|
|
|
|
def walk_folders():
|
|
|
|
|
response = connection.getMusicFolders()
|
|
|
|
|
|
|
|
|
|
for child in response["musicFolders"]["musicFolder"]:
|
|
|
|
|
yield child
|
|
|
|
|
|
|
|
|
|
def walk_directory(directory_id):
|
|
|
|
|
"""
|
|
|
|
|
Request a Subsonic music directory and iterate over each item.
|
|
|
|
|
"""
|
2021-06-28 06:06:59 +02:00
|
|
|
|
#directory_id = 16906
|
2021-06-22 10:25:52 +02:00
|
|
|
|
response = connection.getMusicDirectory(directory_id)
|
2021-06-28 06:06:59 +02:00
|
|
|
|
xbmc.log(directory_id,xbmc.LOGINFO)
|
|
|
|
|
#xbmc.log(str(response),xbmc.LOGINFO)
|
|
|
|
|
try:
|
|
|
|
|
for child in response["directory"]["child"]:
|
|
|
|
|
if child.get("isDir"):
|
|
|
|
|
for child in walk_directory(child["id"]):
|
|
|
|
|
yield child
|
|
|
|
|
else:
|
2021-06-22 10:25:52 +02:00
|
|
|
|
yield child
|
2021-06-28 06:06:59 +02:00
|
|
|
|
except:
|
|
|
|
|
yield from ()
|
2021-06-22 10:25:52 +02:00
|
|
|
|
|
2021-06-22 08:35:39 +02:00
|
|
|
|
def walk_artist(artist_id):
|
|
|
|
|
"""
|
|
|
|
|
Request a Subsonic artist and iterate over each album.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
response = connection.getArtist(artist_id)
|
|
|
|
|
|
|
|
|
|
for child in response["artist"]["album"]:
|
|
|
|
|
yield child
|
|
|
|
|
|
|
|
|
|
def walk_artists():
|
|
|
|
|
"""
|
|
|
|
|
(ID3 tags)
|
|
|
|
|
Request all artists and iterate over each item.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
response = connection.getArtists()
|
|
|
|
|
|
|
|
|
|
for index in response["artists"]["index"]:
|
|
|
|
|
for artist in index["artist"]:
|
|
|
|
|
yield artist
|
|
|
|
|
|
|
|
|
|
def walk_genres():
|
|
|
|
|
"""
|
|
|
|
|
(ID3 tags)
|
|
|
|
|
Request all genres and iterate over each item.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
response = connection.getGenres()
|
|
|
|
|
|
|
|
|
|
for genre in response["genres"]["genre"]:
|
|
|
|
|
yield genre
|
|
|
|
|
|
|
|
|
|
def walk_albums(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.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if ltype == 'byGenre' and genre is None:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if ltype == 'byYear' and (fromYear is None or toYear is None):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
response = connection.getAlbumList2(
|
|
|
|
|
ltype=ltype, size=size, fromYear=fromYear, toYear=toYear,genre=genre, offset=offset)
|
|
|
|
|
|
|
|
|
|
if not response["albumList2"]["album"]:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
for album in response["albumList2"]["album"]:
|
|
|
|
|
yield album
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def walk_album(album_id):
|
|
|
|
|
"""
|
|
|
|
|
(ID3 tags)
|
|
|
|
|
Request an album and iterate over each item.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
response = connection.getAlbum(album_id)
|
|
|
|
|
|
|
|
|
|
for song in response["album"]["song"]:
|
|
|
|
|
yield song
|
|
|
|
|
|
|
|
|
|
def walk_tracks_random(size=None, genre=None, fromYear=None,toYear=None):
|
|
|
|
|
"""
|
|
|
|
|
Request random songs by genre and/or year and iterate over each song.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
response = connection.getRandomSongs(
|
|
|
|
|
size=size, genre=genre, fromYear=fromYear, toYear=toYear)
|
|
|
|
|
|
|
|
|
|
for song in response["randomSongs"]["song"]:
|
|
|
|
|
yield song
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def walk_tracks_starred():
|
|
|
|
|
"""
|
|
|
|
|
Request Subsonic's starred songs and iterate over each item.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
response = connection.getStarred()
|
|
|
|
|
|
|
|
|
|
for song in response["starred"]["song"]:
|
|
|
|
|
yield song
|
2016-10-03 03:07:19 +02:00
|
|
|
|
|
2016-10-02 20:10:40 +02:00
|
|
|
|
|
|
|
|
|
# Start plugin from within Kodi.
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
# Map actions
|
|
|
|
|
# Note that we map callable objects without brackets ()
|
|
|
|
|
plugin.run()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|