Created
January 19, 2021 18:19
-
-
Save dklann/506ee2a805c2c97de8ccde2bf4739b8d to your computer and use it in GitHub Desktop.
This file contains 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
<?php | |
/* | |
* Nautel RDS Text injector. | |
* | |
* This script accepts text from several sources: | |
* | |
* - an external entity that knows to set "artist" and "title" (and | |
* possibly other parameters) in the GET request (e.g., Spinitron | |
* Metadata Push) (non-interactive mode). | |
* Example Spinitron configuration: | |
* http://my-rds-proxy-hostname.example.com:9000/?title=%sn%&artist=%an%&release=%dn%&playlist=%wn%spinnote=%se% | |
* | |
* - this script (via a web browser), by setting 'our_text' in the GET | |
* request (interactive mode). | |
* | |
* - this script (via the command line), by setting 'artist' and 'title' | |
* OR 'our_text' as command line arguments (non-interactive mode). | |
* | |
* Used interactively (via a web browser) this script uses the Bonsai | |
* CSS styling library. This is the thing that gives the web page such | |
* an awesome look! | |
* | |
* See also these links for more details: | |
* | |
* - https://spinitron.com/about/help/metadata-push.html | |
* - https://gist.github.com/spinitron/1950987cf8deb1013a350a698268e6e0 | |
* - socat(1) for testing and simulating an RDS encoder | |
* - https://github.com/bonsaicss/bonsai.css | |
*/ | |
/* | |
* RDS network settings. | |
* | |
* Set $rds_address to the IP address of the Nautel transmitter. Per | |
* Nautel docs, the port is hard-coded (and not configurable) to 7005. | |
* | |
* Set $rds_address to localhost and some high port for testing. | |
* | |
* For testing, also run something like this command: | |
* | |
* socat 'TCP4-LISTEN:localhost:9000,reuseaddr,fork' - | |
* | |
* in a terminal window to see what this script will send to the RDS | |
* encoder. | |
* | |
*/ | |
//$rds_address = '192.168.0.1'; | |
$rds_port = 7005; | |
$rds_address = '127.0.0.1'; | |
/* | |
* Pre-programmed "quickie" button values. | |
* Add more items here to display more buttons on the page. | |
* Remember to keep the character count to 64 or fewer. | |
*/ | |
$buttons = array( | |
"CALL SIGN Morning Program", | |
"CALL SIGN: Generic Tag", | |
"CALL SIGN: Other Generic Tag", | |
); | |
/* | |
* HTML details. | |
* | |
* The header contains the site boilerplate and page style details. | |
*/ | |
$header =<<<EndOfHeader | |
<!DOCTYPE HTML> | |
<head lang='en'> | |
<meta http-equiv=EXPIRES content=0> | |
<meta http-equiv=CONTENT-TYPE content="text/html; charset=UTF-8"> | |
<meta charset='utf-8'> | |
<title>RDS Text Injector</title> | |
<meta name='viewport' content='width=device-width, initial-scale=1.0'> | |
<meta name='apple-mobile-web-app-title' content='CALL SIGN RDS Text Injector'> | |
<meta name='application-name' content='CALL SIGN RDS Text Injector'> | |
<meta name='msapplication-config' content='icons/browserconfig.xml'> | |
<meta name='theme-color' content='#ffffff'> | |
<meta name="description" content="Simple web form used to accept text and send it to the Nautel transmitter RDS encoder."> | |
<link id='stylesheet' rel='stylesheet' href='dist/bonsai.min.css'> | |
<link rel='manifest' href='icons/manifest.json'> | |
<link rel='shortcut icon' href='icons/favicon.ico'> | |
<style> | |
section { | |
margin: 2em 0 4em; | |
} | |
.grid-demo { | |
grid-auto-rows: minmax(50px, auto); | |
} | |
.grid-demo > * { | |
background: var(--primary); | |
min-height: 3rem; | |
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06); | |
border-radius: 4px; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
color: white; | |
text-align: center; | |
} | |
.grid-demo > *:nth-child(even) { | |
background: #3f5aa4; | |
} | |
</style> | |
</head> | |
<body style="--mv:20px; --p:0; --bg:#f5f5f5; --c:#202020"> | |
<div style="--wrapper:1000px; --gutter:3vw;"> | |
<img src="logo.png" alt="CALL SIGN Logo" style="--m:40px auto 0; --d:block"> | |
<h1 class="primary" style="--ta:center">RDS Text Injector</h1> | |
<p style="--ta:center; --maxw:450px; --m:0 auto 3rem"> | |
Fill in the blank or touch a pre-programmed button. | |
</p> | |
EndOfHeader; | |
/* | |
* This section contains the HTML input form that accepts user-entered | |
* text. | |
*/ | |
$input_form =<<<EndOfInputForm | |
<section> | |
<blockquote> | |
<mark> | |
This updates the live radio data broadcast system ('RDBS', or simply 'RDS') | |
text as soon as you press the button or press <Enter>. | |
</mark> | |
</blockquote> | |
</section> | |
<form name="rdsdata_user" action="index.php" method="GET"> | |
<div class="grid" style="--col: 1"> | |
<label>RDS Text | |
<input type="text" name="our_text" maxlength="64" size="64" placeholder="Your meaningful text here (up tp 64 characters)..." onfocus="this.value=''" /> | |
</label> | |
</div> | |
<br /> | |
<input type="submit" value="Tell the world..." /> | |
</form> | |
EndOfInputForm; | |
/* | |
* The header for the "button form" that contains pre-selected text | |
* buttons for quick RDS updates. | |
*/ | |
$button_form_start =<<<EndOfButtonFormStart | |
<form name="rdsdata_generic" action="index.php" method="GET"> | |
<div class="grid" style="--col: 2"> | |
EndOfButtonFormStart; | |
$button_form_end =<<<EndOfButtonFormEnd | |
</div> | |
</form> | |
EndOfButtonFormEnd; | |
/* | |
* The page footer that wraps everything up. | |
*/ | |
$footer =<<<EndOfFooter | |
</div> <!-- style="wrapper:1000px..." --> | |
</body> | |
EndOfFooter; | |
// Make it work on the command line too. | |
if ($argc > 1) | |
parse_str(implode('&', array_slice($argv, 1)), $_GET); | |
// Send the first part of the web page if called interactively | |
// ('artist' is unset). | |
if (!array_key_exists('artist', $_GET)) { echo $header; } | |
// The guts of this script: figure out how we were called, and send | |
// the message text to the RDS encoder (aka Nautel transmitter). | |
if (array_key_exists('artist', $_GET) || | |
array_key_exists('our_text', $_GET)) { | |
// Prolly being called from the Spinitron Metadata Push. | |
if (array_key_exists('artist', $_GET) && | |
$_GET['artist'] != '') { | |
// The bare minumum message text. | |
$message_text = '"' . $_GET['title'] . '" by ' . $_GET['artist']; | |
// DISABLED THIS DUE TO LACK OF SPACE IN RDS (limit is 64 characters) | |
// if (array_key_exists('playlist', $_GET) && | |
// $_GET['playlist'] != '' ) { | |
// // Add the show name (aka playlist name) if it is | |
// // available. | |
// // $message_text = 'Now playing on ' . $_GET['playlist'] . ': ' . $message_text; | |
// $message_text = $_GET['playlist'] . ': ' . $message_text; | |
// } | |
// if (array_key_exists('spinnote', $_GET) && | |
// $_GET['spinnote'] != '') { | |
// // Add the spin note if it is available. | |
// $message_text = $message_text . ' (' . $_GET['spinnote'] . ')'; | |
// } | |
} elseif (array_key_exists('our_text', $_GET) && | |
$_GET['our_text'] != '') { | |
// Prolly being called from above (this script). | |
$message_text = $_GET['our_text']; | |
} else { | |
// Uh. Who knows how we got here... | |
$message_text = ''; | |
} | |
/* | |
* Convert the message text to plain ASCII and send it to the | |
* transmitter. | |
*/ | |
if ($message_text) { | |
// Convert text encoding if needed. Spinitron sends UTF-8. Idk if the RDS encoder expects a | |
// specific encoding or if it is transparent, in which case what car radios expect. | |
$display_text = iconv('UTF-8', 'ASCII//TRANSLIT', $message_text); | |
// Generate the "commands" to send to the RDS encoder. We could also truncate over-long messages | |
// but I think they do no harm. How to terminate the command? CR or LF, or both, what order, how many? | |
$message = "TEXT=$display_text\r\n"; | |
// Make sure strlen() returns the byte length. | |
ini_set('mbstring.func_overload', 0); | |
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); | |
if (socket_connect($socket, $rds_address, $rds_port)) { | |
$sent_bytes = socket_send($socket, $message, strlen($message), MSG_EOF); | |
} | |
} | |
// Show what we sent if this is running on the local network(s). | |
if (($_SERVER['REMOTE_ADDR'] == '127.0.0.1') || | |
($_SERVER['REMOTE_ADDR'] == 'localhost') || | |
(preg_match('/192\.168\..*/', $_SERVER['REMOTE_ADDR']))) { | |
if ($message_text) { | |
echo '<p>Last message sent: "' . $message_text . '"</p>'; | |
} | |
} | |
} | |
/* | |
* Likewise with the main body of the page with the forms, only send | |
* it if interactive ('artist' is unset). | |
*/ | |
if (!array_key_exists('artist', $_GET)) { | |
echo $button_form_start; | |
foreach ($buttons as $button) { | |
$element = '<button name="our_text" type="submit" value="' . $button . '">' . $button . '</button>'; | |
echo $element; | |
} | |
echo $button_form_end; | |
echo '<hr />'; | |
echo $input_form; | |
echo $footer; | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment