Пример #1
0
class BasePlayer(xbmc.Player):

    WINDOW = 12006  # music visualization

    def __init__(self):
        log('creating player class')
        xbmc.Player.__init__(self)
        self.history = None
        self.tracks = None
        self.stopped = True
        self.dlgProgress = None
        pass

    def init(self):
        log('initializing player class')
        self.dlgProgress = xbmcgui.DialogProgress()
        self.dlgProgress.create('Openlast', 'Initializing addon...')
        self.dlgProgress.update(0)

        self.tracks = self.loadAllTracks()

        artistCount = len(self.tracks.keys())
        trackCount = len(self.tracks.values())
        self.history = History(
            artistCount if artistCount < MAX_ARTIST_COUNT else
            MAX_ARTIST_COUNT, trackCount * 2 / 3)
        random.seed()

        lastfmUser = ''
        try:
            lastfmAddon = xbmcaddon.Addon('service.scrobbler.lastfm')
            lastfmUser = lastfmAddon.getSetting('lastfmuser')
        except RuntimeError:
            pass
        if 0 < len(lastfmUser):
            self.loadRecentTracks(lastfmUser)

        if self.dlgProgress.iscanceled() or self.tracks is None:
            self.dlgProgress.close()
            self.dlgProgress = None
            return False

        return True

    def play(self):
        log('play')
        self.stopped = False
        nextItem = self.generateNextTrack()
        self.dlgProgress.update(100)
        playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
        playlist.clear()
        playlist.add(nextItem[0], nextItem[1])
        self.dlgProgress.close()
        xbmc.Player.play(self, playlist)
        xbmc.executebuiltin("ActivateWindow(%d)" % (self.WINDOW, ))
        pass

    def onPlayBackStarted(self):
        #log('onPlayBackStarted')
        # tags are not available instantly and we don't what to announce right
        # away as the user might be skipping through the songs
        xbmc.sleep(500)

        # get tags
        entry = self._get_tags()
        self.history.addEntry(entry[0], entry[1])

        # choose the next track
        playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
        if playlist.getposition() + 1 == playlist.size():
            nextItem = self.generateNextTrack()
            playlist.add(nextItem[0], nextItem[1])
        pass

    def onPlayBackStopped(self):
        log('onPlayBackStopped')
        self.stopped = True
        pass

    def _get_tags(self):
        # get track tags
        artist = self.getMusicInfoTag().getArtist().decode("utf-8")
        album = self.getMusicInfoTag().getAlbum().decode("utf-8")
        title = self.getMusicInfoTag().getTitle().decode("utf-8")
        duration = str(self.getMusicInfoTag().getDuration())
        # get duration from xbmc.Player if the MusicInfoTag duration is invalid
        if int(duration) <= 0:
            duration = str(int(self.getTotalTime()))
        track = str(self.getMusicInfoTag().getTrack())
        path = self.getPlayingFile().decode("utf-8")
        thumb = xbmc.getCacheThumbName(path)
        #log('artist: ' + artist)
        #log('title: ' + title)
        #log('album: ' + album)
        #log('track: ' + str(track))
        #log('duration: ' + str(duration))
        #log('path: ' + path)
        #log('local path: ' + user)
        #log('thumb: ' + thumb)

        #log('cover art: ' + str(xbmc.getInfoLabel('MusicPlayer.Cover')))
        #log('thumb art: ' + str(xbmc.getInfoLabel('Player.Art(thumb)')))
        #log('fan art: ' + str(xbmc.getInfoLabel('MusicPlayer.Property(Fanart_Image)')))

        return [artist, title]

    def loadRecentTracks(self, username):
        url = build_url(
            lastfmApi, {
                'method': 'user.getrecenttracks',
                'user': username,
                'format': 'json',
                'api_key': lastfmApiKey,
                'limit': 500,
                'page': 1
            })
        # xbmc.log(url)
        reply = urllib.urlopen(url)
        # xbmc.log(str(reply))
        resp = json.load(reply)
        if "error" in resp:
            raise Exception("Error! DATA: " + str(resp))
        else:
            # xbmc.log(str(resp))
            log('Successfully loaded recent tracks')

            if self.dlgProgress.iscanceled():
                return None
            self.dlgProgress.update(90)

            for t in resp['recenttracks']['track']:
                trackname = t['name'].lower()
                artistname = t['artist']['#text'].lower()
                #log(str(artistname.encode('utf-8')) + " -- " + str(trackname.encode('utf-8')))
                self.history.addEntry(artistname, trackname)

    def loadAllTracks(self):
        raise Exception("Should not be reached, use overridden method instead")
        pass

    def findTrack(self, artistname, trackname):
        # Find the song
        chars0 = trackname[0].lower() + trackname[1].lower()
        chars1 = trackname[0].upper() + trackname[1].lower()
        chars2 = trackname[0].lower() + trackname[1].upper()
        chars3 = trackname[0].upper() + trackname[1].upper()
        req = {
            "jsonrpc": "2.0",
            "method": "AudioLibrary.GetSongs",
            "id": "libSongs",
            "params": {
                "properties": [
                    "artist", "duration", "album", "title", "file",
                    "thumbnail", "fanart", "track"
                ],
                "limits": {
                    "start": 0,
                    "end": 1000
                },
                "sort": {
                    "order": "ascending",
                    "method": "track",
                    "ignorearticle": True
                },
                "filter": {
                    "and": [
                        {
                            "field": "artist",
                            "operator": "is",
                            "value": artistname.encode('utf-8')
                        },
                        {
                            "or": [
                                #{"field": "title", "operator": "is", "value": trackname.lower().encode('utf-8')}
                                {
                                    "field": "title",
                                    "operator": "startswith",
                                    "value": chars0.encode('utf-8')
                                },
                                {
                                    "field": "title",
                                    "operator": "startswith",
                                    "value": chars1.encode('utf-8')
                                },
                                {
                                    "field": "title",
                                    "operator": "startswith",
                                    "value": chars2.encode('utf-8')
                                },
                                {
                                    "field": "title",
                                    "operator": "startswith",
                                    "value": chars3.encode('utf-8')
                                },
                            ]
                        }
                    ]
                }
            }
        }

        rpcresp = xbmc.executeJSONRPC(json.dumps(req))
        # xbmc.log(str(rpcresp))
        rpcresp = json.loads(rpcresp)
        found = 0 < int(rpcresp['result']['limits']['end'])
        ret = None
        #strInd = '    '
        if found:
            # xbmc.log(str(rpcresp))
            trackname_stripped = strip_accents(trackname.strip().lower())
            for s in rpcresp['result']['songs']:
                if trackname_stripped == strip_accents(
                        s['title'].strip().lower()):
                    path = xbmc.translatePath(s['file'])
                    path = xbmc.validatePath(path)
                    if os.path.exists(path):
                        ret = s
                        break

                    log("Found in library, but file doesn't exist: %s" % path)
                    xbmc.executebuiltin(
                        'Notification(%s,%s, 1000)' %
                        ("File doesn't exist", os.path.basename(path)))

            #if artistname.lower() <> ret['artist'][0].lower() or trackname.lower() <> ret['title'].lower():
            #if ret is not None:
            #    strInd = '+++ '
            #    xbmc.log(strInd + str(artistname.encode('utf-8')) + " -- " + str(trackname.encode('utf-8')))
            #    xbmc.log('    ' + str(ret['artist'][0].encode('utf-8')) + " -- " + str(ret['title'].encode('utf-8')))
        #else:
        #xbmc.log('NOT found track ' + str(artistname.encode('utf-8')) + " -- " + str(trackname.encode('utf-8')))

        #xbmc.log(strInd + str(artistname.encode('utf-8')) + " -- " + str(trackname.encode('utf-8')))
        return ret

    def findTracks(self, artistname, tracks):
        ret = []
        # Find artists with name starts with ...
        chars0 = artistname[0].lower() + artistname[1].lower()
        chars1 = artistname[0].upper() + artistname[1].lower()
        chars2 = artistname[0].lower() + artistname[1].upper()
        chars3 = artistname[0].upper() + artistname[1].upper()
        req = {
            "jsonrpc": "2.0",
            "method": "AudioLibrary.GetArtists",
            "id": "libSongs",
            "params": {
                #"properties": ["thumbnail", "fanart"],
                "limits": {
                    "start": 0,
                    "end": 1000
                },
                "sort": {
                    "order": "ascending",
                    "method": "track",
                    "ignorearticle": True
                },
                "filter": {
                    "or": [
                        #{"field": "artist", "operator": "is", "value": artistname.encode('utf-8')}
                        {
                            "field": "artist",
                            "operator": "startswith",
                            "value": chars0.encode('utf-8')
                        },
                        {
                            "field": "artist",
                            "operator": "startswith",
                            "value": chars1.encode('utf-8')
                        },
                        {
                            "field": "artist",
                            "operator": "startswith",
                            "value": chars2.encode('utf-8')
                        },
                        {
                            "field": "artist",
                            "operator": "startswith",
                            "value": chars3.encode('utf-8')
                        },
                    ]
                }
            }
        }
        # xbmc.log(json.dumps(req))
        rpcresp = xbmc.executeJSONRPC(json.dumps(req))
        # xbmc.log(str(rpcresp))
        rpcresp = json.loads(rpcresp)

        found = False
        foundTrack = False
        if 'error' in rpcresp:
            log(str(rpcresp))
            pass
        elif 'result' in rpcresp:
            found = 0 < int(rpcresp['result']['limits']['end'])
            pass

        if found:
            found = False
            artistname_stripped = strip_accents(artistname)
            for a in rpcresp['result']['artists']:
                if strip_accents(
                        a['artist'].strip().lower()) == artistname_stripped:
                    #xbmc.log('Found artist: ' + str(artistname.encode('utf-8')))
                    found = True
                    # xbmc.log(str(rpcresp['result']))
                    for t in tracks:
                        item = self.findTrack(a['artist'], t)
                        if item is not None:
                            foundTrack = True
                            ret.append(item)

        if foundTrack:
            #log(len(ret))
            a = ret[len(ret) - 1]['artist'][0]
            if artistname.lower() <> a.lower():
                log("WARNING: artist name has some differencies: '" +
                    str(artistname.encode('utf-8')) + "' --- '" +
                    str(a.encode('utf-8')) + "'")

        if not found:
            log('NOT found artist: "' + str(artistname.encode('utf-8')) + '"')
            pass

        return ret

    def _preGenerateTrack(self, artist):
        return True

    def generateNextTrack(self):
        #log('Generating the next track')
        found = False
        item = None
        while not found:
            # Choose an artist
            a = random.randint(0, len(self.tracks) - 1)
            #log('Generated artist number %i' % a)
            artists = self.tracks.keys()
            artist = artists[a]
            #log('Generated artist "%s"' % artist)
            if self.history.isArtistRecentlyPlayed(artist):
                continue

            tryCount = 5
            while not found and 0 < tryCount:
                if not self._preGenerateTrack(artist):
                    break
                tryCount = tryCount - 1
                # Choose a track
                t = random.randint(0, len(self.tracks[artist]) - 1)
                #log('Generated track number %i' % t)
                res = self.findTracks(artist, [self.tracks[artist][t]])
                if len(res) == 0:
                    # Track not found
                    log('Track not found: %s - "%s", removing...' %
                        (artist, self.tracks[artist][t]))
                    # Remove non-existent track from the list
                    del self.tracks[artist][t]
                    if 0 == len(self.tracks[artist]):
                        log('Artist "%s" has no more tracks, removing...' %
                            artist)
                        del self.tracks[artist]
                        # Go to the another artist
                        tryCount = 0

                    # Rescale the history
                    artistCount = len(self.tracks.keys())
                    trackCount = len(self.tracks.values())
                    self.history.rescale(
                        artistCount if artistCount < MAX_ARTIST_COUNT else
                        MAX_ARTIST_COUNT, trackCount * 2 / 3)
                    # Try to choose another track of the same artist

                elif not self.history.isTrackRecentlyPlayed(
                        self.tracks[artist][t]):
                    item = res[0]
                    log('The next track is: %s - "%s"' %
                        (item['artist'][0], item['title']))
                    found = True

                else:
                    log('Track was recently played: %s - "%s", skipping...' %
                        (artist, self.tracks[artist][t]))
                    # Try to choose another track of the same artist

        thumb = item['thumbnail']
        xlistitem = xbmcgui.ListItem(item['title'])
        xlistitem.setInfo("music", infoLabels={"Title": item['title']})
        xlistitem.setArt({'thumb': thumb})  # , 'fanart': thumb})

        return [item['file'], xlistitem]
