Kodi Community Forum
Help to set "UserRating" in a song using script - Printable Version

+- Kodi Community Forum (https://forum.kodi.tv)
+-- Forum: Development (https://forum.kodi.tv/forumdisplay.php?fid=32)
+--- Forum: Add-ons (https://forum.kodi.tv/forumdisplay.php?fid=26)
+--- Thread: Help to set "UserRating" in a song using script (/showthread.php?tid=371574)

Pages: 1 2 3 4 5


RE: Help to set "UserRating" in a song using script - izprtxqkft - 2023-02-04

ah! i see, so ive come up against this before in umpteenth projects, youre tracking a song but then the song youre tracking changes before youve saved what you need for the last one

try some variation of this
set a "previousSong" variable and a "currentSong" variable

on initial play set currentSong

on saving save for previousSong then at the end of your save set previousSong to the value of currentSong

it gets tricky around the first time you do it because no save has occurred and therefor previousSong is empty
so, when you set currentSong (during initial play) if previousSong is empty also set previousSong to currentSong

and the rest should carry on through, the save function keeping previousSong updated from currentSong
inital play keeping currentSong set

see how this works for you


-------
EDIT:

i suppose theres another way

on initial play:
if songPlaying != currentSong
saveRating(currentSong)
currentSong=songPlaying

a lot simpler than the first, each with merit


RE: Help to set "UserRating" in a song using script - jbinkley60 - 2023-02-04

I concur with @jepsizofye  on using two variables to track the song title, if you are trying to detect a change.  You might again take a look at my autostop addon in the Kodi repo.  Specifically look at the XBMCplayer class in the service.py file (lines 24-53).  I set flags based upon playback conditions.  The current playing file name could be anther variable.  Then look at the playCount function (lines 25-51) in the common.py file.  The flags are passed to this function on a periodic basis by the service.py file line 81.

The playCount function is where I assess the current state of playback to increment the playcounter which tracks how long something has been playing (similar to your userrating calculation) or reset to 0 based upon certain conditions.  The match = 1, match = 2 etc. statements are simply there for debugging so I can see which conditional statement matched.   The result of whether to increment the playcounter is then evaluated in the servie.py file (lines 83-106) on what t do, if anything give the returned value of plcount (i.e. playcount). 

Maybe this will help.


Jeff


RE: Help to set "UserRating" in a song using script - sagrath - 2023-02-04

Good morning @jbinkley60 and @jepsizofye !!! (here in brazil is now 09:09 AM)
I will look to your code angin and trying to make. The real problems is:
I'm too noob yet hahahaha.

I can understand the logic, but I'm no so good in python, still learning to program, as this is my first program language (and to be honest, it's very cool!).
and another problem as that english is not my native language, as much as I know a lot of English, I still miss some natural nuances of the language, which helps to make things a little more complex hahahaha!

But i'm not give UP!!! 
Brace yourselfs!! The winter is comming!!!


RE: Help to set "UserRating" in a song using script - jbinkley60 - 2023-02-04

(2023-02-04, 14:08)sagrath Wrote: Good morning @jbinkley60 and @jepsizofye !!! (here in brazil is now 09:09 AM)
I will look to your code angin and trying to make. The real problems is:
I'm too noob yet hahahaha.

I can understand the logic, but I'm no so good in python, still learning to program, as this is my first program language (and to be honest, it's very cool!).
and another problem as that english is not my native language, as much as I know a lot of English, I still miss some natural nuances of the language, which helps to make things a little more complex hahahaha!

But i'm not give UP!!! 
Brace yourselfs!! The winter is comming!!!

Not to worry, we were all noobs at one time.  I too ended up teaching myself Python and learning to program Kodi addons.  I learned the most by studying other people's code and struggling through problems.  I believe service addons tend to be a bit more difficult due to having to deal with the various events (i.e. Kodi callbacks, event timer lops etc..).  Glad you aren't giving up.  I am still learning more about Kodi addon programming everyday.  I  look back and some of my earlier code and chuckle.  On occasion I will do some major rewriting.  If progress gets too slow or difficult for you on this, I'll take a crack at some coding to get you past some hurdles.


Jeff


RE: Help to set "UserRating" in a song using script - izprtxqkft - 2023-02-04

yea what jeff said

i first learned python because i was translating someone elses python project to PHP to work server side on a webserver that didnt like python

its a grind, takes a lot of trial and error, but in the end as long as you learned something its worth it


RE: Help to set "UserRating" in a song using script - sagrath - 2023-02-05

(2023-02-04, 19:53)jbinkley60 Wrote: I too ended up teaching myself Python and learning to program Kodi addons. 

Yeah, I'm in the same way!!!! 
(2023-02-04, 20:00)jepsizofye Wrote: its a grind, takes a lot of trial and error, but in the end as long as you learned something its worth it

Yeah yeah!! I'm into this hahahaha

So guys, 13 hours and only a parcial progress (here in Brazil is 21:16 and very hot summer!) 

Read, re-read, re-re-re-read documentation and code that @jbinkley60 suggests, lots os trial and error (in meantime I help my wife to cleanup the house), made some modifications (I've learn how to put some settings in addon settings, very cool!) , but cant find a logic to do a simple solution:

Need to find a way to save the new_rating vaue from line 66 or 68  in the skipped song, not in the next song

I manage to identify when the song is skipped, now if you skip the song, a notification you show up with the new song title.
BUT (there's aways a but!)

when the song is skipped, the value of the rating goes to the actual music, not the skipped music: 

python:

import xbmc
import xbmcaddon
import json
import time

class Monitor(xbmc.Monitor):
    def __init__(self):
        self.is_library_song = False        
        pass
        
    def status_check(self):
        # perform monitor status check
        pass
    
    def onPlayBackStarted(self):
        self.is_library_song = True
    
    # When the song ends, either by user action, or when the song is
    # the last in the playlist and the "repeat" function is disabled.
    def onPlayBackStopped(self, new_rating, current_time):
        if self.is_library_song and current_time > 5:
            # Saving the new rating as the song stops.
            xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"AudioLibrary.SetSongDetails","params":{"songid": %d, "userrating":%d},"id":1}' % (song_id, new_rating))
            xbmc.executebuiltin("Notification(%s, %s)" % (song_title, 'Final Rating: {}'.format(new_rating)))
            current_song = ''
            self.is_library_song = False

    def calculate_rating(self, new_rating):
        xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"AudioLibrary.SetSongDetails","params":{"songid": %d, "userrating":%d},"id":1}' % (song_id, new_rating))
        if song_rating == 0:
            xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(song_title), 'Final rating 0: {}'.format(new_rating)))
        else: 
            xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(song_title), 'Final rating P: {}'.format(new_rating)))
        current_song =''
        
