XBMC Community Forum
[RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - Printable Version

+- XBMC Community Forum (http://forum.xbmc.org)
+-- Forum: Help and Support (/forumdisplay.php?fid=33)
+--- Forum: Add-ons Help and Support (/forumdisplay.php?fid=27)
+---- Forum: Music Add-ons (/forumdisplay.php?fid=148)
+---- Thread: [RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists (/showthread.php?tid=83915)

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15


RE: [RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - kiboy6 - 2013-02-02 07:18

As a long time user of this great script just wanted to say how excited I am by the new love it's getting.

Looking forward to the perfection-seeking tweaking that is to come Smile

Thanks guys!


RE: [RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - gliko8 - 2013-02-06 12:29

(2013-01-26 00:56)gliko8 Wrote:  Hey,
Another question, is there any way to enable Hebrew search in the script?
I can find the artist in lastfm and get recommendations but the script can't find nothing.

Hi,
sorry for the re-post.
can it be done?
Last.fm is supporting Hebrew and other languages in search and recommendations. does the script can support other lang search?

Thanks again for a great script!


RE: [RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - ErlendSB - 2013-02-06 15:25

Can you post a log file showing what the addon is searching for?
I guess we need to add unicode support somewhere.


RE: [RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - gliko8 - 2013-02-06 16:23

(2013-02-06 15:25)ErlendSB Wrote:  Can you post a log file showing what the addon is searching for?
I guess we need to add unicode support somewhere.

This is what in XBMC log file after start playing an Hebrew song.
as you can see.. the song name is in gibberish:
16:16:06 T:48296 NOTICE: Thread CFileCache start, auto delete: false
16:16:06 T:48216 NOTICE: Thread PAPlayer start, auto delete: false
16:16:06 T:39816 NOTICE: [LFM PLG(PM)] onPlayBackStarted waiting: 2 seconds
16:16:06 T:39816 NOTICE: [LFM PLG(PM)] onPlayBackStarted waiting: 2 seconds
16:16:08 T:46416 NOTICE: [LFM PLG(PM)] onPlayBackStarted started
16:16:08 T:46416 NOTICE: [LFM PLG(PM)] áçåìöú ôñéí started playing
16:16:09 T:46416 NOTICE: [LFM PLG(PM)] Count: 0
16:16:12 T:46416 NOTICE: [LFM PLG(PM)] None found


RE: [RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - heldchen - 2013-02-07 16:46

(removed due to user stupidity reading first instead of last posts, duh!)


[RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - gliko8 - 2013-02-09 13:15

(2013-02-06 16:23)gliko8 Wrote:  
(2013-02-06 15:25)ErlendSB Wrote:  Can you post a log file showing what the addon is searching for?
I guess we need to add unicode support somewhere.

This is what in XBMC log file after start playing an Hebrew song.
as you can see.. the song name is in gibberish:
16:16:06 T:48296 NOTICE: Thread CFileCache start, auto delete: false
16:16:06 T:48216 NOTICE: Thread PAPlayer start, auto delete: false
16:16:06 T:39816 NOTICE: [LFM PLG(PM)] onPlayBackStarted waiting: 2 seconds
16:16:06 T:39816 NOTICE: [LFM PLG(PM)] onPlayBackStarted waiting: 2 seconds
16:16:08 T:46416 NOTICE: [LFM PLG(PM)] onPlayBackStarted started
16:16:08 T:46416 NOTICE: [LFM PLG(PM)] áçåìöú ôñéí started playing
16:16:09 T:46416 NOTICE: [LFM PLG(PM)] Count: 0
16:16:12 T:46416 NOTICE: [LFM PLG(PM)] None found

Hi,
Any news on Unicode support?
Can I do something locally on my machine?
Can I give you more details?
Thanks.


RE: [RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - ErlendSB - 2013-02-09 16:39

I'm having trouble with this one.
It seems to be working for me with artist names like: Røyksopp

One thing you could do is give me the url to the last.fm api.
Uncomment (remove the #) at line 110 in pm.py:
print Base_URL
When you restart the addon and start playing the same track, the log will contain something like: http://ws.audioscrobbler.com/2.0/?api_key=3ae834eee073c460a250ee08979184ec&method=track.getsimilar&limit=50&artist=R%C3%B8yksopp&track=Vision+One

I'm not sure, but the databases might handle these characters differently. I'm using mysql and I have not had any problems with nordic characters yet.

Let me know what the api url looks like, and also how you would spell out the artist name in plain text, and I'll do some more testing.


[RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - jhnnsrs - 2013-02-16 01:46

Hey,
i took the liberty to modify your addon to an standalone application that can
be called by xbmc.
It also includes full unicode support. So artists like Sigur Rós will not fail Wink
Thought you might find it usefull Wink

PHP Code:
import random
# Will Populate the hell out of you
import json
import socket
import re
time
import urllib
urllib2


class bcolors:
    
HEADER '\033[95m'
    
OKBLUE '\033[94m'
    
OKGREEN '\033[92m'
    
WARNING '\033[93m'
    
FAIL '\033[91m'
    
ENDC '\033[0m'

    
def disable(self):
        
self.HEADER ''
        
self.OKBLUE ''
        
self.OKGREEN ''
        
self.WARNING ''
        
self.FAIL ''
        
self.ENDC ''
        
       

class LastFMPlaylist():
    
    
def __init__ (self):
        
self.delay 1
        self
.add_limit 3
        self
.search_limit 240
        self
.api "62c8ee5856ab5f3424315a83ddb29e80"
        
self.allAddedSongs = []
        
self.timer None
        self
.artist "Not working"
        
self.title "Hallo"
        
self.newSongs = []
        
self.prepend bcolors.OKGREEN "[LastFM-Populater]" bcolors.ENDC"   "
        
self.prepend2 bcolors.HEADER "[LastFM-Populater]" bcolors.ENDC"   "
        
self.prepend3 bcolors.WARNING "[LastFM-Populater]" bcolors.ENDC"   "
        
self.prepend4 bcolors.FAIL "[LastFM-Populater]" bcolors.ENDC"   "
        
        
print self.prepend3 "Version 1.02 with Socket and Unicode Support"
        
print self.prepend3 "Feedback is very welcome"
        
    
def startTitleSearch(self):
        
'''Gets currently Played Song from XBMC'''
        
time.sleep(self.delay)
        
jdata ='{"jsonrpc": "2.0", "method": "Player.GetItem", "params": { "properties": ["title","artist"], "playerid": 0 }, "id": "AudioGetItem"}'
        
decoded self.getJson(jdata)
        
# CHECKS IF IT IS PLAYING MUSIC
        
        
if decoded.has_key('result') and decoded['result'] != None and decoded['result'].has_key('item'):
            if 
self.title != decoded['result']['item']['title']:
                print 
self.prepend "Currently Playing:    " decoded['result']['item']['artist'][0] + ' - ' decoded['result']['item']['title']
            
                
self.artist decoded['result']['item']['artist'][0]
                
self.title decoded['result']['item']['title']
        
                
answer self.dumbUnavailable(self.fetchSimilarTracks(self.artist,self.title))
                
self.allAddedSongs += answer
                
print self.prepend "Total amount of Songs added: " str(len(self.allAddedSongs))
                
                
                
                
                
# AND IT GOES ON
                
                
                
self.addToPlaylist()
                
self.printPlaylist()
            else:
                print 
self.prepend4 "Falty Reiteration of already Played Artist"
        
else:
            print 
self.prepend4 "Falty JSON Object from XBMC"

    
def fetchSimilarTracks(selfartist,title):
        
'''Gets similar Artists Song'''
        
print self.prepend "Search LastFM for Artist"
        
## Build our post data dict
        
data = {
            
'api_key' unicode(self.api), 
            
'method' u'track.getSimilar'
            
'artist' artist
            
'track' title
            
'limit' unicode(str(self.search_limit))
            }
        
        
## Encode the unicode using an appropriate charset
        
data dict([(keyvalue.encode('utf-8')) for keyvalue in data.iteritems()])

        
## Urlencode it for POSTing
        
data urllib.urlencode(data)

        
## Build a POST request, get the response
        
url 'http://ws.audioscrobbler.com/2.0/'
        
request urllib2.Request(urldata)
        
hallo urllib2.urlopen(request)
        
WebHTML hallo.read()
        print 
self.prepend "Search Done"
        
        
# Gets Array with [ TRACK, MATCHVALUE, ARTIST]
        
similarTracks re.findall("<track>.+?<name>(.+?)</name>.+?<match>(.+?)</match>.+?<artist>.+?<name>(.+?)</name>.+?</artist>.+?</track>"WebHTMLre.DOTALL )
        
        return 
similarTracks
        
    def checkWanted
(selfartisttitle,matchValue,album,duration):
        
'''Check for Wanted Status SHOULD RETURN FALSE IF NOT MET '''
        
# CAN BE PRETTY MUCH ANY CRITERIA YOU WANT
        
        
if matchValue <= 0.00005
            return 
False
            
        
# CHECKS AGAINST ALREADY ADDED SONGS
        
for item in self.allAddedSongs:
            if 
item[0] == artist and item[1] == title# IS EQUAL TO NOT THE SAME SONG AGAIN
                
return False
        
return True
        
    def printPlaylist
(self):
        print 
self.prepend2 "Added Songs: "
        
for item in self.allAddedSongs:
            print 
self.prepend2 "      " item[0] + " - " item[1]
        print 
self.prepend "Total Songs Added: " str(len(self.allAddedSongs))
            
    
def addToPlaylist(self):
        for 
item in self.newSongs :
            
link item[3]
            
addRequest u'{"jsonrpc": "2.0", "method": "Playlist.Add", "params": { "item": {"file": "' link u'"}, "playlistid": 0 }, "id": 1}'
            
#print addRequest
            #addRequest = '{ "jsonrpc": "2.0", "method": "JSONRPC.Introspect", "params": { "filter": { "id": "Playlist.Add", "type": "method" } }, "id": 1 }'
            
self.getJson(addRequest)
        
    
def getJson(self,string):
    
        try:
            
string string.encode('utf8')
            
gsocket socket.socket(socket.AF_INETsocket.SOCK_STREAM)
            
gsocket.connect(("localhost"9090))
            
# Whatever structure you need to send goes here:
            #jdata = json.dumps({"username":"...", "password":"..."})
            
gsocket.send(str(string))
            
response gsocket.recv(2048)
                
            
json_query unicode(response'utf-8'errors='ignore')
            
json_response json.loads(json_query)
            return 
json_response
        except
:
            print 
self.prepend4 "JSON ERROR"
        
    
def dumbUnavailable (selfsimilarTracks):
    
        
songsAdded 0
        songsToAdd 
= []
        
# Shuffles the Result
        
random.shuffle(similarTracks)
        for 
trackmatchValueartist in similarTracks:
        
            
track track.replace("+"," ").replace("("," ").replace(")"," ").replace("&quot","''").replace("'","''").replace("&amp;","&")
            
artist artist.replace("+"," ").replace("("," ").replace(")"," ").replace("&quot","''").replace("'","''").replace("&amp;","&")
            
#Query LastFM for TRACK
            
            
track unicode(track'utf-8'errors='ignore')
            
artist unicode(artist'utf-8'errors='ignore')
            
            
json_response '{"jsonrpc": "2.0", "method": "AudioLibrary.GetSongs", "params": { "properties": ["title", "artist", "album", "file", "thumbnail", "duration", "fanart"], "limits": {"end":1}, "sort": {"method":"random"}, "filter": { "and":[{"field":"title","operator":"contains","value":"%s"},{"field":"artist","operator":"contains","value":"%s"}] } }, "id": 1}' % (trackartist
            
json_response self.getJson(json_response)
            
# separate the records
            
if json_response.has_key('result') and json_response['result'] != None and json_response['result'].has_key('songs'):
                
nur 0
                
for item in json_response['result']['songs']:
                
                    
artist item["artist"][0]
                    
trackTitle item["title"]
                    
album item["album"]
                    
trackPath item["file"]
                    
thumb item["thumbnail"]
                    
duration int(item["duration"])
                    
fanart item["fanart"]
                
                    if (
self.checkWanted(artist,trackTitle,matchValue,album,duration) != False or nur != 0):
                        
songsToAdd.append([artist,trackTitle,album,trackPath,thumb,duration,fanart]) #if file is wanted add to list
                        
songsAdded += 1
                        nur 
+= 1
                    
                    
            
if (songsAdded >= self.add_limit):
                break
                    
        
self.newSongs songsToAdd
        
return songsToAdd
        
        
        
class SocketListen():
    
def __init__(self):
        
''' Initialisierung '''
        
self.Encoded 0
        self
.host socket.gethostname()
        
self.port 33902
        self
.socket socket.socket() 
        
self.socket.bind((self.host,self.port))
        
self.buffersize 1024
        self
.prepend3 bcolors.WARNING "[LastFM-Populater]" bcolors.ENDC"   "
        
        
self.awaitConnection()
       
    
def awaitConnection(self):
        
'''Waits for server Connection with Setup details'''
        
## CONNECTION PROCESS STILL DUE
        
self.socket.listen(5)
        
LastFMPlaylist()
        while 
1:
            print 
self.prepend3 "Awaiting new Connection"
            
self.ClientConnectionself.ClientAddr self.socket.accept()
            
            
response self.ClientConnection.recv(self.buffersize)
            
            
p.startTitleSearch()
            
            
            
start SocketListen() 

Cheers Wink


RE: [RELEASE] Last.FM Playlist Generator Script-Auto Generate Similar Music Playlists - ErlendSB - 2013-02-16 08:09

Wow! Great job!

LastFM-Populater eh Smile

I will definitely check this out. Hats off to you good Sir for cleaning up the code. This should be the base for a new version.
I'm curious about the socket communication. Is this a requirement for service addons?
There has been requests for turing this into a service addon, and to me this seems like it.
Am I right in guessing that skins can use your code to enable/disable the addon easily?

Anyway, well done!


[RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - jhnnsrs - 2013-02-16 17:05

I used sockets because this script runs outside of xbmc (i wanted this script to integrate with some sort of home automation i'm
currently developing).
In XBMC itself i have a small XBMC.player class that activates the script via a socket call everytime the song changes.


as an addonI guess, you would not need the sockets at all as well use XBMC.executeJSON instead of the JSON calls
Unfortunately i have no glue whatsoever how to develop addons in xbmc right now. so that will be your tasty piece of cake Tongue

one could also get rid of the re part by calling lastfm for JSON objects Wink


Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - gliko8 - 2013-04-17 14:32

Hi,
I'v been very busy lately and couldn't get back to you on the Hebrew search issue.
i have checked the script with a friend and we managed to figure out the problem.
it seems that last.fm api cant query for tracks in hebrew, only artists.
we have add another loop to the script that checks for similar artists if similar tracks are return count=0:
PHP Code:
"""
    Script for generating smart playlists based on a seeding track and last.fm api
    Created by: ErlendSB
"""

import os
import random
import httplib
urlliburllib2
import sys
time
import threading
thread
import xbmc
xbmcguixbmcaddon
from urllib import quote_plus
unquote_plus
import re
from os
.path import exists
from os import remove
if sys.version_info < (27):
    
import simplejson
else:
    
import json as simplejson

__settings__ 
xbmcaddon.Addon(id='script.lastfmplaylistgeneratorPM')
__addonversion__ __settings__.getAddonInfo('version')
__cwd__          __settings__.getAddonInfo('path')

def log(message):
    
xbmc.log(msg=message)
class 
MyPlayerxbmc.Player ) :
    
countFoundTracks 0
    addedTracks 
= []
    
currentSeedingTrack 0
    firstRun 
0
    dbtype 
'sqlite3'
    
timeStarted time.time()
    
SCRIPT_NAME "LAST.FM Playlist Generator"
    
    
allowtrackrepeat =  __settings__.getSetting"allowtrackrepeat" )
    
preferdifferentartist __settings__.getSetting"preferdifferentartist" )
    
numberoftrackstoadd = ( 13510, )[ int__settings__.getSetting"numberoftrackstoadd" ) ) ]
    
delaybeforesearching= ( 21030, )[ int__settings__.getSetting"delaybeforesearching" ) ) ]
    
limitlastfmresult= ( 50100250, )[ int__settings__.getSetting"limitlastfmresult" ) ) ]
    
timer None


    
#apiPath = "http://ws.audioscrobbler.com/2.0/?api_key=71e468a84c1f40d4991ddccc46e40f1b"
    
apiPath "http://ws.audioscrobbler.com/2.0/?api_key=3ae834eee073c460a250ee08979184ec"
    
    
def __init__ self ):
        if 
not os.path.exists(xbmc.translatePath("special://userdata/advancedsettings.xml")):
            
self.dbtype 'sqlite3'
        
else:
            
from xml.etree.ElementTree import ElementTree
            advancedsettings 
ElementTree()
            
advancedsettings.parse(xbmc.translatePath("special://userdata/advancedsettings.xml"))
            
settings advancedsettings.getroot().find("musicdatabase")
            if 
settings is not None:
                for 
setting in settings:
                    if 
setting.tag == 'type':
                        
self.dbtype setting.text
            
else:
                
self.dbtype 'sqlite3'
        
xbmc.Player.__init__self )
        
xbmc.PlayList(0).clear()
        
self.firstRun 1
        BASE_RESOURCE_PATH 
os.path.join__cwd__"resources" )
        
process os.path.joinBASE_RESOURCE_PATH "pm.pid")
        
removeauto('lastfmplaylistgeneratorpm')
        
addauto("if os.path.exists('" os.path.normpath(process).replace('\\','\\\\') + "'):#lastfmplaylistgeneratorpm\n\tos.remove('" os.path.normpath(process).replace('\\','\\\\') + "')","lastfmplaylistgeneratorpm")
        
xbmc.executebuiltin("Notification(" self.SCRIPT_NAME+",Start by playing a song)")
    
    
def startPlayBack(self):
        print 
"[LFM PLG(PM)] onPlayBackStarted started"
        
if xbmc.Player().isPlayingAudio() == True:
            
currentlyPlayingTitle xbmc.Player().getMusicInfoTag().getTitle()
            print 
"[LFM PLG(PM)] " currentlyPlayingTitle " started playing"
                        
currentlyPlayingArtist xbmc.Player().getMusicInfoTag().getArtist()
            
self.countFoundTracks 0
            
if (self.firstRun == 1):
                
self.firstRun 0
                
#print "firstRun - clearing playlist"
                
album xbmc.Player().getMusicInfoTag().getAlbum()
                
cache_name xbmc.getCacheThumbName(os.path.dirname(xbmc.Player().getMusicInfoTag().getURL()))
                print 
"[LFM PLG(PM)] Playing file: %s" xbmc.Player().getMusicInfoTag().getURL()
                
thumb "special://profile/Thumbnails/Music/%s/%s" % ( cache_name[:1], cache_name, )
                
duration xbmc.Player().getMusicInfoTag().getDuration()
                
fanart ""
                
listitem self.getListItem(currentlyPlayingTitle,currentlyPlayingArtist,album,thumb,fanart,duration)
                
xbmc.PlayList(0).clear()
                
#xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "Playlist.Clear", "params": { "playlistid": 0 }, "id": 1}')
                
xbmc.executebuiltin('XBMC.ActivateWindow(10500)')
                
xbmc.PlayList(0).add(urlxbmc.Player().getMusicInfoTag().getURL(), listitem listitem)
                
#trackPath = xbmc.Player().getMusicInfoTag().getURL()
                #xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "Playlist.Add", "params": { "item": {"file": "%s"}, "playlistid": 0 }, "id": 1}' % trackPath)
                
self.addedTracks += [xbmc.Player().getMusicInfoTag().getURL()]
            
#print "Start looking for similar tracks"
            
self.fetch_similarTracks(currentlyPlayingTitle,currentlyPlayingArtist)

    
def onPlayBackStarted(self):
        print 
"[LFM PLG(PM)] onPlayBackStarted waiting:  " str(self.delaybeforesearching) +" seconds"
        
if (self.timer is not None and self.timer.isAlive()):
            
self.timer.cancel()
            
        
self.timer threading.Timer(self.delaybeforesearching,self.startPlayBack)
        
self.timer.start()

    
def fetch_similarTracksselfcurrentlyPlayingTitlecurrentlyPlayingArtist ):
        
