9e1bb5e17b
To work around the issue that a few AVRs blindly append parameters with an ampersand. This also allows us to get rid of the search logic workaround. Furthermore the vTuner API also hacks around the issue this way. So this seems like the correct way to handle that even if it looks ugly.
199 lines
7.4 KiB
Python
199 lines
7.4 KiB
Python
import logging
|
|
|
|
import yaml
|
|
from flask import Flask, request, url_for
|
|
|
|
import ycast.vtuner as vtuner
|
|
import ycast.radiobrowser as radiobrowser
|
|
|
|
|
|
PATH_ROOT = 'ycast'
|
|
PATH_CUSTOM_STATIONS = 'my_stations'
|
|
PATH_RADIOBROWSER = 'radiobrowser'
|
|
PATH_RADIOBROWSER_COUNTRY = 'country'
|
|
PATH_RADIOBROWSER_GENRE = 'genre'
|
|
PATH_RADIOBROWSER_POPULAR = 'popular'
|
|
PATH_RADIOBROWSER_SEARCH = 'search'
|
|
|
|
my_stations = {}
|
|
app = Flask(__name__)
|
|
|
|
|
|
def run(config, address='0.0.0.0', port=8010):
|
|
try:
|
|
get_stations(config)
|
|
app.run(host=address, port=port)
|
|
except PermissionError:
|
|
logging.error("No permission to create socket. Are you trying to use ports below 1024 without elevated rights?")
|
|
|
|
|
|
def get_stations(config):
|
|
global my_stations
|
|
if not config:
|
|
logging.warning("If you want to use the 'My Stations' feature, please supply a valid station configuration")
|
|
return
|
|
try:
|
|
with open(config, 'r') as f:
|
|
my_stations = yaml.safe_load(f)
|
|
except FileNotFoundError:
|
|
logging.error("Station configuration '%s' not found", config)
|
|
return
|
|
except yaml.YAMLError as e:
|
|
logging.error("Config error: %s", e)
|
|
return
|
|
|
|
|
|
def get_directories_page(subdir, directories, requestargs):
|
|
page = vtuner.Page()
|
|
if len(directories) == 0:
|
|
page.add(vtuner.Display("No entries found."))
|
|
return page
|
|
offset = 0
|
|
limit = len(directories)
|
|
if get_element_offset(requestargs) is not None and get_element_limit(requestargs) is not None:
|
|
offset = get_element_offset(requestargs)
|
|
limit = get_element_limit(requestargs)
|
|
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, requestargs):
|
|
page = vtuner.Page()
|
|
if len(stations) == 0:
|
|
page.add(vtuner.Display("No stations found."))
|
|
return page
|
|
offset = 0
|
|
limit = len(stations)
|
|
if get_element_offset(requestargs) is not None and get_element_limit(requestargs) is not None:
|
|
offset = get_element_offset(requestargs)
|
|
limit = get_element_limit(requestargs)
|
|
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
|
|
|
|
|
|
def get_element_offset(requestargs):
|
|
if requestargs.get('startitems'):
|
|
return int(requestargs.get('startitems')) - 1
|
|
elif requestargs.get('start'):
|
|
return int(requestargs.get('start')) - 1
|
|
else:
|
|
return None
|
|
|
|
|
|
def get_element_limit(requestargs):
|
|
if requestargs.get('enditems'):
|
|
return int(requestargs.get('enditems'))
|
|
elif requestargs.get('start') and requestargs.get('howmany'):
|
|
return int(requestargs.get('start')) - 1 + int(requestargs.get('howmany'))
|
|
else:
|
|
return None
|
|
|
|
|
|
# TODO: vtuner doesn't do https (e.g. for logos). make an icon cache
|
|
|
|
|
|
@app.route('/', defaults={'path': ''})
|
|
@app.route('/setupapp/<path:path>')
|
|
@app.route('/' + PATH_ROOT + '/', defaults={'path': ''})
|
|
def landing(path):
|
|
if request.args.get('token') == '0':
|
|
return vtuner.get_init_token()
|
|
page = vtuner.Page()
|
|
page.add(vtuner.Directory('Radiobrowser', url_for('radiobrowser_landing', _external=True)))
|
|
page.add(vtuner.Directory('My Stations', url_for('custom_stations_landing', _external=True)))
|
|
return page.to_string()
|
|
|
|
|
|
@app.route('/' + PATH_ROOT + '/' + PATH_CUSTOM_STATIONS + '/')
|
|
def custom_stations_landing():
|
|
page = vtuner.Page()
|
|
page.add(vtuner.Previous(url_for("landing", _external=True)))
|
|
if not my_stations:
|
|
page.add(vtuner.Display("No stations found"))
|
|
else:
|
|
for category in sorted(my_stations, key=str.lower):
|
|
directory = vtuner.Directory(category, url_for('custom_stations_category',
|
|
_external=True, category=category))
|
|
page.add(directory)
|
|
return page.to_string()
|
|
|
|
|
|
@app.route('/' + PATH_ROOT + '/' + PATH_CUSTOM_STATIONS + '/<category>')
|
|
def custom_stations_category(category):
|
|
page = vtuner.Page()
|
|
page.add(vtuner.Previous(url_for('custom_stations_landing', _external=True)))
|
|
if category not in my_stations:
|
|
page.add(vtuner.Display("Category '" + category + "' not found"))
|
|
else:
|
|
for station in sorted(my_stations[category], key=str.lower):
|
|
station = vtuner.Station(None, station, None, my_stations[category][station],
|
|
None, None, None, None, None, None)
|
|
page.add(station)
|
|
return page.to_string()
|
|
|
|
|
|
@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/')
|
|
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)))
|
|
page.add(vtuner.Directory('Countries', url_for('radiobrowser_countries', _external=True)))
|
|
page.add(vtuner.Directory('Most Popular', url_for('radiobrowser_popular', _external=True)))
|
|
page.add(vtuner.Search('Search', url_for('radiobrowser_search', _external=True, path='')))
|
|
return page.to_string()
|
|
|
|
|
|
@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).to_string()
|
|
|
|
|
|
@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_COUNTRY + '/<directory>')
|
|
def radiobrowser_country_stations(directory):
|
|
stations = radiobrowser.get_stations_by_country(directory)
|
|
return get_stations_page(stations, request.args).to_string()
|
|
|
|
|
|
@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).to_string()
|
|
|
|
|
|
@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_GENRE + '/<directory>')
|
|
def radiobrowser_genre_stations(directory):
|
|
stations = radiobrowser.get_stations_by_genre(directory)
|
|
return get_stations_page(stations, request.args).to_string()
|
|
|
|
|
|
@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_POPULAR + '/')
|
|
def radiobrowser_popular():
|
|
stations = radiobrowser.get_stations_by_votes()
|
|
return get_stations_page(stations, request.args).to_string()
|
|
|
|
|
|
@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_SEARCH, defaults={'path': ''})
|
|
@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_SEARCH + '<path:path>')
|
|
def radiobrowser_search(path):
|
|
query = request.args.get('search')
|
|
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)
|
|
return get_stations_page(stations, request.args).to_string()
|