monitor = Monitor()
player = xbmc.Player()
calculated_rating = 0
new_rating = 0
current_time = 0
total_time = 0
# Variables to check song skipping
song_title = ''
current_song =''

while not monitor.abortRequested():
    try:
        while True:
            if xbmc.Player().isPlayingAudio():
                if not xbmc.getInfoLabel('MusicPlayer.Title') == '': 
                    
                    # Retrieve data from the currently playing song
                    song_title      = xbmc.getInfoLabel('MusicPlayer.Title')
                    song_rating     = xbmc.getInfoLabel('MusicPlayer.UserRating')
                    song_length     = int(xbmc.Player().getTotalTime())
                    current_time    = int(player.getTime())
                    song_parts      = int(song_length / 11)
                    
                    if song_rating == '':
                        song_rating = 0    
                    else:
                        song_rating = int(song_rating)
                    
                    if current_time > 0:
                        calculated_rating = int(current_time / song_parts)
                    
                    if song_rating == 0 or song_rating == '':
                        new_rating = int(current_time/song_parts)
                    if song_rating > 0:
                        new_rating = int((song_rating + calculated_rating) / 2)
                    
                    
                    if current_song == '':
                        monitor.onPlayBackStarted()
                        current_song = song_title
                        
                    xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"JSONRPC.Introspect","id":1}')
                    result = xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":0,"properties":["userrating"]},"id":1}')
                    song_details = json.loads(result)["result"]["item"]
                    song_id = song_details["id"]
                    
                    song_time_left = (song_length - current_time)
                    
                    # Resting only two seconds before the song finish.
                    if song_time_left <= 2:                
                        monitor.calculate_rating(new_rating)
                        current_song = xbmc.getInfoLabel('MusicPlayer.Title')
                        
                    # HERE NEED TO FIND A WAY TO SAVE THE NEW_RATING FROM LINE 66 OR 68
                    # IN THE SKIPPED SONG, NOT IN THE NEXT SONG
                    if song_time_left > 2 and current_song != song_title:
                        
                        xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(current_song), 'Song Skipped'))
                        
                        monitor.calculate_rating(new_rating)
                        current_song = xbmc.getInfoLabel('MusicPlayer.Title')
                        
            else:
                monitor.onPlayBackStopped(new_rating, current_time)
            
            monitor.status_check()
            
            if monitor.waitForAbort(1): 
                break                  
    except Exception as e:
        xbmc.executebuiltin("Notification(%s, %s)" % ("An error occurred: ", e))