apiMethod "&method=track.getsimilar&limit=" str(self.limitlastfmresult)

        
# The url in which to use
        
Base_URL self.apiPath apiMethod "&artist=" urllib.quote_plus(currentlyPlayingArtist) + "&track=" urllib.quote_plus(currentlyPlayingTitle)
        print 
Base_URL
        WebSock 
urllib.urlopen(Base_URL)  # Opens a 'Socket' to URL
        
WebHTML WebSock.read()            # Reads Contents of URL and saves to Variable
        
WebSock.close()                     # Closes connection to url
        #xbmc.executehttpapi("setresponseformat(openRecordSet;<recordset>;closeRecordSet;</recordset>;openRecord;<record>;closeRecord;</record>;openField;<field>;closeField;</field>)");
        #print WebHTML
        
similarTracks re.findall("<track>.+?<name>(.+?)</name>.+?<match>(.+?)</match>.+?<artist>.+?<name>(.+?)</name>.+?</artist>.+?</track>"WebHTMLre.DOTALL )
        
random.shuffle(similarTracks)
        
foundArtists = []
        
countTracks len(similarTracks)
        print 
"[LFM PLG(PM)] Count: " str(countTracks)
        if 
countTracks==0:
            
apiMethod "&method=artist.getsimilar&limit=" str(self.limitlastfmresult)
            