Пример #2
0
class BasePlayer(xbmc.Player):

    WINDOW = 12006 # music visualization

    def __init__(self):
        log('creating player class')
        xbmc.Player.__init__(self)
        self.history = None
        self.tracks = None
        self.stopped = True
        self.dlgProgress = None
        pass

    def init(self):
        log('initializing player class')
        self.dlgProgress = xbmcgui.DialogProgress()
        self.dlgProgress.create('Openlast', 'Initializing addon...')
        self.dlgProgress.update(0)

        self.tracks = self.loadAllTracks()

        artistCount = len(self.tracks.keys())
        trackCount = len(self.tracks.values())
        self.history = History(artistCount if artistCount < MAX_ARTIST_COUNT else MAX_ARTIST_COUNT, trackCount * 2 / 3)
        random.seed()

        lastfmUser = ''
        try:
            lastfmAddon = xbmcaddon.Addon('service.scrobbler.lastfm')
            lastfmUser = lastfmAddon.getSetting('lastfmuser')
        except RuntimeError:
            pass
        if 0 < len(lastfmUser):
            self.loadRecentTracks(lastfmUser)

        if self.dlgProgress.iscanceled() or self.tracks is None:
            self.dlgProgress.close()
            self.dlgProgress = None
            return False

        return True

    def play(self):
        log('play')
        self.stopped = False
        nextItem = self.generateNextTrack()
        self.dlgProgress.update(100)
        playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
        playlist.clear()
        playlist.add(nextItem[0], nextItem[1])
        self.dlgProgress.close()
        xbmc.Player.play(self, playlist)
        xbmc.executebuiltin("ActivateWindow(%d)" % (self.WINDOW,))
        pass

    def onPlayBackStarted(self):
        #log('onPlayBackStarted')
        # tags are not available instantly and we don't what to announce right
        # away as the user might be skipping through the songs
        xbmc.sleep(500)

        # get tags
        entry = self._get_tags()
        self.history.addEntry(entry[0], entry[1])

        # choose the next track
        playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
        if playlist.getposition() + 1 == playlist.size():
            nextItem = self.generateNextTrack()
            playlist.add(nextItem[0], nextItem[1])
        pass

    def onPlayBackStopped(self):
        log('onPlayBackStopped')
        self.stopped = True
        pass

    def _get_tags(self):
        # get track tags
        artist = self.getMusicInfoTag().getArtist().decode("utf-8")
        album = self.getMusicInfoTag().getAlbum().decode("utf-8")
        title = self.getMusicInfoTag().getTitle().decode("utf-8")
        duration = str(self.getMusicInfoTag().getDuration())
        # get duration from xbmc.Player if the MusicInfoTag duration is invalid
        if int(duration) <= 0:
            duration = str(int(self.getTotalTime()))
        track = str(self.getMusicInfoTag().getTrack())
        path = self.getPlayingFile().decode("utf-8")
        thumb = xbmc.getCacheThumbName(path)
        #log('artist: ' + artist)
        #log('title: ' + title)
        #log('album: ' + album)
        #log('track: ' + str(track))
        #log('duration: ' + str(duration))
        #log('path: ' + path)
        #log('local path: ' + user)
        #log('thumb: ' + thumb)

        #log('cover art: ' + str(xbmc.getInfoLabel('MusicPlayer.Cover')))
        #log('thumb art: ' + str(xbmc.getInfoLabel('Player.Art(thumb)')))
        #log('fan art: ' + str(xbmc.getInfoLabel('MusicPlayer.Property(Fanart_Image)')))

        return [artist, title]

    def loadRecentTracks(self, username):
        url = build_url(lastfmApi, {'method': 'user.getrecenttracks', 'user': username,
                        'format': 'json', 'api_key': lastfmApiKey, 'limit': 500, 'page': 1})
        # xbmc.log(url)
        reply = urllib.urlopen(url)
        # xbmc.log(str(reply))
        resp = json.load(reply)
        if "error" in resp:
            raise Exception("Error! DATA: " + str(resp))
        else:
            # xbmc.log(str(resp))
            log('Successfully loaded recent tracks')

            if self.dlgProgress.iscanceled():
                return None
            self.dlgProgress.update(90)

            for t in resp['recenttracks']['track']:
                trackname = t['name'].lower()
                artistname = t['artist']['#text'].lower()
                #log(str(artistname.encode('utf-8')) + " -- " + str(trackname.encode('utf-8')))
                self.history.addEntry(artistname, trackname)

    def loadAllTracks(self):
        raise Exception("Should not be reached, use overridden method instead")
        pass

    def findTrack(self, artistname, trackname):
        # Find the song
        chars0 = trackname[0].lower() + trackname[1].lower()
        chars1 = trackname[0].upper() + trackname[1].lower()
        chars2 = trackname[0].lower() + trackname[1].upper()
        chars3 = trackname[0].upper() + trackname[1].upper()
        req = {
            "jsonrpc": "2.0",
            "method": "AudioLibrary.GetSongs",
            "id": "libSongs",
            "params": {
                "properties": ["artist", "duration", "album", "title", "file", "thumbnail", "fanart", "track"],
                "limits": {"start": 0, "end": 1000},
                "sort": {"order": "ascending", "method": "track", "ignorearticle": True},
                "filter": {"and": [
                    {"field": "artist", "operator": "is", "value": artistname.encode('utf-8')},
                    {"or": [
                        #{"field": "title", "operator": "is", "value": trackname.lower().encode('utf-8')}
                        {"field": "title", "operator": "startswith", "value": chars0.encode('utf-8')},
                        {"field": "title", "operator": "startswith", "value": chars1.encode('utf-8')},
                        {"field": "title", "operator": "startswith", "value": chars2.encode('utf-8')},
                        {"field": "title", "operator": "startswith", "value": chars3.encode('utf-8')},
                    ]}
                ]}
            }
        }

        rpcresp = xbmc.executeJSONRPC(json.dumps(req))
        # xbmc.log(str(rpcresp))
        rpcresp = json.loads(rpcresp)
        found = 0 < int(rpcresp['result']['limits']['end'])
        ret = None
        #strInd = '    '
        if found:
            # xbmc.log(str(rpcresp))
            trackname_stripped = strip_accents(trackname.strip().lower())
            for s in rpcresp['result']['songs']:
                if trackname_stripped == strip_accents(s['title'].strip().lower()):
                    path = xbmc.translatePath(s['file'])
                    path = xbmc.validatePath(path)
                    if os.path.exists(path):
                        ret = s
                        break

                    log("Found in library, but file doesn't exist: %s" % path)
                    xbmc.executebuiltin('Notification(%s,%s, 1000)' % ("File doesn't exist", os.path.basename(path)))

            #if artistname.lower() <> ret['artist'][0].lower() or trackname.lower() <> ret['title'].lower():
            #if ret is not None:
            #    strInd = '+++ '
            #    xbmc.log(strInd + str(artistname.encode('utf-8')) + " -- " + str(trackname.encode('utf-8')))
            #    xbmc.log('    ' + str(ret['artist'][0].encode('utf-8')) + " -- " + str(ret['title'].encode('utf-8')))
        #else:
            #xbmc.log('NOT found track ' + str(artistname.encode('utf-8')) + " -- " + str(trackname.encode('utf-8')))

        #xbmc.log(strInd + str(artistname.encode('utf-8')) + " -- " + str(trackname.encode('utf-8')))
        return ret


    def findTracks(self, artistname, tracks):
        ret = []
        # Find artists with name starts with ...
        chars0 = artistname[0].lower() + artistname[1].lower()
        chars1 = artistname[0].upper() + artistname[1].lower()
        chars2 = artistname[0].lower() + artistname[1].upper()
        chars3 = artistname[0].upper() + artistname[1].upper()
        req = {
            "jsonrpc": "2.0",
            "method": "AudioLibrary.GetArtists",
            "id": "libSongs",
            "params": {
                #"properties": ["thumbnail", "fanart"],
                "limits": {"start": 0, "end": 1000},
                "sort": {"order": "ascending", "method": "track", "ignorearticle": True},
                "filter": {"or": [
                    #{"field": "artist", "operator": "is", "value": artistname.encode('utf-8')}
                    {"field": "artist", "operator": "startswith", "value": chars0.encode('utf-8')},
                    {"field": "artist", "operator": "startswith", "value": chars1.encode('utf-8')},
                    {"field": "artist", "operator": "startswith", "value": chars2.encode('utf-8')},
                    {"field": "artist", "operator": "startswith", "value": chars3.encode('utf-8')},
                ]}
            }
        }
        # xbmc.log(json.dumps(req))
        rpcresp = xbmc.executeJSONRPC(json.dumps(req))
        # xbmc.log(str(rpcresp))
        rpcresp = json.loads(rpcresp)

        found = False
        foundTrack = False
        if 'error' in rpcresp:
            log(str(rpcresp))
            pass
        elif 'result' in rpcresp:
            found = 0 < int(rpcresp['result']['limits']['end'])
            pass

        if found:
            found = False
            artistname_stripped = strip_accents(artistname)
            for a in rpcresp['result']['artists']:
                if strip_accents(a['artist'].strip().lower()) == artistname_stripped:
                    #xbmc.log('Found artist: ' + str(artistname.encode('utf-8')))
                    found = True
                    # xbmc.log(str(rpcresp['result']))
                    for t in tracks:
                        item = self.findTrack(a['artist'], t)
                        if item is not None:
                            foundTrack = True
                            ret.append(item)

        if foundTrack:
            #log(len(ret))
            a = ret[len(ret) - 1]['artist'][0]
            if artistname.lower() <> a.lower():
                log("WARNING: artist name has some differencies: '" + str(artistname.encode('utf-8')) + "' --- '" + str(a.encode('utf-8')) + "'")

        if not found:
            log('NOT found artist: "' + str(artistname.encode('utf-8')) + '"')
            pass

        return ret

    def _preGenerateTrack(self, artist):
        return True

    def generateNextTrack(self):
        #log('Generating the next track')
        found = False
        item = None
        while not found:
            # Choose an artist
            a = random.randint(0, len(self.tracks) - 1)
            #log('Generated artist number %i' % a)
            artists = self.tracks.keys()
            artist = artists[a]
            #log('Generated artist "%s"' % artist)
            if self.history.isArtistRecentlyPlayed(artist):
                continue

            tryCount = 5
            while not found and 0 < tryCount:
                if not self._preGenerateTrack(artist):
                    break
                tryCount = tryCount - 1
                # Choose a track
                t = random.randint(0, len(self.tracks[artist]) - 1)
                #log('Generated track number %i' % t)
                res = self.findTracks(artist, [self.tracks[artist][t]])
                if len(res) == 0:
                    # Track not found
                    log('Track not found: %s - "%s", removing...' % (artist, self.tracks[artist][t]))
                    # Remove non-existent track from the list
                    del self.tracks[artist][t]
                    if 0 == len(self.tracks[artist]):
                        log('Artist "%s" has no more tracks, removing...' % artist)
                        del self.tracks[artist]
                        # Go to the another artist
                        tryCount = 0

                    # Rescale the history
                    artistCount = len(self.tracks.keys())
                    trackCount = len(self.tracks.values())
                    self.history.rescale(artistCount if artistCount < MAX_ARTIST_COUNT else MAX_ARTIST_COUNT, trackCount * 2 / 3)
                    # Try to choose another track of the same artist

                elif not self.history.isTrackRecentlyPlayed(self.tracks[artist][t]):
                    item = res[0]
                    log('The next track is: %s - "%s"' % (item['artist'][0], item['title']))
                    found = True

                else:
                    log('Track was recently played: %s - "%s", skipping...' % (artist, self.tracks[artist][t]))
                    # Try to choose another track of the same artist

        thumb = item['thumbnail']
        xlistitem = xbmcgui.ListItem(item['title'])
        xlistitem.setInfo("music", infoLabels={"Title": item['title']})
        xlistitem.setArt({'thumb': thumb})  # , 'fanart': thumb})

        return [item['file'], xlistitem]