I think that I'm very close to find. 
And i think the secret will be in passing the value of current_song to the JsonRPC... or I'm wrong?


RE: Help to set "UserRating" in a song using script - jbinkley60 - 2023-02-05

I think the answer may be to pass song_id  (JSON database identifier for the skipped song) and song_title to the monitor.calculate_rating class method on line 95 like so:

python:

monitor.calculate_rating(new_rating, song_id, song_title)

Then modify the class method on lines 28-34 like so:

python:


    def calculate_rating(self, new_rating, song_id, song_title):
        xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"AudioLibrary.SetSongDetails","params":{"songid": %d, "userrating":%d},"id":1}' % (song_id, new_rating))
        if song_rating == 0:
            xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(song_title), 'Final rating 0: {}'.format(new_rating)))
        else:
            xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(song_title), 'Final rating P: {}'.format(new_rating)))
        current_song =''


This appears to be a scope variable issue I mentioned earlier. 


Jeff


RE: Help to set "UserRating" in a song using script - izprtxqkft - 2023-02-05

reading someone elses code isnt very easy so stay with me

line 23 inside the monitor class actually saves the rating for "song_id"

line 80 sets "song_id" that is used on line 23 to save rating for the song

so, your save on 23 is happening after its set on line 80 which is causing it to save to the wrong id

it needs to save the rating for "song_id" before its set on line 80 so you have the rating for the last song

so, see if you can set "song_id" from onPlayBackStarted on line 15 instead

when you do that you can also set last_id


im trying to work out some code for you but ive never used an xbmc monitor so im not sure when onplaybackstarts fires

basically, onplaybackstart can do the 2 variables as suggested

python:
    def onPlayBackStarted(self):
        self.is_library_song = True
        #Setting song_id, probably can move the code block up here from lines 50-96 in your while statement
        result = xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":0,"properties":["userrating"]},"id":1}')
        song_details = json.loads(result)["result"]["item"]
        song_id = song_details["id"]
        if last_id!=0 and song_id!=last_id:
            xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"AudioLibrary.SetSongDetails","params":{"songid": %d, "userrating":%d},"id":1}' % (last_id, new_rating))
        last_id=song_id

then you need to add "last_id=0" on line 45

(obviously thats rough and shouldnt be copy/pasted directly, just showing whats going on in my head)


RE: Help to set "UserRating" in a song using script - sagrath - 2023-02-05

(2023-02-05, 04:07)jepsizofye Wrote:  but ive never used an xbmc monitor so im not sure when onplaybackstarts fires


Good morning!!! So, i'm not so familiar too with the monitor thing hahaha, but, I think that is fired when the playback starts, not the song or video itself, when the player starts. and it's fired just once until the player stops. if some song finish and the next one start, onPlayBackStarted() will not fired again, because it's already stared and the player no stopped yet.

for this reason I'm made:

python:

if current_song == '':
      monitor.onPlayBackStarted()
      current_song = song_title

because current_song it's set empty just when script starts with kodi, and then when playback stared the current song will be the actual song, when playbackstop the current song will be set empty again. When playback starts againg, the loop is restarted.
(2023-02-05, 03:50)jbinkley60 Wrote: This appears to be a scope variable issue I mentioned earlier. 

Not yet

: I made some heavy tests here and this is the result: 

the songs has this userratings:

Image
Then I play "Reach - Wake Me Up" until it get the rating 2 and skipped. The rating 2 must be saved on "Reach - Wake Me Up" but:

the song reach (irony?) the rating 2:

Image

Song skipped and the the next song playing (noted the orange color on "Soundgarden - Kickstand)
Image

and the result: 

Image
The first song did not receive the rating, and the second one changed from 5 to 3. This change it's because the original rating from Kickstand was 5 and the plus 2 divided by 2 is equal to 3.5. then is set 3.
 
(2023-02-05, 04:07)jepsizofye Wrote: so, see if you can set "song_id" from onPlayBackStarted on line 15 instead

Then I test this suggestion, but as I mentioned in a start this post, onPlayBackStarted() is fired just once, so, if I move xbmc.executeJSONRPC to inside the function "def onPlayBackStarted(self):" the song_id will never be updated.

so... at least I have a very good sleep hahahaha.

And i wanna ask: My code? It's in good shape? The problem with living in a small town in the interior of Brazil is this... I don't know anyone here who knows python.... or who uses kodi....

EDIT:
I added the option to hide notification. Just keep the "Song Skipped". This will be completely remove when (if) the service has been completed: 

python:

import xbmc
import xbmcaddon
import json
import time

addon = xbmcaddon.Addon()
addon_name = addon.getAddonInfo('name')


class Monitor(xbmc.Monitor):
    def __init__(self):
        self.is_library_song = False        
        pass
        
    def status_check(self):
        # perform monitor status check
        pass
    
    def onPlayBackStarted(self):        
        self.is_library_song = True
    
    # When the song ends, either by user action, or when the song is
    # the last in the playlist and the "repeat" function is disabled.
    def onPlayBackStopped(self, new_rating, current_time):
        if self.is_library_song and current_time > 5:
            # Saving the new rating as the song stops.
            xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"AudioLibrary.SetSongDetails","params":{"songid": %d, "userrating":%d},"id":1}' % (song_id, new_rating))
            if show_notification == 'true':
                xbmc.executebuiltin("Notification(%s, %s)" % (addon_name, '{} rated to {}'.format(song_title, new_rating)))
            current_song = ''
            self.is_library_song = False

    def calculate_rating(self, new_rating, song_id, song_title, last_id):
        if last_id !=0 and song_id != last_id:
            xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"AudioLibrary.SetSongDetails","params":{"songid": %d, "userrating":%d},"id":1}' % (song_id, new_rating))
        if show_notification == 'true':
            if song_rating == 0:            
                xbmc.executebuiltin("Notification(%s, %s, time=2000)" % (addon_name, '{} rated to {}'.format(song_title, new_rating)))
            else: 
                xbmc.executebuiltin("Notification(%s, %s, time=2000)" % (addon_name, '{} rated to {}'.format(song_title, new_rating)))
        current_song = ''
        last_id=song_id
        
monitor             = Monitor()
player              = xbmc.Player()
calculated_rating   = 0
new_rating          = 0
current_time        = 0
total_time          = 0
# Variables to check song skipping
song_title          = ''
current_song        = ''
last_id             = ''
show_notification   = ''
real_show_notification   = ''

while not monitor.abortRequested():
    try:
        while True:
            if xbmc.Player().isPlayingAudio():
                if addon.getSetting("show_notification") == 'true':
                    show_notification = 'true' 
                else:
                    show_notification = 'false' 
                
                if addon.getSetting("real_show_notification") == 'true':
                    real_show_notification = 'true' 
                else:
                    real_show_notification = 'false' 
                    
                if not xbmc.getInfoLabel('MusicPlayer.Title') == '': 
                    
                    # Retrieve data from the currently playing song
                    song_title      = xbmc.getInfoLabel('MusicPlayer.Title')
                    song_rating     = xbmc.getInfoLabel('MusicPlayer.UserRating')
                    song_length     = int(xbmc.Player().getTotalTime())
                    current_time    = int(player.getTime())
                    song_parts      = int(song_length / 11)
                    
                    if song_rating == '':
                        song_rating = 0    
                    else:
                        song_rating = int(song_rating)
                    
                    if current_time > 0:
                        calculated_rating = int(current_time / song_parts)                
                        if song_rating == 0 or song_rating == '':
                            new_rating = int(current_time/song_parts)
                        if song_rating > 0:
                            new_rating = int((song_rating + calculated_rating) / 2)
                    
                    xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"JSONRPC.Introspect","id":1}')
                    result = xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":0,"properties":["userrating"]},"id":1}')
                    song_details = json.loads(result)["result"]["item"]
                    song_id = song_details["id"]

                    if current_song == '':
                        monitor.onPlayBackStarted()
                        current_song = song_title                    
                    
                    song_time_left = (song_length - current_time)
                    
                    # Resting only two seconds before the song finish.
                    if song_time_left <= 2:                
                        monitor.calculate_rating(new_rating, song_id, song_title, last_id)
                        current_song = xbmc.getInfoLabel('MusicPlayer.Title')
                        
                    # HERE NEED TO FIND A WAY TO SAVE THE NEW_RATING FROM LINE 66 OR 68
                    # IN THE SKIPPED SONG, NOT IN THE NEXT SONG
                    if song_time_left > 2 and current_song != song_title:                        
                        xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(current_song), 'Song Skipped'))                        
                        monitor.calculate_rating(new_rating, song_id, song_title, last_id)
                        current_song = xbmc.getInfoLabel('MusicPlayer.Title')
                    
                    if real_show_notification == 'true':
                        xbmc.executebuiltin("Notification(%s, %s, time=2000)" % (addon_name, '{} rated to {}'.format(song_title, new_rating)))                    
            else:
                monitor.onPlayBackStopped(new_rating, current_time)
            
            monitor.status_check()
            
            if monitor.waitForAbort(1): 
                break                  
    except Exception as e:
        xbmc.executebuiltin("Notification(%s, %s)" % ("An error occurred: ", e))

The Kodi forum has an option to set "spoiler"?. Look at the size of this "walltext" hahahahaah.


RE: Help to set "UserRating" in a song using script - jbinkley60 - 2023-02-05

(2023-02-05, 15:21)sagrath Wrote: And i wanna ask: My code? It's in good shape? The problem with living in a small town in the interior of Brazil is this... I don't know anyone here who knows python.... or who uses kodi....

EDIT:
I added the option to hide notification. Just keep the "Song Skipped". This will be completely remove when (if) the service has been completed: 


If you can post your latest code again I will take a crack at a fix when I get time.  I have an idea but will need your latest code.


Thanks,

Jeff


RE: Help to set "UserRating" in a song using script - sagrath - 2023-02-05

This is the code before your last suggestiom, that I posted yesterday at 21:00.
It's this one or the one before?

python:

import xbmc
import xbmcaddon
import json
import time

class Monitor(xbmc.Monitor):
    def __init__(self):
        self.is_library_song = False        
        pass
        
    def status_check(self):
        # perform monitor status check
        pass
    
    def onPlayBackStarted(self):
        self.is_library_song = True
    
    # When the song ends, either by user action, or when the song is
    # the last in the playlist and the "repeat" function is disabled.
    def onPlayBackStopped(self, new_rating, current_time):
        if self.is_library_song and current_time > 5:
            # Saving the new rating as the song stops.
            xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"AudioLibrary.SetSongDetails","params":{"songid": %d, "userrating":%d},"id":1}' % (song_id, new_rating))
            xbmc.executebuiltin("Notification(%s, %s)" % (song_title, 'Final Rating: {}'.format(new_rating)))
            current_song = ''
            self.is_library_song = False

    def calculate_rating(self, new_rating):
        xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"AudioLibrary.SetSongDetails","params":{"songid": %d, "userrating":%d},"id":1}' % (song_id, new_rating))
        if song_rating == 0:
            xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(song_title), 'Final rating 0: {}'.format(new_rating)))
        else: 
            xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(song_title), 'Final rating P: {}'.format(new_rating)))
        current_song =''
        
monitor = Monitor()
player = xbmc.Player()
calculated_rating = 0
new_rating = 0
current_time = 0
total_time = 0
# Variables to check song skipping
song_title = ''
current_song =''

