mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2024-11-10 04:06:11 +00:00
exit loop
This commit is contained in:
parent
2e1a2328fd
commit
065bff5215
1 changed files with 286 additions and 285 deletions
|
@ -1,286 +1,287 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
|
|
||||||
from mutagen.flac import FLAC, Picture
|
from mutagen.flac import FLAC, Picture
|
||||||
from mutagen.id3 import ID3
|
from mutagen.id3 import ID3
|
||||||
from mutagen import id3
|
from mutagen import id3
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
import read_embyserver as embyserver
|
import read_embyserver as embyserver
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
# Helper for the music library, intended to fix missing song ID3 tags on Emby
|
# Helper for the music library, intended to fix missing song ID3 tags on Emby
|
||||||
|
|
||||||
def logMsg(msg, lvl=1):
|
def logMsg(msg, lvl=1):
|
||||||
utils.logMsg("%s %s" % ("Emby", "musictools"), msg, lvl)
|
utils.logMsg("%s %s" % ("Emby", "musictools"), msg, lvl)
|
||||||
|
|
||||||
def getRealFileName(filename, isTemp=False):
|
def getRealFileName(filename, isTemp=False):
|
||||||
#get the filename path accessible by python if possible...
|
#get the filename path accessible by python if possible...
|
||||||
|
|
||||||
if not xbmcvfs.exists(filename):
|
if not xbmcvfs.exists(filename):
|
||||||
logMsg( "File does not exist! %s" %(filename), 0)
|
logMsg( "File does not exist! %s" %(filename), 0)
|
||||||
return (False, "")
|
return (False, "")
|
||||||
|
|
||||||
#if we use os.path method on older python versions (sunch as some android builds), we need to pass arguments as string
|
#if we use os.path method on older python versions (sunch as some android builds), we need to pass arguments as string
|
||||||
if os.path.supports_unicode_filenames:
|
if os.path.supports_unicode_filenames:
|
||||||
checkfile = filename
|
checkfile = filename
|
||||||
else:
|
else:
|
||||||
checkfile = filename.encode("utf-8")
|
checkfile = filename.encode("utf-8")
|
||||||
|
|
||||||
# determine if our python module is able to access the file directly...
|
# determine if our python module is able to access the file directly...
|
||||||
if os.path.exists(checkfile):
|
if os.path.exists(checkfile):
|
||||||
filename = filename
|
filename = filename
|
||||||
elif os.path.exists(checkfile.replace("smb://","\\\\").replace("/","\\")):
|
elif os.path.exists(checkfile.replace("smb://","\\\\").replace("/","\\")):
|
||||||
filename = filename.replace("smb://","\\\\").replace("/","\\")
|
filename = filename.replace("smb://","\\\\").replace("/","\\")
|
||||||
else:
|
else:
|
||||||
#file can not be accessed by python directly, we copy it for processing...
|
#file can not be accessed by python directly, we copy it for processing...
|
||||||
isTemp = True
|
isTemp = True
|
||||||
if "/" in filename: filepart = filename.split("/")[-1]
|
if "/" in filename: filepart = filename.split("/")[-1]
|
||||||
else: filepart = filename.split("\\")[-1]
|
else: filepart = filename.split("\\")[-1]
|
||||||
tempfile = "special://temp/"+filepart
|
tempfile = "special://temp/"+filepart
|
||||||
xbmcvfs.copy(filename, tempfile)
|
xbmcvfs.copy(filename, tempfile)
|
||||||
filename = xbmc.translatePath(tempfile).decode("utf-8")
|
filename = xbmc.translatePath(tempfile).decode("utf-8")
|
||||||
|
|
||||||
return (isTemp,filename)
|
return (isTemp,filename)
|
||||||
|
|
||||||
def getEmbyRatingFromKodiRating(rating):
|
def getEmbyRatingFromKodiRating(rating):
|
||||||
# Translation needed between Kodi/ID3 rating and emby likes/favourites:
|
# Translation needed between Kodi/ID3 rating and emby likes/favourites:
|
||||||
# 3+ rating in ID3 = emby like
|
# 3+ rating in ID3 = emby like
|
||||||
# 5+ rating in ID3 = emby favourite
|
# 5+ rating in ID3 = emby favourite
|
||||||
# rating 0 = emby dislike
|
# rating 0 = emby dislike
|
||||||
# rating 1-2 = emby no likes or dislikes (returns 1 in results)
|
# rating 1-2 = emby no likes or dislikes (returns 1 in results)
|
||||||
favourite = False
|
favourite = False
|
||||||
deletelike = False
|
deletelike = False
|
||||||
like = False
|
like = False
|
||||||
if (rating >= 3): like = True
|
if (rating >= 3): like = True
|
||||||
if (rating == 0): like = False
|
if (rating == 0): like = False
|
||||||
if (rating == 1 or rating == 2): deletelike = True
|
if (rating == 1 or rating == 2): deletelike = True
|
||||||
if (rating >= 5): favourite = True
|
if (rating >= 5): favourite = True
|
||||||
return(like, favourite, deletelike)
|
return(like, favourite, deletelike)
|
||||||
|
|
||||||
def getAdditionalSongTags(embyid, emby_rating, API, kodicursor, emby_db, enableimportsongrating, enableexportsongrating, enableupdatesongrating):
|
def getAdditionalSongTags(embyid, emby_rating, API, kodicursor, emby_db, enableimportsongrating, enableexportsongrating, enableupdatesongrating):
|
||||||
|
|
||||||
emby = embyserver.Read_EmbyServer()
|
emby = embyserver.Read_EmbyServer()
|
||||||
|
|
||||||
previous_values = None
|
previous_values = None
|
||||||
filename = API.getFilePath()
|
filename = API.getFilePath()
|
||||||
rating = 0
|
rating = 0
|
||||||
emby_rating = int(round(emby_rating, 0))
|
emby_rating = int(round(emby_rating, 0))
|
||||||
|
|
||||||
#get file rating and comment tag from file itself.
|
#get file rating and comment tag from file itself.
|
||||||
if enableimportsongrating:
|
if enableimportsongrating:
|
||||||
file_rating, comment, hasEmbeddedCover = getSongTags(filename)
|
file_rating, comment, hasEmbeddedCover = getSongTags(filename)
|
||||||
else:
|
else:
|
||||||
file_rating = 0
|
file_rating = 0
|
||||||
comment = ""
|
comment = ""
|
||||||
hasEmbeddedCover = False
|
hasEmbeddedCover = False
|
||||||
|
|
||||||
|
|
||||||
emby_dbitem = emby_db.getItem_byId(embyid)
|
emby_dbitem = emby_db.getItem_byId(embyid)
|
||||||
try:
|
try:
|
||||||
kodiid = emby_dbitem[0]
|
kodiid = emby_dbitem[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# Item is not in database.
|
# Item is not in database.
|
||||||
currentvalue = None
|
currentvalue = None
|
||||||
else:
|
else:
|
||||||
query = "SELECT rating FROM song WHERE idSong = ?"
|
query = "SELECT rating FROM song WHERE idSong = ?"
|
||||||
kodicursor.execute(query, (kodiid,))
|
kodicursor.execute(query, (kodiid,))
|
||||||
try:
|
try:
|
||||||
currentvalue = int(round(float(kodicursor.fetchone()[0]),0))
|
currentvalue = int(round(float(kodicursor.fetchone()[0]),0))
|
||||||
except: currentvalue = None
|
except: currentvalue = None
|
||||||
|
|
||||||
# Only proceed if we actually have a rating from the file
|
# Only proceed if we actually have a rating from the file
|
||||||
if file_rating is None and currentvalue:
|
if file_rating is None and currentvalue:
|
||||||
return (currentvalue, comment, False)
|
return (currentvalue, comment, False)
|
||||||
elif file_rating is None and not currentvalue:
|
elif file_rating is None and not currentvalue:
|
||||||
return (emby_rating, comment, False)
|
return (emby_rating, comment, False)
|
||||||
|
|
||||||
logMsg("getAdditionalSongTags --> embyid: %s - emby_rating: %s - file_rating: %s - current rating in kodidb: %s" %(embyid, emby_rating, file_rating, currentvalue))
|
logMsg("getAdditionalSongTags --> embyid: %s - emby_rating: %s - file_rating: %s - current rating in kodidb: %s" %(embyid, emby_rating, file_rating, currentvalue))
|
||||||
|
|
||||||
updateFileRating = False
|
updateFileRating = False
|
||||||
updateEmbyRating = False
|
updateEmbyRating = False
|
||||||
|
|
||||||
if currentvalue != None:
|
if currentvalue != None:
|
||||||
# we need to translate the emby values...
|
# we need to translate the emby values...
|
||||||
if emby_rating == 1 and currentvalue == 2:
|
if emby_rating == 1 and currentvalue == 2:
|
||||||
emby_rating = 2
|
emby_rating = 2
|
||||||
if emby_rating == 3 and currentvalue == 4:
|
if emby_rating == 3 and currentvalue == 4:
|
||||||
emby_rating = 4
|
emby_rating = 4
|
||||||
|
|
||||||
#if updating rating into file is disabled, we ignore the rating in the file...
|
#if updating rating into file is disabled, we ignore the rating in the file...
|
||||||
if not enableupdatesongrating:
|
if not enableupdatesongrating:
|
||||||
file_rating = currentvalue
|
file_rating = currentvalue
|
||||||
#if convert emby likes/favourites convert to song rating is disabled, we ignore the emby rating...
|
#if convert emby likes/favourites convert to song rating is disabled, we ignore the emby rating...
|
||||||
if not enableexportsongrating:
|
if not enableexportsongrating:
|
||||||
emby_rating = currentvalue
|
emby_rating = currentvalue
|
||||||
|
|
||||||
if (emby_rating == file_rating) and (file_rating != currentvalue):
|
if (emby_rating == file_rating) and (file_rating != currentvalue):
|
||||||
#the rating has been updated from kodi itself, update change to both emby ands file
|
#the rating has been updated from kodi itself, update change to both emby ands file
|
||||||
rating = currentvalue
|
rating = currentvalue
|
||||||
updateFileRating = True
|
updateFileRating = True
|
||||||
updateEmbyRating = True
|
updateEmbyRating = True
|
||||||
elif (emby_rating != currentvalue) and (file_rating == currentvalue):
|
elif (emby_rating != currentvalue) and (file_rating == currentvalue):
|
||||||
#emby rating changed - update the file
|
#emby rating changed - update the file
|
||||||
rating = emby_rating
|
rating = emby_rating
|
||||||
updateFileRating = True
|
updateFileRating = True
|
||||||
elif (file_rating != currentvalue) and (emby_rating == currentvalue):
|
elif (file_rating != currentvalue) and (emby_rating == currentvalue):
|
||||||
#file rating was updated, sync change to emby
|
#file rating was updated, sync change to emby
|
||||||
rating = file_rating
|
rating = file_rating
|
||||||
updateEmbyRating = True
|
updateEmbyRating = True
|
||||||
elif (emby_rating != currentvalue) and (file_rating != currentvalue):
|
elif (emby_rating != currentvalue) and (file_rating != currentvalue):
|
||||||
#both ratings have changed (corner case) - the highest rating wins...
|
#both ratings have changed (corner case) - the highest rating wins...
|
||||||
if emby_rating > file_rating:
|
if emby_rating > file_rating:
|
||||||
rating = emby_rating
|
rating = emby_rating
|
||||||
updateFileRating = True
|
updateFileRating = True
|
||||||
else:
|
else:
|
||||||
rating = file_rating
|
rating = file_rating
|
||||||
updateEmbyRating = True
|
updateEmbyRating = True
|
||||||
else:
|
else:
|
||||||
#nothing has changed, just return the current value
|
#nothing has changed, just return the current value
|
||||||
rating = currentvalue
|
rating = currentvalue
|
||||||
else:
|
else:
|
||||||
# no rating yet in DB
|
# no rating yet in DB
|
||||||
if enableimportsongrating:
|
if enableimportsongrating:
|
||||||
#prefer the file rating
|
#prefer the file rating
|
||||||
rating = file_rating
|
rating = file_rating
|
||||||
#determine if we should also send the rating to emby server
|
#determine if we should also send the rating to emby server
|
||||||
if enableexportsongrating:
|
if enableexportsongrating:
|
||||||
if emby_rating == 1 and file_rating == 2:
|
if emby_rating == 1 and file_rating == 2:
|
||||||
emby_rating = 2
|
emby_rating = 2
|
||||||
if emby_rating == 3 and file_rating == 4:
|
if emby_rating == 3 and file_rating == 4:
|
||||||
emby_rating = 4
|
emby_rating = 4
|
||||||
if emby_rating != file_rating:
|
if emby_rating != file_rating:
|
||||||
updateEmbyRating = True
|
updateEmbyRating = True
|
||||||
|
|
||||||
elif enableexportsongrating:
|
elif enableexportsongrating:
|
||||||
#set the initial rating to emby value
|
#set the initial rating to emby value
|
||||||
rating = emby_rating
|
rating = emby_rating
|
||||||
|
|
||||||
if updateFileRating and enableupdatesongrating:
|
if updateFileRating and enableupdatesongrating:
|
||||||
updateRatingToFile(rating, filename)
|
updateRatingToFile(rating, filename)
|
||||||
|
|
||||||
if updateEmbyRating and enableexportsongrating:
|
if updateEmbyRating and enableexportsongrating:
|
||||||
# sync details to emby server. Translation needed between ID3 rating and emby likes/favourites:
|
# sync details to emby server. Translation needed between ID3 rating and emby likes/favourites:
|
||||||
like, favourite, deletelike = getEmbyRatingFromKodiRating(rating)
|
like, favourite, deletelike = getEmbyRatingFromKodiRating(rating)
|
||||||
utils.window("ignore-update-%s" %embyid, "true") #set temp windows prop to ignore the update from webclient update
|
utils.window("ignore-update-%s" %embyid, "true") #set temp windows prop to ignore the update from webclient update
|
||||||
emby.updateUserRating(embyid, like, favourite, deletelike)
|
emby.updateUserRating(embyid, like, favourite, deletelike)
|
||||||
|
|
||||||
return (rating, comment, hasEmbeddedCover)
|
return (rating, comment, hasEmbeddedCover)
|
||||||
|
|
||||||
def getSongTags(file):
|
def getSongTags(file):
|
||||||
# Get the actual ID3 tags for music songs as the server is lacking that info
|
# Get the actual ID3 tags for music songs as the server is lacking that info
|
||||||
rating = 0
|
rating = 0
|
||||||
comment = ""
|
comment = ""
|
||||||
hasEmbeddedCover = False
|
hasEmbeddedCover = False
|
||||||
|
|
||||||
isTemp,filename = getRealFileName(file)
|
isTemp,filename = getRealFileName(file)
|
||||||
logMsg( "getting song ID3 tags for " + filename)
|
logMsg( "getting song ID3 tags for " + filename)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
###### FLAC FILES #############
|
###### FLAC FILES #############
|
||||||
if filename.lower().endswith(".flac"):
|
if filename.lower().endswith(".flac"):
|
||||||
audio = FLAC(filename)
|
audio = FLAC(filename)
|
||||||
if audio.get("comment"):
|
if audio.get("comment"):
|
||||||
comment = audio.get("comment")[0]
|
comment = audio.get("comment")[0]
|
||||||
for pic in audio.pictures:
|
for pic in audio.pictures:
|
||||||
if pic.type == 3 and pic.data:
|
if pic.type == 3 and pic.data:
|
||||||
#the file has an embedded cover
|
#the file has an embedded cover
|
||||||
hasEmbeddedCover = True
|
hasEmbeddedCover = True
|
||||||
if audio.get("rating"):
|
break
|
||||||
rating = float(audio.get("rating")[0])
|
if audio.get("rating"):
|
||||||
#flac rating is 0-100 and needs to be converted to 0-5 range
|
rating = float(audio.get("rating")[0])
|
||||||
if rating > 5: rating = (rating / 100) * 5
|
#flac rating is 0-100 and needs to be converted to 0-5 range
|
||||||
|
if rating > 5: rating = (rating / 100) * 5
|
||||||
###### MP3 FILES #############
|
|
||||||
elif filename.lower().endswith(".mp3"):
|
###### MP3 FILES #############
|
||||||
audio = ID3(filename)
|
elif filename.lower().endswith(".mp3"):
|
||||||
|
audio = ID3(filename)
|
||||||
if audio.get("APIC:Front Cover"):
|
|
||||||
if audio.get("APIC:Front Cover").data:
|
if audio.get("APIC:Front Cover"):
|
||||||
hasEmbeddedCover = True
|
if audio.get("APIC:Front Cover").data:
|
||||||
|
hasEmbeddedCover = True
|
||||||
if audio.get("comment"):
|
|
||||||
comment = audio.get("comment")[0]
|
if audio.get("comment"):
|
||||||
if audio.get("POPM:Windows Media Player 9 Series"):
|
comment = audio.get("comment")[0]
|
||||||
if audio.get("POPM:Windows Media Player 9 Series").rating:
|
if audio.get("POPM:Windows Media Player 9 Series"):
|
||||||
rating = float(audio.get("POPM:Windows Media Player 9 Series").rating)
|
if audio.get("POPM:Windows Media Player 9 Series").rating:
|
||||||
#POPM rating is 0-255 and needs to be converted to 0-5 range
|
rating = float(audio.get("POPM:Windows Media Player 9 Series").rating)
|
||||||
if rating > 5: rating = (rating / 255) * 5
|
#POPM rating is 0-255 and needs to be converted to 0-5 range
|
||||||
else:
|
if rating > 5: rating = (rating / 255) * 5
|
||||||
logMsg( "Not supported fileformat or unable to access file: %s" %(filename))
|
else:
|
||||||
|
logMsg( "Not supported fileformat or unable to access file: %s" %(filename))
|
||||||
#the rating must be a round value
|
|
||||||
rating = int(round(rating,0))
|
#the rating must be a round value
|
||||||
|
rating = int(round(rating,0))
|
||||||
except Exception as e:
|
|
||||||
#file in use ?
|
except Exception as e:
|
||||||
utils.logMsg("Exception in getSongTags", str(e),0)
|
#file in use ?
|
||||||
rating = None
|
utils.logMsg("Exception in getSongTags", str(e),0)
|
||||||
|
rating = None
|
||||||
#remove tempfile if needed....
|
|
||||||
if isTemp: xbmcvfs.delete(filename)
|
#remove tempfile if needed....
|
||||||
|
if isTemp: xbmcvfs.delete(filename)
|
||||||
return (rating, comment, hasEmbeddedCover)
|
|
||||||
|
return (rating, comment, hasEmbeddedCover)
|
||||||
def updateRatingToFile(rating, file):
|
|
||||||
#update the rating from Emby to the file
|
def updateRatingToFile(rating, file):
|
||||||
|
#update the rating from Emby to the file
|
||||||
f = xbmcvfs.File(file)
|
|
||||||
org_size = f.size()
|
f = xbmcvfs.File(file)
|
||||||
f.close()
|
org_size = f.size()
|
||||||
|
f.close()
|
||||||
#create tempfile
|
|
||||||
if "/" in file: filepart = file.split("/")[-1]
|
#create tempfile
|
||||||
else: filepart = file.split("\\")[-1]
|
if "/" in file: filepart = file.split("/")[-1]
|
||||||
tempfile = "special://temp/"+filepart
|
else: filepart = file.split("\\")[-1]
|
||||||
xbmcvfs.copy(file, tempfile)
|
tempfile = "special://temp/"+filepart
|
||||||
tempfile = xbmc.translatePath(tempfile).decode("utf-8")
|
xbmcvfs.copy(file, tempfile)
|
||||||
|
tempfile = xbmc.translatePath(tempfile).decode("utf-8")
|
||||||
logMsg( "setting song rating: %s for filename: %s - using tempfile: %s" %(rating,file,tempfile))
|
|
||||||
|
logMsg( "setting song rating: %s for filename: %s - using tempfile: %s" %(rating,file,tempfile))
|
||||||
if not tempfile:
|
|
||||||
return
|
if not tempfile:
|
||||||
|
return
|
||||||
try:
|
|
||||||
if tempfile.lower().endswith(".flac"):
|
try:
|
||||||
audio = FLAC(tempfile)
|
if tempfile.lower().endswith(".flac"):
|
||||||
calcrating = int(round((float(rating) / 5) * 100, 0))
|
audio = FLAC(tempfile)
|
||||||
audio["rating"] = str(calcrating)
|
calcrating = int(round((float(rating) / 5) * 100, 0))
|
||||||
audio.save()
|
audio["rating"] = str(calcrating)
|
||||||
elif tempfile.lower().endswith(".mp3"):
|
audio.save()
|
||||||
audio = ID3(tempfile)
|
elif tempfile.lower().endswith(".mp3"):
|
||||||
calcrating = int(round((float(rating) / 5) * 255, 0))
|
audio = ID3(tempfile)
|
||||||
audio.add(id3.POPM(email="Windows Media Player 9 Series", rating=calcrating, count=1))
|
calcrating = int(round((float(rating) / 5) * 255, 0))
|
||||||
audio.save()
|
audio.add(id3.POPM(email="Windows Media Player 9 Series", rating=calcrating, count=1))
|
||||||
else:
|
audio.save()
|
||||||
logMsg( "Not supported fileformat: %s" %(tempfile))
|
else:
|
||||||
|
logMsg( "Not supported fileformat: %s" %(tempfile))
|
||||||
#once we have succesfully written the flags we move the temp file to destination, otherwise not proceeding and just delete the temp
|
|
||||||
#safety check: we check the file size of the temp file before proceeding with overwite of original file
|
#once we have succesfully written the flags we move the temp file to destination, otherwise not proceeding and just delete the temp
|
||||||
f = xbmcvfs.File(tempfile)
|
#safety check: we check the file size of the temp file before proceeding with overwite of original file
|
||||||
checksum_size = f.size()
|
f = xbmcvfs.File(tempfile)
|
||||||
f.close()
|
checksum_size = f.size()
|
||||||
if checksum_size >= org_size:
|
f.close()
|
||||||
xbmcvfs.delete(file)
|
if checksum_size >= org_size:
|
||||||
xbmcvfs.copy(tempfile,file)
|
xbmcvfs.delete(file)
|
||||||
else:
|
xbmcvfs.copy(tempfile,file)
|
||||||
logMsg( "Checksum mismatch for filename: %s - using tempfile: %s - not proceeding with file overwite!" %(rating,file,tempfile))
|
else:
|
||||||
|
logMsg( "Checksum mismatch for filename: %s - using tempfile: %s - not proceeding with file overwite!" %(rating,file,tempfile))
|
||||||
#always delete the tempfile
|
|
||||||
xbmcvfs.delete(tempfile)
|
#always delete the tempfile
|
||||||
|
xbmcvfs.delete(tempfile)
|
||||||
except Exception as e:
|
|
||||||
#file in use ?
|
except Exception as e:
|
||||||
logMsg("Exception in updateRatingToFile %s" %e,0)
|
#file in use ?
|
||||||
|
logMsg("Exception in updateRatingToFile %s" %e,0)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue