The process can be refined by disallowing certain files or folders or specifing allowed filetypes
* 2- The song information is parsed from the from ID3/other media tags by way of the getID3 php class, or the filename
* 3- Basic and Advanced Filtering (single or multiple subfolder filters)
* 4- Playlist sorting options are built in:
* mtime (file modified time -- default), file name, ID3 artist name, ID3 track title, random.
* Sorting preferences based on configurable setting or use request (URL argument)
* 5- Overrides to many default configuration options are allowed bia URL argument:
* autoplay, sorting, basic filtering
*
*/
/*
* === Changelog ===
*
* v0.3 > v0.4
* _________________
*
* 1- added filter by folder options
* -listed folders with checkboxes for self calling filter
* -modified main function to add filtered folders to 'disallowed' when filter applied
*
* 2- updated to work with jplayer 2.0 (many changes // mostly in JS functions and source files)
*
* 3- corrected bugs and refined code
*
*
* v0.4 > v0.5
* _________________
*
* 1- enabled config variable and/or URL argument for overriding default autoplay (this was long overdue)
* usage: set $autoplay var ['true'/'false'] or pass argument via URL ([?/&]autoplay=[false/true])
*
* 2- refined sorting options and added the $id3sort config variable
* usage: [assuming $useID3=true] set $ID3sort to 'track' or 'artist' to define sorting terms
*
* 3- added config variable and/or URL argument for disabling non essential information (show only playlist)
* usage: pass argument via URL ([?/&]makesparse=true) or set $skipdesc=true;
*
* 4- updated to accomodate JPlayer v2.1 as a drop-in solution
*
* 5- corrected bugs and refined code
*
*
* v0.5 > v0.6
* _________________
*
* 1- revised based on JPlayer v2.1
*
* 2- added configurable $playermode option to allow for audio only or av mode // created a splash graphic (poster) for default use
* usage: set to 'audio' for an audio only playlist (default)
* or 'av' for audio (with poster) and video playlist
*
* 3- added configurable $showfromtext option // 'showfrom' text can be easily disabled
* usage: set to 'true' to display information about which folder a song resides in (default)
* or 'false' to ignore that information
*
* 4- added configurable $allowdownload option // when disabled, downloads are disabled
* usage: set to 'true' to allow downloads (default) or 'false' to disable
*
* 5- tested with two themes (blue.monday and pink.flag) // i like blue.monday for audio or video, and pink.flag only for video
*
* 6- updated to getID3 v1.9.3
*
*
* v0.6 > v0.65
* _________________
*
* 1- added code to display embedded (via getID3) or name-based album art as poster image
* 2- added code to derive the jPlayer's 'supplied' argument based on occurring and allowed file extensions (fixed firefox issue)
*
* _tbd_
* 3- scrobble to last.fm (work through real api OR use existing code)
*
*
*/
/*
* TBD
* _________________
*
* add title 'right-click to download' as title text for file download // add option to change extension name to 'download'?
* >> looks like will require jplayer.playlist.js modification
*
* update so that multiple files with same name are treated as one item?
* otherwise, video will need to be m4v and flash will play some
*
* using error logs, fix errors.
*
* separate out changelog (already done on page?), tbd, and future
*
*/
/*
* Future
* _________________
*
* enable drag and drop sorting
*
*/
/*
* Development Variables
*/
$ver='v0.65';
$verfulltitle='JPlaylister (JPlayer Playlister)';
$vershorttitle='JPlaylister';
$versubtitle='JPlayer Audio/Video Playlist Generator';
$verreleasedate='2012.06(?)';
$verdev=false;
/*
* Configuration Variables
*/
//set media directory (ex. 'songs', 'music', 'media/songs')
$relMusicDir='media/Key of Bb'; //this directory is where the playlister starts looking for files -- subfolders are recursed
//set default playlist sorting order options
$sortby='name'; //set to 'mtime' for most recently added first, 'name' for alphabetic sorting or 'random' for random order
$ID3sort='track'; //set to 'track' or 'artist' to sort by each ![requires $useID3=true]
//set to true to easily disable the right floating div -- alternately, you can just delete it
$skipdesc=true; //set to true to remove the descriptive right column (also possible using [?/&]makesparse=true argument)
//set playlist action, display, and information options
$autoplay='false'; //set to 'false' to disable autoplay, 'true' to enable (also possible using [?/&]autoplay=[false/true] argument)
$useID3=true; //set to false for filename information only
$showfromtext='true'; // if true, from [folder location information] will be shown under the playlist items
$allowdownload='false'; //if not 'true', download will not be allowed (easily)
$playermode='audio'; //set to 'audio' for audio only, 'av' for poster/video display -- filetypes defined in function
$posterlocation='graphics/jplayer_playlister_poster.png'; //change to set 'default' poster if in av/video mode ![requires $playermode='av']
//html
$charset='utf-8'; //formerly, this was 'iso-8859-1'
//set for debug
$debug=false; //set to true for tons of debug echoes
/*** NOTE ***
There are also configurable settings within the recursiveGetSongs() function at the end of this page
These variables determine which file types (via the file extension) are added to the playlist
****/
/*
* Filtering
*/
//check for POST filter information >> passed from the 'apply filter' button
if($_POST[submit]=='Apply Filter'){
if(count($_POST["filter"])==0){
//error -- all music filtered out
echo 'uh...i can\'t make a playlist if you filter everything out. i\'m playing everything!';
//reset filter -- make them pay
$filtered=(array)$_POST["filter"];
}
else{
//compare reference vs filter
$filtered=array_diff((array)$_POST["filterref"], (array)$_POST["filter"]);
}
if($debug==true){
echo' filtering: ';
var_dump($filtered);
}
}
//check for GET filter information >> passed via the URL from clicking on a subfolder
if($_GET['filtered']!=''){
$filtered=explode(',',$_GET['filtered']);
}
//prep for sorting links
if($filtered!=''){
$filteredarg='&filtered='.implode(',', $filtered);
}
/*
* Check for passed variables
*/
//set $relMusicDir based on name if passed (this is a variant of a filter option)
if($_GET["name"]!='')
$relMusicDir=$relMusicDir.'/'.$_GET["name"];
//check for sort by request
if($_GET["sortby"]!=''){
$sortby=$_GET["sortby"];
$sortbyarg='&sortby='.$sortby;
}
else if(isset($_POST[sortby]))
$sortby=$_POST[sortby];
$sortbyarg='&sortby='.$sortby;
//check for makesparse request
if($_GET["makesparse"]!=''){
$makesparse=$_GET["makesparse"];
$makesparsearg='&makesparse='.$makesparse;
}
else if(isset($_POST[makesparse])){
$makesparse=$_POST[makesparse];
$makesparsearg='&makesparse='.$makesparse;
}
//check for disable/enable autoplay arg
if($_GET["autoplay"]=='false'){
$autoplay='false';
}
//check for disable/enable autoplay arg
else if($_GET["autoplay"]=='true'){
$autoplay='true';
}
/*
* Determine whether playing a subset (subfolder) of music, or all // Set display information
*/
if($_GET["name"]=='')
$from='Displaying music from all folders and subfolders';
else{
$from='Displaying music from '.clean($_GET["name"], 'display', $debug);
$playalllink=' « Back to Play All';
}
/*
* Prep to get artist / track information for playlist
*/
//set temp vars
$fileinfo=array();
$fileinfo['count']=0;
//require getID3 code if using it
if($useID3==true){
//get php version in parts for comparison
$phpvparts=explode('.',phpversion());
//display error if php version is lower than needed for GETID3
if($phpvparts[0]<5 OR ( $phpvparts[0]=5 AND $phpvparts[1]<1 AND $phpvparts[2]<5)){
echo 'GETID3 1.9.3 (used for ID3 tag information parsing) requires PHP 5.0.5 or higher. Get older version or upgrade PHP for ID3 usage. *disabling id3*';
}
else{
//for ID3 (not just filename) information, include getID3 php class
require_once('getid3/getid3.php'); /* Comment out if you only want to use filenames */
// Initialize getID3 engine
$getID3 = new getID3;
}
}
//call function to get information for all songs in target directory (and sub-directories) and store in an array
$fileinfo=recursiveGetSongs($relMusicDir, $fileinfo, $useID3, $getID3, null,$debug, $filtered,$playermode);
//debug -- show results
if($debug==true) var_dump($fileinfo);
/*
* Prepare jplayer filetype inclusions as determined by the available file extensions
*/
//create 'supplied' extension list from the keys of $fileinfo['extensions']
$supplied='supplied: "'.implode(', ',array_keys($fileinfo['extensions'])).'"';
unset($fileinfo['extensions']); //unset subarray so it doesn't break the playlist creation
/*
* Make subfolder links and filtering options
*/
if($fileinfo['folders']!=''){
//clear variables
$comma='';
$count=0;
//get folder and path information
$folders=explode('|', $fileinfo['folders']);
$folderpaths=explode('|', $fileinfo['folderpaths']);
$foldershtml='
';
}
//clear comma
$comma='';
//debug
if($debug==true)echo'--- filename: '.$files[0]
.' ID3 artist - title: '.$tags[0]
.' file count: '.$count;
/*
* Preparing Sorting Links / Notifications
*/
//sort by mtime (most recent first)
if($sortby=='mtime'){
$fileinfo=subval_sort($fileinfo, 'modified', 'd');
$sortedbytext='Sorted by date added';
}
//randomize array if requested
else if($sortby=='random'){
shuffle($fileinfo);
$sortedbytext='Playlist randomized';
}
else if($sortby=='name'){
if($useID3==true){
//sort by track name or artist name
if($ID3sort=='artist'){
$sortedbytext='Sorted by artist name';
$fileinfo=subval_sort($fileinfo, 'artist');
}
else if($ID3sort=='track'){
$sortedbytext='Sorted by track name/title';
$fileinfo=subval_sort($fileinfo, 'title');
}
}
else{
//name can be filename, track title, or 1st display item
$fileinfo=subval_sort($fileinfo, 'fn');
$sortedbytext='Sorted by file name';
}
}
//show sorting information and options
$sortoptions='Date Added | '
.'File Name | '
.'Shuffle Songs';
?>
Spy Eye Studios: PLAY-A-LONG (Key of Bb)
';
//if development version, echo link to main version
if ($verdev==true){
echo'
';
include'../current_version.html';
echo'
';
}
//echo'
'.$verfulltitle.' '.$ver
// .' '.$versubtitle.'
';
echo $notice;
/*
* check for skipdesc variable or makesparse arg || otherwise, set content div -- this section can be deleted safely
*/
if( ($skipdesc==true) OR ($makesparse!='true') ){
//set content div information
$contentdiv=<<
$v) {
$b[$k] = strtolower($v[$subkey]);
}
if($order=='a'){
asort($b);
}
else if($order=='d'){
arsort($b);
}
foreach($b as $key=>$val) {
$c[] = $a[$key];
}
return $c;
}
/*
* function written to clean up my messy code (too many slashes ... display '/' as '»' (>>) for user friendliness )
*/
function clean($dirty, $type='general', $debug=false){
//debug
if($debug==true)
echo' value before clean: '.$dirty.' (first character: '.substr($dirty, 0, 1).')';
/*
* General cleaning -- remove '/' at front and end
*/
if(substr($dirty, 0, 1)=='/'){
//echo' found leading /';
$dirty=substr($dirty, 1, strlen($dirty)-1);
}
if(substr($dirty, -1)=='/')
$dirty=substr($dirty, 0, strlen($dirty)-1);
//prepare the subfolder display information by type
if($type=='link')
$dirty=str_replace(array('//','»'), array('/', '/'), $dirty);
else if($type=='display')
$dirty=str_replace(array('///','//','/'), array('»','»', '»'), $dirty);
else
$dirty=str_replace('»', '/', $dirty);
if($debug==true)echo' | after clean: '.$dirty;
//return
return $dirty;
}
function makelink($linkme, $debug=false){
$link=str_replace('»', '/', $linkme);
$link=str_replace('//', '/', $link);
return $link;
}
function recursiveGetSongs($directory, $fileinfo, $useID3, $getID3, $parent=null, $debug, $filtered=null, $playermode='audio'){
/*
* configure function here:
*
* _usage_
* > the disallowed array should include any folders or files you don't want displayed
* > the allowedfiletypes array should include any file extentions you want to play
*/
$disallowed=array('..', '.', 'js', 'skin', 'z_jquery-ui-1.7.1.custom', 'getid3');
//for audio playermode, only look for defined audio files
if($playermode=='audio'){
$allowedfiletypes=array('mp3', 'ogg', 'oga', 'm4a', 'wma', 'webma');
}
else{
$allowedfiletypes=array('mp3', 'ogg', 'oga', 'm4a', 'wma', 'webma', 'm4v', 'ogv', 'mp4', 'webmv', 'webm');
}
if($filtered!=null){
$disallowed=array_merge((array)$filtered, (array)$disallowed);
}
//simple error fix
if($directory=='./')
$directory='.';
//debug
if ($debug==true)echo'Dir to open: '.$directory;
//open directory
$dir = opendir($directory);
while ($read = readdir($dir)){
//if ( !in_array($read, $disallowed) AND ( $filter!=null AND in_array($read, $filter) ) )
if ( !in_array($read, $disallowed) )
{
if($debug==true)echo $read.' ';
//if is not dir, handle file
if ( !is_dir($directory.'/'.$read) ){
if($debug==true)echo '^^ not dir | dir: '.$directory.' ';
if( in_array(substr($read, -3, 3), $allowedfiletypes) ){
if($useID3==TRUE){
//store id3 info
$FullFileName = realpath($directory.'/'.$read);
if($debug==TRUE)echo' FFN » '.$FullFileName;
$ThisFileInfo = $getID3->analyze($FullFileName);
getid3_lib::CopyTagsToComments($ThisFileInfo);
$fileinfo[$read]['artist']=$ThisFileInfo['comments_html']['artist'][0];
$fileinfo[$read]['album']=$ThisFileInfo['comments_html']['album'][0];
$fileinfo[$read]['title']=$ThisFileInfo['comments_html']['title'][0];
$fileinfo[$read]['filename']=$ThisFileInfo['filename'];
//$fileinfo[$read]['filenamealt']=$read; //alternate filename for hebrew problem
$fileinfo[$read]['modified']=date ("YmdHis", filemtime($directory.'/'.$read));
/*
* ALBUM ART TESTING //ID3
*/
$usealbumart=true;
$albumartdefaultname='album_art.jpg'; //set as text string ('front.jpg', 'album_art.jpg') which will be displayed as art
$albumartaltname=$fileinfo[$read]['artist'].'_'.$fileinfo[$read]['album'].'.jpg'; //allows alternate naming convention
//look for album art in media directory based on default name
if($usealbumart==true AND file_exists($directory.'/'.$albumartdefaultname)){
$fileinfo[$read]['art']=$directory.'/'.$albumartdefaultname;
if($debug==true)
echo'album art already exists for '.$fileinfo[$read]['filename'].' @ '.$fileinfo[$read]['art'].' ';
}
//look for album art in media directory based on alternate name (allow two folder-based locations/naming options)
else if($usealbumart==true AND file_exists($directory.'/'.$albumartaltname)){
$fileinfo[$read]['art']=$directory.'/'.$albumartaltname;
if($debug==true)
echo'album art already exists for '.$fileinfo[$read]['filename'].' @ '.$fileinfo[$read]['art'].' ';
}
//if embedded art exists -- extract it
else if($usealbumart==true AND isset($ThisFileInfo['comments']['picture'][0]['data'])){
//determine filename -- if album name exists, use artist_album -- else use filename
if(!isset($fileinfo[$read]['album']))
$fn='graphics/artstore/'.$fileinfo[$read]['artist'].'_'.$fileinfo[$read]['album'].'.jpg';
else
$fn='graphics/artstore/'.$fileinfo[$read]['filename'].'.jpg';
//if fn doesn't exist, create it
if (!file_exists($fn)){
//create image
$img=imagecreatefromstring($ThisFileInfo['comments']['picture'][0]['data']);
imagejpeg($img, $fn);
if($debug==true)
echo'file created: ';
imagedestroy($img);
}
else if($debug==true){
//file already exists, pass fn back for poster usage
echo'file exists: ';
}
//set fn as array item
$fileinfo[$read]['art']=$fn;
}
//END ALBUM ART TESTING
if($debug==true)
echo " $read was last modified: " . date ("YmdHis", filemtime($directory.'/'.$read));
$fileinfo[$read]['path']=$directory.'/'.$read;
if($debug==true)echo'path:'.$fileinfo[$read]['path'].' > fn: '.$fileinfo[$read]['filename'].'
';
if($parent!=null)
$fileinfo[$read]['from']=str_replace(array('./', '//', '/'), array('', '»', '»'), $directory); // was =$parent
else
$fileinfo[$read]['from']='root'; //testing this
if($debug==true){
echo' '.$fileinfo[$read]['from'].' ';
echo$ThisFileInfo['filename'].' '.$fileinfo[$read]['path'].' ';
}
//capture file extension
$fileinfo['extensions'][substr($ThisFileInfo['filename'],strrpos($ThisFileInfo['filename'],'.')+1)]=1;
}
else{
//store filename
$fileinfo[$fileinfo['count']]['path']=$directory.'/'.$read;
$fileinfo[$fileinfo['count']]['fn']=$read;
if($parent!=null)
$fileinfo[$fileinfo['count']]['from']=str_replace(array('./', '//', '/'), array('', '»', '»'), $directory);
$fileinfo[$fileinfo['count']]['modified']=date ("YmdHis", filemtime($directory.'/'.$read));
//$fileinfo[$fileinfo['count']]=date ("YmdHis", filemtime($directory.'/'.$read));
/*
* ALBUM ART TESTING //NON ID3
*/
$usealbumart=true;
$albumartdefaultname='album_art.jpg'; //set as text string ('front.jpg', 'album_art.jpg') which will be displayed as art
$albumartaltname=$read.'.jpg'; //allows alternate naming convention
//look for album art in media directory based on default name
if($usealbumart==true AND file_exists($directory.'/'.$albumartdefaultname)){
$fileinfo[$read]['art']=$directory.'/'.$albumartdefaultname;
if($debug==true)
echo'album art already exists for '.$fileinfo[$read]['filename'].' @ '.$fileinfo[$read]['art'].' ';
}
//look for album art in media directory based on alternate name (allow two folder-based locations/naming options)
else if($usealbumart==true AND file_exists($directory.'/'.$albumartaltname)){
$fileinfo[$read]['art']=$directory.'/'.$albumartaltname;
if($debug==true)
echo'album art already exists for '.$fileinfo[$read]['filename'].' @ '.$fileinfo[$read]['art'].' ';
}
//if embedded art exists -- extract it
else if($usealbumart==true AND isset($ThisFileInfo['comments']['picture'][0]['data'])){
//determine filename -- if album name exists, use artist_album -- else use filename
if(!isset($fileinfo[$read]['album']))
$fn='graphics/artstore/'.$fileinfo[$read]['artist'].'_'.$fileinfo[$read]['album'].'.jpg';
else
$fn='graphics/artstore/'.$fileinfo[$read]['filename'].'.jpg';
//if fn doesn't exist, create it
if (!file_exists($fn)){
//create image
$img=imagecreatefromstring($ThisFileInfo['comments']['picture'][0]['data']);
imagejpeg($img, $fn);
if($debug==true)
echo'file created: ';
imagedestroy($img);
}
else if($debug==true){
//file already exists, pass fn back for poster usage
echo'file exists: ';
}
//set fn as array item
$fileinfo[$read]['art']=$fn;
}
//END ALBUM ART TESTING
}
//inc counter
$fileinfo['count']=$fileinfo['count']+1; // had ++ and it didn't work
}
else
;//do nothing
}
//else, must be a folder (as determined above), recurse folder
else{
//debug
if($debug==true)echo '^^ DIR ';
//capture subfolders in case they are needed
if($parent!='')$fileinfo['folders'].=$parent.'»'.$read.'|';
else $fileinfo['folders'].=$read.'|';
$fileinfo['folderpaths'].=$directory.'/|';
$fileinfo=recursiveGetSongs($directory.'/'.$read, $fileinfo, $useID3, $getID3, $parent.'/'.$read, $debug, $filtered, $playermode);
}
}
}
closedir($dir);
return $fileinfo;
}
?>