while not monitor.abortRequested():
    try:
        while True:
            if xbmc.Player().isPlayingAudio():
                if not xbmc.getInfoLabel('MusicPlayer.Title') == '': 
                    
                    # Retrieve data from the currently playing song
                    song_title      = xbmc.getInfoLabel('MusicPlayer.Title')
                    song_rating     = xbmc.getInfoLabel('MusicPlayer.UserRating')
                    song_length     = int(xbmc.Player().getTotalTime())
                    current_time    = int(player.getTime())
                    song_parts      = int(song_length / 11)
                    
                    if song_rating == '':
                        song_rating = 0    
                    else:
                        song_rating = int(song_rating)
                    
                    if current_time > 0:
                        calculated_rating = int(current_time / song_parts)
                    
                    if song_rating == 0 or song_rating == '':
                        new_rating = int(current_time/song_parts)
                    if song_rating > 0:
                        new_rating = int((song_rating + calculated_rating) / 2)
                    
                    
                    if current_song == '':
                        monitor.onPlayBackStarted()
                        current_song = song_title
                        
                    xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"JSONRPC.Introspect","id":1}')
                    result = xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":0,"properties":["userrating"]},"id":1}')
                    song_details = json.loads(result)["result"]["item"]
                    song_id = song_details["id"]
                    
                    song_time_left = (song_length - current_time)
                    
                    # Resting only two seconds before the song finish.
                    if song_time_left <= 2:                
                        monitor.calculate_rating(new_rating)
                        current_song = xbmc.getInfoLabel('MusicPlayer.Title')
                        
                    # HERE NEED TO FIND A WAY TO SAVE THE NEW_RATING FROM LINE 66 OR 68
                    # IN THE SKIPPED SONG, NOT IN THE NEXT SONG
                    if song_time_left > 2 and current_song != song_title:
                        
                        xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(current_song), 'Song Skipped'))
                        
                        monitor.calculate_rating(new_rating)
                        current_song = xbmc.getInfoLabel('MusicPlayer.Title')
                        
            else:
                monitor.onPlayBackStopped(new_rating, current_time)
            
            monitor.status_check()
            
            if monitor.waitForAbort(1): 
                break                  
    except Exception as e:
        xbmc.executebuiltin("Notification(%s, %s)" % ("An error occurred: ", e))



RE: Help to set "UserRating" in a song using script - jbinkley60 - 2023-02-06

(2023-02-05, 18:30)sagrath Wrote: This is the code before your last suggestiom, that I posted yesterday at 21:00.

Thanks.  I've started looking at this and have a question on how you are testing skipping.  Specifically I am interested in how you are queuing up the list of music tracks you are playing ?  Are you queuing an albums, doing a play from here, playing a playlist etc  ?  When you say skipping I presume you mean skipping / fast forwarding to the next item in the list (vs. skipping over an item in the list) or what do you mean by skipping ?  My questions will help me reproduce what you are seeing.


Thanks,

Jeff


RE: Help to set "UserRating" in a song using script - sagrath - 2023-02-06

(2023-02-06, 13:20)jbinkley60 Wrote: When you say skipping I presume you mean skipping / fast forwarding to the next item in the list (vs. skipping over an item in the list) or what do you mean by skipping ?

Hello !! Good morning!

OK, let's go! ALL the ways you mentioned:
If the player is playing some music and:
A- The skip-forward key will be pressed (starting the next song in the list).
B- Browse the playlist and select a song.
C- Using context menu on an album, artist or song and select "play".
D- Start a song by pressing "enter" from anywhere, be it a playlist, or list of songs from the album, or from the "music" node.

Anyway, if the player is already playing, when changing the song, all of them count as a jump.
And that's not my choice, it's how Kodi itself handles a skip and a stop.

I did a lot of testing on that part these days.

See this super simplified code, here no calculation is being done, just showing when a song is started, skipped or stopped.

python:

import xbmc
import xbmcaddon
import json
import time

addon = xbmcaddon.Addon()
addon_name = addon.getAddonInfo('name')


class Monitor(xbmc.Monitor):
    def __init__(self):
        self.is_library_song = False        
        pass
        
    def status_check(self):
        # perform monitor status check
        pass
    
    def onPlayBackStarted(self): 
        xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(song_title), 'Song Stared')) 
        self.is_library_song = True
    
    def onPlayBackStopped(self, current_song):
        if self.is_library_song:
            xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(current_song), 'Song Stopped'))  
            current_song = ''
            self.is_library_song = False
       
