Comprehensive program for managing media files; includes library scan, search, tag and playlist maintenance features and more. Download attached zip.
Also download my library of Perl date, CRC, logging, redirection and text utility functions.
#! /usr/bin/perl
# Above line may need installation specific customization.
# Requires Perl language interpreter and an HTTP server.
# Media Collection Manager - Collection Browser.
# "mmgr.pl" created by Andrew Bedno, 2005-2006, Andrew@Bedno.com, All Rights Reserved.
# See associated readme file for details.
# Arguments:
# l = Library.
# s = Seach target.
# n = Name for playlist save.
# w = Name for playlist load.
# v = Name for playlist append.
# b = Playlist text.
# c = Append playlist from category.
# o = Output style.
# g = SubDir search target.
# To install the Perl MP3 module run "ppm install MP3-Info"
use MP3::Info;
use MP3::Info qw(:genres);
$PAGE_CODE = 'mmgr';
require("mcomm.pl");
# Various internal constants.
$playlist_rows = 15;
$playlist_cols = 40;
$playlist_rows_used = 0;
$playlist_cols_used = 0;
$ta_col_exp_fact = 125/100;
$ta_row_exp_add = 3;
$LIBS_TTL{'dirs'} = 0;
$LIBS_TTL{'files'} = 0;
$LIBS_TTL{'media'} = 0;
$LIBS_TTL{'links'} = 0;
$LIBS_TTL{'notes'} = 0;
$LIBS_TTL{'seconds'} = 0;
$LIBS_TTL{'gb'} = 0;
$libs_cnt = 0;
# Various formatting constants.
$html_end = "\n"."<!-- END -->\n"."<a name='bottom'></a>\n".$html_foot;
$Arg_Cat = lc($field{'C'});
$Arg_SubDirSearch = lc($field{'G'});
$SubDirSearch_len = length($Arg_SubDirSearch);
$Arg_Name = $field{'N'};
$Arg_Submit = lc($field{'SUBMITSAVE'});
&clean($Arg_Name);
$Arg_Name =~ s/\s/\_/g;
$Arg_Name =~ s/[^A-Za-z0-9\-\_]//gi;
if (! $Arg_Name) { $Arg_Name = 'default'; }
$Arg_Load = $field{'W'};
&clean($Arg_Load);
$Arg_Load =~ s/\s/\_/g;
$Arg_Load =~ s/[^A-Za-z0-9\-\_]//gi;
$Arg_Append = $field{'V'};
&clean($Arg_Append);
$Arg_Append =~ s/\s/\_/g;
$Arg_Append =~ s/[^A-Za-z0-9\-\_]//gi;
if ( ($Arg_Load) and ($Arg_Append) ) { $Arg_Load = ''; }
$Arg_Body = $field{'B'};
$Arg_Body =~ s/\r//gm;
&clean($Arg_Body);
if (length($Arg_Body) > 0) { $Arg_Body .= "\n"; }
$field{'INCOFS'} =~ s/false//i;
$field{'INCDIR'} =~ s/false//i;
if ( (! $field{'SRCDIR'}) and (! $field{'SRCFILE'}) and (! $field{'SRCTRACK'}) and (! $field{'SRCTITLE'}) and
(! $field{'SRCARTIST'}) and (! $field{'SRCALBUM'}) and (! $field{'SRCDATES'}) and
(! $field{'SRCGENRE'}) and (! $field{'SRCTEXT'}) ) {
$field{'SRCDIR'} = 'on';
$field{'SRCFILE'} = 'on';
$field{'SRCTRACK'} = 'on';
$field{'SRCTITLE'} = 'on';
$field{'SRCARTIST'} = 'on';
$field{'SRCALBUM'} = 'on';
$field{'SRCDATES'} = 'on';
$field{'SRCGENRE'} = 'on';
$field{'SRCTEXT'} = 'on';
}
# Initialize hashes, etc.
$html_head_sent = 0;
$toc_links = '';
$LIB_sum = '';
%subdir_shown = ();
WriteLog("Started.");
if ($cmdline_argcnt < 1) {
syswrite STDOUT, "Content-type: text/html; charset=UTF-8\n\n";
}
if (! $Arg_Libs) {
$error_msg = 'No libraries specified.';
}
&ConfirmDir($SERVER_DIR_ARR{'sys'});
&ConfirmDir($SYS_DIR{'pl'});
&ConfirmDir($SYS_DIR{'m3u'});
# Playlist delete.
if (! $error_msg) {
if ( ($Arg_Name) and ($field{'CHKDEL'}) ) {
$m3u_delname = $SYS_DIR{'pl'}.$Arg_Name.$m3u_ext;
unlink($m3u_delname);
if (-e $m3u_delname) {
$error_msg = 'Error on PlayList delete: "'.$m3u_delname.'"';
}
$Arg_Body = ''; $Arg_Name = '';
}
} # Playlist delete.
# Possible playlist load.
if (! $error_msg) {
if ( ($Arg_Load) or ($Arg_Append) ) {
if ($Arg_Load) { $Arg_Body = ''; }
$m3u_loadfrom = $SYS_DIR{'pl'}.$Arg_Load.$Arg_Append.$m3u_ext;
if (! -e $m3u_loadfrom) {
$error_msg = 'Error on PlayList load: "'.$m3u_loadfrom.'"';
} else {
open(PL_IN, "< ".$m3u_loadfrom);
while (<PL_IN>) {
$pl_in_line = $_;
$pl_in_line =~ s/\r//gm;
$pl_in_line =~ s/\/\//igm;
# $pl_in_line =~ s/$Arg_Folder//igm;
&clean($pl_in_line);
if ($pl_in_line) { $Arg_Body .= $pl_in_line."\n"; }
}
close(PL_IN);
}
$Arg_Name = $Arg_Load.$Arg_Append;
}
} # Playlist load.
# Possible section playlist create/append.
if (! $error_msg) {
if ($Arg_Cat) {
$M_DAT_IDX = $SERVER_DIR_ARR{'sys'}.$Arg_Folder.'.idx';
if (! -e $M_DAT_IDX) {
$error_msg = 'Media search index not found: "'.$M_DAT_IDX.'"';
} else {
# Loop through index file capturing matches.
$cat_playlist_out = "";
open(IDX_DAT, "< ".$M_DAT_IDX);
%idx_head_part = ();
$idx_line_cnt = 0;
while (<IDX_DAT>) {
$idx_line_in = $_;
chomp($idx_line_in);
$idx_line_cnt++;
%idx_part = ();
if ($idx_line_cnt == 1) {
# Read header.
@idx_head_parts = split(/$search_delim/, substr($idx_line_in,2,length($idx_line_in)-2) );
$idx_head_part_last = $#idx_head_parts;
} else {
# Read data.
if (substr($idx_line_in,0,1) ne '#') {
@idx_line_parts = split(/$search_delim/, $idx_line_in);
for ($idx_field_idx = 0; $idx_field_idx <= $idx_head_part_last; $idx_field_idx++) {
$idx_part{$idx_head_parts[$idx_field_idx]} = $idx_line_parts[$idx_field_idx];
}
# syswrite STDOUT, 'file='.$idx_part{'file'}.'; subdir='.$idx_part{'subdir'}.'; line="'.$idx_line_in.'"<br>'."\n";
if ($idx_part{'file'}) {
if ( substr(lc('/'.$idx_part{'subdir'}.'/'),0,length($Arg_Cat)) eq lc($Arg_Cat) ) {
if ( length($cat_playlist_out) < $max_playlist_create_size ) {
$cat_playlist_out .= &FormatLink($Arg_Folder, 'a', $idx_part{'subdir'}, $idx_part{'file'}, $idx_part{'m3uname'}, $idx_part{'url'})."\n";
if ( length($cat_playlist_out) >= $max_playlist_create_size ) {
$cat_playlist_out .= "# truncated\n";
}
}
}
}
}
}
} # Read idx.
close(IDX_DAT);
if ($cat_playlist_out) {
$Arg_Body .= $cat_playlist_out;
@section_playlist_parts = split(/\//, lc($Arg_Cat));
$section_playlist_parts_last = $#section_playlist_parts;
if (! $Arg_Name) {
for ($section_playlist_parts_idx = 0; $section_playlist_parts_idx <= $section_playlist_parts_last; $section_playlist_parts_idx++) {
if ($section_playlist_parts[$section_playlist_parts_idx]) {
$Arg_Name = $section_playlist_parts[$section_playlist_parts_idx];
while ($Arg_Name =~ /[^a-z0-9\_]/) { $Arg_Name =~ s/[^a-z0-9\_]/\_/g; }
while ($Arg_Name =~ /\_\_/) { $Arg_Name =~ s/\_\_/\_/g; }
while ($Arg_Name =~ /\_$/) { $Arg_Name =~ s/\_$//g; }
while ($Arg_Name =~ /^\_/) { $Arg_Name =~ s/^\_//g; }
}
}
}
}
} # Index exists.
} # Cat arg present.
} # Playlist create from section.
# Validate current playlist.
$playlist_sec = 0;
$playlist_bytes = 0;
$playlist_lines_ttl = 0;
$playlist_lines_valid = 0;
if ($Arg_Body) {
$playlist_out = '';
$playlist_out_line = '';
@pl_check_lines = split(/\n/, $Arg_Body);
foreach $pl_check_line (@pl_check_lines) {
&clean($pl_check_line);
$pl_check_line =~ s/^[\#\ ]+//;
if ($pl_check_line) {
$playlist_lines_ttl++;
$pl_check_file = $pl_check_line;
if ($pl_check_file !~ /^http/) {
if (! -e $pl_check_file) {
$playlist_out_line = '# '.$pl_check_line;
} else {
$playlist_lines_valid++;
if ( ($pl_check_line =~ /\.mp3$/i) or
($pl_check_line =~ /\.mpg$/i) or
($pl_check_line =~ /\.mpeg$/i) ) {
$pl_check_fields = MP3::Info->new($pl_check_file);
# Fetch the mp3 info fields, especially the duration.
if ( ($pl_check_fields->{"SS"} > 0) or ($pl_check_fields->{"MM"} > 0) ) {
$playlist_sec += (($pl_check_fields->{"MM"}*60)+$pl_check_fields->{"SS"});
}
}
@pl_file_stats = stat($pl_check_file);
$playlist_bytes += $pl_file_stats[7];
$playlist_out_line = $pl_check_line;
}
} else {
$playlist_out_line = $pl_check_line;
}
if ($playlist_out_line) {
$playlist_rows_used++;
if (length($pl_check_line) > $playlist_cols_used) {
if (length($pl_check_line) <= $max_playlist_cols) {
$playlist_cols_used = length($pl_check_line);
} else {
$playlist_cols_used = $max_playlist_cols;
}
}
$playlist_out .= $playlist_out_line."\n";
}
}
}
if (int($playlist_cols_used*$ta_col_exp_fact) > $playlist_cols) { $playlist_cols = int($playlist_cols_used*$ta_col_exp_fact); }
if (($playlist_rows_used+$ta_row_exp_add) > $playlist_rows) { $playlist_rows = ($playlist_rows_used+$ta_row_exp_add); }
if ($playlist_rows > $max_playlist_rows) { $playlist_rows = $max_playlist_rows; }
$Arg_Body = $playlist_out;
}
# Possible playlist save.
if (! $error_msg) {
if ( ( ($Arg_Submit =~ /save/i) and ($Arg_Name) and ($Arg_Body) ) or
( ($Arg_Name eq 'default') and ($Arg_Body) ) ) {
$m3u_saveas = $SYS_DIR{'pl'}.$Arg_Name.$m3u_ext;
if (-f $m3u_saveas) { unlink($m3u_saveas); }
open(PL_OUT, "> ".$m3u_saveas);
$playlist_out = $Arg_Body;
$playlist_out =~ s/\r//gm;
&clean($playlist_out);
# $playlist_out =~ s/^/$Arg_Folder/gmi;
$playlist_out =~ s/\//\/igm;
print PL_OUT $playlist_out."\n";
close(PL_OUT);
chmod(0666,$m3u_saveas);
if (! -e $m3u_saveas) {
$error_msg = 'Error on PlayList save: "'.$m3u_saveas.'"';
} else {
$Arg_Load = $Arg_Name;
}
}
} # Playlist save.
# Render output.
CLIPPED...