Base_URL self.apiPath apiMethod "&artist=" urllib.quote_plus(currentlyPlayingArtist)
                    print 
Base_URL
                    WebSock 
urllib.urlopen(Base_URL)  # Opens a 'Socket' to URL
                    
WebHTML WebSock.read()            # Reads Contents of URL and saves to Variable
                    
WebSock.close()
            
similarArtists re.findall("<artist>.+?<name>(.+?)</name>.+?</artist>"WebHTMLre.DOTALL)
            
#random.shuffle(similarArtists)
            
countArtists len(similarArtists)
            print 
"[LFM PLG(PM)] Artists Count: " str(countArtists)
            for 
artist in similarArtists:
                
json_query xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "AudioLibrary.GetSongs", "params": { "properties": ["title", "artist", "album", "file", "thumbnail", "duration", "fanart"], "limits": {"end":1}, "sort": {"method":"random"}, "filter": {"field":"artist","operator":"contains","value":"%s"} } , "id": 1}' % (artist))
                
json_query unicode(json_query'utf-8'errors='ignore')
                
json_response simplejson.loads(json_query)
                if 
json_response.has_key('result') and json_response['result'] != None and json_response['result'].has_key('songs'):
                                    
count 0
                                    
for item in json_response['result']['songs']:
                        
