From 16387a3f50fb6fa3c277d423b8e943b399b58996 Mon Sep 17 00:00:00 2001 From: milaq Date: Sun, 18 Aug 2019 13:50:38 +0200 Subject: [PATCH] Rework directory handling This greatly improves loading times and takes some load off the APIs. * Add generic directory class to also hold item count * Radiobrowser: Get station count directly from API * Optionally show broken stations and their count * Remove minimum station bitrate to not filter away some listings * Improve code wording * Log API requests --- ycast/generic.py | 4 ++++ ycast/my_stations.py | 13 ++++++----- ycast/radiobrowser.py | 53 ++++++++++++++++++++++++++----------------- ycast/server.py | 33 ++++++++++----------------- 4 files changed, 55 insertions(+), 48 deletions(-) create mode 100644 ycast/generic.py diff --git a/ycast/generic.py b/ycast/generic.py new file mode 100644 index 0000000..95e5951 --- /dev/null +++ b/ycast/generic.py @@ -0,0 +1,4 @@ +class Directory: + def __init__(self, name, item_count): + self.name = name + self.item_count = item_count diff --git a/ycast/my_stations.py b/ycast/my_stations.py index dc6c11f..4f30ec5 100644 --- a/ycast/my_stations.py +++ b/ycast/my_stations.py @@ -3,6 +3,7 @@ import logging import yaml import ycast.vtuner as vtuner +import ycast.generic as generic ID_PREFIX = "MY_" @@ -24,13 +25,13 @@ def set_config(config): global config_file if config: config_file = config - if get_stations(): + if get_stations_yaml(): return True else: return False -def get_stations(): +def get_stations_yaml(): try: with open(config_file, 'r') as f: my_stations = yaml.safe_load(f) @@ -43,17 +44,17 @@ def get_stations(): return my_stations -def get_categories(): - my_stations_yaml = get_stations() +def get_category_directories(): + my_stations_yaml = get_stations_yaml() categories = [] if my_stations_yaml: for category in my_stations_yaml: - categories.append(category) + categories.append(generic.Directory(category, len(get_stations_by_category(category)))) return categories def get_stations_by_category(category): - my_stations_yaml = get_stations() + my_stations_yaml = get_stations_yaml() stations = [] if my_stations_yaml and category in my_stations_yaml: for station_name in my_stations_yaml[category]: diff --git a/ycast/radiobrowser.py b/ycast/radiobrowser.py index d1802c1..bd08054 100644 --- a/ycast/radiobrowser.py +++ b/ycast/radiobrowser.py @@ -2,11 +2,12 @@ import requests import logging import ycast.vtuner as vtuner +import ycast.generic as generic MINIMUM_COUNT_GENRE = 5 MINIMUM_COUNT_COUNTRY = 5 -MINIMUM_BITRATE = 64 DEFAULT_STATION_LIMIT = 200 +SHOW_BROKEN_STATIONS = False ID_PREFIX = "RB_" @@ -36,6 +37,7 @@ class Station: def request(url): + logging.debug("Radiobrowser API request: %s", url) headers = {'content-type': 'application/json', 'User-Agent': 'YCast'} response = requests.get('http://www.radio-browser.info/webservice/json/' + url, headers=headers) if response.status_code != 200: @@ -51,48 +53,56 @@ def get_station_by_id(uid): def search(name, limit=DEFAULT_STATION_LIMIT): stations = [] - stations_json = request('stations/search?order=name&reverse=false&bitrateMin=' + - str(MINIMUM_BITRATE) + '&limit=' + str(limit) + '&name=' + str(name)) + stations_json = request('stations/search?order=name&reverse=false&limit=' + str(limit) + '&name=' + str(name)) for station_json in stations_json: - stations.append(Station(station_json)) + if SHOW_BROKEN_STATIONS or get_json_attr(station_json, 'lastcheckok') == '1': + stations.append(Station(station_json)) return stations -def get_countries(): - countries = [] - countries_raw = request('countries') +def get_country_directories(): + country_directories = [] + apicall = 'countries' + if not SHOW_BROKEN_STATIONS: + apicall += '?hidebroken=true' + countries_raw = request(apicall) for country_raw in countries_raw: if get_json_attr(country_raw, 'name') and get_json_attr(country_raw, 'stationcount') and \ int(get_json_attr(country_raw, 'stationcount')) > MINIMUM_COUNT_COUNTRY: - countries.append(get_json_attr(country_raw, 'name')) - return countries + country_directories.append(generic.Directory(get_json_attr(country_raw, 'name'), + get_json_attr(country_raw, 'stationcount'))) + return country_directories -def get_genres(): - genres = [] - genres_raw = request('tags?hidebroken=true') +def get_genre_directories(): + genre_directories = [] + apicall = 'tags' + if not SHOW_BROKEN_STATIONS: + apicall += '?hidebroken=true' + genres_raw = request(apicall) for genre_raw in genres_raw: if get_json_attr(genre_raw, 'name') and get_json_attr(genre_raw, 'stationcount') and \ int(get_json_attr(genre_raw, 'stationcount')) > MINIMUM_COUNT_GENRE: - genres.append(get_json_attr(genre_raw, 'name')) - return genres + genre_directories.append(generic.Directory(get_json_attr(genre_raw, 'name'), + get_json_attr(genre_raw, 'stationcount'))) + return genre_directories def get_stations_by_country(country): stations = [] - stations_json = request('stations/search?order=name&reverse=false&bitrateMin=' + - str(MINIMUM_BITRATE) + '&countryExact=true&country=' + str(country)) + stations_json = request('stations/search?order=name&reverse=false&countryExact=true&country=' + str(country)) for station_json in stations_json: - stations.append(Station(station_json)) + if SHOW_BROKEN_STATIONS or get_json_attr(station_json, 'lastcheckok') == '1': + stations.append(Station(station_json)) return stations def get_stations_by_genre(genre): stations = [] - stations_json = request('stations/search?order=name&reverse=false&bitrateMin=' + - str(MINIMUM_BITRATE) + '&tagExact=true&tag=' + str(genre)) + stations_json = request('stations/search?order=name&reverse=false&tagExact=true&tag=' + str(genre)) for station_json in stations_json: - stations.append(Station(station_json)) + if SHOW_BROKEN_STATIONS or get_json_attr(station_json, 'lastcheckok') == '1': + stations.append(Station(station_json)) return stations @@ -100,5 +110,6 @@ def get_stations_by_votes(limit=DEFAULT_STATION_LIMIT): stations = [] stations_json = request('stations?order=votes&reverse=true&limit=' + str(limit)) for station_json in stations_json: - stations.append(Station(station_json)) + if SHOW_BROKEN_STATIONS or get_json_attr(station_json, 'lastcheckok') == '1': + stations.append(Station(station_json)) return stations diff --git a/ycast/server.py b/ycast/server.py index b016464..eb399f6 100644 --- a/ycast/server.py +++ b/ycast/server.py @@ -32,20 +32,14 @@ def check_my_stations_feature(config): my_stations_enabled = my_stations.set_config(config) -def get_directories_page(subdir, directories, requestargs, item_count_function=None): +def get_directories_page(subdir, directories, requestargs): page = vtuner.Page() if len(directories) == 0: page.add(vtuner.Display("No entries found.")) return page for directory in get_paged_elements(directories, requestargs): - vtuner_directory = vtuner.Directory(directory, url_for(subdir, _external=True, directory=directory)) - if item_count_function: - try: - item_count = len(item_count_function(directory)) - vtuner_directory.set_item_count(item_count) - except TypeError: - logging.error("Could not get item count of directory '%s'", directory) - pass + vtuner_directory = vtuner.Directory(directory.name, url_for(subdir, _external=True, directory=directory.name), + directory.item_count) page.add(vtuner_directory) page.set_count(len(directories)) return page @@ -96,7 +90,7 @@ def landing(path): page.add(vtuner.Directory('Radiobrowser', url_for('radiobrowser_landing', _external=True), 4)) if my_stations_enabled: page.add(vtuner.Directory('My Stations', url_for('my_stations_landing', _external=True), - len(my_stations.get_categories()))) + len(my_stations.get_category_directories()))) else: page.add(vtuner.Display("'My Stations' feature not configured.")) return page.to_string() @@ -106,9 +100,8 @@ def landing(path): def my_stations_landing(): page = vtuner.Page() page.add(vtuner.Previous(url_for("landing", _external=True))) - directories = my_stations.get_categories() - return get_directories_page('my_stations_category', directories, request.args, - my_stations.get_stations_by_category).to_string() + directories = my_stations.get_category_directories() + return get_directories_page('my_stations_category', directories, request.args).to_string() @app.route('/' + PATH_ROOT + '/' + PATH_MY_STATIONS + '/') @@ -122,9 +115,9 @@ def radiobrowser_landing(): page = vtuner.Page() page.add(vtuner.Previous(url_for('landing', _external=True))) page.add(vtuner.Directory('Genres', url_for('radiobrowser_genres', _external=True), - len(radiobrowser.get_genres()))) + len(radiobrowser.get_genre_directories()))) page.add(vtuner.Directory('Countries', url_for('radiobrowser_countries', _external=True), - len(radiobrowser.get_countries()))) + len(radiobrowser.get_country_directories()))) page.add(vtuner.Directory('Most Popular', url_for('radiobrowser_popular', _external=True), len(radiobrowser.get_stations_by_votes()))) page.add(vtuner.Search('Search', url_for('radiobrowser_search', _external=True, path=''))) @@ -133,9 +126,8 @@ def radiobrowser_landing(): @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_COUNTRY + '/') def radiobrowser_countries(): - directories = radiobrowser.get_countries() - return get_directories_page('radiobrowser_country_stations', directories, request.args, - radiobrowser.get_stations_by_country).to_string() + directories = radiobrowser.get_country_directories() + return get_directories_page('radiobrowser_country_stations', directories, request.args).to_string() @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_COUNTRY + '/') @@ -146,9 +138,8 @@ def radiobrowser_country_stations(directory): @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_GENRE + '/') def radiobrowser_genres(): - directories = radiobrowser.get_genres() - return get_directories_page('radiobrowser_genre_stations', directories, request.args, - radiobrowser.get_stations_by_genre).to_string() + directories = radiobrowser.get_genre_directories() + return get_directories_page('radiobrowser_genre_stations', directories, request.args).to_string() @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_GENRE + '/')