monitor             = Monitor()
player              = xbmc.Player()
current_song        = ''

while not monitor.abortRequested():
    try:
        while True:
            if xbmc.Player().isPlayingAudio():
                song_title = xbmc.getInfoLabel('MusicPlayer.Title')
                if current_song == '':
                    monitor.onPlayBackStarted()
                    
                    current_song = xbmc.getInfoLabel('MusicPlayer.Title')                   
                
                if current_song != song_title:                        
                    xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(current_song), 'Song Skipped'))                        
                    current_song = xbmc.getInfoLabel('MusicPlayer.Title')
                                  
            else:
                monitor.onPlayBackStopped(current_song)
            
            monitor.status_check()
            
            if monitor.waitForAbort(1): 
                break                  
    except Exception as e:
        xbmc.executebuiltin("Notification(%s, %s)" % ("An error occurred: ", e))



RE: Help to set "UserRating" in a song using script - jbinkley60 - 2023-02-06

Ok, if this is working the way you want, to detect a song change then all you need to do is match the song_id similar to the song title and when there is a change make sure you update the original song (not the current song_id) with the last calculated value. 

Here's an update with some code and comments.

python:


import xbmc
import xbmcaddon
import json
import time

addon = xbmcaddon.Addon()
addon_name = addon.getAddonInfo('name')


class Monitor(xbmc.Monitor):
    def __init__(self):
        self.is_library_song = False
        pass

    def status_check(self):
        # perform monitor status check
        pass

    def onPlayBackStarted(self):
        xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(song_title), 'Song Stared'))
        self.is_library_song = True

    def onPlayBackStopped(self, current_song):
        if self.is_library_song:
            xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(current_song), 'Song Stopped'))
            current_song = ''
            self.is_library_song = False

monitor             = Monitor()
player              = xbmc.Player()
current_song        = ''
current_id          = 0                 #  Add to match current song tracking           
song_id             = 0                 #  Add to match original song tracking

while not monitor.abortRequested():
    try:
        while True:
            if xbmc.Player().isPlayingAudio():
                song_title = xbmc.getInfoLabel('MusicPlayer.Title')
                if current_song == '':
                    monitor.onPlayBackStarted()

                    current_song = xbmc.getInfoLabel('MusicPlayer.Title')
                    song_id = xbmc.getInfoLabel('MusicPlayer.DBID')      #  Get database Id of song

                if current_song != song_title:
                    xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(current_song), 'Song Skipped'))
                    current_song = xbmc.getInfoLabel('MusicPlayer.Title')
                    #  Add line here to do skip calculation on song_id
                    current_id = xbmc.getInfoLabel('MusicPlayer.DBID')      #  Get database Id of new song

            else:
                monitor.onPlayBackStopped(current_song)
                #  Add line here to do final calculation on current_id
                current_id = song_id = 0

            monitor.status_check()

            if monitor.waitForAbort(1):
                break
    except Exception as e:
        xbmc.executebuiltin("Notification(%s, %s)" % ("An error occurred: ", e))


If the stripped down code isn't doing the proper detection of skips the way you want then I can look at it.


Thanks,

Jeff


RE: Help to set "UserRating" in a song using script - sagrath - 2023-02-06

(2023-02-06, 18:51)jbinkley60 Wrote: Here's an update with some code and comments.

YES!!!!!!!!!!!!!!!! yesyesyesyes!!!!!!!!!!!

THATS IT!!!!!!!!!!!!!!! @jbinkley60  you are a GENIOUS!!!!!!!!!!!!

It is working EXACTLY as I envisioned. The music starts, the calculations are executed and are only applied when the music changes or stops (either by user intervention or "naturally" when reaching the end).
And your last suggestion made me see where I was going wrong:
In the other codes, the song data was constantly being consulted, so the values ​​were saved in the wrong song. With your suggestion I realized that the data should be consulted only once, and only be consulted again if the music changes. (actually I had thought about it before, but at some point I forgot lol).