count += 1
                        artist 
item["artist"]
                                            
trackTitle item["title"]
                                            
album item["album"]
                                            
trackPath item["file"]
                                            
thumb item["thumbnail"]
                                            
duration int(item["duration"])
                                            
fanart item["fanart"]
                                            
#log("[LFM PLG(PM)] Found: " + str(trackTitle) + " by: " + str(artist))
                                            
if ((self.allowtrackrepeat == "true" or self.allowtrackrepeat == 1) or (trackPath not in self.addedTracks)):
                                                    if ((
self.preferdifferentartist != "true" and self.preferdifferentartist != 1) or (eval(matchValue) < 0.2 and similarArtistName not in foundArtists)):
                                                            
listitem self.getListItem(trackTitle,artist,album,thumb,fanart,duration)
                                                            
xbmc.PlayList(0).add(url=trackPathlistitem=listitem)
                                                            
#xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "Playlist.Add", "params": { "item": {"file": "%s"}, "playlistid": 0 }, "id": 1}' % trackPath)
                                                            
self.addedTracks += [trackPath]
                                                            
xbmc.executebuiltin("Container.Refresh")
                                                            
self.countFoundTracks += 1
                                                            
if (artist not in foundArtists):
                                                                    
foundArtists += [artist]
    
                                    if (
self.countFoundTracks >= self.numberoftrackstoadd):
                        break
                
#for artist in similarArtists:
                #print artist
        
for similarTrackNamematchValuesimilarArtistName in similarTracks:
            
#print "Looking for: " + similarTrackName + " - " + similarArtistName + " - " + matchValue
            
similarTrackName similarTrackName.replace("+"," ").replace("("," ").replace(")"," ").replace("&quot","''").replace("'","''").replace("&amp;","and")
            
similarArtistName similarArtistName.replace("+"," ").replace("("," ").replace(")"," ").replace("&quot","''").replace("'","''").replace("&amp;","and")
            
json_query xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "AudioLibrary.GetSongs", "params": { "properties": ["title", "artist", "album", "file", "thumbnail", "duration", "fanart"], "limits": {"end":1}, "sort": {"method":"random"}, "filter": { "and":[{"field":"title","operator":"contains","value":"%s"},{"field":"artist","operator":"contains","value":"%s"}] } }, "id": 1}' % (similarTrackNamesimilarArtistName)) 
            
json_query unicode(json_query'utf-8'errors='ignore')
            
json_response simplejson.loads(json_query)
            
# separate the records
            
if json_response.has_key('result') and json_response['result'] != None and json_response['result'].has_key('songs'):
                
count 0
                
for item in json_response['result']['songs']:
                    
count += 1
                    artist 
