<?php
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * ZINA (Zina is not Andromeda) v.0.9.12
 *
 * Zina is a graphical interface to your MP3 collection, a personal
 * jukebox, an MP3 streamer.  It is similar to Andromeda, but
 * released under the GNU GPL.
 *
 * http://www.pancake.org/zina.html
 * Author: Ryan Lathouwers <ryanlath@pacbell.net>
 * License: GNU GPL <http://www.gnu.org/copyleft/gpl.html>
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * DEFAULT SETTINGS for Zina
 * You can change settings here or put your changes in 'zina.ini.php'
 * so they won't get overridden when upgrading...
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
$page_width = 725; # width of the page (pixel or use %, i.e. "100%")
$ap_columns = 4; # number of columns on the artist/main page
$ap_artist_truncate = 26; # length to truncate artist on artist page and add "..."
$mp3_file_info = true; # get filesize, length, kpbs from mp3 file? slower, but cooler
$id3_song_titles = false; # use id3 song titles (if they exist) instead of formatting file names
$honor_custom = true; # have random stuff use custom title selections
$local_is_admin = true; # if running on local machine is local user automatically admin?
$main_blurb_file = "index.txt"; # file that the main page will use for text
$ext_music = "mp3|ogg|wav|wma"; # extensions of music files to display
$ext_graphic = "jpg|gif|png|jpeg"; # extensions of graphic files to use
$ext_text = "txt|nfo"; # extensions of text files to use
$root_dir = "."; # the root dir of zina script
$mp3_dir = "."; # root dir of the MP3s relative to this file (e.g. ../mp3s or mp3s )
$image_dir = "$root_dir/_images/";  # dir where zina icons are stored
$zina_dir = "$root_dir/_zina"; # dir where custom playlists and cache kept
$cache = true; # use cache? $zina_dir must be writable by webserver user
$cache_limit = 7; # days to keep cache.
$file_rt_cache = "$zina_dir/_random_title_cache.dat";
$file_rs_cache = "$zina_dir/_random_song_cache.dat";
$skip_dir = "_"; # dirs in base dir that start with this are not "artists"
$amg = true; # show AMG search box on Albums page?
$mp3info_faster = true; # get mp3 info faster / false is more accurate w/ some VBR files
$allow_download = true; # show download icon
$low_quality = false; # look for second mp3 that has the following suffix:
$low_quality_suffix = ".lofi"; # suffix for second mp3 name[suffix].ext e.g. song.lofi.mp3
$display_album_images = true; # display album covers on artist page (2nd level)?
$session_playlist = true; # allow users to have a "session" playlist
$sp_length = 5000; # max length of "session" playlist
$random = true; # show the random playlist generator thing
$r_opts = array(1,5,10,25,50,0); # random number options (0 = all)
$r_opts_default = 25; # default random number "selected"
$r_default = "Songs"; # default random selected ("Albums" or "Songs")
$play_selected = true; # show option to play "selected"
$local_path = true; # use local paths when running zina locally

/*
	Download Selected Files as one compressed file
	Requires an external cmd line compression program like zip or gzip
		or php zlib module 
	For internal: set $cmp_selected = true; & $cmp_int = true;
	For external: set $cmp_selected = true; & $cmp_int = false; &
		make sure to look at othr $cmp_ settings below
	NOTE: May put a strain on your server
*/
$cmp_selected = false; # enable compression option 
$cmp_int = false; #enable internal compression via php zlib module
$cmp_pgm = "C:\cygwin\bin\zip"; # compression program, might need full path
$cmp_ext = "zip"; # compression extention. (e.g. zip, gz, tar.gz)
$cmp_set = "-0 -j -"; # compression program options
$cmp_mime = "application/zip"; # compress mime type

/*
	Automatic Down/Re-Sampling
	WARNING: using this will most like bring your server to its knees.
	But if you want to use it anyway, you will need an external
	encoder like LAME <http://www.mp3dev.org/mp3/> which can take input from
	stdin and output to stdout.
	$mp3enc = your external encoder, might need full path e.g. "/usr/bin/lame"
	$mp3enc_settings = your encoder settings, default downsamples to 56kbs 22k
	$low_quality must be set to 'true' above
*/
$resample = false;
$mp3enc = "lame";
$mp3enc_settings = "--mp3input -f -b56 --lowpass 12.0 --resample 22.05";

/*
 * POSTNUKE/PHPNUKE Module support 
 * See install instructions: <http://www.pancake.org/zina.html#install>
 */
$postnuke = false; # use as postnuke module?
$phpnuke = false;  # use as phpnuke module?
$pn_theme = false; # (post/php)nuke theme support?
$pn_users_only = false; # give access only to logged in postnuke users

/*
 THESE SHOULD BE CHANGED FROM DEFAULT SETTINGS (or set $login_enabled = false;)
 BY DEFAULT: username is "admin" & password is "password"
 if you don't know the crypt program, after you login in to Zina, go here:
 oldkpindex.php?l=5
*/
$login_enabled = true;    # can admin login?
$user_admin = "admin";    # admin user name
$password = "fuNA9szthqt2k"; # crypted admin password
$salt = "fu";             # salt for crypted password

#User settings that override defaults can be put in this file:
@include('zina.ini.php');


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * GLOBAL VARS (not user settings)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
$this_pgm = "Zina";
$version = "0.9.12";
$space_replace = "%20";
$row_colors = array('row1','row2');
$this_page = $HTTP_SERVER_VARS["PHP_SELF"] . "?";
$url_base = "http://" . $HTTP_SERVER_VARS["HTTP_HOST"] . dirname($this_page) . "/$mp3_dir";
$img_add = "<img src=\"${image_dir}add.gif\" border=0 alt=\"Add To:\" title=\"Add To:\">";
$img_delete = "<img src=\"${image_dir}delete.gif\" border=0 alt=\"Delete\" title=\"Delete\">";
$img_download = "<img src=\"${image_dir}download.gif\" border=0 alt=\"Download\" title=\"Download\">";
$img_home = "<img src=\"${image_dir}home.gif\" border=0 alt=\"Home\" title=\"Home\">";
$img_more = "<img src=\"${image_dir}more.gif\" border=0 alt=\"More\" title=\"More\">";
$img_play = "<img src=\"${image_dir}play.gif\" border=0 alt=\"Play\" title=\"Play\">";
$img_play_lofi = "<img src=\"${image_dir}play_lofi.gif\" border=0 alt=\"Play Low Fidelity\" title=\"Play Low Fidelity\">";
$img_playlist = "<img src=\"${image_dir}playlist.gif\" border=0 alt=\"Playlists\" title=\"Playlists\">";
$img_login = "<img src=\"${image_dir}login.gif\" border=0 alt=\"Login\" title=\"Login\">";
$url_main = "<a href=\"$this_page\">$img_home</a>";
$url_login = "";
$z_admin = false;
$artist = isset($HTTP_GET_VARS["a"]) ? $HTTP_GET_VARS["a"] : null;
$title  = isset($HTTP_GET_VARS["t"]) ? $HTTP_GET_VARS["t"] : null;
$song   = isset($HTTP_GET_VARS["s"]) ? $HTTP_GET_VARS["s"] : null;
$level  = isset($HTTP_GET_VARS["l"]) ? $HTTP_GET_VARS["l"] : null;
$m3u    = isset($HTTP_GET_VARS["m"]) ? $HTTP_GET_VARS["m"] : null;
$playlist = isset($HTTP_POST_VARS["playlist"]) ? $HTTP_POST_VARS["playlist"] : (isset($HTTP_GET_VARS["pl"]) ? $HTTP_GET_VARS["pl"] : null);
$z_module = false;

if ($session_playlist) { session_start(); }
if ($postnuke || $phpnuke) { $z_module = true; }

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Postnuke/PHPnuke support
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

