diff --git a/ycast/radiobrowser.py b/ycast/radiobrowser.py index 19491a5..9788daa 100644 --- a/ycast/radiobrowser.py +++ b/ycast/radiobrowser.py @@ -1,9 +1,11 @@ import requests +import ycast.vtuner as vtuner + MINIMUM_COUNT_GENRE = 5 MINIMUM_COUNT_COUNTRY = 5 MINIMUM_BITRATE = 64 -STATION_LIMIT_DEFAULT = 99 +STATION_LIMIT = 1000 ID_PREFIX = "RB_" @@ -27,6 +29,10 @@ class Station: self.codec = get_json_attr(station_json, 'codec') self.bitrate = get_json_attr(station_json, 'bitrate') + def to_vtuner(self): + return vtuner.Station(self.id, self.name, ', '.join(self.tags), self.url, self.icon, + self.tags[0], self.country, self.codec, self.bitrate, None) + def request(url): headers = {'content-type': 'application/json', 'User-Agent': 'YCast'} @@ -42,10 +48,10 @@ def get_station_by_id(uid): return Station(station_json[0]) -def search(name): +def search(name, limit=STATION_LIMIT): stations = [] stations_json = request('stations/search?order=votes&reverse=true&bitrateMin=' + - str(MINIMUM_BITRATE) + '&name=' + str(name)) + str(MINIMUM_BITRATE) + '&limit=' + str(limit) + '&name=' + str(name)) for station_json in stations_json: stations.append(Station(station_json)) return stations @@ -71,7 +77,7 @@ def get_genres(): return genres -def get_stations_by_country(country, limit=STATION_LIMIT_DEFAULT): +def get_stations_by_country(country, limit=STATION_LIMIT): stations = [] stations_json = request('stations/search?order=votes&reverse=true&bitrateMin=' + str(MINIMUM_BITRATE) + '&limit=' + str(limit) + @@ -81,7 +87,7 @@ def get_stations_by_country(country, limit=STATION_LIMIT_DEFAULT): return stations -def get_stations_by_genre(genre, limit=STATION_LIMIT_DEFAULT): +def get_stations_by_genre(genre, limit=STATION_LIMIT): stations = [] stations_json = request('stations/search?order=votes&reverse=true&bitrateMin=' + str(MINIMUM_BITRATE) + '&limit=' + str(limit) + '&tagExact=true&tag=' + str(genre)) @@ -90,7 +96,7 @@ def get_stations_by_genre(genre, limit=STATION_LIMIT_DEFAULT): return stations -def get_stations_by_votes(limit=STATION_LIMIT_DEFAULT): +def get_stations_by_votes(limit=STATION_LIMIT): stations = [] stations_json = request('stations?order=votes&reverse=true&limit=' + str(limit)) for station_json in stations_json: diff --git a/ycast/server.py b/ycast/server.py index d9837f7..4bb9f0d 100644 --- a/ycast/server.py +++ b/ycast/server.py @@ -43,6 +43,48 @@ def get_stations(config): return +def get_directories_page(subdir, directories, startitems, enditems): + page = vtuner.Page() + if len(directories) == 0: + page.add(vtuner.Display("No entries found.")) + return page + page.set_count(len(directories)) + offset = 0 + limit = len(directories) + if startitems and enditems: + offset = int(startitems) - 1 + limit = int(enditems) + if offset > len(directories): + offset = len(directories) + if limit > len(directories): + limit = len(directories) + for directory_num in range(offset, limit): + directory = directories[directory_num] + page.add(vtuner.Directory(directory, url_for(subdir, _external=True, directory=directory))) + return page + + +def get_stations_page(stations, startitems, enditems): + page = vtuner.Page() + if len(stations) == 0: + page.add(vtuner.Display("No stations found.")) + return page + page.set_count(len(stations)) + offset = 0 + limit = len(stations) + if startitems and enditems: + offset = int(startitems) - 1 + limit = int(enditems) + if offset > len(stations): + offset = len(stations) + if limit > len(stations): + limit = len(stations) + for station_num in range(offset, limit): + station = stations[station_num] + page.add(station.to_vtuner()) + return page + + # TODO: vtuner doesn't do https (e.g. for logos). make an icon cache @@ -99,68 +141,39 @@ def radiobrowser_landing(): @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_COUNTRY + '/') def radiobrowser_countries(): - countries = radiobrowser.get_countries() - page = vtuner.Page() - page.add(vtuner.Previous(url_for('radiobrowser_landing', _external=True))) - for country in countries: - page.add(vtuner.Directory(country, url_for('radiobrowser_country_stations', _external=True, country=country))) - return page.to_string() + directories = radiobrowser.get_countries() + return get_directories_page('radiobrowser_country_stations', directories, + request.args.get('startitems'), request.args.get('enditems')).to_string() -@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_COUNTRY + '/') -def radiobrowser_country_stations(country): - stations = radiobrowser.get_stations_by_country(country) - page = vtuner.Page() - page.add(vtuner.Previous(url_for('radiobrowser_countries', _external=True))) - if len(stations) == 0: - page.add(vtuner.Display("No stations found for country '" + country + "'")) - else: - for station in stations: - page.add(vtuner.Station(station.id, station.name, ', '.join(station.tags), station.url, station.icon, - station.tags[0], station.country, station.codec, station.bitrate, None)) - return page.to_string() +@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_COUNTRY + '/') +def radiobrowser_country_stations(directory): + stations = radiobrowser.get_stations_by_country(directory) + return get_stations_page(stations, request.args.get('startitems'), request.args.get('enditems')).to_string() @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_GENRE + '/') def radiobrowser_genres(): - genres = radiobrowser.get_genres() - page = vtuner.Page() - page.add(vtuner.Previous(url_for('radiobrowser_landing', _external=True))) - for genre in genres: - page.add(vtuner.Directory(genre, url_for('radiobrowser_genre_stations', _external=True, genre=genre))) - return page.to_string() + directories = radiobrowser.get_genres() + return get_directories_page('radiobrowser_genre_stations', directories, + request.args.get('startitems'), request.args.get('enditems')).to_string() -@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_GENRE + '/') -def radiobrowser_genre_stations(genre): - stations = radiobrowser.get_stations_by_genre(genre) - page = vtuner.Page() - page.add(vtuner.Previous(url_for('radiobrowser_genres', _external=True))) - if len(stations) == 0: - page.add(vtuner.Display("No stations found for genre '" + genre + "'")) - else: - for station in stations: - page.add(vtuner.Station(station.id, station.name, ', '.join(station.tags), station.url, station.icon, - station.tags[0], station.country, station.codec, station.bitrate, None)) - return page.to_string() +@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_GENRE + '/') +def radiobrowser_genre_stations(directory): + stations = radiobrowser.get_stations_by_genre(directory) + return get_stations_page(stations, request.args.get('startitems'), request.args.get('enditems')).to_string() @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_POPULAR + '/') def radiobrowser_popular(): stations = radiobrowser.get_stations_by_votes() - page = vtuner.Page() - page.add(vtuner.Previous(url_for('radiobrowser_landing', _external=True))) - for station in stations: - page.add(vtuner.Station(station.id, station.name, ', '.join(station.tags), station.url, station.icon, - station.tags[0], station.country, station.codec, station.bitrate, None)) - return page.to_string() + return get_stations_page(stations, request.args.get('startitems'), request.args.get('enditems')).to_string() @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_SEARCH, defaults={'path': ''}) @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_SEARCH + '') def radiobrowser_search(path): - page = vtuner.Page() - page.add(vtuner.Previous(url_for('radiobrowser_landing', _external=True))) # vtuner does totally weird stuff here: TWO request arguments are passed to the search URI # thus, we need to parse the search query as path query = None @@ -168,13 +181,10 @@ def radiobrowser_search(path): path_search = path[path.find('search'):] query = path_search.partition('=')[2] if not query or len(query) < 3: + page = vtuner.Page() + page.add(vtuner.Previous(url_for('landing', _external=True))) page.add(vtuner.Display("Search query too short.")) + return page.to_string() else: stations = radiobrowser.search(query) - if len(stations) == 0: - page.add(vtuner.Display("No results for '" + query + "'")) - else: - for station in stations: - page.add(vtuner.Station(station.id, station.name, ', '.join(station.tags), station.url, station.icon, - station.tags[0], station.country, station.codec, station.bitrate, None)) - return page.to_string() + return get_stations_page(stations, request.args.get('startitems'), request.args.get('enditems')).to_string() diff --git a/ycast/vtuner.py b/ycast/vtuner.py index b46cc11..145997f 100644 --- a/ycast/vtuner.py +++ b/ycast/vtuner.py @@ -10,13 +10,17 @@ def get_init_token(): class Page: def __init__(self): self.items = [] + self.count = -1 def add(self, item): self.items.append(item) + def set_count(self, count): + self.count = count + def to_xml(self): xml = etree.Element('ListOfItems') - etree.SubElement(xml, 'ItemCount').text = str(len(self.items)) + etree.SubElement(xml, 'ItemCount').text = str(self.count) for item in self.items: item.append_to_xml(xml) return xml