item["artist"]
                    
trackTitle item["title"]
                    
album item["album"]
                    
trackPath item["file"]
                    
thumb item["thumbnail"]
                    
duration int(item["duration"])
                    
fanart item["fanart"]
                    
log("[LFM PLG(PM)] Found: " str(trackTitle) + " by: " str(artist))
                    if ((
self.allowtrackrepeat == "true" or self.allowtrackrepeat == 1) or (trackPath not in self.addedTracks)):
                        if ((
self.preferdifferentartist != "true" and self.preferdifferentartist != 1) or (eval(matchValue) < 0.2 and similarArtistName not in foundArtists)):
                            
listitem self.getListItem(trackTitle,artist,album,thumb,fanart,duration)
                            
xbmc.PlayList(0).add(url=trackPathlistitem=listitem)
                            
#xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "Playlist.Add", "params": { "item": {"file": "%s"}, "playlistid": 0 }, "id": 1}' % trackPath)
                            
self.addedTracks += [trackPath]
                            
xbmc.executebuiltin("Container.Refresh")
                            
self.countFoundTracks += 1
                            
if (similarArtistName not in foundArtists):
                                
foundArtists += [similarArtistName]

                if (
self.countFoundTracks >= self.numberoftrackstoadd):
                    break
            
        if (
self.countFoundTracks == 0):
            
