This file is indexed.

/usr/lib/exaile/xl/dynamic.py is in exaile 3.3.2-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# Copyright (C) 2008-2010 Adam Olsen
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
#
# The developers of the Exaile media player hereby grant permission
# for non-GPL compatible GStreamer and Exaile plugins to be used and
# distributed together with GStreamer and Exaile. This permission is
# above and beyond the permissions granted by the GPL license by which
# Exaile is covered. If you modify this code, you may extend this
# exception to your version of the code, but you are not obligated to
# do so. If you do not wish to do so, delete this exception statement
# from your version.

import logging
import os
import random
import time

from xl.nls import gettext as _
from xl import xdg, common, event, providers, settings, metadata
from xl.trax import search

logger = logging.getLogger(__name__)

class DynamicManager(providers.ProviderHandler):
    """
        handles matching of songs for dynamic playlists
    """
    def __init__(self, collection=[]):
        providers.ProviderHandler.__init__(self, "dynamic_playlists")
        self.buffersize = settings.get_option("playback/dynamic_buffer", 5)
        self.collection = collection
        self.cachedir = os.path.join(xdg.get_cache_dir(), 'dynamic')
        if not os.path.exists(self.cachedir):
            os.makedirs(self.cachedir)

    def find_similar_tracks(self, track, limit=-1, exclude=[]):
        """
            finds tracks from the collection that are similar
            to the passed track.

            @param track: the track to find similar tracks to
            @param limit: limit the returned list to this many
                tracks. If there are more tracks than this
                found, a random selection of those tracks is
                returned.
        """
        logger.debug(u"Searching for %(limit)s tracks related to %(track)s" %
                {'limit' : limit, 'track' : track})
        artists = self.find_similar_artists(track)
        if artists == []:
            return []
        tracks = []
        random.shuffle(artists)
        i = 0
        while (limit > len(tracks) or limit == -1) and i < len(artists):
            artist = artists[i][1].replace('"', '\\\"')
            i += 1
            searchres = search.search_tracks_from_string(
                self.collection, 'artist=="%s"'%artist, case_sensitive=False)
            choices = [x.track for x in searchres]
            if choices == []:
                continue
            random.shuffle(choices)
            j = 0
            while j < len(choices):
                track = choices[j]
                if track not in exclude:
                    tracks.append(track)
                    break
                j += 1
        return tracks

    def find_similar_artists(self, track):
        info = self._load_saved_info(track)
        if info == []:
            info = self._query_sources(track)
            self._save_info(track, info)

        return info

    def _query_sources(self, track):
        info = []
        artist = track.get_tag_raw('artist')
        if not artist: return info
        for source in self.get_providers():
            sinfo = source.get_results(','.join(artist))
            info += sinfo
        info.sort(reverse=True) #TODO: merge artists that are the same
        return info

    def _load_saved_info(self, track):
        artist = track.get_tag_raw('artist')
        if not artist: return []
        filename = os.path.join(self.cachedir, ','.join(artist))
        if not os.path.exists(filename):
            return []
        f = open(filename)
        line = f.readline()
        if line == '':
            return []
        last_update = float(line)
        if 604800 < time.time() - last_update: # one week
            info = self._query_sources(track)
            if info != []:
                self._save_info(track, info)
                return info
        info = []
        for line in f:
            try:
                rel, artist = line.strip().split(" ",1)
                info.append((rel, artist))
            except:
                pass
        f.close()
        return info

    def _save_info(self, track, info):
        if info == []:
            return
        filename = os.path.join(self.cachedir,
                track.get_tag_raw('artist', join=True))
        f = open(filename, 'w')
        f.write("%s\n"%time.time())
        for item in info:
            f.write("%.2f %s\n"%item)
        f.close()

    def populate_playlist(self, playlist):
        """
            adds tracks to playlists as needed.
            called when the position of a playlist changes.
        """
        if not playlist:
            return
        current_pos = playlist.current_position
        if current_pos < 0 or current_pos >= len(playlist):
            return
        needed = self.buffersize - (len(playlist) - current_pos)

        if needed < 1:
            needed = 1
        curr = playlist.current

        starttime = time.time()
        tracks = self.find_similar_tracks(curr, needed,
                playlist)

        remainingtime = 5 - (time.time()-starttime)

        if remainingtime > 0:
            time.sleep(remainingtime)

        if playlist.current_position != current_pos:
            return # we skipped in that 5 seconds, so ignore it
        playlist.extend(tracks)
        logger.debug("Added %s tracks." % len(tracks))


MANAGER = DynamicManager()


class DynamicSource(object):
    def __init__(self):
        pass

    def get_results(self, artist):
        raise NotImplementedError

    def _set_manager(self, manager):
        self.manager = manager

# vim: et sts=4 sw=4