View file aio-radio-station-player-1.14/inc/handler.php

File size: 18.2Kb
<?php

	// Include other needed files
	include 'inc/lib/cache.class.php';
	include 'inc/lib/image-resize.class.php';
	if ( is_file('inc/conf/channels.php') ) include 'inc/conf/channels.php';
	if ( !is_array($channels) ) $channels = array();


	// Start few functions and init objects
	header ("Content-Type: application/json");
	$cache = new cache(array('path' => 'tmp/cache/'));


	/* Initial player run, get all channels in nice json object
	============================================================================================== */
	if ( isset( $_GET['c'] ) && $_GET['c'] == 'all' ) {

		if ( is_array($channels[0]) ) {

			// Sort array by name ascending
			foreach ($channels as $key => $row) { $ssby[$key] = $row['name']; } 		## Find common key
			array_multisort($ssby, SORT_ASC, $channels); 								## Sort

			// Output
			$out = array();		## Temp output array
			foreach ($channels as $key => $chn) {

				// Remove sensitive stuff
				unset( $chn['stats'] );
				$out[$key] = $chn;

			}

			$jsondata = json_encode($out);
			echo (( !empty($_GET['callback']) && $settings['api'] == 'true' ) ? "{$_GET['callback']}({$jsondata});" : $jsondata);
			exit;

		} else { // No channels defined

			exitJSON();

		}

	}


	/* URL Parameter C is checked here, if channel doesn't exist, return empty json
	============================================================================================== */
	foreach ( $channels as $key => $channel ) {	if ( $channel['name'] == $_GET['c'] ) break; }	## Find specified channel
	if ( !is_array( $channels[$key] ) ) { echo json_encode( array() ); exit; }					## Specified channel doesn't exist

	// Set few vars before attempting fate :)
	$info 	= array();
	$c 		= $channels[$key];
	$ctime	= ( ( $settings['stats_refresh'] - 1 ) <= 1) ? 10 : ( $settings['stats_refresh']-1 );


	/* Now do the heavy work, use configured method to get stats information
	============================================================================================== */
	switch ($c['stats']['method']) {


		/* Connects to specified stream and opens it as a player, then it reads sent track ID. (NO CURL)
		============================================================================================== */
		case 'direct':

			if ( !$info = $cache->get('stream.' . $key . '.info') ) {

				// Attempt to read few bytes of stream to get current playing track information
				$getinfo = streaminfo( $c['stats']['url'] );

				// Use backup if first failed
				if ( ( empty( $getinfo ) OR $getinfo === false ) AND !empty( $c['fallback'] ) ) {
					$getinfo = streaminfo( $c['fallback'] );
				}


				// Connection failed, log it
				if ( $getinfo === false ) {
					writeLog('player.api', "{$c['name']}: Connection to remote stream {$c['stats']['url']} failed!");
				}

				// Now, result must not be empty or err occured
				preg_match('/' . $settings['track_regex'] . '/', $getinfo, $track);

				$info['artist'] 	= (( empty($track['artist']) ) ? $settings['artist_default'] : trim($track['artist']));
				$info['title'] 		= (( empty($track['title']) ) ? $settings['title_default'] : trim($track['title']));
				$info['image'] 		= getArtwork($track['artist'], $settings);
				$info['status']		= 'no-cache';

				// Cache result
				$cache->set('stream.' . $key . '.info', $info, $ctime);

			} else {

				// Info only
				$info['status']		= 'cached';

			}

			break;



			/* Connect's to shoutcast admin panel and read's XML of a station
			============================================================================================== */
		case 'shoutcast':

			// Check conf first
			if ( empty($c['stats']['url']) OR empty($c['stats']['auth']) ) {

				writeLog('player.api', "{$c['name']}: Invalid configuration! Missing Shoutcast URL or authorization password");
				exitJSON();

			} else if ( !function_exists('curl_version') ) {

				writeLog('player.api', "{$c['name']}: CURL extension is not loaded!");
				exitJSON();

			} else if ( !function_exists('simplexml_load_string') ) {

				writeLog('player.api', "{$c['name']}: SimpleXML extension is not loaded!");
				exitJSON();

			}


			// Check cache
			if ( !$info = $cache->get('stream.' . $key . '.info') ) {

				if ( !$xmlfile = get("{$c['stats']['url']}/admin.cgi?pass={$c['stats']['auth']}&mode=viewxml&sid={$c['stats']['sid']}") ) {

					writeLog('player.api', "{$c['name']}: Connection to Shoutcast server failed!");

				} else {

					$scdata = xml2array($xmlfile, true);

					// Log error if song title is empty
					if ( empty($scdata['songtitle']) ) {
						writeLog('player.api', "{$c['name']}: Unable to get song title, it would seem server response was \"OK\" but result is unknown.");
					}


					// Now, result must not be empty or err occurred
					preg_match('/' . $settings['track_regex'] . '/', $scdata['songtitle'], $track);

					$info['artist'] 	= (( empty($track['artist']) ) ? $settings['artist_default'] : trim($track['artist']));
					$info['title'] 		= (( empty($track['title']) ) ? $settings['title_default'] : trim($track['title']));
					$info['image'] 		= getArtwork($track['artist'], $settings);
					$info['status']		= 'no-cache';

					// Cache result
					$cache->set('stream.' . $key . '.info', $info, $ctime);

				} 

			} else {

				// Info only
				$info['status']		= 'cached';

			}

			break;



			/* Connects to Shoutcast admin panel and reads XML of a station
			============================================================================================== */
		case 'icecast':

			// Check conf first
			if ( empty($c['stats']['url']) OR empty($c['stats']['auth-user']) OR empty($c['stats']['auth-pass']) ) {

				writeLog('player.api', "{$c['name']}: Invalid configuration! Missing Icecast URL, authorization or mount details!");
				exitJSON();

			} else if ( !function_exists('curl_version') ) {

				writeLog('player.api', "{$c['name']}: CURL extension is not loaded!");
				exitJSON();

			} else if ( !function_exists('simplexml_load_string') ) {

				writeLog('player.api', "{$c['name']}: SimpleXML extension is not loaded!");
				exitJSON();

			}


			// Check cache
			if ( !$info = $cache->get('stream.' . $key . '.info') ) {


				// Icecast requires proper HTTP auth, so we provide it!
				if ( !$xmlfile = get("{$c['stats']['url']}/admin/stats", null, "{$c['stats']['auth-user']}:{$c['stats']['auth-pass']}") ) {	

					writeLog('player.api', "{$c['name']}: Connection to Icecast stats server failed!");

				} else if ( preg_match("/You need to authenticate/s", $xmlfile) ) {

					writeLog('player.api', "{$c['name']}: Unable to authorize, login failed!");

				} else { // Now we should have details, attempt to use them.

					$ice = array();
					$icedata = xml2array($xmlfile, true);

					// Multiple mount points
					if ( is_array($icedata['source'][0]) ) {

						foreach ( $icedata['source'] as $mount ) {

							// Parse mount name				
							$mountName = $mount['@attributes']['mount'];
							unset($mount['@attributes']);

							// Make nice array with mount name as key (for fall-back <3)
							$ice[$mountName] = $mount;

						}

						// Single mount point
					} else {

						// Get mount name
						$mountName = $icedata['source']['@attributes']['mount'];
						unset($icedata['source']['@attributes']);

						// Set mount info
						$ice[$mountName] = $icedata['source'];

					}


					// Check if specified mount or fall-back mount exist
					if ( !is_array($ice[$c['stats']['mount']]) AND !is_array($ice[$c['stats']['fallback']]) ) {

						writeLog('player.api', "{$c['name']}: Specified mount and fall-back mount were not found!");

					} else {


						// Attempt to use main mount, else use backup one
						if ( !empty($ice[$c['stats']['mount']]['title']) OR !empty($ice[$c['stats']['mount']]['artist']) ) {

							$icetrack = ((empty($ice[$c['stats']['mount']]['artist'])) ? $ice[$c['stats']['mount']]['title'] : $ice[$c['stats']['mount']]['artist'] . ' - ' . $ice[$c['stats']['mount']]['title']);

						} else { // Backup mount

							$icetrack = ((empty($ice[$c['stats']['fallback']]['artist'])) ? $ice[$c['stats']['fallback']]['title'] : $ice[$c['stats']['fallback']]['artist'] . ' - ' . $ice[$c['stats']['fallback']]['title']);

						}


						// Now, after so much checks and stuff, do track match
						preg_match('/' . $settings['track_regex'] . '/', $icetrack, $track);

						$info['artist'] 	= (( empty($track['artist']) ) ? $settings['artist_default'] : trim($track['artist']));
						$info['title'] 		= (( empty($track['title']) ) ? $settings['title_default'] : trim($track['title']));
						$info['image'] 		= getArtwork($track['artist'], $settings);
						$info['status']		= 'no-cache';

						// Cache
						$cache->set('stream.' . $key . '.info', $info, $ctime);

					}

				}


			} else {

				// Info only
				$info['status']		= 'cached';

			}

			break;



			/* Uses MySQLi extension to connect to the specified stream. This may be the most reliable option
			============================================================================================== */
		case 'sam':

			// Check conf first
			if ( empty($c['stats']['host']) OR empty($c['stats']['auth-user']) OR empty($c['stats']['auth-pass']) OR empty($c['stats']['db']) ) {

				writeLog('player.api', "{$c['name']}: Invalid configuration! Missing all required information to access SAM Broadcaster's database.");
				exitJSON();

			} else if ( !class_exists('mysqli') ) {

				writeLog('player.api', "{$c['name']}: MySQLi extension is not loaded, unable to connect to database!");
				exitJSON();

			}


			// Check for cache
			if ( !$info = $cache->get('stream.' . $key . '.info') ) {

				$db = new mysqli($c['stats']['host'], $c['stats']['auth-user'], $c['stats']['auth-pass'], $c['stats']['db']);

				if ( $db->connect_errno > 0 ) { // Failed to connect

					writeLog('player.api', "{$c['name']}: Database connection failed, MySQL returned: {$db->connect_error}");
					exitJSON();

				} else { // Connected!

					// Fetch SAM history
					$sam = mysqli_fetch_assoc($db->query("
						SELECT songID, artist, title, date_played, duration
						FROM {$c['stats']['db']}.historylist
						ORDER BY `historylist`.`date_played` DESC LIMIT 0 , 1
						"));

					// Check if query failed?
					if ( $db->error ) { // Failed to connect

						writeLog('player.api', "{$c['name']}: SAM Database query failed with error: {$db->error}");
						exitJSON();

					} else {


						// Sometimes SAM ID3 tags are incorrect
						if ( !empty($sam['artist']) AND empty($sam['title']) ) { 

							preg_match('/' . $settings['track_regex'] . '/', $sam['artist'], $track);

						} else if ( empty($sam['artist']) AND !empty($sam['title']) ) {

							preg_match('/' . $settings['track_regex'] . '/', $sam['title'], $track);

						} else {

							$track = $sam;

						}


						// Now, after so much checks and stuff, do track match
						$info['artist'] 	= (( empty($track['artist']) ) ? $settings['artist_default'] : trim($track['artist']));
						$info['title'] 		= (( empty($track['title']) ) ? $settings['title_default'] : trim($track['title']));
						$info['image'] 		= getArtwork($track['artist'], $settings);
						$info['status']		= 'no-cache';

						// Cache
						$cache->set('stream.' . $key . '.info', $info, $ctime);

					}
				}

			} else {

				// Info only
				$info['status']		= 'cached';

			}

			break;



			/* This will connect to Centova-cast API which is usually located on streaming provider. Requires enabled track info widget
			============================================================================================== */
		case 'centovacast':

			// Check config first
			if ( empty($c['stats']['url']) OR empty($c['stats']['user']) ) {

				writeLog('player.api', "{$c['name']}: Invalid configuration! Missing Centova-cast URL or username!");
				exitJSON();				

			} else if ( !function_exists('curl_version') ) {

				writeLog('player.api', "{$c['name']}: CURL extension is not loaded!");
				exitJSON();

			}


			// Check for cache
			if ( !$info = $cache->get('stream.' . $key . '.info') ) {

				if ( !$centova = get("{$c['stats']['url']}/external/rpc.php?m=streaminfo.get&username={$c['stats']['user']}&rid={$c['stats']['user']}&charset=utf8") ) {

					writeLog('player.api', "{$c['name']}: Connection to Centova-cast RPC API failed!");

				} else {

					$centova = json_decode($centova, true);
					if ( !empty($centova['error']) ) {

						writeLog('player.api', "{$c['name']}: Centova-cast returned error: {$centova['error']}!");

					} else {

						$track = $centova['data'][0]['track'];

						// Now, after so much checks and stuff, do track match
						$info['artist'] 	= (( empty($track['artist']) ) ? $settings['artist_default'] : trim($track['artist']));
						$info['title'] 		= (( empty($track['title']) ) ? $settings['title_default'] : trim($track['title']));
						$info['image'] 		= getArtwork($track['artist'], $settings);
						$info['status']		= 'no-cache';

						// Cache
						$cache->set('stream.' . $key . '.info', $info, $ctime);

					}

				}

			} else {

				// Info only
				$info['status']		= 'cached';

			}

			break;



			/* Radionomy is Shoutcast provider who has their own API. Since company purchased Shoutcast this is cool.
			============================================================================================== */
		case 'radionomy':

			// Check config first
			if ( empty($c['stats']['user-id']) OR empty($c['stats']['api-key']) ) {

				writeLog('player.api', "{$c['name']}: Invalid configuration, missing Radionomy Radio ID or API key!");
				exitJSON();	

			} else if ( !function_exists('curl_version') ) {

				writeLog('player.api', "{$c['name']}: CURL extension is not loaded!");
				exitJSON();

			} else if ( !function_exists('simplexml_load_string') ) {

				writeLog('player.api', "{$c['name']}: SimpleXML extension is not loaded!");
				exitJSON();

			}


			// Check for cache
			if ( !$info = $cache->get('stream.' . $key . '.info') ) {

				// Connect to API
				if ( !$onmy = get("http://api.radionomy.com/currentsong.cfm?radiouid={$c['stats']['user-id']}&apikey={$c['stats']['api-key']}&callmeback=yes&type=xml&cover=yes&previous=no") ) {	

					writeLog('player.api', "{$c['name']}: Connection to Radionomy API failed!");

				} else {

					$radioxml = xml2array($onmy, true);

					if ( $radioxml === false || !is_array($radioxml) ) {

						writeLog('player.api', "{$c['name']}: Unable to decode Radionomy response!");

					} else {


						$track = $radioxml['track'];

						// Now, after so much checks and stuff, do track match
						$info['artist'] 	= (( empty($track['artists']) ) ? $settings['artist_default'] : trim($track['artists']));
						$info['title'] 		= (( empty($track['title']) ) ? $settings['title_default'] : trim($track['title']));
						$info['image'] 		= getArtwork( 
							$track['artists'], 
							$settings + array( 'radionomy' => $c['stats']['use-cover'], 'radionomy-cover' => $track['cover'] )
						); ## Special handler for artwork
						$info['status']		= 'no-cache';

						// Cache
						$cache->set('stream.' . $key . '.info', $info, floor($radioxml['track']['callmeback'] / 1000));


					}					

				}


			} else {

				// Info only
				$info['status']		= 'cached';

			}

			break;


			/* Custom (URL) is option that parses ONLY artist - title from external text, php or any other file
			============================================================================================== */
		case 'custom':

			// Check config first
			if ( empty($c['stats']['url']) ) {

				writeLog('player.api', "{$c['name']}: Invalid configuration! Missing Custom URL!");
				exitJSON();	

			} else if ( !function_exists('curl_version') ) {

				writeLog('player.api', "{$c['name']}: CURL extension is not loaded!");
				exitJSON();

			}


			// Check for cache
			if ( !$info = $cache->get('stream.' . $key . '.info') ) {

				// Connect to API
				if ( !$txt = get($c['stats']['url'], false, ((!empty($c['stats']['user']) && !empty($c['stats']['pass'])) ? "{$c['stats']['user']}:{$c['stats']['pass']}" : '')) ) {	

					writeLog('player.api', "{$c['name']}: Connection to Custom URL \"{$c['stats']['url']}\" failed!");

				} else {

					if ( $txt === false || empty($txt) ) {

						writeLog('player.api', "{$c['name']}: Connection to Custom URL was successful but there was no data returned!");

					} else {

						// Now, after so much checks and stuff, do track match
						preg_match('/' . $settings['track_regex'] . '/', $txt, $track);

						$info['artist'] 	= (( empty($track['artist']) ) ? $settings['artist_default'] : trim($track['artist']));
						$info['title'] 		= (( empty($track['title']) ) ? $settings['title_default'] : trim($track['title']));
						$info['image'] 		= getArtwork($track['artist'], $settings);
						$info['status']		= 'no-cache';

						// Cache
						$cache->set('stream.' . $key . '.info', $info, $ctime);

					}					

				}


			} else {

				// Info only
				$info['status']		= 'cached';

			}

			break;



			/* Disabled, simply return defaults
			============================================================================================== */
		case 'disabled':

			// Disabled or ERROR occurred
			$jsondata = json_encode( array(
				'artist'			=>	$settings['artist_default'],
				'title'				=>	$settings['title_default'],
				'image'				=>	getArtwork(null),
				'status'			=>	'disabled'
			) );

			echo (( !empty($_GET['callback']) && $settings['api'] == 'true' ) ? "{$_GET['callback']}({$jsondata});" : $jsondata);
			exit();
			break;


			/* This should not happen, at all.
			============================================================================================== */
		default:
			writeLog('player.api', "{$c['name']}: Invalid method! This is truly fancy error which should never happen!");
			die('AIO - Radio Station Player API');
			break;

	}


	/* Heavy work done, handle data returned from API's and show it in JSON encoded format
	============================================================================================== */
	if ( $info !== false ) {

		// Encode gathered information
		$jsondata = json_encode( $info );

	} else { 

		// Create simple & empty JSON array
		$jsondata = json_encode( array() );

	}


	// Show output (if this is JSONP request, adapt response to its requirements
	echo (( !empty($_GET['callback']) && $settings['api'] == 'true' ) ? "{$_GET['callback']}({$jsondata});" : $jsondata);		


	// If cache is initiated, close & save its status.
	if ( is_object($cache) ) { $cache->quit(); }

?>