time.sleep(3)
            
#self.firstRun = 1
            
log("[LFM PLG(PM)] None found")
            
xbmc.executebuiltin("Notification(" self.SCRIPT_NAME+",No similar tracks were found)")
            return 
False
            
        xbmc
.executebuiltin('SetCurrentPlaylist(0)')
        
    
def getListItem(selftrackTitleartistalbumthumbfanart,duration):
        
listitem xbmcgui.ListItem(trackTitle)
        if (
fanart == ""):
            
cache_name xbmc.getCacheThumbNamestr(artist) )
            
fanart "special://profile/Thumbnails/Music/%s/%s" % ( "Fanart"cache_name, )
        
listitem.setProperty('fanart_image',fanart)
        
listitem.setInfo('music', { 'title'trackTitle'artist'artist'album'album'duration'duration })
        
listitem.setThumbnailImage(thumb)
        
#log("[LFM PLG(PM)] Fanart:%s" % fanart)
        
return listitem

def addauto
(newentryscriptcode):
    
autoexecfile xbmc.translatePath('special://home/userdata/autoexec.py')
    
#autoexecfile = "special://masterprofile/autoexec.py"
    
if exists(autoexecfile):
        
fh open(autoexecfile)
        
lines = []
        for 
line in fh.readlines():
            
