Last active
May 31, 2021 04:28
-
-
Save NonaSuomy/5f09f76d60e30ad127230cf22709f986 to your computer and use it in GitHub Desktop.
Send A Radio Station From streamtheworld.com to https://github.com/Edzelf/Esp-radio hardware ESP8266 + VS1053
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
''' | |
User: NonaSuomy | |
Date: 20170406 | |
Upda: 20170422 | |
Desc: Found this script at: https://www.toofishes.net/blog/streamtheworld-streams-command-line/ | |
Desc: Modified to send the streams to esp-radio further information below in the comments and https://github.com/Edzelf/Esp-radio/issues/70 . | |
Usage: python2 streamtheworldespradio.py CIMXFM 10.13.37.65 | |
^Radio callsign ^IP address of esp-radio Device | |
Requirements: python2 + curl | |
''' | |
from random import choice | |
import os | |
import sys | |
import urllib2 | |
import xml.dom.minidom as minidom | |
def validate_callsign(cs): | |
''' | |
Normal callsign format is 'WWWWFFAAA', where 'WWWW' is the radio station | |
callsign, 'FF' is either 'AM' or 'FM', and 'AAA' is always 'AAC'. | |
For this function, we expect the 'WWWWFF' part as input. | |
''' | |
if not cs or not isinstance(cs, str): | |
raise ValueError('callsign \'%s\' is not a string.' % cs) | |
#if len(cs) < 6: | |
# raise ValueError('callsign \'%s\' is too short.' % cs) | |
if not cs.endswith('AAC'): | |
cs = cs + 'AAC' | |
band = cs[-5:-3] | |
#if band != 'AM' and band != 'FM': | |
# raise ValueError('callsign \'%s\' is missing \'FM\' or \'AM\'.' % cs) | |
return cs | |
def make_request(callsign): | |
host = 'playerservices.streamtheworld.com' | |
req = urllib2.Request( | |
'http://%s/api/livestream?version=1.5&mount=%s&lang=en' % | |
(host, callsign)) | |
req.add_header('User-Agent', 'Mozilla/5.0') | |
return req | |
## Example XML document we are parsing follows, as the minidom code is so beautiful to follow | |
# | |
#<?xml version="1.0" encoding="UTF-8" standalone="yes"?> | |
#<live_stream_config xmlns="http://provisioning.streamtheworld.com/player/livestream-1.5" version="1.5"> | |
# <script/> | |
# <mountpoints> | |
# <mountpoint> | |
# <status> | |
# <status-code>200</status-code> | |
# <status-message>OK</status-message> | |
# </status> | |
# <transports> | |
# <transport>http</transport> | |
# </transports> | |
# <metadata> | |
# <shoutcast-v1 enabled="true" mountSuffix="_SC"/> | |
# <shoutcast-v2 enabled="false" mountSuffix="_SC"/> | |
# <sse-sideband enabled="true" streamSuffix="_SC" metadataSuffix="_SBM"/> | |
# </metadata> | |
# <servers> | |
# <server sid="18853"> | |
# <ip>18853.live.streamtheworld.com</ip> | |
# <ports> | |
# <port type="http">80</port> | |
# <port type="http">3690</port> | |
# </ports> | |
# </server> | |
# <server sid="17023"> | |
# <ip>17023.live.streamtheworld.com</ip> | |
# <ports> | |
# <port type="http">80</port> | |
# <port type="http">3690</port> | |
# </ports> | |
# </server> | |
# <server sid="13733"> | |
# <ip>13733.live.streamtheworld.com</ip> | |
# <ports> | |
# <port type="http">80</port> | |
# <port type="http">3690</port> | |
# </ports> | |
# </server> | |
# <server sid="14213"> | |
# <ip>14213.live.streamtheworld.com</ip> | |
# <ports> | |
# <port type="http">80</port> | |
# <port type="http">3690</port> | |
# </ports> | |
# </server> | |
# </servers> | |
# <metrics> | |
# <tag name="uuid"/> | |
# </metrics> | |
# <mount>CIMXFM</mount> | |
# <format>FLV</format> | |
# <bitrate>48000</bitrate> | |
# <media-format container="flv" cuepoints="stwcue"> | |
# <audio index="0" samplerate="22050" codec="mp3" bitrate="48000" channels="2"/> | |
# </media-format> | |
# <authentication>0</authentication> | |
# <timeout>0</timeout> | |
# <send-page-url>0</send-page-url> | |
# </mountpoint> | |
# </mountpoints> | |
#</live_stream_config> | |
def t(element): | |
'''get the text of a DOM element''' | |
return element.firstChild.data | |
def check_status(ele): | |
# should only be one status element inside a mountpoint | |
status = ele.getElementsByTagName('status')[0] | |
if t(status.getElementsByTagName('status-code')[0]) != '200': | |
msg = t(status.getElementsByTagName('status-message')[0]) | |
raise Exception('Error locating stream: ' + msg) | |
def create_stream_urls(srcfile): | |
doc = minidom.parse(srcfile) | |
mp = doc.getElementsByTagName('mountpoint')[0] | |
check_status(mp) | |
mt = t(mp.getElementsByTagName('mount')[0]) | |
allurls = [] | |
for s in mp.getElementsByTagName('server'): | |
# a thing of beauty, right? | |
ip = t(s.getElementsByTagName('ip')[0]) | |
ports = [t(p) for p in s.getElementsByTagName('port')] | |
# yes, it is always HTTP. We see ports 80, 443, and 3690 usually | |
urls = ['http://%s:%s/%s' % (ip, p, mt) for p in ports] | |
allurls.extend(urls) | |
return allurls | |
def start_mplayer(playerip, location): | |
return os.system('curl http://{0}/?station="{1}"_SC'.format(playerip, location)) | |
if __name__ == '__main__': | |
if len(sys.argv) < 2: | |
print 'usage: station callsign must be the first argument' | |
sys.exit(1) | |
callsign = validate_callsign(sys.argv[1]) | |
playerip = sys.argv[2] | |
req = make_request(callsign) | |
result = urllib2.urlopen(req) | |
urls = create_stream_urls(result) | |
if len(urls) > 0: | |
u = choice(urls) | |
sys.exit(start_mplayer(playerip,u)) | |
sys.exit(1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Send a radio station to your esp-radio player: GIST
Simple script, at the top of the script, is the usage example, basically, find a radio station on iheartradio:
iheartradio station list USA
iheartradio
iheartradio Canada
Look at the source code of the iheartradio page (click on song history will help a bit) you will find things like this in it:
view-source:http://www.iheartradio.ca/all-stations/7.13793734?mode=history
This is what we want:
IHR_TRAN is the callsign for the station you want to play. (Trancid)
Another stations callsign CIMXFM etc.
This is the radio station we are grabbing below from iheartradio 89x "CIMXFM"
Make sure you have python2 and curl installed on the system you are sending the station from, download the script to a file on that device, then type the command, I named the script streamtheworldespradio.py, so type on the command line:
First, test with the call sign above/below make sure it is all in CAPS as I know that one works (may not work outside of Canada), the hard part would be finding how they named the call sign for the station you want, and some may be location locked so be aware. Also add FM/AM on the end of the call sign or the script will complain "Callsign is missing FM, AM".
The script then tells the website to give us the ShoutCast stream URL for audio stream compatibility purposes with _SC.
The script is grabbing the stream URL that is a randomly encoded string of characters for that station you want, then send the URL to the ESP8266.
The script requests a URL like this:
http://playerservices.streamtheworld.com/api/livestream?version=1.5&mount=CIMXFM&lang=en
Then it sends back an XML which we extract the values from:
The script extracts,
Random generated IP: 18853.live.streamtheworld.com
Port: 80, 3690, etc
MountPoint(callsign): CIMXFM
I noticed that there was ShoutCast compatibility in the XML which it shows to add "_CS" onto the mount point: CIMXFMAAC_CS
Then the script sends this data gathered from the XML to esp-radio device:
curl http://10.13.37.65/?station="18853.live.streamtheworld.com:3690/CIMXFMAAC_SC"
Then esp-radio should start playing the stream.
If you attach a serial cable to esp-radio you should be able to debug what URL is getting passed to it, to see if the station is getting sent properly.
Successful run should look like this on the serial line:
Hope it helps!