Anyway: Here is the final code. now I'm going to apply the tweaks to hide the notifications and also to disable the addon (for those who don't want to "rank" their songs at some point).

python:

import xbmc
import xbmcaddon
import json
import time

addon = xbmcaddon.Addon()
addon_name = addon.getAddonInfo('name')

class Monitor(xbmc.Monitor):
    def __init__(self):
        self.is_library_song = False
        pass

    def status_check(self):
        # perform monitor status check
        pass

    def onPlayBackStarted(self):
        # Music Library music is set to "True"
        xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(song_title), 'Song Stared'))
        self.is_library_song = True

    def onPlayBackStopped(self, current_song, song_id, new_rating):
        # The song stops, the UserRating of the song is saved, then song ID is set to "0", 
        # and currentMusic to "empty" again. Library music is set to "False"
        if self.is_library_song:            
            xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"AudioLibrary.SetSongDetails","params":{"songid": %d, "userrating":%d},"id":1}' % (song_id, new_rating))
            xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(current_song), 'Rated as: {}'.format(new_rating)))
            current_song = ''
            song_id = 0
            self.is_library_song = False

monitor             = Monitor()
player              = xbmc.Player()
current_song        = ''
new_rating          = 0
current_id          = 0 #  Add to match current song tracking
song_id             = 0 #  Add to match original song tracking

while not monitor.abortRequested():
    try:
        while True:
            if xbmc.Player().isPlayingAudio():
                
                # Retrieve Song Data from the player
                song_title = xbmc.getInfoLabel('MusicPlayer.Title')
                song_rating     = xbmc.getInfoLabel('MusicPlayer.UserRating')
                song_length     = int(xbmc.Player().getTotalTime())
                current_time    = int(player.getTime())
                song_parts      = int(song_length / 11)
                song_time_left  = (song_length - current_time)
                
                # To to ensure UserRating is at least "0"
                if song_rating == '':
                    song_rating = 0    
                else:
                    song_rating = int(song_rating)
                
                # To avoid error "divided by zero"
                if current_time > 0:
                    calculated_rating = int(current_time / song_parts)                
                    if song_rating == 0 or song_rating == '':
                        new_rating = int(current_time/song_parts)
                    if song_rating > 0:
                        new_rating = int((song_rating + calculated_rating) / 2)
                
                if current_song == '':
                    monitor.onPlayBackStarted()
                    
                    # Retrieve Song Data as player start:
                    xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"JSONRPC.Introspect","id":1}')
                    result = xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":0,"properties":["userrating"]},"id":1}')
                    song_details = json.loads(result)["result"]["item"]
                    song_id = song_details["id"]
                    
                    # Set current song from player:
                    current_song = xbmc.getInfoLabel('MusicPlayer.Title')
                
                # If song has changed, first will save current data in previous song,
                # The will retrieve data for the new song:
                if current_song != song_title:
                    # Save Song Data in previous song:
                    xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"AudioLibrary.SetSongDetails","params":{"songid": %d, "userrating":%d},"id":1}' % (song_id, new_rating))
                    xbmc.executebuiltin("Notification(%s, %s, time=2000)" % ('{}'.format(current_song), 'Rated as: {}'.format(new_rating)))
                    
                    # Retrieve Song Data from new song:
                    xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"JSONRPC.Introspect","id":1}')
                    result = xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":0,"properties":["userrating"]},"id":1}')
                    song_details = json.loads(result)["result"]["item"]
                    song_id = song_details["id"]
                    
                    # Set new current song from player:
                    current_song = xbmc.getInfoLabel('MusicPlayer.Title')

            else:
                # For use when the user stops playback.
                monitor.onPlayBackStopped(current_song, song_id, new_rating)
                
            monitor.status_check()

            if monitor.waitForAbort(1):
                break
    except Exception as e:
        xbmc.executebuiltin("Notification(%s, %s)" % ("An error occurred: ", e))

Thanks too @jepsizofye pelo seu tempo! (edit: hahahah I type this in portuguese, it says "for your time")
You both help me to not give up in my first "Addon"

PS. This is not over yet, after including the settings, I'm planning to include a "album rating" in this code. Where the Album Rating will be calculated by averaging the notes of the songs. What you think?