livetv/livetv.py

190 lines
17 KiB
Python
Executable File

#!/usr/bin/python3
"""
A little script for viewing German Live TV (and scraping the URLs off
of mediathekview's metadata.
Distributed under CC-0 (public domain), except for the contents of
LIST_CACHE, which is formally GPL-3 (but it's hard to figure out
who holds the copyright, and I don't think anyone wants to claim it).
"""
import json
import os
import re
import sys
import time
from urllib import request
LIST_URL = ("https://raw.githubusercontent.com/mediathekview"
"/MServer/master/dist/live-streams.json")
LIST_CACHE = """{
\"Filmliste\" : [ \"22.10.2018, 19:29\", \"22.10.2018, 19:29\", \"3\", \"MSearch [Rel: 550] - Compiled: 29.05.2016 / 16:16:25\", \"92bdd1d2c16779f916e64d0bcf493c81\" ],
\"Filmliste\" : [ \"Sender\", \"Thema\", \"Titel\", \"Datum\", \"Zeit\", \"Dauer\", \"Größe [MB]\", \"Beschreibung\", \"Url\", \"Website\", \"Untertitel\", \"UrlRTMP\", \"Url_Klein\", \"UrlRTMP_Klein\", \"Url_HD\", \"UrlRTMP_HD\", \"DatumL\", \"Url_History\", \"Geo\", \"neu\" ],
\"X\" : [ \"3Sat\", \"Livestream\", \"3Sat Livestream\", \"\", \"\", \"\", \"\", \"\", \"http://zdf-hls-18.akamaized.net/hls/live/2016501/dach/high/master.m3u8\", \"http://www.zdf.de/ZDFmediathek/hauptnavigation/live\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ARD\", \"Livestream\", \"ARD Livestream\", \"\", \"\", \"\", \"\", \"\", \"http://mcdn.daserste.de/daserste/de/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL2Rhc2Vyc3RlLmRlL0xpdmVzdHJlYW0tRGFzRXJzdGU/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ARD\", \"Livestream\", \"ARD Alpha Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://mcdn.br.de/br/fs/ard_alpha/hls/de/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL2JyLmRlL0xpdmVzdHJlYW0tQVJELUFscGhh/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ARD\", \"Livestream\", \"ARD ONE Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://mcdn.one.ard.de/ardone/hls/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL3dkci5kZS9CZWl0cmFnLTFlNjA0YWFlLTViODctNGMzNC04ZDhmLTg4OWI1ZjE2ZDU3Mw/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ARD\", \"Livestream\", \"ARD Tagesschau Livestream\", \"\", \"\", \"\", \"\", \"\", \"http://tagesschau-lh.akamaihd.net/i/tagesschau_1@119231/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL2Rhc2Vyc3RlLmRlL3RhZ2Vzc2NoYXUvbGl2ZXN0cmVhbQ/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ARTE.DE\", \"Livestream\", \"ARTE.DE Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://artesimulcast.akamaized.net/hls/live/2030993/artelive_de/index.m3u8\", \"https://www.arte.tv/de/live/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ARTE.FR\", \"Livestream\", \"ARTE.FR Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://artesimulcast.akamaized.net/hls/live/2031003/artelive_fr/index.m3u8\", \"https://www.arte.tv/fr/direct/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"BR\", \"Livestream\", \"BR Nord Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://mcdn.br.de/br/fs/bfs_nord/hls/de/master.m3u8\", \"https://www.br.de/mediathek/live\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"BR\", \"Livestream\", \"BR Süd Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://mcdn.br.de/br/fs/bfs_sued/hls/de/master.m3u8\", \"https://www.br.de/mediathek/live\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"DW\", \"Livestream\", \"DW Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://dwamdstream111.akamaized.net/hls/live/2017972/dwstream111/index.m3u8\", \"https://www.dw.com\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"HR\", \"Livestream\", \"HR Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://hrhls.akamaized.net/hls/live/2024525/hrhls/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL2hyLmRlL0xpdmVzdHJlYW0tSFI/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"KiKA\", \"Livestream\", \"KiKA Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://kikageohls.akamaized.net/hls/live/2022693/livetvkika_de/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL2tpa2EuZGUvTGl2ZXN0cmVhbS1LaUth/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"MDR\", \"Livestream\", \"MDR Sachsen Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://mdrtvsnhls.akamaized.net/hls/live/2016928/mdrtvsn/master.m3u8\", \"https://www.mdr.de/video/livestreams/fernsehen/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"MDR\", \"Livestream\", \"MDR Sachsen-Anhalt Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://mdrtvsahls.akamaized.net/hls/live/2016879/mdrtvsa/master.m3u8\", \"https://www.mdr.de/video/livestreams/sachsen-anhalt/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"MDR\", \"Livestream\", \"MDR Thüringen Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://mdrtvthhls.akamaized.net/hls/live/2016880/mdrtvth/master.m3u8\", \"https://www.mdr.de/video/livestreams/thueringen/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"NDR\", \"Livestream\", \"NDR Hamburg\", \"\", \"\", \"\", \"\", \"\", \"https://ndrfs-lh.akamaihd.net/i/ndrfs_hh@430231/master.m3u8\", \"https://www.ndr.de/fernsehen/livestream/livestream223.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"NDR\", \"Livestream\", \"NDR Mecklenburg-Vorpommern\", \"\", \"\", \"\", \"\", \"\", \"https://mcdn.ndr.de/ndr/hls/ndr_fs/ndr_mv/master.m3u8\", \"https://www.ndr.de/fernsehen/livestream/livestream221.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"NDR\", \"Livestream\", \"NDR Niedersachsen\", \"\", \"\", \"\", \"\", \"\", \"https://mcdn.ndr.de/ndr/hls/ndr_fs/ndr_nds/master.m3u8\", \"https://www.ndr.de/fernsehen/livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"NDR\", \"Livestream\", \"NDR Schleswig-Holstein\", \"\", \"\", \"\", \"\", \"\", \"https://mcdn.ndr.de/ndr/hls/ndr_fs/ndr_sh/master.m3u8\", \"https://www.ndr.de/fernsehen/livestream/livestream219.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ORF\", \"Livestream\", \"ORF-1 Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://orf1.mdn.ors.at/out/u/orf1/qxb/manifest.m3u8\", \"http://tvthek.orf.at/live\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ORF\", \"Livestream\", \"ORF-2 Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://orf2.mdn.ors.at/out/u/orf2/qxb/manifest.m3u8\", \"http://tvthek.orf.at/live\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ORF\", \"Livestream\", \"ORF-3 Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://orf3.mdn.ors.at/out/u/orf3/qxb/manifest.m3u8\", \"http://tvthek.orf.at/live\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ORF\", \"Livestream\", \"ORF-Sport Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://orfs.mdn.ors.at/out/u/orfs/qxb/manifest.m3u8\", \"http://tvthek.orf.at/live\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"RBB\", \"Livestream\", \"RBB Brandenburg Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://rbb-hls-brandenburg.akamaized.net/hls/live/2017825/rbb_brandenburg/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL3JiYi1vbmxpbmUuZGUvcmJiZmVybnNlaGVuL2xpdmVfYnJhbmRlbmJ1cmcvc2VuZGVwbGF0ei0tLWxpdmVzdHJlYW0tLS1icmFuZGVuYnVyZy0tLWhsczE/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"RBB\", \"Livestream\", \"RBB Berlin Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://rbb-hls-berlin.akamaized.net/hls/live/2017824/rbb_berlin/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL3JiYi1vbmxpbmUuZGUvcmJiZmVybnNlaGVuL2xpdmVfYnJhbmRlbmJ1cmcvc2VuZGVwbGF0ei0tLWxpdmVzdHJlYW0tLS1icmFuZGVuYnVyZy0tLWhsczE/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"SR\", \"Livestream\", \"SR Livestream\", \"\", \"\", \"\", \"\", \"\", \"http://srlive24-lh.akamaihd.net/i/sr_universal02@107595/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL3NyLW9ubGluZS5kZS8yODQ4NjAvbGl2ZXN0cmVhbQ/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"SWR\", \"Livestream\", \"SWR BW Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://swrbwd-hls.akamaized.net/hls/live/2018672/swrbwd/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL3N3ci5kZS8xMzQ4MTA0Mg/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"SWR\", \"Livestream\", \"SWR RP Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://swrrpd-hls.akamaized.net/hls/live/2018676/swrrpd/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL3N3ci5kZS8xMzQ4MTA0Mg/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Livestream (Deutschland)\", \"\", \"\", \"\", \"\", \"\", \"https://wdrfs247.akamaized.net/hls/live/681509/wdr_msl4_fs247/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Livestream (weltweit)\", \"\", \"\", \"\", \"\", \"\", \"https://mcdn.wdr.de/wdr/wdrfs/de/master.m3u8\", \"https://www.ardmediathek.de/live/Y3JpZDovL3dkci5kZS9CZWl0cmFnLTNkYTY2NGRlLTE4YzItNDY1MC1hNGZmLTRmNjQxNDcyMDcyYg/\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Lokalzeit Aachen Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://wdrlokalzeit.akamaized.net/hls/live/2018019/wdrlz_aachen/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/lokalzeit-livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Lokalzeit Bergisches Land Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://wdrlokalzeit.akamaized.net/hls/live/2018028/wdrlz_wuppertal/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/lokalzeit-livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Lokalzeit Bonn Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://wdrlokalzeit.akamaized.net/hls/live/2018021/wdrlz_bonn/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/lokalzeit-livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Lokalzeit Dortmund Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://wdrlokalzeit.akamaized.net/hls/live/2018022/wdrlz_dortmund/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/lokalzeit-livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Lokalzeit Düsseldorf Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://wdrlokalzeit.akamaized.net/hls/live/2018023/wdrlz_duesseldorf/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/lokalzeit-livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Lokalzeit Duisburg Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://wdrlokalzeit.akamaized.net/hls/live/2018024/wdrlz_duisburg/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/lokalzeit-livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Lokalzeit Köln Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://wdrlokalzeit.akamaized.net/hls/live/2023550/wdrlz_koeln/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/lokalzeit-livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Lokalzeit Münsterland Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://wdrlokalzeit.akamaized.net/hls/live/2018025/wdrlz_muensterland/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/lokalzeit-livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Lokalzeit Ostwestfalen Lippe Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://wdrlokalzeit.akamaized.net/hls/live/2018026/wdrlz_bielefeld/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/lokalzeit-livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Lokalzeit Ruhr Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://wdrlokalzeit.akamaized.net/hls/live/2018027/wdrlz_essen/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/lokalzeit-livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"WDR\", \"Livestream\", \"WDR Lokalzeit Südwestfalen Livestream\", \"\", \"\", \"\", \"\", \"\", \"https://wdrlokalzeit.akamaized.net/hls/live/2018020/wdrlz_siegen/index.m3u8\", \"https://www1.wdr.de/fernsehen/livestream/lokalzeit-livestream/index.html\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ZDF\", \"Livestream\", \"ZDF Livestream\", \"\", \"\", \"\", \"\", \"\", \"http://zdf-hls-15.akamaized.net/hls/live/2016498/de/high/master.m3u8\", \"http://www.zdf.de/ZDFmediathek/hauptnavigation/live\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ZDF\", \"Livestream\", \"ZDF.info Livestream\", \"\", \"\", \"\", \"\", \"\", \"http://zdf-hls-17.akamaized.net/hls/live/2016500/de/high/master.m3u8\", \"http://www.zdf.de/ZDFmediathek/hauptnavigation/live\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"ZDF\", \"Livestream\", \"ZDF.neo Livestream\", \"\", \"\", \"\", \"\", \"\", \"http://zdf-hls-16.akamaized.net/hls/live/2016499/de/high/master.m3u8\", \"http://www.zdf.de/ZDFmediathek/hauptnavigation/live\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ],
\"X\" : [ \"PHOENIX\", \"Livestream\", \"PHOENIX Livestream\", \"\", \"\", \"\", \"\", \"\", \"http://zdf-hls-19.akamaized.net/hls/live/2016502/de/high/master.m3u8\", \"http://www.zdf.de/ZDFmediathek/hauptnavigation/live\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ]
}
"""
def update_stations():
"""modifies sys.argv[0]'s source to update the live URL list
from mediathekview's list.
Of course, this only works if the user has write permission to
this script.
"""
in_bytes = request.urlopen(LIST_URL).read()
self_path = sys.argv[0]
with open(self_path, "rb") as f:
src = f.read()
src = re.sub(b'(?s)LIST_CACHE\x20= """.*?"""',
b'LIST_CACHE\x20= """%s"""'%(in_bytes.replace(b'"', b'\\"')),
src)
with open(self_path, "wb") as f:
f.write(src)
def load_stations():
"""returns a mapping of station_id -> stream URL from the built-in json
station list.
"""
channels = {}
def collect(args):
for name, val in args:
if name=="X":
channels[val[2]] = val[8]
dec = json.JSONDecoder(object_pairs_hook=collect)
dec.decode(LIST_CACHE)
return channels
def write_dump(hls_url, args):
"""writes a dump of the stream to recorded.mp4.
This uses streamlink and changes the interpretation of max-bitrate.
Ah well.
"""
# regrettaly, mpv has dumped the copy codecs, it seems. I'll
# switch to streamlink for now. But that doesn't have
# mpv's nice bitrate selection. Aw, dang. I'll think
# about it if I actually care one day.
import streamlink
streams = streamlink.streams(hls_url)
try:
src = streams[args.hls_bitrate].open()
except KeyError:
sys.exit("Setze --max-bitrate auf eines von "+(" ".join(streams)))
start_time = time.time()
with open("recorded.mp4", "wb") as dest:
while True:
dest.write(src.read(2**20))
if time.time()-start_time>args.record:
break
def parse_command_line():
import argparse
parser = argparse.ArgumentParser(description="Livestream glotzen")
parser.add_argument("sender", help="Sendername -- leer lassen für"
" Liste, 'update', um interne Senderliste zu aktualisieren.",
nargs="*")
parser.add_argument("--max-bitrate", help="Max. Bitrate für die"
" Streamauswahl (kann auch min/max sein).",
dest="hls_bitrate", default="2000000")
parser.add_argument("--record", help="Nimm ZEIT Sekunden zu 'recorded.mp4'"
"auf.", dest="record", default=None, metavar="ZEIT", type=int)
return parser.parse_args()
def main():
args = parse_command_line()
station = " ".join(args.sender).lower()
if station=="":
print("\n".join(load_stations()))
elif station=="update":
update_stations()
else:
stations = load_stations()
matches = []
for key in stations:
matcher = key.lower()
if station==matcher:
matches = [key]
break
else:
if station in matcher:
matches.append(key)
if len(matches)==0:
sys.exit("Nichts passendes")
elif len(matches)>1:
sys.exit("? ".join(matches)+"?")
else:
if args.record:
write_dump(stations[matches[0]], args)
else:
os.execlp("mpv", "mpv",
f"--hls-bitrate={args.hls_bitrate}", stations[matches[0]])
if __name__=="__main__":
main()