2018-07-23 12:23:57 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
import os
|
2018-07-23 16:36:47 +00:00
|
|
|
import sys
|
2018-07-23 16:27:15 +00:00
|
|
|
import argparse
|
2018-07-23 12:23:57 +00:00
|
|
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
|
|
import xml.etree.cElementTree as etree
|
|
|
|
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
YCAST_LOCATION = 'ycast'
|
|
|
|
|
|
|
|
stations = {}
|
|
|
|
|
|
|
|
|
|
|
|
def get_stations():
|
|
|
|
global stations
|
|
|
|
ycast_dir = os.path.dirname(os.path.realpath(__file__))
|
2018-07-23 16:36:47 +00:00
|
|
|
try:
|
|
|
|
with open(ycast_dir + '/stations.yml', 'r') as f:
|
|
|
|
stations = yaml.load(f)
|
|
|
|
except FileNotFoundError:
|
|
|
|
print("ERROR: Station configuration not found. Please supply a proper stations.yml.")
|
|
|
|
sys.exit(1)
|
2018-07-23 12:23:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
def text_to_url(text):
|
|
|
|
return text.replace(' ', '%20')
|
|
|
|
|
|
|
|
|
|
|
|
def url_to_text(url):
|
|
|
|
return url.replace('%20', ' ')
|
|
|
|
|
|
|
|
|
|
|
|
class YCastServer(BaseHTTPRequestHandler):
|
|
|
|
def do_GET(self):
|
|
|
|
get_stations()
|
2019-01-21 01:38:56 +00:00
|
|
|
self.address = 'http://' + self.headers['Host']
|
2019-01-24 21:07:06 +00:00
|
|
|
if 'loginXML.asp?token=0' in self.path:
|
2019-01-21 01:59:21 +00:00
|
|
|
self.send_xml_header()
|
2019-01-21 01:46:37 +00:00
|
|
|
self.wfile.write(bytes('<EncryptedToken>stub</EncryptedToken>', 'utf-8'))
|
|
|
|
elif self.path == '/' \
|
2018-07-23 12:23:57 +00:00
|
|
|
or self.path == '/' + YCAST_LOCATION \
|
|
|
|
or self.path == '/' + YCAST_LOCATION + '/'\
|
2019-01-24 21:07:06 +00:00
|
|
|
or self.path.startswith('/setupapp'):
|
2019-01-21 01:59:21 +00:00
|
|
|
self.send_xml_header()
|
2018-07-23 12:23:57 +00:00
|
|
|
xml = self.create_root()
|
|
|
|
for category in sorted(stations, key=str.lower):
|
|
|
|
self.add_dir(xml, category,
|
2019-01-21 01:38:56 +00:00
|
|
|
self.address + '/' + YCAST_LOCATION + '/' + text_to_url(category))
|
2018-07-23 12:23:57 +00:00
|
|
|
self.wfile.write(bytes(etree.tostring(xml).decode('utf-8'), 'utf-8'))
|
|
|
|
elif self.path.startswith('/' + YCAST_LOCATION + '/'):
|
|
|
|
category = url_to_text(self.path[len(YCAST_LOCATION) + 2:].partition('?')[0])
|
|
|
|
if category not in stations:
|
|
|
|
self.send_error(404)
|
|
|
|
return
|
|
|
|
xml = self.create_root()
|
|
|
|
for station in sorted(stations[category], key=str.lower):
|
|
|
|
self.add_station(xml, station, stations[category][station])
|
2019-01-21 01:59:21 +00:00
|
|
|
self.send_xml_header()
|
2018-07-23 12:23:57 +00:00
|
|
|
self.wfile.write(bytes(etree.tostring(xml).decode('utf-8'), 'utf-8'))
|
|
|
|
else:
|
|
|
|
self.send_error(404)
|
|
|
|
|
2019-01-21 01:59:21 +00:00
|
|
|
def send_xml_header(self):
|
|
|
|
self.send_response(200)
|
|
|
|
self.send_header('Content-type', 'text/html')
|
|
|
|
self.end_headers()
|
|
|
|
self.wfile.write(bytes('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>', 'utf-8'))
|
|
|
|
|
2018-07-23 12:23:57 +00:00
|
|
|
def create_root(self):
|
|
|
|
return etree.Element('ListOfItems')
|
|
|
|
|
|
|
|
def add_dir(self, root, name, dest):
|
|
|
|
item = etree.SubElement(root, 'Item')
|
|
|
|
etree.SubElement(item, 'ItemType').text = 'Dir'
|
|
|
|
etree.SubElement(item, 'Title').text = name
|
|
|
|
etree.SubElement(item, 'UrlDir').text = dest
|
|
|
|
return item
|
|
|
|
|
|
|
|
def add_station(self, root, name, url):
|
|
|
|
item = etree.SubElement(root, 'Item')
|
|
|
|
etree.SubElement(item, 'ItemType').text = 'Station'
|
|
|
|
etree.SubElement(item, 'StationName').text = name
|
|
|
|
etree.SubElement(item, 'StationUrl').text = url
|
|
|
|
return item
|
|
|
|
|
|
|
|
|
2018-07-23 16:27:15 +00:00
|
|
|
parser = argparse.ArgumentParser(description='vTuner API emulation')
|
|
|
|
parser.add_argument('-l', action='store', dest='address', help='Listen address', default='0.0.0.0')
|
|
|
|
parser.add_argument('-p', action='store', dest='port', type=int, help='Listen port', default=80)
|
|
|
|
arguments = parser.parse_args()
|
2019-01-21 00:57:50 +00:00
|
|
|
get_stations()
|
2018-07-23 16:27:15 +00:00
|
|
|
try:
|
|
|
|
server = HTTPServer((arguments.address, arguments.port), YCastServer)
|
|
|
|
except PermissionError:
|
2018-07-23 16:36:47 +00:00
|
|
|
print("ERROR: No permission to create socket. Are you trying to use ports below 1024 without elevated rights?")
|
|
|
|
sys.exit(1)
|
2018-07-23 16:27:15 +00:00
|
|
|
print('YCast server listening on %s:%s' % (arguments.address, arguments.port))
|
2018-07-23 12:23:57 +00:00
|
|
|
try:
|
|
|
|
server.serve_forever()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
pass
|
|
|
|
print('YCast server shutting down')
|
|
|
|
server.server_close()
|