lines.append(line)
        
lines.append("import time" "#" scriptcode "\n")
        
lines.append("time.sleep(2)" "#" scriptcode "\n")
        
lines.append(newentry "#" scriptcode "\n")
        
fh.close()
        
open(autoexecfile"w")
        if 
not "import xbmc\n" in lines:
            
f.write("import xbmc" "#" scriptcode "\n")
        if 
not "import os\n" in lines:
            
f.write("import os" "#" scriptcode "\n")
        
f.writelines(lines)
        
f.close()
    else:
        
open(autoexecfile"w")
        
f.write("import time" "#" scriptcode "\n")
        
f.write("time.sleep(2)" "#" scriptcode "\n")
        
f.write("import os" "#" scriptcode "\n")
        
f.write("import xbmc" "#" scriptcode "\n")
        
f.write(newentry "#" scriptcode "\n")
        
f.close()

def removeauto(scriptcode):
    
autoexecfile xbmc.translatePath('special://home/userdata/autoexec.py')
    
#autoexecfile = "special://masterprofile/autoexec.py"
    
if exists(autoexecfile):
        
fh open(autoexecfile)
        
lines = [ line for line in fh if not line.strip().endswith("#" scriptcode) ]
        
fh.close()
        
open(autoexecfile"w")
        
f.writelines(lines)
        
f.close()
        
BASE_RESOURCE_PATH os.path.join__cwd__"resources" )

process os.path.joinBASE_RESOURCE_PATH "pm.pid")
p=MyPlayer()
while(
1):
    if 
os.path.exists(process):
        if (
xbmc.abortRequested):
            
os.remove(process)
            print 
"[LFM PLG(PM)] deleting pid"
        
xbmc.sleep(500)
    else:
        break 



Skiping songs - scrolling - 2013-05-27 21:47

I've been using this add-on for awhile now. Lately I've noticed that when I start up this add-on it'll play the first song than skip the next 4 or 5 songs. I'm not sure what's happening here. I'm using Frodo 12.2. Any help would be appreciated thanks


Last.FM Playlist Generator Script - Auto Generate Similar Music Playlists - salfab - 2013-06-23 12:38

I suggest you commit unzipped files on your github account, si I would then contribute in any way I can Smile

I would gladly add support for opt-in feature for the playlist generation to be album-based instead of track-based, for instance.

or even push the envelope further and use the recommended artists instead of similar artists, for instance.

anyway... I'd be glad to help, and then github would allow you to accept or reject my changes : your call Smile

EDIT: ooh ! I hadn't looked correctly : it *is* committed properly Tongue


Last.FM Playlist Generator Script - ErlendSB - 2013-06-23 13:00

Salfab: Brilliant! Feel free to contribute in any way you can.
I'm currently using spotify for music and not using this addon much myself, so if someone's willing to keep maintaing it, that's great!


[RELEASE] Last.FM Playlist Generator Script - Auto Generate Similar Music - olivine - 2013-07-03 23:08

I somehow cannot get this add-on to work on my set-up (installed from Mortstart's repo, on Frodo on linux). I followed the instructions I found: start script ("would you like to exit party mode?" [no]), start playing a track, but no new tracks ever show up in the playlist. Am I missing something?

Thanks in advance for any help/advice.