Merge remote-tracking branch 'MediaBrowser/master' into develop
This commit is contained in:
commit
a16b2bdeb3
10 changed files with 528 additions and 344 deletions
|
@ -94,15 +94,15 @@ if __name__ == '__main__':
|
|||
ret = xbmcgui.Dialog().select(header, options)
|
||||
if ret != -1:
|
||||
if options[ret] == utils.language(30402):
|
||||
API.updateUserRating(embyid, deletelike=True)
|
||||
emby.updateUserRating(embyid, deletelike=True)
|
||||
if options[ret] == utils.language(30403):
|
||||
API.updateUserRating(embyid, like=True)
|
||||
emby.updateUserRating(embyid, like=True)
|
||||
if options[ret] == utils.language(30404):
|
||||
API.updateUserRating(embyid, like=False)
|
||||
emby.updateUserRating(embyid, like=False)
|
||||
if options[ret] == utils.language(30405):
|
||||
API.updateUserRating(embyid, favourite=True)
|
||||
emby.updateUserRating(embyid, favourite=True)
|
||||
if options[ret] == utils.language(30406):
|
||||
API.updateUserRating(embyid, favourite=False)
|
||||
emby.updateUserRating(embyid, favourite=False)
|
||||
if options[ret] == utils.language(30407):
|
||||
kodiconn = utils.kodiSQL('music')
|
||||
kodicursor = kodiconn.cursor()
|
||||
|
@ -117,7 +117,7 @@ if __name__ == '__main__':
|
|||
musicutils.updateRatingToFile(newvalue, API.getFilePath())
|
||||
if utils.settings('enableExportSongRating') == "true":
|
||||
like, favourite, deletelike = musicutils.getEmbyRatingFromKodiRating(newvalue)
|
||||
API.updateUserRating(embyid, like, favourite, deletelike)
|
||||
emby.updateUserRating(embyid, like, favourite, deletelike)
|
||||
query = ' '.join(( "UPDATE song","SET rating = ?", "WHERE idSong = ?" ))
|
||||
kodicursor.execute(query, (newvalue,itemid,))
|
||||
kodiconn.commit()
|
||||
|
|
|
@ -1,240 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<strings>
|
||||
<string id="30000">Primaire server adres:</string>
|
||||
<string id="30001">Enkele mappen automatisch openen:</string>
|
||||
<string id="30002">Afspelen van stream ipv SMB:</string>
|
||||
<string id="30004">Log niveau:</string>
|
||||
<string id="30005">Gebruikersnaam: </string>
|
||||
<string id="30006">Wachtwoord: </string>
|
||||
<string id="30007">Samba gebruikersnaam: </string>
|
||||
<string id="30008">Samba wachtwoord: </string>
|
||||
<string id="30009">Transcode: </string>
|
||||
<string id="30010">Prestatie profilering inschakelen:</string>
|
||||
<string id="30011">Lokale cache systeem</string>
|
||||
|
||||
<string id="30014">Emby</string>
|
||||
<string id="30015">Netwerk</string>
|
||||
<string id="30016">Apparaatnaam</string>
|
||||
|
||||
<string id="30022">Geavanceerd</string>
|
||||
<string id="30024">Gebruikersnaam:</string>
|
||||
<string id="30025">Wachtwoord:</string>
|
||||
<string id="30026">Gebruik SIMPLEJSON ipv JSON</string>
|
||||
|
||||
<string id="30030">Poortnummer:</string>
|
||||
<string id="30036">Aantal recente films die getoond worden:</string>
|
||||
<string id="30037">Aantal recente TV-series die getoond worden:</string>
|
||||
<string id="30035">Aantal recente muziekalbums die getoond worden:</string>
|
||||
<string id="30038">Markeer als bekeken bij starten van afspelen:</string>
|
||||
<string id="30039">Gebruik seizoen poster bij afleveringen</string>
|
||||
|
||||
<string id="30040">Genre filter ...</string>
|
||||
<string id="30041">Speel alles vanaf hier</string>
|
||||
<string id="30042">Vernieuwen</string>
|
||||
<string id="30043">Wissen</string>
|
||||
<string id="30046">Voeg film toe aan CouchPotato</string>
|
||||
|
||||
<string id="30044">Ongeldige gebruikersnaam/wachtwoord</string>
|
||||
<string id="30045">Gebruikersnaam niet gevonden</string>
|
||||
|
||||
<string id="30052">Wissen...</string>
|
||||
<string id="30053">Wacht op server voor wissen</string>
|
||||
|
||||
<string id="30059">Server standaard</string>
|
||||
<string id="30060">Titel</string>
|
||||
<string id="30061">Jaar</string>
|
||||
<string id="30062">Premiere datum</string>
|
||||
<string id="30063">Datum toegevoegd</string>
|
||||
<string id="30064">Critici beoordeling</string>
|
||||
<string id="30065">Community beoordeling</string>
|
||||
<string id="30066">Aantal keer bekeken</string>
|
||||
<string id="30067">Budget</string>
|
||||
|
||||
<string id="30068">Sorteer op</string>
|
||||
|
||||
<string id="30069">Geen</string>
|
||||
<string id="30070">Actie</string>
|
||||
<string id="30071">Avontuur</string>
|
||||
<string id="30072">Animatie</string>
|
||||
<string id="30073">Misdaad</string>
|
||||
<string id="30074">Comedy</string>
|
||||
<string id="30075">Documentaire</string>
|
||||
<string id="30076">Drama</string>
|
||||
<string id="30077">Fantasie</string>
|
||||
<string id="30078">Nederlands</string>
|
||||
<string id="30079">Historie</string>
|
||||
<string id="30080">Horror</string>
|
||||
<string id="30081">Muziek</string>
|
||||
<string id="30082">Musical</string>
|
||||
<string id="30083">Mysterie</string>
|
||||
<string id="30084">Romantiek</string>
|
||||
<string id="30085">Science Fiction</string>
|
||||
<string id="30086">Kort</string>
|
||||
<string id="30087">Spanning</string>
|
||||
<string id="30088">Thriller</string>
|
||||
<string id="30089">Western</string>
|
||||
|
||||
<string id="30090">Genre filter</string>
|
||||
<string id="30091">Bevestig wissen</string>
|
||||
<string id="30092">Dit item wissen ? Dit zal het bestand volledig verwijderen.</string>
|
||||
|
||||
<string id="30093">Markeer als bekeken</string>
|
||||
<string id="30094">Mark als onbekeken</string>
|
||||
<string id="30095">Voeg toe aan favorieten</string>
|
||||
<string id="30096">Verwijder uit favorieten</string>
|
||||
<string id="30097">Sorteer op...</string>
|
||||
<string id="30098">Sorteer oplopend</string>
|
||||
<string id="30099">Sorteer aflopend</string>
|
||||
<string id="30100">Toon acteurs</string>
|
||||
|
||||
<!-- resume dialog -->
|
||||
<string id="30105">Hervatten</string>
|
||||
<string id="30106">Hervatten vanaf</string>
|
||||
<string id="30107">Start vanaf begin</string>
|
||||
|
||||
<string id="30110">Interface</string>
|
||||
<string id="30111">Inclusief stream info</string>
|
||||
<string id="30112">Inclusief personen</string>
|
||||
<string id="30113">Inclusief filminfo</string>
|
||||
<string id="30114">Bij hervatten aantal seconden terugspringen</string>
|
||||
<string id="30115">Markeer als bekeken wanneer na percentage gestopt</string>
|
||||
<string id="30116">Inclusief aantal en afspeel tellers</string>
|
||||
<string id="30117"> - Achtergrond plaatjes verversen (secondes)</string>
|
||||
<string id="30118">Inclusief hervat-percentage</string>
|
||||
<string id="30119">Afleveringnummer tonen in titel</string>
|
||||
<string id="30120">Toon voortgang</string>
|
||||
<string id="30121">Laden van content</string>
|
||||
<string id="30122">Retrieving Data</string>
|
||||
<string id="30123">Parsing Jason Data</string>
|
||||
<string id="30124">Downloading Jason Data</string>
|
||||
<string id="30125">Done</string>
|
||||
<string id="30126">Processing Item : </string>
|
||||
<string id="30127">Toon wismogelijkheid na bekijken van aflevering</string>
|
||||
<string id="30128">Afspeelfout!</string>
|
||||
<string id="30129">Dit item kan niet worden afgespeeld</string>
|
||||
<string id="30130">Lockaal pad gedetecteerd</string>
|
||||
<string id="30131">De MB3 bibliotheek bevat lokale paden. U moet UNC-paden gebruiken of afspelen van stream inschakelen. Pad: </string>
|
||||
<string id="30132">Waarschuwing</string>
|
||||
<string id="30133">Debug logging ingeschakeld.</string>
|
||||
<string id="30134">Dit heeft effect op de performance.</string>
|
||||
<string id="30135">Fout</string>
|
||||
<string id="30136">XBMB3C service werkt niet</string>
|
||||
<string id="30137">Herstart XBMC aub</string>
|
||||
<string id="30138">Zoeken</string>
|
||||
|
||||
<string id="30139">Schakel Themamuziek in (vereist herstart)</string>
|
||||
<string id="30140"> - Herhalen van themamuziek</string>
|
||||
<string id="30141">Activeer achtergrondafbeelding (vereist herstart)</string>
|
||||
<string id="30142">Services</string>
|
||||
<string id="30143">Activeer Info Loader (vereist herstart)</string>
|
||||
<string id="30144">Activeer Menu Loader (vereist herstart)</string>
|
||||
<string id="30145">Activeer WebSocket Remote (vereist herstart)</string>
|
||||
<string id="30146">Activeer In Progress Loader (vereist herstart)</string>
|
||||
<string id="30147">Activeer Recent Info Loader (vereist herstart)</string>
|
||||
<string id="30148">Activeer Random Loader (vereist herstart)</string>
|
||||
<string id="30149">Activeer Next Up Loader (vereist herstart)</string>
|
||||
|
||||
<string id="30150">Skin ondersteund het vastleggen van views niet</string>
|
||||
<string id="30151">Selecteer item actie (vereist herstart)</string>
|
||||
|
||||
<string id="30152">Toon indactors</string>
|
||||
<string id="30153"> - Toon bekeken indator</string>
|
||||
<string id="30154"> - Toon aantal onbekeken indicator</string>
|
||||
<string id="30155"> - Toon afspeel-percentage indicator</string>
|
||||
<string id="30156">Sorteer volgende (NextUp) op titel</string>
|
||||
<string id="30157">Deactiveer speciale afbeeldingen (bv CoverArt)</string>
|
||||
<string id="30158">Metadata</string>
|
||||
<string id="30159">Afbeeldingen</string>
|
||||
<string id="30160">Video kwaliteit</string>
|
||||
|
||||
<string id="30161">Activeer Suggested Loader (vereist herstart)</string>
|
||||
<string id="30162">Seizoen tonen in titel</string>
|
||||
<string id="30163">Seizoenen verbergen</string>
|
||||
|
||||
<string id="30164">Direct Play - HTTP</string>
|
||||
<string id="30165">Direct Play</string>
|
||||
<string id="30166">Transcoding</string>
|
||||
<string id="30167">Server Detection Succeeded</string>
|
||||
<string id="30168">Found server</string>
|
||||
<string id="30169">Address : </string>
|
||||
|
||||
|
||||
<string id="30170">Recent toegevoegde TV-series</string>
|
||||
<string id="30171">Niet afgekeken TV-series</string>
|
||||
|
||||
<string id="30172">Alle Muziek</string>
|
||||
<string id="30173">Kanalen</string>
|
||||
<string id="30174">Recent toegevoegde films</string>
|
||||
<string id="30175">Recent toegevoegde afleveringen</string>
|
||||
<string id="30176">Recent toegevoegde albums</string>
|
||||
<string id="30177">Niet afgekeken films</string>
|
||||
<string id="30178">Niet afgekeken afleveringen</string>
|
||||
<string id="30179">NextUp afleveringen</string>
|
||||
<string id="30180">Favoriete films</string>
|
||||
<string id="30181">Favoriete TV-series</string>
|
||||
<string id="30182">Favoriete afleveringen</string>
|
||||
<string id="30183">Vaak afgespeelde albums</string>
|
||||
<string id="30184">Upcoming TV</string>
|
||||
<string id="30185">BoxSets</string>
|
||||
<string id="30186">Trailers</string>
|
||||
<string id="30187">Muziek videos</string>
|
||||
<string id="30188">Fotos</string>
|
||||
<string id="30189">Onbekeken films</string>
|
||||
|
||||
<string id="30191">Film Studios</string>
|
||||
<string id="30192">Film Acteurs</string>
|
||||
<string id="30193">Onbekeken afleveringen</string>
|
||||
<string id="30194">TV Genres</string>
|
||||
<string id="30195">TV Networks</string>
|
||||
<string id="30196">TV Acteurs</string>
|
||||
<string id="30197">Afspeellijsten</string>
|
||||
<string id="30198">Zoeken</string>
|
||||
<string id="30199">Views instellen</string>
|
||||
|
||||
<string id="30200">Selecteer gebruiker</string>
|
||||
<string id="30201">Profilering ingeschakeld.</string>
|
||||
<string id="30202">Svp onthouden om weer uit te schakelen na het testen.</string>
|
||||
<string id="30203">Error in ArtworkRotationThread</string>
|
||||
<string id="30204">Kan niet verbinden met server</string>
|
||||
<string id="30205">Error in LoadMenuOptionsThread</string>
|
||||
|
||||
<string id="30206">Activeer Playlists Loader (vereist herstart)</string>
|
||||
|
||||
<string id="30207">Liedjes</string>
|
||||
<string id="30208">Albums</string>
|
||||
<string id="30209">Album artiesten</string>
|
||||
<string id="30210">Artiesten</string>
|
||||
<string id="30211">Muziek Genres</string>
|
||||
|
||||
<string id="30212">Schakel Themavideos in (vereist herstart)</string>
|
||||
<string id="30213"> - Herhalen van themavideos</string>
|
||||
|
||||
<string id="30214">Schakel het forceren van view uit</string>
|
||||
<string id="30215">Schakel snelle modus in (beta)</string>
|
||||
<string id="30216">Automatisch resterende afleveringen in een seizoen afspelen</string>
|
||||
<string id="30217">Boxsets tonen in de overzichten (vereist herstart)</string>
|
||||
<string id="30218">Afbeeldingen comprimeren</string>
|
||||
<string id="30219">Activeer Skin Helper (vereust herstart)</string>
|
||||
<string id="30220">Laatste </string>
|
||||
<string id="30221">Bezig </string>
|
||||
<string id="30222">Volgende </string>
|
||||
<string id="30223">Gebruikerweergaven </string>
|
||||
|
||||
<!-- Default views -->
|
||||
<string id="30300">Actief</string>
|
||||
<string id="30301">Herstel standaard</string>
|
||||
<string id="30302">Films</string>
|
||||
<string id="30303">BoxSets</string>
|
||||
<string id="30304">Trailers</string>
|
||||
<string id="30305">Series</string>
|
||||
<string id="30306">Seizoenen</string>
|
||||
<string id="30307">Afleveringen</string>
|
||||
<string id="30308">Muziek - artiesten</string>
|
||||
<string id="30309">Muziek - albums</string>
|
||||
<string id="30310">Muziekvideos</string>
|
||||
<string id="30311">Muziek - liedjes</string>
|
||||
<string id="30312">Kanalen</string>
|
||||
|
||||
|
||||
|
||||
</strings>
|
|
@ -500,7 +500,7 @@ def getAllArtwork(self, item, parentInfo=False):
|
|||
|
||||
server = self.server
|
||||
|
||||
id = item['Id']
|
||||
itemid = item['Id']
|
||||
artworks = item['ImageTags']
|
||||
backdrops = item.get('BackdropImageTags',[])
|
||||
|
||||
|
@ -526,15 +526,12 @@ def getAllArtwork(self, item, parentInfo=False):
|
|||
}
|
||||
|
||||
# Process backdrops
|
||||
backdropIndex = 0
|
||||
for backdroptag in backdrops:
|
||||
for index, tag in enumerate(backdrops):
|
||||
artwork = (
|
||||
"%s/emby/Items/%s/Images/Backdrop/%s?"
|
||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||
% (server, id, backdropIndex,
|
||||
maxWidth, maxHeight, backdroptag, customquery))
|
||||
% (server, itemid, index, maxWidth, maxHeight, tag, customquery))
|
||||
allartworks['Backdrop'].append(artwork)
|
||||
backdropIndex += 1
|
||||
|
||||
# Process the rest of the artwork
|
||||
for art in artworks:
|
||||
|
@ -544,7 +541,7 @@ def getAllArtwork(self, item, parentInfo=False):
|
|||
artwork = (
|
||||
"%s/emby/Items/%s/Images/%s/0?"
|
||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||
% (server, id, art, maxWidth, maxHeight, tag, customquery))
|
||||
% (server, itemid, art, maxWidth, maxHeight, tag, customquery))
|
||||
allartworks[art] = artwork
|
||||
|
||||
# Process parent items if the main item is missing artwork
|
||||
|
@ -558,15 +555,12 @@ def getAllArtwork(self, item, parentInfo=False):
|
|||
# If there is a parentId, go through the parent backdrop list
|
||||
parentbackdrops = item['ParentBackdropImageTags']
|
||||
|
||||
backdropIndex = 0
|
||||
for parentbackdroptag in parentbackdrops:
|
||||
for index, tag in enumerate(parentbackdrops):
|
||||
artwork = (
|
||||
"%s/emby/Items/%s/Images/Backdrop/%s?"
|
||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||
% (server, parentId, backdropIndex,
|
||||
maxWidth, maxHeight, parentbackdroptag, customquery))
|
||||
% (server, parentId, index, maxWidth, maxHeight, tag, customquery))
|
||||
allartworks['Backdrop'].append(artwork)
|
||||
backdropIndex += 1
|
||||
|
||||
# Process the rest of the artwork
|
||||
parentartwork = ['Logo', 'Art', 'Thumb']
|
||||
|
|
269
resources/lib/connect.py
Normal file
269
resources/lib/connect.py
Normal file
|
@ -0,0 +1,269 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import json
|
||||
import requests
|
||||
import logging
|
||||
|
||||
import utils
|
||||
import clientinfo
|
||||
|
||||
##################################################################################################
|
||||
|
||||
# Disable requests logging
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning, InsecurePlatformWarning
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
requests.packages.urllib3.disable_warnings(InsecurePlatformWarning)
|
||||
#logging.getLogger('requests').setLevel(logging.WARNING)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class ConnectUtils():
|
||||
|
||||
# Borg - multiple instances, shared state
|
||||
_shared_state = {}
|
||||
clientInfo = clientinfo.ClientInfo()
|
||||
addonName = clientInfo.getAddonName()
|
||||
|
||||
# Requests session
|
||||
c = None
|
||||
timeout = 30
|
||||
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.__dict__ = self._shared_state
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def setUserId(self, userId):
|
||||
# Reserved for userclient only
|
||||
self.userId = userId
|
||||
self.logMsg("Set connect userId: %s" % userId, 2)
|
||||
|
||||
def setServer(self, server):
|
||||
# Reserved for userclient only
|
||||
self.server = server
|
||||
self.logMsg("Set connect server: %s" % server, 2)
|
||||
|
||||
def setToken(self, token):
|
||||
# Reserved for userclient only
|
||||
self.token = token
|
||||
self.logMsg("Set connect token: %s" % token, 2)
|
||||
|
||||
|
||||
def startSession(self):
|
||||
|
||||
log = self.logMsg
|
||||
|
||||
self.deviceId = self.clientInfo.getDeviceId()
|
||||
|
||||
# User is identified from this point
|
||||
# Attach authenticated header to the session
|
||||
verify = False
|
||||
header = self.getHeader()
|
||||
|
||||
# If user enabled host certificate verification
|
||||
try:
|
||||
verify = self.sslverify
|
||||
if self.sslclient is not None:
|
||||
verify = self.sslclient
|
||||
except:
|
||||
log("Could not load SSL settings.", 1)
|
||||
|
||||
# Start session
|
||||
self.c = requests.Session()
|
||||
self.c.headers = header
|
||||
self.c.verify = verify
|
||||
# Retry connections to the server
|
||||
self.c.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||
self.c.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||
|
||||
log("Requests session started on: %s" % self.server, 1)
|
||||
|
||||
def stopSession(self):
|
||||
try:
|
||||
self.c.close()
|
||||
except Exception as e:
|
||||
self.logMsg("Requests session could not be terminated: %s" % e, 1)
|
||||
|
||||
def getHeader(self, authenticate=True):
|
||||
|
||||
clientInfo = self.clientInfo
|
||||
version = clientInfo.getVersion()
|
||||
|
||||
if not authenticate:
|
||||
# If user is not authenticated
|
||||
header = {
|
||||
|
||||
'X-Application': "Kodi/%s" % version,
|
||||
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'Accept': "application/json"
|
||||
}
|
||||
self.logMsg("Header: %s" % header, 1)
|
||||
|
||||
else:
|
||||
token = self.token
|
||||
# Attached to the requests session
|
||||
header = {
|
||||
|
||||
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'Accept': "application/json",
|
||||
'X-Application': "Kodi/%s" % version,
|
||||
'X-Connect-UserToken': token
|
||||
}
|
||||
self.logMsg("Header: %s" % header, 1)
|
||||
|
||||
return header
|
||||
|
||||
def doUrl(self, url, data=None, postBody=None, rtype="GET",
|
||||
parameters=None, authenticate=True, timeout=None):
|
||||
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
|
||||
log("=== ENTER connectUrl ===", 2)
|
||||
default_link = ""
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
|
||||
# Get requests session
|
||||
try:
|
||||
# If connect user is authenticated
|
||||
if authenticate:
|
||||
try:
|
||||
c = self.c
|
||||
# Replace for the real values
|
||||
url = url.replace("{server}", self.server)
|
||||
url = url.replace("{UserId}", self.userId)
|
||||
|
||||
# Prepare request
|
||||
if rtype == "GET":
|
||||
r = c.get(url, json=postBody, params=parameters, timeout=timeout)
|
||||
elif rtype == "POST":
|
||||
r = c.post(url, data=data, timeout=timeout)
|
||||
elif rtype == "DELETE":
|
||||
r = c.delete(url, json=postBody, timeout=timeout)
|
||||
|
||||
except AttributeError:
|
||||
# request session does not exists
|
||||
self.server = "https://connect.emby.media/service"
|
||||
self.userId = window('embyco_currUser')
|
||||
self.token = window('embyco_accessToken%s' % self.userId)
|
||||
|
||||
header = self.getHeader()
|
||||
verifyssl = False
|
||||
|
||||
# If user enables ssl verification
|
||||
try:
|
||||
verifyssl = self.sslverify
|
||||
if self.sslclient is not None:
|
||||
verifyssl = self.sslclient
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Prepare request
|
||||
if rtype == "GET":
|
||||
r = requests.get(url,
|
||||
json=postBody,
|
||||
params=parameters,
|
||||
headers=header,
|
||||
timeout=timeout,
|
||||
verify=verifyssl)
|
||||
|
||||
elif rtype == "POST":
|
||||
r = requests.post(url,
|
||||
data=data,
|
||||
headers=header,
|
||||
timeout=timeout,
|
||||
verify=verifyssl)
|
||||
# If user is not authenticated
|
||||
else:
|
||||
header = self.getHeader(authenticate=False)
|
||||
verifyssl = False
|
||||
|
||||
# If user enables ssl verification
|
||||
try:
|
||||
verifyssl = self.sslverify
|
||||
if self.sslclient is not None:
|
||||
verifyssl = self.sslclient
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Prepare request
|
||||
if rtype == "GET":
|
||||
r = requests.get(url,
|
||||
json=postBody,
|
||||
params=parameters,
|
||||
headers=header,
|
||||
timeout=timeout,
|
||||
verify=verifyssl)
|
||||
|
||||
elif rtype == "POST":
|
||||
r = requests.post(url,
|
||||
data=data,
|
||||
headers=header,
|
||||
timeout=timeout,
|
||||
verify=verifyssl)
|
||||
|
||||
##### THE RESPONSE #####
|
||||
log(r.url, 1)
|
||||
log(r, 1)
|
||||
|
||||
if r.status_code == 204:
|
||||
# No body in the response
|
||||
log("====== 204 Success ======", 1)
|
||||
|
||||
elif r.status_code == requests.codes.ok:
|
||||
|
||||
try:
|
||||
# UNICODE - JSON object
|
||||
r = r.json()
|
||||
log("====== 200 Success ======", 1)
|
||||
log("Response: %s" % r, 1)
|
||||
return r
|
||||
|
||||
except:
|
||||
if r.headers.get('content-type') != "text/html":
|
||||
log("Unable to convert the response for: %s" % url, 1)
|
||||
else:
|
||||
r.raise_for_status()
|
||||
|
||||
##### EXCEPTIONS #####
|
||||
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
# Make the addon aware of status
|
||||
pass
|
||||
|
||||
except requests.exceptions.ConnectTimeout as e:
|
||||
log("Server timeout at: %s" % url, 0)
|
||||
log(e, 1)
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
|
||||
if r.status_code == 401:
|
||||
# Unauthorized
|
||||
pass
|
||||
|
||||
elif r.status_code in (301, 302):
|
||||
# Redirects
|
||||
pass
|
||||
elif r.status_code == 400:
|
||||
# Bad requests
|
||||
pass
|
||||
|
||||
except requests.exceptions.SSLError as e:
|
||||
log("Invalid SSL certificate for: %s" % url, 0)
|
||||
log(e, 1)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
log("Unknown error connecting to: %s" % url, 0)
|
||||
log(e, 1)
|
||||
|
||||
return default_link
|
|
@ -184,6 +184,21 @@ class Embydb_Functions():
|
|||
return item
|
||||
except: return None
|
||||
|
||||
def getItem_byWildId(self, embyid):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT kodi_id, media_type",
|
||||
"FROM emby",
|
||||
"WHERE emby_id LIKE ?"
|
||||
))
|
||||
embycursor.execute(query, (embyid+"%",))
|
||||
items = embycursor.fetchall()
|
||||
|
||||
return items
|
||||
|
||||
def getItem_byView(self, mediafolderid):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
@ -358,3 +373,9 @@ class Embydb_Functions():
|
|||
|
||||
query = "DELETE FROM emby WHERE emby_id = ?"
|
||||
self.embycursor.execute(query, (embyid,))
|
||||
|
||||
def removeWildItem(self, embyid):
|
||||
|
||||
query = "DELETE FROM emby WHERE emby_id LIKE ?"
|
||||
self.embycursor.execute(query, (embyid+"%",))
|
||||
|
|
@ -2008,6 +2008,7 @@ class Music(Items):
|
|||
# Process single song
|
||||
kodiversion = self.kodiversion
|
||||
kodicursor = self.kodicursor
|
||||
emby = self.emby
|
||||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
artwork = self.artwork
|
||||
|
@ -2132,14 +2133,22 @@ class Music(Items):
|
|||
item.attrib.get('parentRatingKey'))
|
||||
albumid = emby_dbalbum[0]
|
||||
except KeyError:
|
||||
# No album Id associated to the song.
|
||||
self.logMsg("Song itemid: %s has no albumId." % itemid, 1)
|
||||
return
|
||||
# Verify if there's an album associated.
|
||||
album_name = item.get('Album')
|
||||
if album_name:
|
||||
self.logMsg("Creating virtual music album for song: %s." % itemid, 1)
|
||||
albumid = kodi_db.addAlbum(album_name, API.getProvider('MusicBrainzAlbum'))
|
||||
emby_db.addReference("%salbum%s" % (itemid, albumid), albumid, "MusicAlbum_", "album")
|
||||
else:
|
||||
# No album Id associated to the song.
|
||||
self.logMsg("Song itemid: %s has no albumId associated." % itemid, 1)
|
||||
return False
|
||||
|
||||
except TypeError:
|
||||
# No album found. Let's create it
|
||||
self.logMsg("Album database entry missing.", 1)
|
||||
emby_albumId = item.attrib.get('parentRatingKey')
|
||||
album = GetPlexMetadata(emby_albumId)
|
||||
album = emby.getItem(emby_albumId)
|
||||
self.add_updateAlbum(album)
|
||||
emby_dbalbum = emby_db.getItem_byId(emby_albumId)
|
||||
try:
|
||||
|
@ -2214,43 +2223,47 @@ class Music(Items):
|
|||
'''
|
||||
)
|
||||
kodicursor.execute(query, (songid, albumid, track, title, duration))
|
||||
# Verify if album has artists
|
||||
addArtist = False
|
||||
query = ' '.join((
|
||||
# Link song to artists
|
||||
for index, artist in enumerate(item['ArtistItems']):
|
||||
|
||||
"SELECT strArtists",
|
||||
"FROM album",
|
||||
"WHERE idAlbum = ?"
|
||||
))
|
||||
kodicursor.execute(query, (albumid,))
|
||||
result = kodicursor.fetchone()
|
||||
if result and result[0] == "":
|
||||
addArtist = True
|
||||
artist_name = artist['Name']
|
||||
artist_eid = artist['Id']
|
||||
artist_edb = emby_db.getItem_byId(artist_eid)
|
||||
try:
|
||||
artistid = artist_edb[0]
|
||||
except TypeError:
|
||||
# Artist is missing from emby database, add it.
|
||||
artist_full = emby.getItem(artist_eid)
|
||||
self.add_updateArtist(artist_full)
|
||||
artist_edb = emby_db.getItem_byId(artist_eid)
|
||||
artistid = artist_edb[0]
|
||||
finally:
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO song_artist(idArtist, idSong, iOrder, strArtist)
|
||||
|
||||
# if item['AlbumArtists']:
|
||||
# album_artists = item['AlbumArtists']
|
||||
# else:
|
||||
# album_artists = item['ArtistItems']
|
||||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
kodicursor.execute(query, (artistid, songid, index, artist_name))
|
||||
|
||||
# Link song to artist
|
||||
artist_name = item.attrib.get('grandparentTitle')
|
||||
emby_dbartist = emby_db.getItem_byId(
|
||||
item.attrib.get('grandparentRatingKey'))
|
||||
try:
|
||||
artistid = emby_dbartist[0]
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO song_artist(idArtist, idSong, strArtist)
|
||||
# Verify if album artist exists
|
||||
album_artists = []
|
||||
for artist in item['AlbumArtists']:
|
||||
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
kodicursor.execute(query, (artistid, songid, artist_name))
|
||||
|
||||
if addArtist:
|
||||
artist_name = artist['Name']
|
||||
album_artists.append(artist_name)
|
||||
artist_eid = artist['Id']
|
||||
artist_edb = emby_db.getItem_byId(artist_eid)
|
||||
try:
|
||||
artistid = artist_edb[0]
|
||||
except TypeError:
|
||||
# Artist is missing from emby database, add it.
|
||||
artist_full = emby.getItem(artist_eid)
|
||||
self.add_updateArtist(artist_full)
|
||||
artist_edb = emby_db.getItem_byId(artist_eid)
|
||||
artistid = artist_edb[0]
|
||||
finally:
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
|
||||
|
@ -2259,17 +2272,101 @@ class Music(Items):
|
|||
'''
|
||||
)
|
||||
kodicursor.execute(query, (artistid, albumid, artist_name))
|
||||
# Update discography
|
||||
if item.get('Album'):
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
|
||||
|
||||
if addArtist:
|
||||
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
||||
kodicursor.execute(query, (artist_name, albumid))
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
kodicursor.execute(query, (artistid, item['Album'], 0))
|
||||
else:
|
||||
album_artists = " / ".join(album_artists)
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT strArtists",
|
||||
"FROM album",
|
||||
"WHERE idAlbum = ?"
|
||||
))
|
||||
kodicursor.execute(query, (albumid,))
|
||||
result = kodicursor.fetchone()
|
||||
if result and result[0] != album_artists:
|
||||
# Field is empty
|
||||
if kodiversion in (16, 17):
|
||||
# Kodi Jarvis, Krypton
|
||||
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
||||
kodicursor.execute(query, (album_artists, albumid))
|
||||
elif kodiversion == 15:
|
||||
# Kodi Isengard
|
||||
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
||||
kodicursor.execute(query, (album_artists, albumid))
|
||||
else:
|
||||
# Kodi Helix
|
||||
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
||||
kodicursor.execute(query, (album_artists, albumid))
|
||||
|
||||
# Add genres
|
||||
kodi_db.addMusicGenres(songid, genres, "song")
|
||||
|
||||
# Update artwork
|
||||
allart = API.getAllArtwork(parentInfo=True)
|
||||
artwork.addArtwork(allart, songid, "song", kodicursor)
|
||||
|
||||
if item.get('AlbumId') is None:
|
||||
# Update album artwork
|
||||
artwork.addArtwork(allart, albumid, "album", kodicursor)
|
||||
|
||||
def updateUserdata(self, item):
|
||||
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
|
||||
# Poster with progress bar
|
||||
kodicursor = self.kodicursor
|
||||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
API = api.API(item)
|
||||
|
||||
# Get emby information
|
||||
itemid = item['Id']
|
||||
checksum = API.getChecksum()
|
||||
userdata = API.getUserData()
|
||||
runtime = API.getRuntime()
|
||||
rating = userdata['UserRating']
|
||||
|
||||
# Get Kodi information
|
||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
||||
try:
|
||||
kodiid = emby_dbitem[0]
|
||||
mediatype = emby_dbitem[4]
|
||||
self.logMsg("Update playstate for %s: %s" % (mediatype, item['Name']), 1)
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
if mediatype == "song":
|
||||
|
||||
#should we ignore this item ?
|
||||
#happens when userdata updated by ratings method
|
||||
if utils.window("ignore-update-%s" %itemid):
|
||||
utils.window("ignore-update-%s" %itemid,clear=True)
|
||||
return
|
||||
|
||||
# Process playstates
|
||||
playcount = userdata['PlayCount']
|
||||
dateplayed = userdata['LastPlayedDate']
|
||||
|
||||
#process item ratings
|
||||
rating, comment, hasEmbeddedCover = musicutils.getAdditionalSongTags(itemid, rating, API, kodicursor, emby_db, self.enableimportsongrating, self.enableexportsongrating, self.enableupdatesongrating)
|
||||
|
||||
query = "UPDATE song SET iTimesPlayed = ?, lastplayed = ?, rating = ? WHERE idSong = ?"
|
||||
kodicursor.execute(query, (playcount, dateplayed, rating, kodiid))
|
||||
|
||||
elif mediatype == "album":
|
||||
# Process playstates
|
||||
query = "UPDATE album SET iRating = ? WHERE idAlbum = ?"
|
||||
kodicursor.execute(query, (rating, kodiid))
|
||||
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
|
||||
def remove(self, itemid):
|
||||
# Remove kodiid, fileid, pathid, emby reference
|
||||
emby_db = self.emby_db
|
||||
|
@ -2295,6 +2392,21 @@ class Music(Items):
|
|||
if mediatype == "song":
|
||||
# Delete song
|
||||
self.removeSong(kodiid)
|
||||
# This should only address single song scenario, where server doesn't actually
|
||||
# create an album for the song.
|
||||
customitems = emby_db.getItem_byWildId(itemid)
|
||||
emby_db.removeWildItem(itemid)
|
||||
|
||||
for item in customitems:
|
||||
|
||||
item_kid = item[0]
|
||||
item_mediatype = item[1]
|
||||
|
||||
if item_mediatype == "album":
|
||||
childs = emby_db.getItem_byParentId(item_kid, "song")
|
||||
if not childs:
|
||||
# Delete album
|
||||
self.removeAlbum(item_kid)
|
||||
|
||||
##### IF ALBUM #####
|
||||
|
||||
|
|
|
@ -1157,6 +1157,7 @@ class Kodidb_Functions():
|
|||
|
||||
def addAlbum(self, name, musicbrainz):
|
||||
|
||||
kodiversion = self.kodiversion
|
||||
cursor = self.cursor
|
||||
|
||||
query = ' '.join((
|
||||
|
@ -1172,14 +1173,24 @@ class Kodidb_Functions():
|
|||
# Create the album
|
||||
cursor.execute("select coalesce(max(idAlbum),0) from album")
|
||||
albumid = cursor.fetchone()[0] + 1
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID)
|
||||
if kodiversion in (15, 16, 17):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strReleaseType)
|
||||
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (albumid, name, musicbrainz))
|
||||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (albumid, name, musicbrainz, "album"))
|
||||
else: # Helix
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID)
|
||||
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (albumid, name, musicbrainz))
|
||||
|
||||
return albumid
|
||||
|
||||
|
|
|
@ -3,13 +3,19 @@
|
|||
#################################################################################################
|
||||
|
||||
import os
|
||||
import xbmc, xbmcaddon, xbmcvfs
|
||||
import utils
|
||||
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
import xbmcvfs
|
||||
|
||||
from mutagen.flac import FLAC, Picture
|
||||
from mutagen.id3 import ID3
|
||||
from mutagen import id3
|
||||
import base64
|
||||
|
||||
import read_embyserver as embyserver
|
||||
import utils
|
||||
|
||||
#################################################################################################
|
||||
|
||||
# Helper for the music library, intended to fix missing song ID3 tags on Emby
|
||||
|
@ -62,6 +68,9 @@ def getEmbyRatingFromKodiRating(rating):
|
|||
return(like, favourite, deletelike)
|
||||
|
||||
def getAdditionalSongTags(embyid, emby_rating, API, kodicursor, emby_db, enableimportsongrating, enableexportsongrating, enableupdatesongrating):
|
||||
|
||||
emby = embyserver.Read_EmbyServer()
|
||||
|
||||
previous_values = None
|
||||
filename = API.getFilePath()
|
||||
rating = 0
|
||||
|
@ -163,7 +172,7 @@ def getAdditionalSongTags(embyid, emby_rating, API, kodicursor, emby_db, enablei
|
|||
# sync details to emby server. Translation needed between ID3 rating and emby likes/favourites:
|
||||
like, favourite, deletelike = getEmbyRatingFromKodiRating(rating)
|
||||
utils.window("ignore-update-%s" %embyid, "true") #set temp windows prop to ignore the update from webclient update
|
||||
API.updateUserRating(embyid, like, favourite, deletelike)
|
||||
emby.updateUserRating(embyid, like, favourite, deletelike)
|
||||
|
||||
return (rating, comment, hasEmbeddedCover)
|
||||
|
||||
|
|
|
@ -562,4 +562,29 @@ class Read_EmbyServer():
|
|||
if mediatype:
|
||||
sorted_items.setdefault(mediatype, []).append(item)
|
||||
|
||||
return sorted_items
|
||||
return sorted_items
|
||||
|
||||
def updateUserRating(self, itemid, like=None, favourite=None, deletelike=False):
|
||||
# Updates the user rating to Emby
|
||||
doUtils = self.doUtils
|
||||
|
||||
if favourite:
|
||||
url = "{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid
|
||||
doUtils(url, type="POST")
|
||||
elif favourite == False:
|
||||
url = "{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid
|
||||
doUtils(url, type="DELETE")
|
||||
|
||||
if not deletelike and like:
|
||||
url = "{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=true&format=json" % itemid
|
||||
doUtils(url, type="POST")
|
||||
elif not deletelike and like == False:
|
||||
url = "{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=false&format=json" % itemid
|
||||
doUtil(url, type="POST")
|
||||
elif deletelike:
|
||||
url = "{server}/emby/Users/{UserId}/Items/%s/Rating?format=json" % itemid
|
||||
doUtils(url, type="DELETE")
|
||||
|
||||
self.logMsg("Update user rating to emby for itemid: %s "
|
||||
"| like: %s | favourite: %s | deletelike: %s"
|
||||
% (itemid, like, favourite, deletelike), 1)
|
|
@ -8,6 +8,7 @@ import json
|
|||
import pstats
|
||||
import sqlite3
|
||||
from datetime import datetime, timedelta
|
||||
import StringIO
|
||||
import time
|
||||
import unicodedata
|
||||
import xml.etree.ElementTree as etree
|
||||
|
@ -538,44 +539,26 @@ def reset():
|
|||
line1="Database reset has completed, Kodi will now restart to apply the changes.")
|
||||
xbmc.executebuiltin('RestartApp')
|
||||
|
||||
def startProfiling():
|
||||
|
||||
pr = cProfile.Profile()
|
||||
pr.enable()
|
||||
|
||||
return pr
|
||||
def profiling(sortby="cumulative"):
|
||||
# Will print results to Kodi log
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
|
||||
pr = cProfile.Profile()
|
||||
|
||||
def stopProfiling(pr, profileName):
|
||||
from datetime import time
|
||||
pr.disable()
|
||||
ps = pstats.Stats(pr)
|
||||
|
||||
profiles = xbmc.translatePath("%sprofiles/"
|
||||
% xbmcaddon.Addon().getAddonInfo('profile')).decode('utf-8')
|
||||
pr.enable()
|
||||
result = func(*args, **kwargs)
|
||||
pr.disable()
|
||||
|
||||
if not xbmcvfs.exists(profiles):
|
||||
# Create the profiles folder
|
||||
xbmcvfs.mkdir(profiles)
|
||||
s = StringIO.StringIO()
|
||||
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
|
||||
ps.print_stats()
|
||||
logMsg("EMBY Profiling", s.getvalue(), 1)
|
||||
|
||||
timestamp = time.strftime("%Y-%m-%d %H-%M-%S")
|
||||
profile = "%s%s_profile_(%s).tab" % (profiles, profileName, timestamp)
|
||||
|
||||
f = xbmcvfs.File(profile, 'w')
|
||||
f.write("NumbCalls\tTotalTime\tCumulativeTime\tFunctionName\tFileName\r\n")
|
||||
for (key, value) in ps.stats.items():
|
||||
(filename, count, func_name) = key
|
||||
(ccalls, ncalls, total_time, cumulative_time, callers) = value
|
||||
try:
|
||||
f.write(
|
||||
"%s\t%s\t%s\t%s\t%s\r\n"
|
||||
% (ncalls, "{:10.4f}".format(total_time),
|
||||
"{:10.4f}".format(cumulative_time), func_name, filename))
|
||||
except ValueError:
|
||||
f.write(
|
||||
"%s\t%s\t%s\t%s\t%s\r\n"
|
||||
% (ncalls, "{0}".format(total_time),
|
||||
"{0}".format(cumulative_time), func_name, filename))
|
||||
f.close()
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
def convertdate(date):
|
||||
try:
|
||||
|
|
Loading…
Reference in a new issue