if ($z_module) {
	if ($phpnuke) {
		if (!eregi("modules.php", $HTTP_SERVER_VARS["PHP_SELF"]) ) {
			die ("You can't access this file directly...");
 		}
		require_once("mainfile.php");
		if ($pn_users_only) {
			if (!is_user($user)) { Header("Location: modules.php?name=Your_Account"); }
		}
		if (is_admin($admin)) { $z_admin = true; }
	}

	if ($postnuke) {
		if (!defined("LOADED_AS_MODULE")) {
			die ("You can't access this file directly...");
		}
		if ($pn_users_only) {
			if(!pnUserLoggedIn()) { pnRedirect("user.php"); } 
		}
		if (pnSecAuthAction(0, "Zina::", "::", ACCESS_ADMIN)) { $z_admin = true; }
	}

	$this_name = $GLOBALS['name'];
	$this_page = "${this_page}name=$this_name&op=modload&file=index&";
	$url_main = "<a href=\"$this_page\">$img_home</a>";
	$login_enabled = false;
	#HACK -> find out why \/ is appearing...
	$url_base = str_replace("\/","/",$url_base);
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * MAIN
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
if (strstr($artist,"..")) { pageError("'..' Not Allowed in Artist Name.");}


#use local path instead of url for playlists
if ($HTTP_SERVER_VARS["SERVER_ADDR"] == $HTTP_SERVER_VARS["REMOTE_ADDR"]) {
	if ($local_path) {
		$url_base = $HTTP_SERVER_VARS["DOCUMENT_ROOT"];
	
		if (substr($url_base, strlen($url_base) - 1,1) == '/') {
			$url_base = substr($url_base,0,-1);
		}
		$url_base .= "/$mp3_dir";
		$space_replace = " ";
		$z_admin = ($z_admin | $local_is_admin);
	}
}

if ($login_enabled) {
	session_start();
	if (isset($HTTP_SESSION_VARS['admin'])) {
		if ($HTTP_SESSION_VARS['admin'] == true) {
			$z_admin = true;
		}
	}
	if (!$z_admin) {
		$url_login = "<a href=\"${this_page}l=9\">$img_login</a>";
	}
}

switch ($level) {

	case "1" : # ALBUM PAGE
		pageTitles($artist);
		break;

	case "2" : # SONGS (TITLE DETAIL) PAGE
		pageSongs($artist, $title, $mp3_file_info);
		break ;

	case "3" : # CUSTOM PLAYLISTS
		$songs = isset($HTTP_POST_VARS["mp3s"]) ? $HTTP_POST_VARS["mp3s"] : null;
		$lps = isset($HTTP_POST_VARS["lps"]) ? $HTTP_POST_VARS["lps"] : null;
		$level_prev = isset($HTTP_POST_VARS["level_prev"]) ? $HTTP_POST_VARS["level_prev"] : null;
		$mode = isset($HTTP_GET_VARS["z"]) ? $HTTP_GET_VARS["z"] : null;

		if ($z_admin || $session_playlist) {
			switch ($mode) {
				Case "1" : # View
					pageViewEditPlaylist ($playlist, $artist, $title, $level_prev);
				break;
			
				Case "2" : # Add New Playlist
					if (!$z_admin) { $playlist = "zina_session_playlist"; }
					if ($playlist == "new_zina_list") {
						pageNewPlaylist($artist, $title, $level_prev, $songs, $lps);
					} else {
						$filename = "$zina_dir/_zina_$playlist.m3u";
						if (!empty($lps)) {
							writeCustomPlaylist($lps, "a", $filename);
						} else {
							foreach($songs as $key => $song) {
								$songs[$key] = "$artist/$title/$song";
							}
							writeCustomPlaylist($songs, "a", $filename);
						}

						if (empty($artist)) {
							$redirect = "Location: ${this_page}l=3&z=1&pl=" . urlencode($playlist);
						} else {
							$redirect = "Location: ${this_page}l=$level_prev&a=" . urlencode($artist) . "&t=" . urlencode($title);
						}
						header($redirect);
						exit;
					}
				break;

				Case "3" : # Add Custom Title
					if ($z_admin) {
						writeCustomPlaylist($songs, "w", "$zina_dir/$artist - $title.m3u", true);
						header("Location: ${this_page}l=2&a=" . urlencode($artist) . "&t=" . urlencode($title));
						exit;
					}
				break;
			
				Case "4" : # Update Playlist
					#TODO: has to be a moe betta way to reorder playlists =(
					$order = isset($HTTP_POST_VARS["order"]) ? $HTTP_POST_VARS["order"] : null;
					$new = null;
					$count = sizeof($songs);
					for ($i = 0; $i < $count; $i++){
						if ($order[$i] == 0) continue; # 0 == delete
						$new[$order[$i]][] = $songs[$i];
					}
					ksort($new);		
					$songs = null;

					foreach($new as $neworder) {
						foreach($neworder as $song) {
							$songs[] = urldecode($song);
						}
					}
			
					if (!$z_admin) { $playlist = "zina_session_playlist"; }
					writeCustomPlaylist($songs, "w", "$zina_dir/_zina_$playlist.m3u");
					pageViewEditPlaylist($playlist, $artist, $title, $level_prev);
				break;
			
				Case "5" : # Delete Playlist
					if ($z_admin) {
						deleteCustomPlaylist($playlist);
						header("Location: ${this_page}l=3&z=1&a=" . urlencode($artist) . "&t=" . urlencode($title));
						exit;
					}
				break;
			
				default :
					pageError("Not a valid selection.");
			}
		} else { # NOT ADMIN
			if ($mode == "1") {
				pageViewEditPlaylist ($playlist, $artist, $title, $level_prev);
			} else {
				pageError("Not a valid selection.");
			}
		}
		break;

	Case "4" : # SEARCH
		$search_type = $HTTP_POST_VARS["type"];
		$search_term = $HTTP_POST_VARS["search"];
		pageSearchResults($search_type, $search_term);
		break;

	Case "5" : # CRYPTOMATIC
		if ($z_admin) {
			$text = isset($HTTP_POST_VARS["text"]) ? $HTTP_POST_VARS["text"] : null;
			$salt = isset($HTTP_POST_VARS["salt"]) ? $HTTP_POST_VARS["salt"] : null;
			pageCryptomatic($text, $salt);
		}
		break;

	Case "6" : # Return resampled MP3 (see settings)
		#Use: oldkpindex.php?l=6&s=FILE
		if ($resample) { sendResampledMP3($song); }
		break;

	
	Case "8" : # RETURN PLAYLISTS
		$num = isset($HTTP_POST_VARS["n"]) ? $HTTP_POST_VARS["n"] : null;
		$lofi = isset($HTTP_GET_VARS["lf"]) ? $HTTP_GET_VARS["lf"] : false;
		$songs = isset($HTTP_POST_VARS["mp3s"]) ? $HTTP_POST_VARS["mp3s"] : null;
		if (!isset($m3u)) {$m3u = $HTTP_POST_VARS["m"]; }

		$use_cache = array(0 => '', 1 => 'Cached');
		$playfun = array(
				'sendTitlePlaylist($artist, $title, false, $lofi);', # 0
				'sendSongPlaylist($artist, $title, $song);',         # 1
				'sendTitlePlaylist($artist, $title, true, $lofi);',  # 2
				'sendCustomPlaylist($playlist, $lofi);',             # 3
				'sendRandom'.$use_cache[$cache].'($num,"t",$lofi);', # 4
				'sendRandom'.$use_cache[$cache].'($num,"s",$lofi);', # 5
				'sendSongPlaylistResampled($artist, $title, $song);',# 6
				'sendSelectedSongs($artist, $title, $songs, $lofi);',# 7
				'sendSelectedCompressed($artist, $title, $songs, "$artist - $title", $lofi);' # 8
			);
		eval($playfun[$m3u]);
		break;

	Case "9": # LOGIN
		if ($login_enabled) {
			$un = isset($HTTP_POST_VARS["un"]) ? $HTTP_POST_VARS["un"] : null;
			$up = isset($HTTP_POST_VARS["up"]) ? $HTTP_POST_VARS["up"] : null;
			$e = isset($HTTP_GET_VARS["e"]) ? $HTTP_GET_VARS["e"] : null;

			if (empty($up) || !empty($e)) {
				pageLogin($un, $e);
			} else {
				Login($un, $up);
			}
		}
		break;

	default : # MAIN PAGE
		pageArtists($mp3_dir,$ap_columns,$ap_artist_truncate);
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * PAGE FUNCTIONS (GUI/display stuff)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

function pageHeader($title, $left="&nbsp;", $right="&nbsp;", $center="&nbsp;"){
	global $this_pgm, $page_width, $url_login, $root_dir, $z_module, $postnuke, $phpnuke, $pn_theme;

	$css = "<link rel=\"stylesheet\" href=\"$root_dir/index.css\" type=\"text/css\">";
	$submit = "<script>function SubmitForm(form, a) { document.forms[form].action = document.forms[form].action + a; document.pf.submit(); }</script>";

	if ($z_module) {
		include("header.php");
		if ($pn_theme) {
			global $bgcolor1, $bgcolor4;
			$bgc02 = ($postnuke) ? $bgcolor4 : $bgcolor2;
			echo "<style type=\"text/css\">.row1 { background-color:$bgcolor1; }" .
				".row2 { background-color:$bgc02; }</style>";
		} else {
			echo $css;
		}
		echo "<link rel=\"stylesheet\" href=\"$root_dir/common.css\" type=\"text/css\">$submit";
		if ($pn_theme) { OpenTable(); }
	} else {
		echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">" .
			"<html><head><title>$this_pgm: " . htmlentities($title) . "</title>" .
			"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" .
			"$css<link rel=\"stylesheet\" href=\"$root_dir/common.css\" type=\"text/css\">" .
			"$submit</head><body>";
	}

	echo "<table border=0 width=\"$page_width\">" .
		"<tr><td colspan=3><p class=title><b>" . htmlentities($title) . "</b></td></tr>" .
		"<tr><td valign=top width=\"34%\" nowrap><small>$left</small></td>" .
		"<td align=center valign=top width=\"33%\" nowrap>$center</td>" .
		"<td align=right valign=top width=\"33%\" nowrap>$right</td></tr></table>";
}


function pageFooter() {
	global $page_width, $this_pgm, $version, $z_module, $pn_theme;
	
	echo "<table border=0 width=\"$page_width\"><tr><td align=center><div class=footer><a class=foot title=\"$this_pgm\" href=\"http://www.pancake.org/zina.html\">$this_pgm v.$version</a></div></td></tr></table>";

	if ($z_module) {
		if ($pn_theme) { CloseTable(); }
		include("footer.php");
	} else {
		echo "</body></html>";
	}
}


function pageArtists ($dir_name, $cols, $artist_length) {
	global $page_width, $this_page, $url_login, $main_blurb_file;

	if (file_exists("$dir_name/$main_blurb_file")) {
		$blurb = implode("", file("$dir_name/$main_blurb_file"));
	}

	$artists = getArtists($dir_name);
	$col_width = round(100 / $cols);
	#TODO: THIS fails on cols = 4 and 5-6 artists...(prob more)
	$new_col = ceil(sizeof($artists)/$cols);
	$row = 0;

	$play_form =  "<form name=pf method=POST action=\"${this_page}l=3\" class=small>$url_login" .
		htmlPlaylistFormElements(0,"","l x p q v") . "</form>";

	pageHeader("Artists",$play_form,htmlRandom(),htmlSearchForm());

	echo "<table border=0 cellpadding=2 cellspacing=5 width=\"$page_width\">";

	if (!empty($blurb)) { echo "<tr><td colspan=$cols>" . nl2br($blurb) . "</td></tr>"; }

	echo "<tr>";

	natcasesort($artists);
	foreach($artists as $artist) {
		if ($row == 0) { echo "<td class=artistcols valign=top nowrap width=\"$col_width%\">"; }
		echo "<a class=artist href=\"${this_page}l=1&a=" . urlencode($artist) . "\">" .
			strTruncate(htmlentities($artist),"...",$artist_length) . "</a><br>";

		$row = ++$row % $new_col;
		if ($row == 0) { echo "</td>"; }
	}
	echo "</tr></table>";
	pageFooter();
}


function pageTitles($artist) {
	global $this_page, $url_main, $page_width, $img_more, $img_play, $z_admin,
		$amg, $low_quality, $img_play_lofi, $session_playlist;

	if (empty($artist)) pageError("bad option");

	$array = getTitles($artist);
	$titles = $array['titles'];
	$covers_found = $array['covers'];
	$artist_blurb = $array['blurb'];
	$artist_pic = $array['artist_image'];
	#TODO: make this a global options?
	$cols = 3;
	$wt = 0;
	$col_width = round(100 / $cols);
	$title_length = 23;
	if ($low_quality) { $title_length=21; }

	pageHeader($artist, $url_main, htmlRandom(),htmlSearchForm());

	echo "<table border=0 cellspacing=5 width=\"$page_width\">" .
			"<tr><td width=\"" . ($col_width * ($cols - 1)) . "%\" colspan=" . ($cols - 1) . " valign=top>";

	if (!empty($artist_pic)) { echo "<img border=0 class=artist-image align=left src=\"$artist_pic\">"; }
	if (!empty($artist_blurb)) { echo "<p class=artist-blurb>" . nl2br($artist_blurb) . "</p>"; }

	echo "</td><td nowrap valign=top width=\"$col_width%\"><p><b>Albums</b></p>" .
			"<form name=pf method=POST action=\"${this_page}a=" . urlencode($artist) . "\">";

	if (!empty($titles)) {
		foreach($titles as $title => $cover) {
			$url_tmp = "<a href=\"${this_page}a=" . urlencode($artist) . "&t=" . urlencode($title) ;
			echo "$url_tmp&l=8&m=0\">$img_play</a> &nbsp;";
			if ($low_quality) {
				echo "$url_tmp&l=8&m=0&lf=true\">$img_play_lofi</a> &nbsp;";
			}

			echo "$url_tmp&l=2\">$img_more</a> &nbsp; ";
			if ($z_admin || $session_playlist) {
				echo "<input type=checkbox name=lps[] value=\"" . urlencode($artist) . "/" . urlencode($title) . "/.lp\"> $url_tmp&l=2\"> ";
			}
			echo "$url_tmp&l=2\">" . strTruncate(htmlentities($title),"...",$title_length) . "</a><br>";
		}
		echo "<p><br>" . htmlPlaylistFormElements(1) . "</form>";
	}
	if ($amg) { echo "<p><br>" . htmlAMG(htmlentities($artist), "AMG"); }

	echo "</td></tr>";

	if ($covers_found > 0) {
		echo "<tr><td colspan=$cols><p><br><b>Albums</b></td></tr>";

		foreach($titles as $title => $cover) {
			if ($wt==0) { echo "<tr>"; }

			if (!empty($cover)) {
				echo "<td width=\"$col_width\">" .
					"<a href=\"${this_page}l=2&a=" . urlencode($artist) . "&t=" . urlencode($title) . "\">" .
					"<img src=\"$cover\" border=0 title=\"" . htmlentities($title) . "\"></a></td>";
			} else {
				$wt--;
			}

			if ($wt==$cols) { echo "</tr>"; }
			$wt = ++$wt % $cols;
		}
		if ($wt <> 0 ) {
			for ($i = $wt; $i < $cols; $i++) {
				echo "<td width=\"$col_width%\">&nbsp;</td>";
			}
		}
		echo "</tr>";
	}
	echo "</table>";

	pageFooter();
}


function pageSongs($artist, $title, $mp3_file_info) {
	global $url_base, $url_main, $this_page, $page_width, $img_play,
		$img_play_lofi, $img_download, $row_colors, $zina_dir, $z_admin, $cache,
		$id3_song_titles, $mp3_dir, $mp3info_faster, $allow_download,
		$resample, $low_quality, $low_quality_suffix, $ext_music, $session_playlist,
		$play_selected, $cmp_selected;

	$array = getSongs($artist,$title,true,false,false,$low_quality);
	$songs = $array['songs'];
	$cover = $array['cover'];

	$title_blurb = null;
	$playlist = 0;
	$playlist_file = "$zina_dir/$artist - $title.m3u";
	$title_file = "$mp3_dir/$artist/$title/$title.txt";
	$url_begin = "<a href=\"${this_page}a=" . urlencode($artist) . "&t=" . urlencode($title);
	$url_play = "$url_begin&l=8&m=0\">";
	$html_title = htmlentities($title);
	$check_boxes = false;

	if (($z_admin && $cache) || $session_playlist || $play_selected || $cmp_selected) {
		$check_boxes = true;
	}

	if (file_exists($title_file)) {
		$title_blurb = implode("", file($title_file));
	}

	if (file_exists($playlist_file)) {
		$playlist = 1;
		$playlist_mod = file($playlist_file);
		foreach ($playlist_mod as $key => $value) {
			$playlist_mod[$key] = rtrim($value);
		}
	}

	pageHeader("$artist: $title", "$url_main &nbsp;$url_begin&l=1\">" . htmlentities($artist) . "</a>",htmlRandom(),htmlSearchForm());

	echo "<table cellspacing=5 border=0 width=\"$page_width\"><tr><td valign=top>";

	if (!empty($cover)) {
		echo "<p>$url_play<img class=album-image src=\"$cover\" title=\"Play $html_title\" align=left border=0></a>";
	}

	echo "$url_play$img_play</a> &nbsp;";

	if ($low_quality) {
		echo "$url_begin&l=8&m=0&lf=true\">$img_play_lofi</a> &nbsp; ";
	}
	echo "${url_play}Play <i>$html_title</i></a><br><br>";

	if ($playlist) {
		echo "$url_begin&l=8&m=2\">$img_play</a> &nbsp;";
		if ($low_quality) {
			echo "$url_begin&l=8&m=2&lf=true\">$img_play_lofi</a> &nbsp; ";
		}
		echo "$url_begin&l=8&m=2\">Play <i>$html_title</i> (Custom)</a><br><br>";
	}

	if (!empty($title_blurb)) { echo nl2br($title_blurb); }

	echo "</td></tr>";

	if (sizeof($songs) > 0) {
		$i = 0;
		echo "<tr><td><p><b>Songs</b></td></tr><tr><td>" .
			"<form name=pf method=POST action=\"${this_page}a=" . urlencode($artist) . "&t=" . urlencode($title) . "\">" .
			"<table border=0 cellpadding=5 cellspacing=0 width=100%>";

		foreach ($songs as $song => $opts) {
			$blurb = $opts['blurb'];
			if ($mp3_file_info || $id3_song_titles) {
				$mp3 = new mp3("$mp3_dir/$artist/$title/$song", $mp3_file_info, $id3_song_titles, $mp3info_faster);
			}
			$valign = "";
			$url_play = "<a href=\"${this_page}l=8&a=" . urlencode($artist) . "&t=" . urlencode($title) . "&s=" . urlencode($song) . "&m=1\">";

			if ($blurb) {
				$valign = " valign=top";
				$blurb = "<br>$blurb";
			}
			echo "<tr class=$row_colors[$i]><td nowrap $valign>";

			if ($check_boxes) {
				$checked = "";
				if ($playlist) {
					if (in_array($song, $playlist_mod)) { $checked = " checked"; }
				}
				echo "<input type=checkbox name=\"mp3s[]\" value=\"" . urlencode($song). "\"$checked> &nbsp; ";
			} else { echo "&nbsp;"; }

			if ($allow_download || $z_admin) {
				echo "<a href=\"$url_base/" . rawurlencode($artist) . "/" . rawurlencode($title) . "/" . rawurlencode($song) . "\">$img_download</a> &nbsp;&nbsp;&nbsp; ";
			}
			echo "$url_play$img_play</a> &nbsp;";

			if ($low_quality) {
				$tmp = "&nbsp;<a href=\"${this_page}l=8&a=" . urlencode($artist) . "&t=" . urlencode($title) . "&s=";
				if (isset($opts['lofi'])) {
					echo "$tmp" . urlencode(preg_replace("/(\.$ext_music)$/i", "$low_quality_suffix$1", $song)) .
						"&m=1\">$img_play_lofi</a> &nbsp;";
				} elseif($resample && stristr(substr($song,-3),'mp3')) {
					echo "$tmp" . urlencode($song) . "&m=6\">$img_play_lofi</a> &nbsp;";
				}
			}
			echo "</td><td width=\"100%\"$valign>$url_play";

			if ($id3_song_titles && $mp3->tag) {
				echo htmlentities($mp3->title);
			} else {
				echo formatSongTitle(htmlentities($song));
			}
			echo "</a>$blurb</td>";

			if ($mp3_file_info) {
				$tmp = "<td align=right nowrap$valign>";
				if ($mp3->info == 1) {
					echo "$tmp$mp3->time</td>$tmp$mp3->bitrate kbps</td>" .
						"$tmp" . round($mp3->frequency/1000,1) . " kHz</td>";
				} else {
					echo "$tmp&nbsp;</td>$tmp&nbsp</td>$tmp&nbsp;</td>";	
				}
				echo "$tmp" . sprintf("%.2f", round($mp3->filesize/1000000,2)) . " Mb</td>";
			} else {
				echo "<td colspan=4>&nbsp;</td>";
			}
			echo "</tr>";
			$i = ++$i % 2;
		}
		echo "</tr><tr><td colspan=2 nowrap>";

		if ($z_admin && $cache) {
			$submit = ($playlist) ? "Update" : "Create";
			echo "<input type=submit value=\"$submit Custom\" onClick=\"SubmitForm('pf','&l=3&z=3');\">";
		}
		if ($check_boxes) {
			echo "&nbsp; Check: &nbsp;<a href='javascript:void 0;' onClick='CheckBoxes(true);'>All</a> | ".
				"<a href='javascript:void 0;' onClick='CheckBoxes(false);'>None</a>";
		}
		
		if ($check_boxes) {
			#TODO: make moe pretty formatting
			echo " &nbsp; &nbsp; &nbsp; &nbsp; ";
			if ($play_selected || $cmp_selected) {
				echo "Selected: ";
				if ($play_selected) {
					echo " &nbsp; <a href=\"JavaScript:SubmitForm('pf','&l=8&m=7');\">$img_play</a>";
				}
				if ($cmp_selected) {
					echo " &nbsp; <a href=\"JavaScript:SubmitForm('pf','&l=8&m=8');\">$img_download</a>";
				}
			}
		}
		
		echo "&nbsp;</td><td colspan=4 align=right nowrap>" .  htmlPlaylistFormElements(2) .
			"</td></tr></table></form></td></tr>";
	}
	echo "</table>" .
		'<script>function CheckBoxes(v){for(var i=0;i<document.pf.elements.length;i++){var j=document.pf.elements[i];if(j.type=="checkbox"){j.checked=v;}}}</script>';

	pageFooter();
}


function pageNewPlaylist($artist, $title, $level_prev, $songs, $lps){
	global $zina_dir, $this_page, $url_main;
	pageHeader("New Playlist",$url_main);

	$fromSearch = "";
	#if (empty($artist)) { $fromSearch = "&z=1"; }

	echo "<form method=post action=\"${this_page}l=3&z=2$fromSearch&a=" . urlencode($artist) . "&t=" . urlencode($title) ."\">" .
			"<input type=hidden name=level_prev value=\"$level_prev\">" .
			"<table border=0 cellspacing=5>" .
			"<tr><td>Playlist Name:</td><td><input name=playlist type=text size=25 maxlength=30></td>" .
			"<td><input type=submit value=Submit></td></tr></table>";

	if (sizeof($songs) != 0) {
		sort($songs);
		foreach ($songs as $song) {
			echo "<input type=hidden name=\"mp3s[]\" value=\"$song\">";
		}
	}
	if (sizeof($lps) != 0) {
		sort($lps);
		foreach($lps as $lp) {
			echo "<input type=hidden name=\"lps[]\" value=\"$lp\">";
		}
	}
	echo "</form>";
	pageFooter();
}


function pageViewEditPlaylist ($playlist, $artist, $title, $level_prev) {
	global $zina_dir, $url_base, $space_replace, $page_width, $row_colors,
		$img_play, $this_page, $url_main, $img_delete, $img_more, $z_admin, $cache,
		$session_playlist, $HTTP_SESSION_VARS;

	$filename = "$zina_dir/_zina_$playlist.m3u";
	$url_play = "<a href=\"${this_page}l=8&m=3&pl=";

	$sp = false;
	if($session_playlist && $playlist == "zina_session_playlist") {
		if (isset($HTTP_SESSION_VARS['s_p'])) {
			$songs = explode("\n",$HTTP_SESSION_VARS['s_p']);
			unset($songs[sizeof($songs) - 1]);
			$sp = true;
		}
	}

	if ($sp || file_exists($filename)){
		$searchform = "<form name=pf method=POST action=\"${this_page}a=" . urlencode($artist) . "&t=" . urlencode($title) . "\">";
		$searchform .= htmlPlaylistFormElements($level_prev, $playlist, "l x p q v d") . "</form>";
		pageHeader("View/Edit Playlist", $url_main, $searchform, htmlSearchForm());

		if (!$sp) { $songs = file($filename); }
		$url_play .= urlencode($playlist) . "\">";

		if (sizeof($songs) > 0) {
			#$i = color; $j = order
			$i = 0;
			$j = 1;
			
			echo "<form method=POST name=form01 action=\"${this_page}l=3&a=" . urlencode($artist) . "&t=" . urlencode($title) . "\">" .
				"<input type=hidden name=level_prev value=\"$level_prev\">" .
				"<input type=hidden name=playlist value=\"$playlist\">" .
				"<table border=0 cellpadding=5 cellspacing=0 width=\"$page_width\">";

			if ($sp) {
				global $sp_length;
				$sp_perc = round(strlen($HTTP_SESSION_VARS['s_p']) / $sp_length * 100, 1);
				echo "<tr><td colspan=4><br><b>Session Playlist</b> ($sp_perc% full)<br><br></td></tr>";
			}

			echo "<tr><td>&nbsp;</td><td><b>Order</b></td><td><b>Artist</b></td><td><b>Title</b></td><td><b>Song</b></td></tr>";

			foreach($songs as $song) {
				$pieces = explode("/", $song);
				$url_a = "${this_page}a=" . urlencode($pieces[0]);
				$url_t = "$url_a&t=" . urlencode($pieces[1]);
				#TODO: don't show checkboxes/order? when not admin and not session_playlist == zina_session_playlist
				echo "<tr class=$row_colors[$i]><td align=center nowrap>&nbsp;<input type=checkbox name=\"mp3s[]\" value=\"" . urlencode(rtrim($song)) . "\" checked>&nbsp;</td>" .
					"<td><input type=text name=order[] value=\"$j\" size=2></td>" .
					"<td nowrap><a href=\"$url_a&l=1\" class=view-search>" . strTruncate(htmlentities($pieces[0]),"...",30) . "</a> &nbsp; &nbsp; </td>" .
					"<td nowrap><a href=\"$url_t&l=2\" class=view-search>" . strTruncate(htmlentities($pieces[1]),"...",30) . "</a> &nbsp; &nbsp; </td>" .
					"<td width=\"100%\">";
				if (rtrim($pieces[2]) != ".lp") {
					echo "<a href=\"$url_t&s=" . urlencode($pieces[2]) . "&l=8&m=1\" class=view-search>" . strTruncate(htmlentities(formatSongTitle($pieces[2])),"...",45) . "</a>";
				}
				echo "&nbsp;</td></tr>";
				$i = ++$i % 2;
				$j++;
			}

			echo "<tr><td colspan=5>";
			if (($z_admin && $cache) || ($session_playlist && $playlist == "zina_session_playlist")) { 
			echo "<input type=submit value=\"Update\" onClick=\"SubmitForm('form01','&z=4');\">"; }
			echo "</td></tr></table></form>";
		}
	} else {
		pageHeader("Playlists", $url_main, htmlRandom(), htmlSearchForm());
		$playlists = getCustomPlaylists();
		if ($session_playlist) {
			#TODO: make moe elegant
			if (empty($playlists)) { 
				$playlists[] = "zina_session_playlist"; 
			} else {
				array_unshift($playlists, "zina_session_playlist");
			}
		}
		if (!empty($playlists)) {
			echo "<table border=0 cellpadding=5 width=\"$page_width\"><tr><td colspan=3><b>Playlists</b></td></tr>";

			$i = 0;
			foreach($playlists as $playlist) {
				$pl = urlencode($playlist);
				if ($session_playlist && $playlist == "zina_session_playlist") {
					$playlist = "Session Playlist";
				}
				echo "<tr class=$row_colors[$i]><td>$url_play$pl\">$img_play</a> &nbsp;" .
					"<a href=\"${this_page}l=3&z=1&pl=$pl\">$img_more</a> &nbsp;";
				echo ($z_admin && $cache && $playlist != "Session Playlist") ? "<a href=\"${this_page}l=3&z=5&pl=$pl\">$img_delete</a> &nbsp;" : "&nbsp";
				echo "<a href=\"${this_page}l=3&z=1&pl=$pl\">$playlist</a></td></tr>";
				$i = ++$i % 2;
			}
			echo "</table>";
		} else {
			echo "<table border=0 width=\"$page_width\"><tr><td align=center>Sorry, there are no playlists.</td></tr></table>";
		}
	}
	pageFooter();
}


function pageSearchResults($type, $term) {
	global $page_width, $row_colors, $url_main, $this_page, $mp3_dir,
		$mp3_file_info, $id3_song_titles, $z_admin, $cache, $url_base,
		$allow_download, $img_download, $img_play, $img_play_lofi, $low_quality,
		$low_quality_suffix, $ext_music, $resample, $session_playlist;

	if (!empty($term)) {
		$searchfun = array(
				'a' => '$found = searchArtist($term);',
				't' => '$found = searchTitle($term);',
				's' => '$found = searchSong($term);'
			);

		eval($searchfun[$type]);
	}
	pageHeader("Search Results",$url_main,htmlRandom(),htmlSearchForm($type,$term));

	echo "<table border=0 width=\"$page_width\"><tr><td>";

	if (!empty($found)) {
		$lofi_trun = $low_quality ? 2 : 0;

		$artist_truncate = 22 - $lofi_trun;
		$title_truncate = 22 - $lofi_trun;
		$song_truncate = 31 - $lofi_trun;
		$i = 0;
		$file_info = true;

		#TODO: make global option (only show file_info if > n)?
		if ($type = "s" && sizeof($found) > 20) {
			$file_info = false;
			$artist_truncate = 25 - $lofi_trun;
			$title_truncate = 25 - $lofi_trun;
			$song_truncate = 45 - $lofi_trun;
		}
		echo "<form name=pf method=POST action=\"${this_page}z=1\">" .
			"<table border=0 cellpadding=5 cellspacing=0 width=\"100%\">";

		foreach($found as $artist => $titles) {
			foreach($titles as $title => $songs) {
				$url_a = "${this_page}a=" . urlencode($artist);
				$url_t = "$url_a&t=" . urlencode($title);
				$at = "<td nowrap width=\"100%\"><b><a href=\"$url_a&l=1\" class=view-search>" . strTruncate(htmlentities($artist),"...",$artist_truncate) . "</a></b> " .
					"<a href=\"$url_t&l=2\" class=view-search>" . strTruncate(htmlentities($title),"...",$title_truncate) . "</a> &nbsp;";
				if (!empty($songs)){
					foreach($songs as $song) {
						$lofi = false;
						if ($low_quality) {
							if (preg_match("/($low_quality_suffix)\.($ext_music)$/i", $song)) {
								$song_lofi = $song;
								$song = preg_replace("/($low_quality_suffix)(\.$ext_music)$/i","$2", $song);
								$lofi = true;
							}
						}
						$url_play = "<a href=\"$url_t&l=8&s=" . urlencode($song) . "&m=";
						echo  "<tr class=$row_colors[$i]><td nowrap>";

						if (($z_admin && $cache) || $session_playlist) {
							echo "<input type=checkbox name=\"lps[]\" value=\"" . urlencode($artist) . "/" . urlencode($title) . "/" . urlencode($song) . "\"> &nbsp; ";
						} else { echo "&nbsp;"; }

						if ($allow_download || $z_admin) {
							echo "<a href=\"$url_base/$artist/$title/$song\">$img_download</a> &nbsp;&nbsp;&nbsp; ";
						}
						echo "${url_play}1\">$img_play</a> &nbsp;";

						if ($low_quality) {
							if ($lofi) {
								$url_play_lofi = "<a href=\"$url_t&l=8&s=" . urlencode($song_lofi) . "&m=";
								echo "&nbsp;${url_play_lofi}1\">$img_play_lofi</a> &nbsp;";
							} elseif($resample && stristr(substr($song,-3),'mp3')) {
								echo "&nbsp;${url_play}6\">$img_play_lofi</a> &nbsp;";
							}
						}

						$song_title = $song;
						if ($file_info && ($mp3_file_info || $id3_song_titles)) {
							$mp3 = new mp3("$mp3_dir/$artist/$title/$song", $mp3_file_info, $id3_song_titles);
							if ($id3_song_titles && $mp3->tag) {
								$song_title = $mp3->title;
							}
						}

						echo  "</td>$at<b><i>${url_play}1\" class=view-search>" .
							strTruncate(htmlentities(formatSongTitle($song_title)),"...",$song_truncate) . "</a></i></b></td>";

						if ($file_info && $mp3_file_info) {
							$tmp = "<td align=right nowrap>";
							echo "$tmp$mp3->time</td>$tmp$mp3->bitrate kbps</td>" .
								"$tmp" . sprintf("%.2f", round($mp3->filesize/1000000,2)) . " Mb</td>";
						} else {
							echo "<td colspan=3>&nbsp;</td>";
						}
						echo "</tr>";

						$i = ++$i % 2;
					}
				} else {
					$at = "<td nowrap><b><a href=\"$url_a&l=1\" class=view-search>" . htmlentities($artist) . "</a></b> " .
						"<a href=\"$url_t&l=2\" class=view-search>" . htmlentities($title) . "</a> &nbsp;";
					$song = '.lp';
					echo "<tr class=$row_colors[$i]><td nowrap>";
					if (($z_admin && $cache) || $session_playlist) {
						echo "<input type=checkbox name=\"lps[]\" value=\"" . urlencode($artist) . "/" . urlencode($title) . "/$song\"> &nbsp; ";
					} else { echo "&nbsp;"; }
					echo "</td>$at</td><td width=\"100%\">&nbsp;</td></tr>";
					$i = ++$i % 2;
				}
			}
		}
		echo "</table><br>" . htmlPlaylistFormElements(1) . "</form>";
	} else {
		echo " <h3>Not Found</h3>";
	}
	echo "</td></tr></table>";
	pageFooter();
}


function pageCryptomatic($text, $salt) {
	global $this_page, $url_main;

	pageHeader("Cryptomatic", $url_main);

	echo "<form method=post action=\"${this_page}l=5\">" .
		"<table border=0><tr><td>Password:</td><td><input type=text name=text value=\"$text\"></td></tr>" .
		"<tr><td>Salt:</td><td><input type=text name=salt value=\"$salt\"></td></tr>" .
		"<tr><td>&nbsp;</td><td><input type=submit value=\"Generate\"></td></tr>" .
		"</form>";

	if (!empty($text)) {
		echo "<tr><td>Crypt:</td><td><input type=text value=\"" . crypt($text, $salt) . "\"></td></tr>";
	}

	echo "</table>";
	pageFooter();
}

function pageLogin($un = "", $error = "") {
	global $this_page, $url_main;
	pageHeader("Login",$url_main);

	echo "<form method=post action=\"${this_page}l=9\"><table border=0 cellpadding=5>" .
		"<tr><td colspan=2><h3>$error</h3></td></tr>" .
		"<tr><td>Username:</td><td><input type=text name=un value=\"$un\"></td></tr>" .
		"<tr><td>Password:</td><td><input type=password name=up></td></tr>" .
		"<tr><td>&nbsp;</td><td><input type=submit value=\"Login\"></td></tr>" .
		"</table></form>";

	pageFooter();
}


function pageError($error) {
	global $page_width;
	pageHeader ("Error");
	echo "<table border=0 width=\"$page_width\"><tr><td><b>$error</b></td></tr></table>";
	pageFooter();
	exit;
}

function htmlAMG($text= '', $target = '_blank') {
	return "<form action=\"http://www.allmusic.com/cg/amg.dll\" method=POST target=\"$target\">" .
		"<input type=HIDDEN name=P value=amg>" .
		"AMG: <input class=small type=textfield name=sql size=15 maxlength=30 value=\"$text\">" .
		"<select name=opt1 class=small><option value=1>Artist<option value=2>Album<option value=3>Songs</select>" .
		"<input type=submit class=small value=Go></form>";
}


function htmlSearchForm($type="",$term="") {
	global $this_page, $img_more;

	$selected = array("a" => null, "t" => null, "s" => null);

	$selected[$type] = " selected";

	return "<form name=fs method=POST action=\"${this_page}l=4\" class=small>" .
			"<input name=search type=text class=small value=\"$term\"><select name=type class=small>" .
			"<option value=a$selected[a]>Artist" .
			"<option value=t$selected[t]>Title" .
			"<option value=s$selected[s]>Song</select>" .
			" <a href='javascript:document.fs.submit();'>$img_more</a></form>" .
			"<script language=JavaScript>document.fs.search.focus();</script>";
			;
}


function htmlPlaylistFormElements($level_prev, $playlist_sel = "", $type = "l a x p q v") {
	global $this_page, $img_play, $img_more, $img_add, $img_delete,
		$img_playlist, $img_play_lofi, $z_admin, $cache, $low_quality,
		$session_playlist;

	$options = explode(" ", $type);
	$playlists = getCustomPlaylists();

	if (!$z_admin && empty($playlists)) return "";

	$tmp = "&nbsp;<a href=\"javascript:SubmitForm('pf',";

	$elements = array(
		'd' => "$tmp'&l=3&z=5');\">$img_delete</a>&nbsp;",
		'a' => "$tmp'&l=3&z=2');\">$img_add</a>&nbsp;",
		'v' => "$tmp'&l=3&z=1');\">$img_more</a>&nbsp;",
		'p' => "$tmp'&l=8&m=3');\">$img_play</a>&nbsp;",
		'q' => "$tmp'&l=8&m=3&lf=true');\">$img_play_lofi</a>&nbsp;",
		'l' => "&nbsp;<a href=\"${this_page}l=3&z=1\">$img_playlist</a>&nbsp;",
		'x' => '<select class=small name="playlist">'
		);

	if (!$z_admin || !$cache) { $elements["d"] = ""; }
	if ((!$z_admin || !$cache) && !$session_playlist) { $elements["a"] = ""; }
	if (!$low_quality) { $elements["q"] = ""; }

	if ($z_admin && strstr($type,"a") && $cache) {
		$elements["x"] .= '<option value=new_zina_list> - New Playlist - </option>';
	}

	if ($session_playlist) {
		$elements["x"] .= '<option value=zina_session_playlist> - Session Playlist - </option>';
	}

	if (!empty($playlists)) {
		foreach($playlists as $playlist) {
			$selected = ($playlist == $playlist_sel) ? " selected" : "";
			$elements["x"] .= "<option value=\"$playlist\"$selected>" . strTruncate($playlist,"...",15) . "</option>";
		}
	}
	$elements["x"] .= "</select>";
	$html = "";
	foreach($options as $option) {
		$html .= $elements[$option];
	}
	$html .= "<input type=hidden name=level_prev value=\"$level_prev\">";
	return $html;
}


function htmlRandom() {
	global $random;

	$form = "";

	if ($random) {
		global $this_page, $img_play, $cache, $low_quality, $r_opts, $r_opts_default, $r_default;
		$r_type = array(4 => "Albums",5 =>"Songs");

		$all = $cache ? "<option value=0>ALL" : "";
		$low = "";
		if ($low_quality) {
			global $img_play_lofi;
			#TODO: make SubmitForm() more flexible to handle this case and below
			$low = " <a href=\"javascript:document.fr.action='${this_page}l=8&lf=true'; document.fr.submit();\">$img_play_lofi</a>";
		}
		$form = "<span class=small><form class=small name=fr method=post action=\"${this_page}\">Random: " .
			"<select class=small name=n>";
		foreach($r_opts as $option) {
			$selected = "";
			if ($option == $r_opts_default) $selected = " selected";
			$form .= "<option value=$option$selected>";
			$form .= ($option != 0) ? $option : "ALL";
		}
		$form .= "</select><select class=small name=m>";
		foreach($r_type as $key => $type) {
			$selected = "";
			if ($type == $r_default) $selected = " selected";
			$form .= "<option value=$key$selected>$type";
		}
		$form .= "</select> <a href=\"javascript:document.fr.action='${this_page}l=8'; document.fr.submit();\">" .
			"$img_play</a>$low</form></span>";
	} 
	return $form;
}


function formatSongTitle($song) {
	global $ext_music;
	$tmp = preg_replace("/^.*[0-9][0-9][ _]?-[ _]?/i","",rtrim($song));
	$tmp = preg_replace("/\.($ext_music)$/i","",$tmp);
	return $tmp;
}


function strTruncate($str, $replace, $length) {
	if (strlen($str) > $length)
		return substr($str,0,$length) . $replace;

	return $str;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * GET functions
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

function getArtists ($dir_name) {
	global $skip_dir;
	$d = dir($dir_name);
	while($entry = $d->read()) {
		if ($entry == "." || $entry == "..") { continue;}
		if (is_dir("$dir_name/$entry") && substr($entry,0,1) != $skip_dir) {
			$artists[] = $entry;
		}
	}
	return $artists;
}


function getTitles($artist) {
	global $ext_graphic, $ext_text, $mp3_dir, $display_album_images;
	$art_dir = "$mp3_dir/$artist";

	$d = dir($art_dir);
	$covers_found = 0;
	$titles = $artist_blurb = $artist_pic = null;

	while($entry = $d->read()) {
		if ($entry == "." || $entry == "..") { continue; }
		if (is_dir("$art_dir/$entry")) {
			if ($display_album_images) {
				$s = dir("$art_dir/$entry");
				while($file = $s->read()) {
					$cover = null;
					if  (preg_match( "/\.($ext_graphic)$/i", $file)) {
						$cover = $mp3_dir . "/" . rawurlencode($artist) . "/" . rawurlencode($entry) . "/" .  rawurlencode($file);
						$covers_found++;
						break;
					}
				}
				$s->close();
			} else {
				$cover = null;
			}
			$titles[$entry] = $cover;
		} else {
			if  (preg_match("/\.($ext_graphic)$/i", $entry)) {
				$artist_pic = $mp3_dir . "/" . rawurlencode($artist) . "/" . rawurlencode($entry);
			} elseif (preg_match("/\.($ext_text)$/i", $entry)) {
				$artist_blurb = implode("", file("$art_dir/$entry"));
			}
		}
	}
	$d->close();

	if (!empty($titles)) { natcasesort($titles);}

	return array('titles' => $titles, 'artist_image' => $artist_pic, 'blurb' => $artist_blurb, 'covers' => $covers_found);
}


function getTitlesByArtist($artist) {
	global $mp3_dir;
	$d = dir("$mp3_dir/$artist");
	$titles = null;

	while($entry = $d->read()) {
		if ($entry != "." && $entry != "..") {
			if (is_dir("$mp3_dir/$artist/$entry")) {
				$titles[] = $entry;
			}
		}
	}
	$d->close();

	return $titles;
}


#TODO: error checking so when files change names and cache isn't updated...
function getSongs($artist, $title, $sort = true, $playlist = true, $custom = false, $low = false) {
	global $ext_music, $mp3_dir, $low_quality_suffix, $ext_graphic,
		$honor_custom, $zina_dir, $low_quality;

	$dir_name = "$mp3_dir/$artist/$title";
	$d = dir($dir_name);
	$image = $songs = null;

	while($entry = $d->read()) {
		if (!is_dir("$entry")) {
			if (preg_match("/\.($ext_music)$/i", $entry)) {
				$song_blurb = null;
				if ($low_quality && preg_match("/($low_quality_suffix)\.($ext_music)$/i", $entry)) {
						$entry = preg_replace("/($low_quality_suffix)(\.$ext_music)$/i", "$2", $entry);
						$songs[$entry]['lofi'] = 1;
				} else {
					if (!$playlist) {
						$song_blurb_file = "$dir_name/" . preg_replace("/\.($ext_music)$/i", ".txt", $entry);
						if (file_exists($song_blurb_file)) {
							$song_blurb = implode("", file($song_blurb_file));
						}
					}
					$songs[$entry]['blurb'] = $song_blurb;
				}
			} elseif(!$playlist && preg_match( "/\.($ext_graphic)$/i", $entry)) {
				$image = $mp3_dir . "/" . rawurlencode($artist) . "/" . rawurlencode($title) . "/" . rawurlencode($entry);
			}
		}
	}
	$d->close();

	if (!empty($songs)) {
		if ($playlist) {
			#TODO: standardize your array crap and combine the following two...?
			# i.e. $songs[] = $title || $songs['title']
			if ($custom && $honor_custom && file_exists("$zina_dir/$artist - $title.m3u")) {
				$songs2 = file("$zina_dir/$artist - $title.m3u");
				foreach ($songs2 as $key => $value) {
					$value = rtrim($value);
					$songs2[$key] = $value;
					if ($low && isset($songs[$value]['lofi'])) {
						$songs2[$key] = preg_replace("/(\.$ext_music)$/i", "$low_quality_suffix$1", $value);
					}
				}
			} else {
				foreach($songs as $key => $value) {
					if ($low && isset($value['lofi'])) {
						$songs2[] = preg_replace("/(\.$ext_music)$/i", "$low_quality_suffix$1", $key);
					} else {
						$songs2[] = $key;
					}
				}
			}
			if ($sort) { sort($songs2); }
			return $songs2;
		} else {
			if ($sort) { ksort($songs); }
			return array('songs' => $songs, 'cover' => $image);
		}
	}
	return null;
}


function getTitlePlaylist($artist, $title, $custom = false, $low = false) {
	global $url_base, $zina_dir, $space_replace, $resample,
		$low_quality_suffix, $ext_music, $mp3_root;

	$playlist = null;
	$songs = getSongs($artist, $title, true, true, $custom, $low);

	$playlist = getPlaylist($artist, $title, $songs, $low);

	return $playlist;
}


function getCustomPlaylists() {
	global $zina_dir;
	$custom_playlists = null;
	
	if (file_exists($zina_dir)) {
		$d = dir($zina_dir);

		while($entry = $d->read()) {
			if (substr($entry,0,6) == "_zina_" && preg_match("/\.m3u$/i", $entry) ) {
				$entry = preg_replace("/^_zina_/i","",$entry);
				$custom_playlists[] = preg_replace("/\.m3u$/i","",$entry);
			}
		}
		$d->close();

		if (!empty($custom_playlists)) { natcasesort($custom_playlists); }
	}
	return $custom_playlists;
}


function getPlaylist($artist, $title, $songs, $low) {
	global $space_replace, $url_base;
	$playlist = null;

	if (!empty($songs)) {
		if ($low) {
			global $low_quality_suffix, $ext_music;
			foreach($songs as $song) {
				$song = urldecode($song);
				if (preg_match("/($low_quality_suffix)\.($ext_music)$/i", $song) || !$resample || !stristr(substr($song,-3),'mp3')) {
					$playlist .= str_replace(" ",$space_replace,"$url_base/$artist/$title/$song\n");
				} else {
					$playlist .= getSongResampledURL($artist, $title, $song);
				}
			}
		} else {
			foreach($songs as $song) {
				$playlist .= str_replace(" ",$space_replace,"$url_base/$artist/$title/" . urldecode($song) . "\n");
			}
		}
	}
	return $playlist;
}


function getSongResampledURL($artist, $title, $song) {
	global $this_page, $HTTP_SERVER_VARS;
	$url_base = "http://" . $HTTP_SERVER_VARS["HTTP_HOST"];
	return "${url_base}${this_page}l=6&s=" . urlencode("$artist/$title/$song") . "\n";
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * WRITE/UPDATE/DELETE functions
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

function writeCustomPlaylist($songs, $type, $filename, $title = false) {
	global $zina_dir, $session_playlist, $HTTP_SESSION_VARS;

	$playlist = null;

	if (!empty($songs)) {
		foreach($songs as $song) { $playlist .= urldecode($song) . "\n"; }
	}

	if($session_playlist && strstr($filename,"zina_session_playlist")) {
		global $mode, $sp_length;
		if ($mode == 4) { #update
			$HTTP_SESSION_VARS['s_p'] = $playlist;
		} else {
			if (strlen($HTTP_SESSION_VARS['s_p']) + strlen($playlist) < $sp_length) {
				$HTTP_SESSION_VARS['s_p'] .= $playlist;
			}
		}
	} else {
		$fh = fopen ($filename, $type);
		fwrite($fh,$playlist);
		fclose($fh);
	}

	if ($title && empty($songs)) {
		if (file_exists($filename)) { unlink($filename); }
	}
}


function writeRandomCache($type = "t", $low = false) {
	global $cache_limit, $file_rt_cache, $file_rs_cache, $mp3_dir, $low_quality_suffix;

	$files = null;
	$cache_file = $file_rt_cache;

	if ($type == "s") {
		writeRandomCache("t");
		$cache_file = $file_rs_cache;
		if ($low) { $cache_file = preg_replace("/(\.dat)$/i", "$low_quality_suffix$1", $cache_file); }
	}

	if (file_exists($cache_file)) {
		$cache_life = ($cache_limit * 86400); #days
		$lastmtime = 0;
		$now = time();

		$lastmtime = filemtime($cache_file);
		if ($now - $lastmtime < $cache_life) {
			return false;
		}
	}

	if ($type == "s") {
		$titles = file($file_rt_cache);

		foreach ($titles as $title) {
			$at = explode("/", $title);
			$songs = getSongs($at[0],rtrim($at[1]),false,true,true,$low);
			if (empty($songs)) { continue; }
			foreach ($songs as $song) {
				$files .= rtrim($title) . "/$song\n";
			}
		}
	} else {
		$artists = getArtists($mp3_dir);

		foreach ($artists as $artist) {
			$titles = getTitlesByArtist($artist);
			if (empty($titles)) { continue; }
			foreach ($titles as $title) {
				$files .= "$artist/$title\n";
			}
		}
	}

	$fp = fopen ($cache_file, "w");
	fwrite($fp,$files);
	fclose($fp);

	return true;
}


function deleteCustomPlaylist($playlist_name) {
	global $zina_dir;

	if ($playlist_name != "new_zina_list") {
		$filename = "$zina_dir/_zina_$playlist_name.m3u";
		if (file_exists($filename)) { unlink($filename); }
	}
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * SEND functions
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

function sendTitlePlaylist ($artist, $title, $custom, $low = false) {
	sendResponse(getTitlePlaylist($artist,$title, $custom, $low));
}


function sendSongPlaylist ($artist, $title, $song) {
	global $url_base, $space_replace;
	sendResponse(str_replace(" ",$space_replace,"$url_base/$artist/$title/$song\n"));
}


function sendSongPlaylistResampled ($artist, $title, $song) {
	sendResponse(getSongResampledURL($artist, $title, $song));
}

#TODO: test performance on really big playlist
function sendCustomPlaylist($playlist_name, $low = false) {
	global $zina_dir, $url_base, $space_replace, $honor_custom,
		$ext_music, $low_quality_suffix, $resample, $mp3_dir,
		$session_playlist, $HTTP_SESSION_VARS;

	$playlist = $nl = "";

	if($session_playlist && $playlist_name == "zina_session_playlist") {
		if (isset($HTTP_SESSION_VARS['s_p'])) {
			$songs = explode("\n",$HTTP_SESSION_VARS['s_p']);
			unset($songs[sizeof($songs) - 1]);
			$nl = "\n";
		}
	} else {
		$filename = "$zina_dir/_zina_$playlist_name.m3u";
		$songs = file($filename);
	}

	if (sizeof($songs) > 0) {
		foreach($songs as $song) {
			if (preg_match("/\.lp$/i", rtrim($song))) {
				$pieces = explode("/", $song);
				$custom = $honor_custom ? getTitlePlaylist($pieces[0], rtrim($pieces[1]), true, $low) : null;
				$playlist .= empty($custom) ? getTitlePlaylist($pieces[0], $pieces[1], false, $low) : $custom;
			} else {
				if (!$low) {
					$playlist .= str_replace(" ",$space_replace,"$url_base/$song$nl");
				} else {
					$song_lofi = preg_replace("/(\.$ext_music)$/i", "$low_quality_suffix$1", rtrim($song));
					if (file_exists("$mp3_dir/$song_lofi")) {
						$playlist .= str_replace(" ",$space_replace,"$url_base/$song_lofi\n");
					} elseif ($resample && stristr(substr(rtrim($song),-3),'mp3')) {
						$pieces = explode("/", $song);
						$playlist .= getSongResampledURL($pieces[0], $pieces[1], $pieces[2]);
					} else {
						$playlist .= str_replace(" ",$space_replace,"$url_base/$song$nl");
					}
				}
			}
		}
	}
	sendResponse($playlist);
}


function sendRandom($num, $type = "t", $low = false) {
	global $url_base, $space_replace, $honor_custom, $mp3_dir;
	$artists = getArtists($mp3_dir);
	$artists_num = sizeof($artists);
	$playlist = null;
	mt_srand(time());

	if ($type == "t") {
		for($i=0;$i<$num;$i++) {
			$j = mt_rand(0, $artists_num - 1);
			$titles = getTitlesByArtist($artists[$j]);
			$k = mt_rand(0, sizeof($titles) - 1);
			$custom = $honor_custom ? getTitlePlaylist($artists[$j], $titles[$k], true, $low) : null;
			$playlist .= empty($custom) ? getTitlePlaylist($artists[$j], $titles[$k], false, $low) : $custom;
		}
	} elseif ($type == "s") {
		for($i=0;$i<$num;$i++) {
			$j = mt_rand(0, $artists_num - 1);
			$titles = getTitlesByArtist($artists[$j]);
			if (empty($titles)) {
				$i--;
				continue;
			}
			$k = mt_rand(0, sizeof($titles) - 1);
			$songs = getSongs($artists[$j],$titles[$k],false,true,true,$low);
			if (!empty($songs)) {
				$l = mt_rand(0,sizeof($songs) - 1);
				#TODO: resample
				$playlist .= str_replace(" ",$space_replace,"$url_base/$artists[$j]/$titles[$k]/$songs[$l]\n");
			} else { $i--; }
		}
	}

	sendResponse($playlist);
}


function sendRandomCached($num, $type = "t", $low = false) {
	global $file_rt_cache, $file_rs_cache, $honor_custom, $space_replace,
		$url_base, $low_quality_suffix, $resample;

	$playlist = null;
	$file = $file_rt_cache;

	writeRandomCache($type, $low);
	if ($type == "s") {
		$file = $file_rs_cache;
		if ($low) { $file = preg_replace("/(\.dat)$/i", "$low_quality_suffix$1", $file_rs_cache); }
	}
	$array = file($file);
	$total = sizeof($array);

	if ($num == 0 || $num > $total) {
		$num = $total;
	}

	o_shuffle($array);

	if ($type == "t") {
		for($i = 0; $i < $num; $i++) {
			$at = explode("/", $array[$i]);
			$custom = $honor_custom ? getTitlePlaylist($at[0], rtrim($at[1]), true, $low) : null;
			$playlist .= empty($custom) ? getTitlePlaylist($at[0], rtrim($at[1]), false, $low) : $custom;
		}
	} elseif ($type == "s") {
		if ($low && $resample) {
			global $ext_music;
			for($i=0; $i<$num;$i++) {
				$song = rtrim($array[$i]);
				if (preg_match("/($low_quality_suffix\.$ext_music)$/i", $song) ||
						! stristr(substr($song,-3),'mp3')) {
					$playlist .= str_replace(" ",$space_replace,"$url_base/$song\n");
				} else {
					$pieces = explode("/", $song);
					$playlist .= getSongResampledURL($pieces[0], $pieces[1], $pieces[2]);
				}
			}
		} else {
			for($i=0; $i<$num;$i++) {
				$playlist .= str_replace(" ",$space_replace,"$url_base/$array[$i]");
			}
		}
	}
	sendResponse($playlist);
}


function sendSelectedSongs($artist, $title, $songs, $low = false) {
	$playlist = getPlaylist($artist, $title, $songs, $low);
	
	sendResponse($playlist);
}


function sendSelectedCompressed($artist, $title, $songs, $filename, $lofi = false) {
	global $mp3_dir, $cmp_pgm, $cmp_ext, $cmp_set, $cmp_mime, $cmp_selected, $cmp_int;

	#TODO: lowfi, baby
	if ($cmp_selected) {
		$files = null;

		if ($cmp_int) {
			$archive = new zipfile;

			foreach ($songs as $song) {
				$file = "$mp3_dir/$artist/$title/" . urldecode($song);
      		$file_size = filesize($file);
      		$fp = fopen($file, "rb");
        		$file_data = fread($fp, $file_size);
        		fclose($fp);
			
				$archive->add_file($file_data,$file);
				unset($file_data);
			}
			#TODO: oldkpindex.php.zip filename when umlauts and stuff in mozilla (not IE)???
			header("Content-Disposition: inline; filename=$filename.zip");
			header("Content-type: application/zip");
			echo $archive->file();

		} else { # external
			foreach ($songs as $song) {
				$files .= "\"$mp3_dir/$artist/$title/" . urldecode($song) . "\" ";
			}
			header("Content-Disposition: inline; filename=$filename.$cmp_ext");
			header("Content-type: $cmp_mime");
			passthru("$cmp_pgm $cmp_set $files");
		}
	}	
	exit;
}


function sendResponse($content) {
	header("Content-Disposition: inline; filename=playlist.m3u");
	header("Content-type: audio/mpegurl");
	header("Cache-control: private"); #IE seems to need this.
	echo $content;
	exit;
}


function sendResampledMP3 ($file) {
	global $mp3enc, $mp3enc_settings;
	$file="\"$file\"";
	header("Content-Disposition: inline; filename=$file");
	header("Content-type: audio/mpeg");
	passthru("$mp3enc $mp3enc_settings - < $file");
	exit;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * S E A R C H   F U N C T I O N S
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

function searchArtist ($a) {
	global $mp3_dir, $cache, $file_rt_cache;
	$found = null;

	if ($cache && file_exists($file_rt_cache)) {
		#TODO: this is redudant with SearchTitle
		$titles = file($file_rt_cache);
		foreach($titles as $title) {
			if (stristr($title,$a)) {
				$ats = explode("/", $title);
				if (stristr($ats[0],$a)) {
					$found[$ats[0]][rtrim($ats[1])] = null;
				}
			}
		}
	} else {
		$artists = getArtists($mp3_dir);

		foreach ($artists as $artist) {
			if (stristr($artist, $a)) { $artists_found[] = $artist; }
		}

		if (!empty($artists_found)) {
			foreach($artists_found as $af) {
				foreach(getTitlesByArtist($af) as $title){
					$found[$af][$title] = null;
				}
			}
		}
	}
	return $found;
}


function searchTitle($t) {
	global $mp3_dir, $file_rt_cache, $cache;
	$found = null;

	if ($cache && file_exists($file_rt_cache)) {
		$titles = file($file_rt_cache);
		foreach($titles as $title) {
			if (stristr($title,$t)) {
				$ats = explode("/", $title);
				if (stristr($ats[1],$t)) {
					$found[$ats[0]][rtrim($ats[1])] = null;
				}
			}
		}
	} else {
		$artists = getArtists($mp3_dir);

		foreach($artists as $artist) {
			$d = dir("$mp3_dir/$artist");
			while($entry = $d->read()) {
				if ($entry == "." || $entry == "..") { continue; }
				if (is_dir("$mp3_dir/$artist/$entry")) {
					if (stristr($entry, $t)) {
						$found[$artist][$entry] = null;
					}
				}
			}
			$d->close();
		}
	}
	return $found;
}

#TODO-NOTE: search honors custom...
function searchSong($s) {
	global $ext_music, $file_rs_cache, $mp3_dir, $cache, $low_quality, $low_quality_suffix;
	$found = null;

	$cache_file = $file_rs_cache;
	if ($low_quality) {
		$cache_file = preg_replace("/(\.dat)$/i", "$low_quality_suffix$1", $cache_file);
	}

	if ($cache && file_exists($cache_file)) {
		$songs = file($cache_file);
		foreach($songs as $song) {
			if (stristr($song,$s)) {
				$ats = explode("/", $song);
				if (stristr($ats[2],$s)) {
					$found[$ats[0]][$ats[1]][] = rtrim($ats[2]);
				}
			}
		}
	} else {
		$artists = getArtists($mp3_dir);
		foreach($artists as $artist) {
			$titles = getTitlesByArtist($artist);
			if (empty($titles)) continue;
			foreach($titles as $title) {
				$d = dir("$mp3_dir/$artist/$title");
				while($entry = $d->read()) {
					if (preg_match("/\.($ext_music)$/i", $entry)) {
						if (stristr($entry, $s)) {
							$found[$artist][$title][] = $entry;
						}
					}
				}
				$d->close();
			}
		}
		global $low_quality, $low_quality_suffix;
		if ($low_quality) {
			#hack city
			foreach($found as $artist => $titles) {
				foreach($titles as $title => $songs) {
					foreach($songs as $key => $song) {
						if (preg_match("/($low_quality_suffix)\.($ext_music)$/i", $song)) {
							$song_nonlofi = preg_replace("/($low_quality_suffix)(\.$ext_music)$/i","$2", $song);
							if (isset($found[$artist][$title][$key + 1]) && $found[$artist][$title][$key + 1] == $song_nonlofi ) {
								unset($found[$artist][$title][$key + 1]);
							} elseif (isset($found[$artist][$title][$key - 1]) && $found[$artist][$title][$key - 1] == $song_nonlofi ) {
								unset($found[$artist][$title][$key - 1]);
							}
						}
					}
				}
			}
		}
	}
	return $found;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *  MISC FUNCTIONS
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

function Login($un, $up) {
	global $this_page, $password, $user_admin, $HTTP_SESSION_VARS, $salt;
	if ($user_admin == $un && $password == crypt($up,$salt)) {
		$HTTP_SESSION_VARS['admin'] = true;
		$redirect = "Location: $this_page";
	} else {
		$HTTP_SESSION_VARS['admin'] = false;
		$redirect = "Location: ${this_page}l=9&e=" . urlencode("Login incorrect");
	}
	header($redirect);
}


function o_shuffle(&$array){
	if (!isset($randinit)){
		srand((double) microtime() * 1000000);
		static $randinit = 1;
	}
	$last = count($array);
	while ($last > 0){
		$last--;
		$random = rand(0, $last);
		$temp = $array[$random];
		$array[$random] = $array[$last];
		$array[$last] = $temp;
	}
	return 1;
}


function debug($x, $exit = true, $phpinfo = true) {
	echo "<pre>";
	print_r($x);
	echo "</pre>";
	if ($phpinfo) { phpinfo(32); }
	if ($exit) { exit; }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * MP3/OGG file info and tags
 *
 * hacked pretty heavily from...
 * MP3::Info by Chris Nandor <http://sf.net/projects/mp3-info/>
 * class.id3.php by Sandy McArthur, Jr. <http://Leknor.com/code/>
 * getID3() [ogg stuff] by James Heinrich <http://www.silisoftware.com>
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

class mp3 {

	function mp3($file, $info=false, $tag=false, $faster=true) {
		$this->file = $file;
		$this->tag = 0;
		$this->info = 0;
		$this->faster = $faster;

		if (stristr(substr($file,-3),'mp3')) {
 			if ($info) $this->getMP3Info();
			if ($tag)  $this->getID3Tag();
		} elseif (stristr(substr($file,-3),'ogg')) {
			if ($info || $tag) $this->getOgg($info, $tag);
		} else {
			$this->getBadInfo();
		}
	}
	#TODO: support ID3v2
	function getID3Tag() {
		$v1 = $v2 = 0;

		$fh = fopen($this->file, 'rb');

		if (! ($fh = fopen($this->file, 'rb')) ) { return 0; }
		if (fseek($fh, -128, SEEK_END) == -1) { return 0; }
		$tag = fread($fh, 128);
		fclose($fh);

		if ( substr($tag,0,3) == "TAG") {
			$v1 = 1;
			if ($tag[125] == Chr(0) and $tag[126] != Chr(0)) {
				$format = 'a3TAG/a30title/a30artist/a30album/a4year/a28comment/x1/C1track/C1genre';
				$this->tag_version = "ID3v1.1";
			} else {
				$format = 'a3TAG/a30title/a30artist/a30album/a4year/a30comment/C1genre';
				$this->tag_version = "ID3v1";
			}
			$id3tag = unpack($format, $tag);
			foreach ($id3tag as $key => $value) {
				$this->$key = $value;
			}
			unset($this->TAG);
			return $this->tag = 1;
		}
		return $this->tag = 0;
	}

	function getMP3Info() {
		$file = $this->file;

		if (! ($f = fopen($file, 'rb')) ) { return false; }

		$this->filesize = filesize($file);
		$frameoffset = 0;
		$total = 4096;

		if ($frameoffset == 0) {
			if ($tag_size = $this->getV2Header($f)) {
				$total += $frameoffset += $tag_size;
				fseek($f, $frameoffset);
			} else {
				fseek($f, 0);
			}
		}

		if ($this->faster) {
			do {
				while (fread($f,1) != Chr(255)) { // Find the first frame
			  		if (feof($f)) { return false; }
				}
				fseek($f, ftell($f) - 1); // back up one byte
				$frameoffset = ftell($f);
				$r = fread($f, 4);

				$bits =  decbin($this->unpackHeader($r));

				if ($frameoffset > $total) { return $this->getBadInfo(); }
			} while ( !$this->isValidMP3Header($bits) );
		} else { #more accurate with some VBRs
			$r = fread($f, 4);
			$bits =  decbin($this->unpackHeader($r));

			while ( !$this->isValidMP3Header($bits) ) {
				if ($frameoffset > $total) { return $this->getBadInfo(); }
				fseek($f, ++$frameoffset);
				$r = fread($f, 4);
				$bits =  decbin($this->unpackHeader($r));
			}
		}

		#$this->bits = $bits;
		$this->header_found = $frameoffset;
		$this->vbr = 0;
		$vbr = $this->getVBR($f, $bits[12], $bits[24] + $bits[25], $frameoffset);
		fclose($f);

		#TODO: vbr file size

		if ($bits[11] == 0) {
		  $mpeg_ver = "2.5";
		  $bitrates = array(
				'1' => array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0),
				'2' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
				'3' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
					  );
		} else if ($bits[12] == 0) {
		  $mpeg_ver = "2";
		  $bitrates = array(
				'1' => array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0),
				'2' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
				'3' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
					  );
		} else {
		  $mpeg_ver = "1";
		  $bitrates = array(
				'1' => array(0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0),
				'2' => array(0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, 0),
				'3' => array(0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 0),
					  );
		}

		$layers = array(
		  array(0,3),
		  array(2,1),
				  );
		$layer = $layers[$bits[13]][$bits[14]];
		if ($layer == 0) return $this->getBadInfo();

		$bitrate = 0;
		if ($bits[16] == 1) $bitrate += 8;
		if ($bits[17] == 1) $bitrate += 4;
		if ($bits[18] == 1) $bitrate += 2;
		if ($bits[19] == 1) $bitrate += 1;
		if ($bitrate == 0) return $this->getBadInfo();

		$this->bitrate = $bitrates[$layer][$bitrate];

		$frequency = array(
		  '1' => array(
				'0' => array(44100, 48000),
				'1' => array(32000, 0),
					 ),
		  '2' => array(
				'0' => array(22050, 24000),
				'1' => array(16000, 0),
					 ),
		  '2.5' => array(
				'0' => array(11025, 12000),
				'1' => array(8000, 0),
						),
			 );
		$this->frequency = $frequency[$mpeg_ver][$bits[20]][$bits[21]];
		$mfs = $this->frequency / ($bits[12] ? 144000 : 72000);
		if ($mfs == 0) return $this->getBadInfo();
		$frames = (int)($vbr && $vbr['frames'] ? $vbr['frames'] : $this->filesize / $this->bitrate / $mfs);

		if ($vbr) {
			$this->vbr = 1;
			if ($vbr['scale']) $this->vbr_scale = $vbr['scale'];
			$this->bitrate = (int)($this->filesize / $frames * $mfs);
			if (!$this->bitrate) return $this->getBadInfo();
		}

		$s = -1;
		if ($this->bitrate != 0) {
			$s = ((8*($this->filesize))/1000) / $this->bitrate;
		}

		$this->time = sprintf('%.2d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
		$this->info = 1;
	}

	function getV2Header($fh) {
		fseek($fh, 0);
		$bytes = fread($fh, 3);

		if ($bytes != 'ID3') return;

		$bytes = fread($fh, 3);
		#get version
		#$bytes = fread($fh, 2);
		#get flags
		#$bytes = fread($fh, 1);

		#get ID3v2 tag length from bytes 7-10
		$tag_size = 10;
		$bytes = fread($fh, 4);
		$temp = array_reverse(unpack("C4", $bytes));
		for($i=0; $i<=3; $i++) {
			$tag_size += $temp[$i] * pow(128,$i);
		}

		return $tag_size;
	}

	function getVBR($fh, $id, $mode, &$offset) {
		$offset += 4;

		if ($id) {
			$offset += $mode == 2 ? 17 : 32;
		} else {
			$offset += $mode == 2 ? 9 : 17;
		}

		$bytes = $this->Seek($fh, $offset);

		if ($bytes != "Xing") return 0;

		$bytes = $this->Seek($fh, $offset);

		$vbr['flags'] = $this->unpackHeader($bytes);

		if ($vbr['flags'] & 1) {
			$bytes = $this->Seek($fh, $offset);
			$vbr['frames'] = $this->unpackHeader($bytes);
		}

		if ($vbr['flags'] & 2) {
			$bytes = $this->Seek($fh, $offset);
			$vbr['bytes'] = $this->unpackHeader($bytes);
		}

		if ($vbr['flags'] & 4) {
			$bytes = $this->Seek($fh, $offset, 100);
		}

		if ($vbr['flags'] & 8) {
			$bytes = $this->Seek($fh, $offset);
			$vbr['scale'] = $this->unpackHeader($bytes);
		} else {
			$vbr['scale'] = -1;
		}

		return $vbr;
	}

	function isValidMP3Header($bits) {
		if (strlen($bits) != 32) return false;
		if (substr_count(substr($bits,0,11),'0') != 0) return false;
		if ($bits[16] + $bits[17] + $bits[18] + $bits[19] == 4) return false;
		return true;
	}

	function getOgg($info, $tag) {
		$fh = fopen($this->file, 'rb');

		// Page 1 - Stream Header
		$h = null;
		if (!$this->getOggHeader($fh, $h)) { return $this->getBadInfo(); }

		if ($info) {
			$this->filesize = filesize($this->file);

			$data = fread($fh, 23);
			$offset = 0;

			$this->frequency = implode('',unpack('V1', substr($data, 5, 4)));
			$bitrate_average = 0;

			if (substr($data,  9, 4) !== chr(0xFF).chr(0xFF).chr(0xFF).chr(0xFF)) {
				$bitrate_max = implode('',unpack('V1', substr($data, 9, 4)));
			}
			if (substr($data, 13, 4) !== chr(0xFF).chr(0xFF).chr(0xFF).chr(0xFF)) {
				$bitrate_nominal = implode('',unpack('V1', substr($data, 13, 4)));
			}
			if (substr($data, 17, 4) !== chr(0xFF).chr(0xFF).chr(0xFF).chr(0xFF)) {
				$bitrate_min = implode('',unpack('V1', substr($data, 17, 4)));
			}
		}

		if ($tag) {
			// Page 2 - Comment Header
			if (!$this->getOggHeader($fh, $h)) { return $this->getBadInfo(); }
			$data = fread($fh, 16384);
			$offset = 0;
			$vendorsize = implode('',unpack('V1', substr($data, $offset, 4)));
			$offset += (4 + $vendorsize);
			$totalcomments = implode('',unpack('V1', substr($data, $offset, 4)));
			$offset += 4;

			for ($i = 0; $i < $totalcomments; $i++) {
				$commentsize = implode('',unpack('V1', substr($data, $offset, 4)));
				$offset += 4;
				$commentstring = substr($data, $offset, $commentsize);
				$offset += $commentsize;
				$comment = explode('=', $commentstring, 2);
				$comment[0] = strtolower($comment[0]);
				$this->$comment[0] = $comment[1];
			}
			$this->tag = 1;
		}

		if ($info) {
			// Last Page - Number of Samples
			fseek($fh, max($this->filesize - 16384, 0), SEEK_SET);
			$LastChunkOfOgg = strrev(fread($fh, 16384));
			if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
				fseek($fh, 0 - ($LastOggSpostion + strlen('SggO')), SEEK_END);
				if (!$this->getOggHeader($fh, $h)) { return $this->getBadInfo(); }
				$samples = $h->pcm;
				$bitrate_average = ($this->filesize * 8) / ($samples / $this->frequency);
			}

			if ($bitrate_average > 0) {
				$this->bitrate = $bitrate_average;
			} else if (isset($bitrate_nominal) && ($bitrate_nominal > 0)) {
				$this->bitrate = $bitrate_nominal;
			} else if (isset($bitrate_min) && isset($bitrate_max)) {
				$this->bitrate = ($bitrate_min + $bitrate_max) / 2;
			}

			$this->bitrate = (int) ($this->bitrate / 1000);
			$s = -1;
			if (isset($this->bitrate)) {
				$s = (float) (($this->filesize * 8) / $this->bitrate / 1000);
			}
			$this->time = sprintf('%.2d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
			$this->info = 1;
		}
		return true;
	}

	function getOggHeader(&$fh, &$h) {
		$baseoffset = ftell($fh);
		$data = fread($fh, 16384);
		$offset = 0;
		while ((substr($data, $offset++, 4) != 'OggS')) {
			if ($offset >= 10000) { return FALSE; }
		}

		$offset += 5;
		$h->pcm = implode('',unpack('V1', substr($data, $offset)));
		$offset += 20;
		$segments = implode('',unpack('C1', substr($data, $offset)));
		$offset += ($segments + 8);
		fseek($fh, $offset + $baseoffset, SEEK_SET);

		return true;
	}

	function getBadInfo() {
		$this->time = $this->bitrate = $this->frequency = 0;
		$this->filesize = filesize($this->file);
		return false;
	}

	function Seek($fh, &$offset, $n = 4) {
			fseek($fh, $offset);
			$bytes = fread($fh, $n);
			$offset += $n;
			return $bytes;
	}

	function unpackHeader($byte) {
		return implode('', unpack('N', $byte));
	}
}


/*
	Internal compression of files via PHP zlib extension
	This is most likely based on class zipfile by Eric Mueller <www.themepark.com>,
	although that's not where I got it.
*/
class zipfile {
	var $datasec = array();
	var $rootdir = array();
	var $offset	= 0;

	function add_file($data,$name) {
		#$name	= str_replace('\\', '/', $name);
		$name	= basename($name);
		$ctime = getdate();
		$ctime = preg_replace("/(..){1}(..){1}(..){1}(..){1}/","\\x\\4\\x\\3\\x\\2\\x\\1",dechex(($ctime['year']-1980<<25)|($ctime['mon']<<21)|($ctime['mday']<<16)|($ctime['hours']<<11)|($ctime['minutes']<<5)|($ctime['seconds']>>1)));
		eval('$ctime = "'.$ctime.'";');

		$crc = crc32($data);
		$nlength	= strlen($data);
		$zdata = gzcompress($data,0);
		$zdata = substr(substr($zdata,0,strlen($zdata) - 4),2);
		$clength = strlen($zdata);

		$this->datasec[] = "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00$ctime" . pack('V',$crc) . pack('V',$clength) . pack('V',$nlength) . pack('v',strlen($name)) . pack('v',0) . $name . $zdata . pack('V',$crc) . pack('V',$clength) . pack('V',$nlength);
		$this->rootdir[] = "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00$ctime" . pack('V',$crc) . pack('V',$clength) . pack('V',$nlength) . pack('v',strlen($name)) . pack('v',0) . pack('v',0) . pack('v',0) . pack('v',0) . pack('V',32) . pack('V',$this->offset) . $name;
		$this->offset = strlen(implode('',$this->datasec));
	}

	function file() {
		$data	= implode('',$this->datasec);
		$ctrldir	= implode('',$this->rootdir);
		return $data . $ctrldir . "\x50\x4b\x05\x06\x00\x00\x00\x00" . pack('v',sizeof($this->rootdir)) . pack('v',sizeof($this->rootdir)) . pack('V',strlen($ctrldir)) . pack('V',strlen($data)) . "\x00\x00";
	}
} 
?>
