1 ############################################################################
2 # soepkiptng (c) copyright 2000 Eric Lammerts <eric@lammerts.org>.
3 ############################################################################
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License, version 2, as
6 # published by the Free Software Foundation.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 ############################################################################
19 use Digest::MD5 'md5_hex';
24 return undef unless $conf{allow_delete};
27 return -w $dir || -k $dir && -w $file;
34 if($_ > $prev && $_ - $prev <= 26) {
35 $out .= chr(ord('A') - 1 + $_ - $prev);
36 } elsif($_ < $prev && $prev - $_ <= 26) {
37 $out .= chr(ord('a') - 1 + $prev - $_);
39 if($_ - $prev >= 0 && substr($out, -1) =~ /\d/) {
46 $out =~ s/(([a-z])\2{3,})/"_" . length($1) . $2/egi;
54 $str =~ s/_(\d+)([a-z])/$2x$1/egi;
56 if($str =~ s/^([a-z])//) {
57 $val -= ord($1) - (ord('a') - 1);
58 } elsif($str =~ s/^([A-Z])//) {
59 $val += ord($1) - (ord('A') - 1);
60 } elsif($str =~ s/^([-+ ]?\d+)//) {
63 die "Invalid id encoding: '$str'\n";
74 my $bframe_args = "cmd=empty";
75 if(exists $req->{args}->{sval}) {
76 $bframe_args = sprintf("cmd=search&stype=%s&sval=%s",
77 encurl($req->{args}->{stype}) || "any",
78 encurl($req->{args}->{sval}));
79 } elsif(my $np = get_nowplaying($req->{dbh})) {
80 $bframe_args = "cmd=alllist&";
83 $bframe_args .= '&artist_id=' . $np->{arid};
84 $cap = "Artist: $np->{artist}";
85 if($np->{alid}) { $cap .= "; "; }
88 $bframe_args .= '&album_id=' . $np->{alid};
89 $cap .= "Album: $np->{album}";
91 $bframe_args .= "&cap=" . encurl($cap);
96 <title>$conf{title}</title>
98 <frameset rows="$conf{frameheights}">
99 <frame name=tframe src="$req->{self}?cmd=playlist" marginwidth="$conf{marginwidth}" marginheight="$conf{marginheight}">
100 <frame name=bframe src="$req->{self}?$bframe_args" marginwidth="$conf{marginwidth}" marginheight="$conf{marginheight}">
108 sub print_az_table($$) {
109 my ($req, $session) = @_;
110 my ($playlistopts, $editlistopts);
112 my $sth = $req->{dbh}->prepare("SELECT id,name FROM list ORDER BY name");
114 while($_ = $sth->fetchrow_hashref) {
115 $playlistopts .= sprintf(" <option value=%d%s>%s\n", $_->{id},
116 $_->{id} == $session->{playlist}? " selected":"", $_->{name});
117 $editlistopts .= sprintf(" <option value=%d%s>%s\n", $_->{id},
118 $_->{id} == $session->{editlist}? " selected":"", $_->{name});
121 printf <<EOF, $req->{self};
122 <table cellpadding=2 cellspacing=2>
124 <td id=az nowrap colspan=3>
125 <a id=az href="%s?cmd=playlist">Refresh</a>
126 <a id=az href="$req->{self}?cmd=shuffle">Shuffle</a>
127 <a id=az href="$req->{self}?cmd=lists" target=bframe>Playlists</a>
128 <a id=az href="$req->{self}?cmd=recent&days=7" target=bframe>Recent</a>/<!--
129 --><a id=az href="$req->{self}?cmd=recent&days=7&np=1" target=bframe>Never Played</a>/<!--
130 --><a id=az href="$req->{self}?cmd=recent&days=1000000&limit=100" target=bframe>Last 100</a>
131 <a id=az href="$req->{self}?cmd=alllist&rand=50" target=bframe>Random</a>
132 <a id=az href="$req->{self}?cmd=alllist&encoding=^Video" target=bframe>Video</a>
133 <!-- <a id=az target=_blank href="$req->{self}?cmd=maint">*</a> -->
134 <a id=az target=bframe href="$req->{self}?cmd=cdda">CD-DA</a>
135 <a id=az target=bframe href="$req->{self}?cmd=shoutcast">Shoutcast</a>
136 <a id=az target=bframe href="$req->{self}?cmd=sql">SQL</a>
142 printf qq|<a id=az href="%s?cmd=%s&artist=%s" target=bframe>%s</a> |,
143 $req->{self}, $conf{artistlistcmd}, encurl("^$_"), $_;
145 printf <<EOF, $req->{self}, $conf{artistlistcmd}, encurl("^([^a-zA-Z]|\$)");
146 <a id=az href="%s?cmd=%s&artist=%s" target=bframe>Other</a>
149 my $sz = $conf{searchformsize} || 10;
152 <form id=search name=search action="$req->{self}" method=get target=bframe>
153 <td id=az nowrap> Search:
155 printf <<EOF, $req->{args}->{nofocus}? "":"document.search.sval.focus();";
158 function do_onload(){%s}
163 <input type=hidden name=cmd value=search>
164 <input type=text size=$sz name=sval style="$conf{searchformstyle}">
167 <select name=stype style="$conf{searchformstyle}" onChange="form.submit()">
168 <option value=any>Any
169 <option value=artist>Artist
170 <option value=title>Title
171 <option value=album>Album
172 <option value=lyrics>Lyrics
173 <option value=filename>Filename
174 <option value=encoding>Encoding
176 <noscript><input type=submit value="Go"></noscript>
184 #<td id=az>Play:</td>
186 # <form id=search action="$req->{self}" method=get target=tframe>
187 # <select name=list onChange="">
188 # <option value="">All
191 # <input type=hidden name=cmd value=setplaylist>
192 # <input type=submit value="Ok">
196 #<td id=az>Edit:</td>
198 # <form id=search action="$req->{self}" method=get target=tframe>
199 # <select name=list onChange="">
203 # <input type=hidden name=cmd value=seteditlist>
204 # <input type=submit value="Ok">
208 #<td id=az>
209 #<a id=a target=bframe href="$req->{self}?cmd=lists">Playlists</a>
214 sub table_entry($$;$$$$) {
215 my ($req, $q, $col1, $title_href, $ids, $extra) = @_;
217 my $furl = $q->{filename};
219 $furl = $req->{self} . "/" . encurl($furl);
221 return sprintf <<EOF,
223 <td %s> %s </td>
224 <td %s> %s </td>
225 <td %s> %s </td>
226 <td %s> %s </td>
227 <td %s> %s%s%s </td>
228 <td %s> %s </td>
229 <td %s> %s </td>
230 <td %s nowrap> %s %s</td>
234 $conf{td_left}, $col1,
236 $q->{arid}? sprintf(qq|<a id=a href="%s?cmd=alllist&artist_id=%s&cap=%s" target=bframe>%s</a>|,
237 $req->{self}, $q->{arid}, encurl("Artist: $q->{artist}"), enchtml($q->{artist}))
238 : enchtml($q->{artist}),
240 $q->{alid}? sprintf(qq|<a id=a href="%s?cmd=alllist&album_id=%s&cap=%s" target=bframe>%s</a>|,
241 $req->{self}, $q->{alid}, encurl("Album: $q->{album}"), enchtml($q->{album}))
242 : enchtml($q->{album}),
243 $conf{td_track}, $q->{track}? "$q->{track}." : "",
244 $conf{td_song}, $title_href, enchtml($q->{title}), $title_href? "</a>":"",
246 $q->{length}? sprintf("%d:%02d", $q->{length} / 60, $q->{length} % 60) : "?",
247 $conf{td_enc}, enchtml($q->{encoding}, 1),
250 <a id=a href="%s?cmd=edit&id=%d&ids=%s" title="Edit" target=%s>*</a>
251 <a id=a href="%s?cmd=lyrics&id=%d&ids=%s" title="Lyrics" target=%s>L</a>
252 <a id=a href="%s?cmd=download&id=%d" title="Download">D</a>
254 $req->{self}, $q->{id}, $ids, $conf{edit_target} || 'bframe',
255 $req->{self}, $q->{id}, $ids, $conf{edit_target} || 'bframe',
258 $req->{list}? sprintf(<<EOF,
259 <a id=a href="%s?list_id=%d&ids=%d&pq=%s&cmd=%s" title="%s playlist">%s "%s"</a>
261 $req->{self}, $req->{list}, $q->{id},
262 encurl($req->{cgiquery}->query_string()), ($q->{in_list}?
263 ("delfromlist", "Remove from", "Remove from") :
264 ("addtolist", "Add to", "Add to"),
265 enchtml($req->{list_name}))
270 sub print_albumlist($$$$) {
271 my ($req, $albumlist, $sep, $prune) = @_;
276 foreach(@$albumlist) {
277 $_->{al_len} = length($_->{album});
278 $al_len_tot += $_->{al_len};
280 if($conf{albumlist_length_threshold} > 0 &&
281 $al_len_tot >= $conf{albumlist_length_threshold}) {
282 my $len_to_cut = $al_len_tot - $conf{albumlist_length_threshold};
283 foreach(sort { $a->{random_pref} <=> $b->{random_pref} || $b->{al_len} <=> $a->{al_len} } @$albumlist) {
284 $len_to_cut -= $_->{al_len};
287 last if $len_to_cut < 0;
291 foreach(@$albumlist) {
292 next if $_->{al_len} == -1;
293 print $sep if $_ != $albumlist->[0];
294 $_->{album} =~ /^(I+\b|IJ|.?)(.*)/;
295 printf(qq|<a id=a href="%s?cmd=alllist| .
296 qq|&artist_id=%d&album_id=%d&cap=%s"><b>%s</b>%s</a> (%d)|,
297 $req->{self}, $_->{arid}, $_->{alid},
298 encurl("Artist: $_->{artist}; Album: $_->{album}"),
299 enchtml($1 || "?"), enchtml($2), $_->{c});
303 <a id=a href="%s?cmd=albumlist&artist_id=%d&cap=%s">[%d more...].</a>
305 $req->{self}, $albumlist->[0]->{arid},
306 encurl("Artist: $albumlist->[0]->{artist}"), $more;
310 sub print_playlist_table($) {
315 my $query = "SELECT title,artist.name as artist,album.name as album," .
316 "song.id as id, track, length, encoding, queue.user as user," .
317 "song.artist_id as arid, song.album_id as alid, filename," .
318 "left(filename,1) as f" .
319 " FROM song,queue,artist,album WHERE present" .
320 " AND song.artist_id=artist.id AND song.album_id=album.id" .
321 " AND song.id = queue.song_id ORDER BY queue.song_order";
322 my $sth = $req->{dbh}->prepare($query);
323 my $rv = $sth->execute
324 or die "can't do sql command: " . $req->{dbh}->errstr;
327 while($_ = $sth->fetchrow_hashref) {
330 $totaltime += $_->{length};
333 if($_ = get_nowplaying($req->{dbh})) {
334 if($_->{id} > 0) { unshift @ids, $_->{id}; }
336 $killline = table_entry($req, $_,
337 sprintf(qq|<a id=a href="%s?cmd=kill&id=%s">%s</a> <a id=a href="%s?cmd=addnext&id=%s">next</a>|,
338 $req->{self}, $_->{id}, $conf{killtext}, $req->{self}, $_->{id}),
340 ($_->{filename} =~ m|^/|)? ids_encode(@ids) : "",
341 "<td> " . (($conf{show_user} && $_->{user})? "($_->{user})":"") . "</td>"
343 $totaltime += $_->{length};
345 if($conf{title_song}) {
346 my $alb = $_->{album};
347 if($alb && $_->{track}) { $alb .= "/$_->{track}"; }
348 if($alb) { $alb = " [$alb]"; }
349 printf <<EOF, enchtml("$_->{artist} - $_->{title}$alb");
350 <script language="Javascript">
352 parent.document.title="%s";
359 my $ids = ids_encode(@ids);
361 <table border=0 cellspacing=0>
363 <th %s> %s </th>
364 <th %s> Artist </th>
365 <th %s> Album </th>
366 <th %s> # </th>
367 <th %s> Song </th>
368 <th %s> Time </th>
369 <th %s> Encoding </th>
370 <th %s> </th>
372 <tr><td colspan=7></td></tr>
374 <tr><td colspan=7>%d song%s, total time: %d:%02d</td></tr>
377 $conf{th_left}, @ids? sprintf(qq|<a id=a href="%s?cmd=del&ids=%s">| .
378 qq|$conf{delalltext}</a>|, $req->{self}, ids_encode(@ids)) : "",
379 $conf{th_artist}, $conf{th_album}, $conf{th_track}, $conf{th_song},
380 $conf{th_time}, $conf{th_enc}, $conf{th_edit},
382 join("", map { table_entry($req, $_, sprintf(
383 qq|<a id=a href="%s?cmd=del&ids=%s">%s</a> | .
384 qq|<a id=a href="%s?cmd=up&id=%s">%s</a> | .
385 qq|<a id=a href="%s?cmd=down&id=%s">%s</a>|,
386 $req->{self}, $_->{id}, $conf{deltext},
387 $req->{self}, $_->{id}, $conf{uptext},
388 $req->{self}, $_->{id}, $conf{downtext}),
389 undef, $_->{f} eq '/'? $ids : "",
390 ($conf{show_user} && $_->{user})? "<td> (".$_->{user}.")</td>":"")
392 scalar @ids, @ids != 1? "s":"", $totaltime / 60, $totaltime % 60;
395 sub print_artistlist_table($$$@) {
396 my ($req, $session, $caption, $query, @val) = @_;
398 my $sth = $req->{dbh}->prepare($query);
399 my $rv = $sth->execute(@val)
400 or die "can't do sql command: " . $req->{dbh}->errstr;
403 while($_ = $sth->fetchrow_hashref()) {
404 push @artists, $_->{arid} unless $albumlist{$_->{arid}};
405 push @{$albumlist{$_->{arid}}}, $_;
408 print "<center id=hdr>$caption</center>\n";
410 print "No search results.\n";
415 <table border=0 cellspacing=0>
417 <th> Artist </th>
418 <th> Albums </th>
422 foreach(map { $albumlist{$_} } @artists) {
425 <td valign=top> <a id=a href="%s?cmd=alllist&artist_id=%d&cap=%s">%s</a> </td>
426 <td valign=top>
428 $req->{self}, $_->[0]->{arid}, encurl("Artist: $_->[0]->{artist}"),
430 print_albumlist($req, $_, ", ", 1);
438 <tr><td colspan=2>$res search results.</td></tr>
443 sub print_seealso_row($$) {
444 my ($req, $artistid) = @_;
446 my $query = "SELECT seealso.id1 AS id1, seealso.id2 AS id2, ".
447 " artist.name AS artist FROM seealso,artist,song WHERE ".
448 " (seealso.id2=? AND seealso.id1=artist.id AND ".
449 " seealso.id1=song.artist_id AND song.present) OR".
450 " (seealso.id1=? AND seealso.id2=artist.id AND ".
451 " seealso.id2=song.artist_id AND song.present) ".
452 " GROUP BY id1, id2, artist ORDER BY artist";
453 my $sth = $req->{dbh}->prepare($query);
454 $sth->execute($artistid, $artistid)
455 or die "can't do sql command: " . $req->{dbh}->errstr;
458 while($_ = $sth->fetchrow_hashref()) {
459 if($_->{id1} == $artistid) {
460 push @ids, $_->{id2};
461 $seealso{$_->{id2}} = $_->{artist};
463 push @ids, $_->{id1};
464 $seealso{$_->{id1}} = $_->{artist};
471 print ", " unless $_ == $ids[0];
472 printf qq|<a id=a href="%s?cmd=alllist&artist_id=%s&album=%s&cap=%s">$seealso{$_}</a>|,
473 $req->{self}, $_, encurl('^$'), encurl("Artist: $seealso{$_}; Album: ^\$");
479 sub print_albums_row($$$$;$) {
480 my ($req, $baseurl, $artistid, $label, $expand) = @_;
482 my $query = "SELECT DISTINCT binary album.name as album," .
483 " binary artist.name as artist, song.random_pref," .
484 " count(*) as c, song.artist_id as arid, song.album_id as alid " .
485 " FROM song,artist,album WHERE present AND song.artist_id = ?".
486 " AND song.artist_id=artist.id AND song.album_id=album.id".
487 " AND filename LIKE '/%'".
488 " GROUP BY album ORDER BY album";
489 $sth = $req->{dbh}->prepare($query);
490 $sth->execute($artistid)
491 or die "can't do sql command: " . $req->{dbh}->errstr;
493 my $albumlist = $sth->fetchall_arrayref({});
494 if(@$albumlist == 0) {
498 print_albumlist($req, $albumlist, $expand? "<br>\n" : ", \n", !$expand);
503 sub print_alllist_table($$$@) {
504 my ($req, $session, $caption, $query, $limit, @val) = @_;
505 my ($output, $addall);
508 <center id=hdr>$caption</center>
511 my $sth = $req->{dbh}->prepare($query . ($limit? " LIMIT $limit":""));
512 my $rv = $sth->execute(@val)
513 or die "can't do sql command: " . $req->{dbh}->errstr;
518 while($_ = $sth->fetchrow_hashref) {
519 $records{$_->{id}} = $_;
521 $totaltime += $_->{length};
522 $artistids{$_->{arid}}++;
525 if($req->{args}->{artist_id}) {
526 print_seealso_row($req, $req->{args}->{artist_id});
527 print_albums_row($req, $baseurl, $req->{args}->{artist_id}, "Albums: ")
530 print "No search results.\n";
533 my $ids = ids_encode(@ids);
535 my $baseurl = "$req->{self}?";
536 foreach(keys %{$req->{args}}) {
539 $baseurl .= "$_=" . encurl($req->{args}->{$_}) . "&";
544 my $el = $session->{editlist};
547 if($req->{dbh}->do("SELECT entity_id FROM list_contents" .
548 " WHERE list_id=? AND (" .
549 " (type=? AND entity_id=?) OR" .
550 " (type=? AND entity_id=?) OR" .
551 " (type=? AND entity_id=?))", undef,
554 "Artist", $_->{arid},
555 "Album", $_->{alid}) < 1) {
557 <a href="${baseurl}cmd=addtolist&add_list=$el&add_type=song&add_id=$_->{id}" target=bframe>+</a>
562 my $thref = sprintf(qq|<a id=a href="%s?cmd=add&ids=%d" target=tframe>|,
563 $req->{self}, $_->{id});
564 $output .= table_entry($req, $_, "$thref$conf{addtext}</a>", $thref, $ids);
567 if(scalar keys %artistids == 1 && !$req->{args}->{no_artist_rows}) {
568 print_seealso_row($req, (keys %artistids)[0]);
569 print_albums_row($req, $baseurl, (keys %artistids)[0], "Albums: ");
572 print "<table border=0 cellspacing=0>\n";
576 $ids = ids_encode(@ids);
577 $addall = qq|<a id=a href="$req->{self}?cmd=add&ids=$ids" target=tframe>$conf{addalltext}</a>|
581 $revsort{$req->{args}->{sort}} = "r_";
583 my $baseurl = "$req->{self}?";
584 foreach(keys %{$req->{args}}) {
585 next if $_ eq 'sort';
586 $baseurl .= "$_=" . encurl($req->{args}->{$_}) . "&";
591 $limitstr = " <b>(limit of $limit reached)</b>" if $limit && $res == $limit;
592 $totaltime = sprintf "%d:%02d:%02d", $totaltime / 3600,
593 $totaltime / 60 % 60, $totaltime % 60;
594 $totaltime =~ s/^0:0?//;
597 <th $conf{th_left}> $addall </th>
598 <th $conf{th_artist}> <a href="${baseurl}sort=$revsort{artist}artist">Artist</a> </th>
599 <th $conf{th_album}> <a href="${baseurl}sort=$revsort{album}album">Album</a> </th>
600 <th $conf{th_track}> <a href="${baseurl}sort=$revsort{track}track">#</a> </th>
601 <th $conf{th_song}> <a href="${baseurl}sort=$revsort{title}title">Song</a> </th>
602 <th $conf{th_time}> <a href="${baseurl}sort=$revsort{length}length">Time</a> </th>
603 <th $conf{th_enc}> <a href="${baseurl}sort=$revsort{encoding}encoding">Encoding</a> </th>
605 <tr><td colspan=7></td></tr>
607 <tr><td colspan=7>$res search results$limitstr, total time: $totaltime</td></tr>
609 (my $f = $caption) =~ s/^\w+:\s*//;
610 $f =~ s~(/|\\|:|;|")+~_~g;
611 ($f = "$req->{self}/$f.tar") =~ s|//+|/|g;
613 <tr><td colspan=7><a href="$f?cmd=downloadset&ids=$ids">download all as tar</a></td></tr>
620 sub print_edit_page($) {
624 my @ids = ids_decode($req->{args}->{ids});
626 last if $_ == $req->{args}->{id};
629 my $prev = ' ';
631 $prev = "<input type=submit name=go_$ids[$i-1] value=Prev>";
632 } elsif($conf{disabled_buttons}) {
633 $prev = "<input type=submit name=go_$ids[0] value=Prev disabled>";
636 my $next = $conf{disabled_buttons}? '':' ';
638 $next = "<input type=submit name=go_$ids[$i+1] value=Next>";
639 } elsif($conf{disabled_buttons}) {
640 $next = "<input type=submit name=go_$ids[$#ids] value=Next disabled>";
643 my $sth = $req->{dbh}->prepare("SELECT artist.name as artist,album.name as album," .
645 " unix_timestamp(last_played) as lp," .
646 " unix_timestamp(time_added) as ta" .
647 " FROM song,artist,album WHERE song.id=$req->{args}->{id}" .
648 " AND song.artist_id=artist.id AND song.album_id=album.id");
650 or die "can't do sql command: " . $req->{dbh}->errstr;
651 $_ = $sth->fetchrow_hashref()
652 or die sprintf <<EOF,
654 <form action="%s" method=get>
655 <input type=hidden name=id value="%d">
656 <input type=hidden name=cmd value=changefile>
657 <input type=hidden name=ids value="%s">
661 $req->{args}->{id}, $req->{self},
662 $req->{args}->{id}, $req->{args}->{ids},
665 my $f = $_->{filename};
667 if($f =~ m|^(/.*)/(.*?)$|) {
669 ($dir, $dir2, $file) = ($1, $1, $2);
671 $dir2 =~ s/\\/\\\\/g;
672 $dir = sprintf qq|<a href="%s?cmd=alllist&sort=artist&filename=%s">%s</a>|,
673 $req->{self}, encurl($dir2), enchtml($dir);
674 (my $furl = $req->{self} . "/" . encurl($file)) =~ s|//+|/|g;
675 $file = sprintf qq|<a href="%s?cmd=download&id=%d">%s</a>|,
676 $furl, $req->{args}->{id}, enchtml($file);
683 <script language="Javascript">
685 function verifydelete() {
686 return confirm("Are you sure you want to delete this file?");
688 function verifyall() {
689 return confirm("Are you sure you want to apply this value to the entire list (%d entries)?");
691 function closethis() {
697 <form action="%s" method=get>
698 <input type=hidden name=id value="%d">
699 <input type=hidden name=cmd value=changefile>
700 <input type=hidden name=ids value="%s">
702 <caption>Edit Song</caption>
703 <tr><td valign=bottom colspan=2>Present:</td><td valign=bottom>%s</td></tr>
704 <tr><td valign=bottom colspan=2>Artist:</td><td valign=bottom>
705 <input type=text size=60 name=artist value="%s">
706 <input type=submit name=action_clear_artist value="Clear">
707 <input type=submit name=action_fix_artist value="Fix">
708 <input type=submit name=action_swapa value="Swap first/last">
709 <input type=submit name=action_all_artist value="Set entire list"
710 onClick="return verifyall();">
712 <tr><td valign=bottom colspan=2>Title:</td> <td valign=bottom>
713 <input type=text size=60 name=title value="%s">
714 <input type=submit name=action_clear_title value="Clear">
715 <input type=submit name=action_fix_title value="Fix">
716 <input type=submit name=action_swap value="Swap artist/title">
718 <tr><td valign=bottom colspan=2>Album:</td> <td valign=bottom>
719 <input type=text size=60 name=album value="%s">
720 <input type=submit name=action_clear_album value="Clear">
721 <input type=submit name=action_fix_album value="Fix">
722 <input type=submit name=action_all_album value="Set entire list"
723 onClick="return verifyall();">
725 <tr><td valign=bottom colspan=2>Track:</td> <td valign=bottom><input type=text size=3 name=track value="%s" maxlength=3></td></tr>
726 <tr><td valign=bottom colspan=2>Time:</td> <td valign=bottom>%s</td></tr>
727 <tr><td valign=bottom colspan=2>Encoding:</td> <td valign=bottom>%s</td></tr>
728 <tr><td valign=bottom colspan=2 nowrap>Time Added:</td><td valign=bottom>%s</td></tr>
729 <tr><td valign=bottom colspan=2 nowrap>Last played:</td><td valign=bottom>%s%s
730 <input type=submit name=action_setlpall value=\"Set entire list to current time\"></td></tr>
731 <tr><td valign=bottom colspan=2 nowrap>Random pref.:</td><td valign=bottom><input type=text size=8 name=random_pref value="%d" maxlength=8>
732 <input type=submit name=action_setrpall value="Set entire list." onClick="return verifyall();"></td></tr>
733 <tr><td valign=bottom colspan=2>Directory:</td> <td valign=bottom>%s</td></tr>
734 <tr><td valign=bottom colspan=2>Filename:</td> <td valign=bottom>%s</td></tr>
735 <tr><td valign=bottom colspan=2>Size:</td> <td valign=bottom>%dk</td></tr>
736 <tr><td valign=bottom colspan=2>Volume correction:</td><td valign=bottom>%s</td></tr>
738 <td valign=bottom align=center>%s</td>
739 <td valign=bottom align=center>%s</td>
741 <input type=submit value="Update">
742 <script language="Javascript">
744 document.write('<input type=submit value="Close" onClick="javascript:window.close();"> ');
753 $#ids + 1, $req->{self}, $req->{args}->{id}, $req->{args}->{ids},
754 $_->{present}? "Yes" : "No",
755 enchtml($_->{artist}),
756 enchtml($_->{title}),
757 enchtml($_->{album}),
759 $_->{length}? sprintf("%d:%02d", $_->{length} / 60, $_->{length} % 60) : "?",
761 $_->{ta}? scalar localtime($_->{ta}) : "-",
762 $_->{lp}? scalar localtime($_->{lp}) : "-",
763 $_->{lp}? " <font size=-1><input type=submit name=action_clearlp value=Reset> " .
764 "<input type=submit name=action_clearlpall value=\"Reset entire list\"></font>":"",
765 $_->{random_pref}, $dir, $file,
766 ((-s $_->{filename}) + 512) / 1024,
767 defined($_->{gain})? sprintf("%.3fdB", $_->{gain} / 1000) : "-",
769 (can_delete($_->{filename})? qq'<input type=submit ' .
770 qq'name=action_delete value="Delete Song" ' .
771 qq'onclick="return verifydelete();"> ':'');
777 my $ua = new LWP::UserAgent();
778 $ua->agent('Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4b) Gecko/20030516 Mozilla Firebird/0.6');
780 if($conf{http_proxy}) {
781 $ua->proxy('http', $conf{http_proxy});
786 if($conf{no_proxy}) {
787 $ua->no_proxy(split /[\s,]+/, $conf{http_proxy});
792 sub getlyrics_purelyrics_com($) {
795 my $ua = getuseragent();
797 my $baseurl = "http://www.purelyrics.com/index.php";
799 my $url = "$baseurl?search_advsubmit2=Search";
800 $url .= "&search_artist=" . encurl(lc $info->{artist});
801 $url .= "&search_album=" . encurl(lc $info->{album});
802 $url .= "&search_title=" . encurl(lc $info->{title});
804 my $req = HTTP::Request->new(GET => $url);
805 my $res = $ua->request($req);
807 if($res->code != 200) {
808 $info->{errormsg} = sprintf "%d %s", $res->code, $res->message;
812 if($res->content !~ /^(\d+) matches.*<a href="([^"]+)">/m) {
813 $info->{errormsg} = "no matches";
817 my ($num, $url) = ($1, $2);
819 $info->{errormsg} = "more than one match";
823 $info->{description} = "http://www.purelyrics.com/$url";
824 $req = HTTP::Request->new(GET => $info->{description});
825 $res = $ua->request($req);
827 if($res->code != 200) {
828 $info->{errormsg} = sprintf "%d %s", $res->code, $res->message;
832 foreach(split /\n+/, $res->content) {
834 if(s/.*<font class="capitalFont">//) {
838 s|<br />|| or $lyr = 0;
841 $info->{lyrics} .= "$_\n";
847 sub getlyrics_lyricsdomain_com($) {
850 my $ua = getuseragent();
852 my $url = "http://www.lyricsdomain.com/src.php?action=+++Search+++";
853 $url .= "&artist=" . encurl(lc $info->{artist});
854 $url .= "&query=" . encurl(lc $info->{title});
856 my $req = HTTP::Request->new(GET => $url);
857 my $res = $ua->request($req);
859 if($res->code != 200) {
860 $info->{errormsg} = sprintf "%d %s", $res->code, $res->message;
864 if($res->content !~ /Your query generated (\d+) matches.*lyricsdomain\.asp\?cat=(\d+)">/m) {
865 $info->{errormsg} = "no matches";
869 my ($num, $lyrnum) = ($1, $2);
871 $info->{errormsg} = "more than one match";
875 $info->{description} = "http://www.lyricsdomain.com/lyrics/$lyrnum/";
876 $req = HTTP::Request->new(GET => $info->{description});
877 $res = $ua->request($req);
879 if($res->code != 200) {
880 $info->{errormsg} = sprintf "%d %s", $res->code, $res->message;
884 foreach(split /\n+/, $res->content) {
886 if(/sendlyrics.php/) {
891 my $stop = 1 if s|</pre>||;
894 $info->{lyrics} .= "$_\n";
901 sub lyrics_string_simplify($) {
904 $a =~ s/(\d),(\d)/\1\2/g;
907 $a =~ s/[ _]?&[_ ]?/ and /;
908 $a =~ s/([\x80-\xff])/lc($latin9_to_ascii{ord($1)})/ge;
909 $a =~ s/[^'A-Za-z0-9]+/ /g;
911 $a =~ s/^\s+|\s+$//g;
916 my ($info, $try) = @_;
918 # save artist/title/album
919 my ($ar, $ti, $al) = ($info->{artist}, $info->{title}, $info->{album});
922 $info->{artist} =~ s/'//g;
923 $info->{title} =~ s/'//g;
924 $info->{album} =~ s/'//g;
927 $info->{artist} = lyrics_string_simplify($info->{artist});
928 $info->{title} = lyrics_string_simplify($info->{title});
929 $info->{album} = lyrics_string_simplify($info->{album});
931 print "<!-- try$try '$info->{artist}', '$info->{title}', '$info->{album}' -->\n";
933 getlyrics_purelyrics_com($info) || getlyrics_lyricsdomain_com($info);
935 $info->{lyrics} =~ s/^\s+//;
936 $info->{lyrics} =~ s/\s+$/\n/g;
938 # restore artist/title/album
939 ($info->{artist}, $info->{title}, $info->{album}) = ($ar, $ti, $al);
942 sub print_lyrics_google_redir($) {
945 my ($a, $t) = $req->{dbh}->selectrow_array(
946 "SELECT artist.name, song.title FROM song " .
947 "LEFT JOIN artist ON artist.id=song.artist_id " .
948 "WHERE song.id=?", undef, $req->{args}->{id})
949 or die "id $req->{args}->{id} not found.\n";
951 print $req->{cgiquery}->redirect("http://www.google.com/search?q=" .
952 encurl("lyrics \"$a\" \"$t\""));
955 sub print_lyrics_page($) {
958 if($req->{args}->{action_clear}) {
959 $req->{args}->{lyrics} = $req->{args}->{description} = "";
961 if($req->{args}->{action_refetch}) {
962 $req->{dbh}->do("DELETE FROM lyrics WHERE id=?", undef, $req->{args}->{id});
964 $req->{args}->{lyrics} =~ s/\r\n?/\n/g;
965 if($req->{args}->{update}) {
966 $req->{dbh}->do("REPLACE INTO lyrics SET id=?, description=?, language=?, lyrics=?",
967 undef, $req->{args}->{id}, $req->{args}->{description},
968 $req->{args}->{language}, $req->{args}->{lyrics})
972 foreach(keys %{$req->{args}}) {
974 $req->{args}->{id} = $1;
978 my $sth = $req->{dbh}->prepare(
979 "SELECT song.id, title, song.track, album.name as album, " .
980 "artist.name as artist, lyrics.lyrics as lyrics, " .
981 "lyrics.language as language, lyrics.description as description FROM song " .
982 "LEFT JOIN album ON album.id=song.album_id " .
983 "LEFT JOIN artist ON artist.id=song.artist_id " .
984 "LEFT JOIN lyrics ON lyrics.id=song.id " .
985 "WHERE song.id=$req->{args}->{id}");
987 or die "can't do sql command: " . $req->{dbh}->errstr;
988 my $f = $sth->fetchrow_hashref() or die "id $req->{args}->{id} not found.\n";
991 if($conf{auto_download_lyrics} && $f->{lyrics} eq "" && !$req->{args}->{action_clear}) {
993 if($f->{lyrics} eq "") { getlyrics($f, 2); }
994 if($f->{lyrics} eq "") { getlyrics($f, 3); }
996 $downloaded = "<tr><td colspan=2><b>(";
997 if($f->{lyrics} eq "") {
998 $downloaded .= "No lyrics found";
999 $downloaded .= ": $f->{errormsg}" if $f->{errormsg};
1000 delete $f->{description};
1002 $downloaded .= sprintf "Downloaded lyrics from <a href=\"%s\">%s</a>",
1003 $f->{description}, enchtml($f->{description});
1005 $downloaded .= ")</b></td></tr>";
1006 $f->{language} = "eng";
1010 my @ids = ids_decode($req->{args}->{ids});
1012 last if $_ == $req->{args}->{id};
1015 my $prev = ' ';
1017 $prev = "<input type=submit name=go_$ids[$i-1] value=Prev>";
1018 } elsif($conf{disabled_buttons}) {
1019 $prev = "<input type=submit name=go_$ids[0] value=Prev disabled>";
1022 my $next = $conf{disabled_buttons}? '':' ';
1024 $next = "<input type=submit name=go_$ids[$i+1] value=Next>";
1025 } elsif($conf{disabled_buttons}) {
1026 $next = "<input type=submit name=go_$ids[$#ids] value=Next disabled>";
1029 # my $rows = 1 + $f->{lyrics} =~ tr/\n/\n/;
1030 # if($rows == 1) { $rows = 30; }
1033 my $description = $f->{description};
1034 $description =~ s/\s.*//;
1035 if($description =~ /^(ftp|http):/) {
1036 $description = "<a href=\"$description\" target=_blank>Open page</a>";
1042 <form action="%s" method=post>
1043 <input type=hidden name=id value="%d">
1044 <input type=hidden name=cmd value=lyrics>
1045 <input type=hidden name=update value=1>
1046 <input type=hidden name=ids value="%s">
1048 <caption>Edit Song Lyrics</caption>
1050 <tr><td>Artist:</td><td>%s</td></tr>
1051 <tr><td>Title:</td><td>%s</td></tr>
1052 <tr><td>Album:</td><td>%s</td></tr>
1053 <tr><td>Track:</td><td>%s</td></tr>
1055 <td>Description:</td>
1056 <td><input type=text name=description size=74 value="%s">%s</td>
1060 <td><select name=language>%s</select></td></tr>
1062 <tr><td colspan=2><textarea name=lyrics cols=90 rows=%d>%s</textarea></td></tr>
1065 <input type=submit name=action_update value="Update">
1066 <input type=submit name=action_clear value="Clear">
1067 <input type=submit name=action_refetch value="Refetch">
1068 <script language="Javascript">
1070 document.write('<input type=submit value="Close" onClick="javascript:window.close();">');
1078 <form action="%s" method=get target=_blank>
1079 <input type=submit value="Google for lyrics">
1080 <input type=hidden name=id value="%d">
1081 <input type=hidden name=cmd value=googlelyrics>
1087 $req->{self}, $req->{args}->{id}, $req->{args}->{ids}, $downloaded,
1088 enchtml($f->{artist}), enchtml($f->{title}),
1089 enchtml($f->{album}), $f->{track}? $f->{track} : "",
1090 enchtml($f->{description}), $description,
1091 join("\n", map { sprintf "<option value=\"%s\"%s>%s</option>",
1092 $_, $_ eq $f->{language}? " selected":"",
1093 $_? "$iso_639_2{$_} ($_)" : "-------";
1094 } (qw/eng dut ger fre esl ita/, '',
1095 sort { lc($iso_639_2{$a}) cmp lc($iso_639_2{$b}) }
1096 grep { ! /^(eng|dut|ger|fre|esl|ita)$/ } keys %iso_639_2)),
1097 $rows, enchtml($f->{lyrics}),
1098 $prev, $next, $req->{self}, $req->{args}->{id};
1101 sub print_shoutcast_page($) {
1104 $req->{args}->{name} =~ s/^\s+|\s+$//g;
1105 $req->{args}->{url} =~ s/^\s+|\s+$//g;
1106 foreach(keys %{$req->{args}}) {
1107 if(/^delete_(\d+)$/) {
1108 require_write_access;
1109 $req->{dbh}->do("DELETE FROM song WHERE id=?", undef, $1)
1110 or die "can't do sql command: " . $req->{dbh}->errstr;
1111 delete $req->{args}->{editid};
1112 delete $req->{args}->{url};
1115 if($req->{args}->{editid} && $req->{args}->{url}) {
1116 require_write_access;
1117 if($req->{args}->{action_clear_name}) { $req->{args}->{name} = ""; }
1118 $req->{dbh}->do("UPDATE song SET title=?,filename=? WHERE id=?",
1119 undef, $req->{args}->{name}, $req->{args}->{url}, $req->{args}->{editid})
1120 or die "can't do sql command: " . $req->{dbh}->errstr;
1121 } elsif($req->{args}->{url}) {
1122 require_write_access;
1123 my $arid = get_id($req->{dbh}, "artist", '') or die;
1124 my $alid = get_id($req->{dbh}, "album", '') or die;
1125 $req->{dbh}->do("REPLACE INTO song SET title=?, filename=?, album_id=?, " .
1126 "artist_id=?, present=1, encoding='Shoutcast', track=0, " .
1127 "length=0, time_added=NULL", undef,
1128 $req->{args}->{name}, $req->{args}->{url}, $alid, $arid) or die;
1132 <center id=hdr>Shoutcast Radio Channels</center>
1133 <table border=0 cellspacing=0>
1135 <th %s> <a href="%s?cmd=shoutcast">Refresh</a> </th>
1136 <th %s> Name </th>
1137 <th %s> URL </th>
1139 <tr><td colspan=7></td></tr>
1141 $conf{th_left}, $req->{self}, $conf{th_artist}, $conf{th_album};
1143 my $sth = $req->{dbh}->prepare("SELECT id,filename,title FROM song WHERE " .
1144 "filename REGEXP '^[a-z]+:' ORDER BY title");
1146 or die "can't do sql command: " . $req->{dbh}->errstr;
1148 my ($editurl, $editname);
1149 while($_ = $sth->fetchrow_hashref) {
1150 my $ref = sprintf(qq|<a id=a href="%s?cmd=add&ids=%d" target=tframe>|,
1151 $req->{self}, $_->{id});
1154 <td %s> %s </td>
1155 <td %s> %s</a> </td>
1156 <td %s> %s</a> </td>
1157 <td %s> <a id=a href="%s?cmd=shoutcast&editid=%d" target=bframe>%s</a> </td>
1160 $conf{td_left}, "$ref$conf{addtext}</a>",
1161 $conf{td_artist}, $_->{title},
1162 $conf{td_album}, $_->{filename},
1163 $conf{td_track}, $req->{self}, $_->{id}, '*';
1165 if($_->{id} == $req->{args}->{editid}) {
1166 $editurl = $_->{filename};
1167 $editname = $_->{title};
1176 <form action="%s" method=get>
1177 <input type=hidden name=cmd value=shoutcast>
1178 <input type=hidden name=editid value=%d>
1180 <tr><td>URL:</td><td><input type=text size=60 name=url value="%s"></td></tr>
1181 <tr><td>Description:</td><td><input type=text size=60 name=name value="%s">
1182 <input type=submit name=action_clear_name value="Clear"></td></tr>
1184 <tr><td colspan=2><input type=submit value="%s">%s</td></tr>
1189 $req->{args}->{editid}? "Edit" : "Add",
1190 $req->{self}, $req->{args}->{editid},
1191 enchtml($editurl || ""),
1192 enchtml($editname || ""),
1193 $req->{args}->{editid}? "Update" : "Add",
1194 $req->{args}->{editid}?
1195 sprintf(qq| <input type=submit name=delete_%d value="Delete">|,
1196 $req->{args}->{editid}) : "";
1199 sub print_lists($) {
1203 <script language="Javascript">
1205 function verifydelete() {
1206 return confirm("Are you sure you want to delete this playlist and its contents?");
1210 <center id=hdr>Playlists</center>
1211 <table border=0 cellspacing=0>
1213 <th $conf{th_left}> </th>
1214 <th $conf{th_left}> Name </th>
1215 <th $conf{th_left}> # Songs </th>
1216 <th $conf{th_left} id=t> Time </th>
1217 <th $conf{th_left} id=t> Size (Mb)</th>
1219 <tr><td colspan=3></td></tr>
1221 my $sth = $req->{dbh}->prepare(<<EOF);
1222 SELECT list.id AS list_id,list.name,
1223 COUNT(song.present) AS count,
1224 SUM(song.length) AS length,
1225 SUM(FLOOR((song.filesize + 1023) / 1024)) AS size
1227 LEFT JOIN list_contents ON list.id=list_id
1228 LEFT JOIN song ON list_contents.song_id=song.id
1229 WHERE list.id AND (song.present OR song.present IS NULL)
1234 or die "can't do sql command: " . $req->{dbh}->errstr;
1235 while(my $d = $sth->fetchrow_hashref) {
1238 <td %s> <a href="%s?cmd=dellist&id=%d" %s>del</a> </td>
1239 <td> <a id=a href="%s?cmd=lists&list=%d&list_name=%s">%s</a> </td>
1240 <td %s> <a id=a href="%s?cmd=alllist&qlist=%d&cap=%s&no_artist_rows=1">%d</a> </td>
1241 <td id=t> %d:%02d:%02d </td>
1242 <td %s id=t> %.1f </td>
1243 <td> %s </td>
1246 $conf{td_left}, $req->{self}, $d->{list_id}, ($d->{count}? 'onclick="return verifydelete();"':""),
1247 $req->{self}, $d->{list_id}, encurl($d->{name}), $d->{name},
1248 $conf{td_left}, req->{self}, $d->{list_id}, encurl("Playlist \"$d->{name}\""), $d->{count},
1249 $d->{length} / 3600, ($d->{length} / 60) % 60, $d->{length} % 60,
1250 $conf{td_left}, $d->{size} / 1024,
1251 $d->{list_id} == $req->{list}? "(selected)":"";
1253 my ($count, $len, $size) = $req->{dbh}->selectrow_array(
1254 "SELECT COUNT(*),SUM(length),SUM(FLOOR((song.filesize + 1023) / 1024)) FROM song WHERE present");
1258 <td> <a id=a href="%s?cmd=lists&list=&list_name=">(all songs)</a> </td>
1259 <td %s> %d </td>
1260 <td id=t> %d:%02d:%02d </td>
1261 <td %s id=t> %.1f </td>
1262 <td> %s </td>
1267 $conf{td_left}, $count,
1268 $len / 3600, ($len / 60) % 60, $len % 60,
1269 $conf{td_left}, $size / 1024,
1270 $req->{list}? "":"(selected)";
1274 <form id=search action="$req->{self}" method=get target=bframe>
1275 <input type=text size=20 name=listname>
1276 <input type=hidden name=cmd value=addlist>
1277 <input type=submit value="Create new playlist">
1282 sub printhtmlhdr(;$) {
1285 printhttphdr($req->{cookies});
1287 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
1295 <style type="text/css">
1301 <body $conf{body} $_[1]>
1312 sub printredir($$$) {
1313 my ($req, $cmd, $argsref) = @_;
1314 $argsref->{cmd} = $cmd;
1315 print $req->{cgiquery}->redirect(construct_url($req->{self}, $argsref));
1318 sub printredir_pq($) {
1320 print $req->{cgiquery}->redirect($req->{self} . "?" . $req->{cgiquery}->param("pq"));
1323 sub add_search_args($$$$@) {
1324 my ($where, $list, $sort, $val, @fields) = @_;
1327 # split on space and latin1 'no break space'
1328 foreach $v (split /[\s\xa0]+/, $val) {
1331 if($v =~ s/^!//) { $q = "NOT "; }
1332 if($v =~ /^\^/) { $op = "REGEXP"; }
1333 else { $v = "%$v%"; };
1334 $q .= "(" . join(" OR ", map { "$_ $op ?" } @fields) . ")";
1336 foreach(@fields) { push @$list, $v; }
1338 $$sort = $fields[0] unless $$sort;
1341 sub delete_file($$) {
1342 my ($req, $id) = @_;
1344 require_write_access;
1347 printhdr($conf{allstyle});
1350 my ($file) = $req->{dbh}->selectrow_array("SELECT filename FROM song WHERE id=$id")
1351 or die "id $id not found in database\n";
1353 print "$file deleted from disk.\n";
1354 $req->{dbh}->do("UPDATE song SET present=0 WHERE id=$id");
1356 print "$file: <b>$!</b>\n";
1365 if($host =~ /^\d+\.\d+\.\d+\.\d+$/) {
1366 $host = gethostbyaddr(inet_aton($host), AF_INET) || $host;
1369 $host =~ /^([-a-z0-9]*)/;
1370 $user = $conf{lc "user_from_$host"} || $conf{lc "user_from_$1"} || $host;
1375 sub handle_request($) {
1379 foreach($req->{cgiquery}->param) {
1380 $args{$_} = $req->{cgiquery}->param($_);
1381 # warn "\$args{$_} = $args{$_}\n";
1383 $req->{args} = \%args;
1385 # $req->{sid} = $args{$sid};
1386 # } elsif($req->{cookie} =~ /SID=(\w+)/i) {
1389 # $req->{sid} = substr(md5_hex(time . ".$$." . rand), 0, 10);
1391 if(exists $args{list}) {
1392 $req->{cookies}->{list} = $args{list};
1393 $req->{cookies}->{list_name} = $args{list_name};
1395 $req->{list} = $req->{cookies}->{list};
1396 $req->{list_name} = $req->{cookies}->{list_name};
1397 $SIG{__DIE__} = sub {
1399 print "<p><p>$_[0]\n";
1403 my $cmd = $args{cmd};
1404 my $rt = $conf{refreshtime};
1406 if($cmd eq 'empty') {
1408 print "$conf{bframe_start}\n";
1413 add_song($req->{dbh}, "queue", get_user($req->{host}), ids_decode($args{ids}));
1414 printredir($req, 'playlist', undef);
1417 elsif($cmd eq 'del') {
1418 del_song($req->{dbh}, "queue", ids_decode($args{ids}));
1419 printredir($req, 'playlist', undef);
1422 elsif($cmd eq 'up') {
1423 foreach(reverse split /,/, $args{id}) { move_song_to_top($req->{dbh}, "queue", $_); }
1424 printredir($req, 'playlist', undef);
1427 elsif($cmd eq 'down') {
1428 foreach(reverse split /,/, $args{id}) { move_song_to_bottom($req->{dbh}, "queue", $_); }
1429 printredir($req, 'playlist', undef);
1432 elsif($cmd eq 'kill') {
1433 kill_song(get_user($req->{host}));
1434 printredir($req, 'playlist', undef);
1437 elsif($cmd eq 'addnext') {
1438 add_song_next($req->{dbh}, "queue", $args{id}, get_user($req->{host}));
1439 printredir($req, 'playlist', undef);
1442 elsif($cmd eq 'setplaylist') {
1443 $session{playlist} = $args{list};
1444 printredir($req, 'playlist', undef);
1447 elsif($cmd eq 'seteditlist') {
1448 $session{editlist} = $args{list};
1449 printredir($req, 'playlist', undef);
1452 elsif($cmd eq 'addlist') {
1453 require_write_access;
1454 $args{listname} or die "No list name specified.\n";
1455 $req->{dbh}->do("REPLACE INTO list SET name=?", undef, $args{listname})
1457 printredir($req, 'lists', \%args);
1460 elsif($cmd eq 'addtolist') {
1461 require_write_access;
1462 $req->{dbh}->do("REPLACE INTO list_contents SET list_id=?, song_id=?", undef,
1463 $args{list_id}, $args{ids})
1465 printredir_pq($req);
1468 elsif($cmd eq 'delfromlist') {
1469 require_write_access;
1470 $req->{dbh}->do("DELETE FROM list_contents WHERE list_id=? AND song_id=?", undef,
1471 $args{list_id}, $args{ids})
1473 printredir_pq($req);
1476 elsif($cmd eq 'dellist') {
1477 require_write_access;
1478 $req->{dbh}->do("DELETE FROM list WHERE id=?", undef, $args{id})
1480 $req->{dbh}->do("DELETE FROM list_contents WHERE list_id=?", undef, $args{id})
1482 printredir($req, 'lists', \%args);
1485 elsif($cmd eq 'shuffle') {
1486 shuffle_table($req->{dbh}, "queue");
1487 printredir($req, 'playlist', \%args);
1490 elsif($cmd eq 'changefile') {
1493 require_write_access;
1495 if($args{action_delete}) {
1496 delete_file($req, $args{id});
1499 if($args{action_clearlp}) {
1500 $req->{dbh}->do("UPDATE song SET last_played=from_unixtime(0) WHERE id=?",
1502 or die "can't do sql command: " . $req->{dbh}->errstr;
1504 if($args{action_clearlpall}) {
1505 foreach(ids_decode($args{ids})) {
1506 $req->{dbh}->do("UPDATE song SET last_played=from_unixtime(0) WHERE id=?",
1508 or die "can't do sql command: " . $req->{dbh}->errstr;
1511 if($args{action_setlpall}) {
1512 foreach(ids_decode($args{ids})) {
1513 $req->{dbh}->do("UPDATE song SET last_played=NULL WHERE id=?", undef, $_)
1514 or die "can't do sql command: " . $req->{dbh}->errstr;
1517 if($args{action_setrpall}) {
1518 foreach(ids_decode($args{ids})) {
1519 $req->{dbh}->do("UPDATE song SET random_pref=? WHERE id=?",
1520 undef, $args{random_pref}, $_)
1521 or die "can't do sql command: " . $req->{dbh}->errstr;
1524 elsif($args{action_clear_artist}) { $args{artist} = '' }
1525 elsif($args{action_clear_title}) { $args{title} = '' }
1526 elsif($args{action_clear_album}) { $args{album} = '' }
1527 elsif($args{action_fix_artist}) { $args{artist} = cleanup_name($args{artist}); }
1528 elsif($args{action_fix_title}) { $args{title} = cleanup_name($args{title}); }
1529 elsif($args{action_fix_album}) { $args{album} = cleanup_name($args{album}); }
1530 elsif($args{action_swap}) {
1531 ($args{title}, $args{artist}) = ($args{artist}, $args{title});
1533 elsif($args{action_swapa}) {
1534 $args{artist} =~ s/(.*)\s*,\s*(.*)/$2 $1/
1535 or $args{artist} =~ s/(.*?)\s+(.*)/$2 $1/
1537 foreach(keys %args) {
1544 my $arid = get_id($req->{dbh}, "artist", $args{artist}) or die;
1545 my $alid = get_id($req->{dbh}, "album", $args{album}) or die;
1547 $req->{dbh}->do("UPDATE song SET artist_id=?, title=?, album_id=?, track=?, random_pref=? WHERE id=?",
1548 undef, $arid, $args{title}, $alid, $args{track}, $args{random_pref}, $args{id})
1549 or die "can't do sql command: " . $req->{dbh}->errstr;
1551 my ($all_field, $all_field_arg);
1552 if($args{action_all_artist}) {
1553 $all_field = 'artist';
1554 $all_field_arg = $arid;
1555 } elsif($args{action_all_album}) {
1556 $all_field = 'album';
1557 $all_field_arg = $alid;
1560 my @ids = ids_decode($args{ids});
1563 my @ids2 = splice(@ids, 0, 50);
1564 $req->{dbh}->do("UPDATE song SET ${all_field}_id=? WHERE " .
1565 join(" OR ", map { "id=$_" } @ids2),
1566 undef, $all_field_arg, @ids2)
1567 or die "can't do sql command: " . $req->{dbh}->errstr;
1571 if($newid) { $args{id} = $newid; }
1572 printredir($req, 'edit', \%args);
1576 if($cmd eq 'search') {
1579 printhdr($conf{allstyle});
1580 print "Error: No search type specified.\n";
1584 $args{$args{stype}} = $args{sval};
1585 if($args{stype} eq 'artist') {
1586 $cmd = 'artistlist';
1597 elsif($cmd eq 'playlist') {
1600 <META HTTP-EQUIV="Refresh" CONTENT="$rt;URL=$req->{self}?cmd=playlist&s=$args{s}&nofocus=1">
1602 printhdr($conf{plstyle}, "onLoad=do_onload()");
1603 print_az_table($req, \%session);
1604 print_playlist_table($req);
1605 # printf "[[%s]]\n", $bla;
1608 elsif($cmd eq 'artistlist') {
1610 printhdr($conf{artiststyle});
1612 my ($cap, $s, @qw, @qwa);
1614 if($args{artist} =~ /\S/) {
1615 add_search_args(\@qw, \@qwa, \$s, $args{artist}, 'artist.name');
1616 $cap = "Search Artist: $args{artist}";
1618 if($args{album} =~ /\S/) {
1619 add_search_args(\@qw, \@qwa, \$s, $args{album}, 'album.name');
1620 $cap = "Search Album: $args{album}";
1622 if(scalar @qwa > 0) {
1623 $s =~ s/^r_(.*)/\1 DESC/;
1624 my $qw = join(" AND ", @qw);
1625 print_artistlist_table($req, \%session, $cap, <<EOF, @qwa);
1626 SELECT DISTINCT artist.name as artist, album.name as album, count(*) as c,
1627 song.artist_id as arid, song.album_id as alid, song.random_pref
1629 LEFT JOIN artist ON artist.id=song.artist_id
1630 LEFT JOIN album ON album.id=song.album_id
1631 WHERE present AND filename LIKE '/%' AND
1633 GROUP BY artist_id, album_id
1634 ORDER BY UCASE(artist.name),UCASE(album.name)
1637 print "Error: No search terms specified.\n";
1641 elsif($cmd eq 'alllist') {
1643 printhdr($conf{allstyle});
1645 "artist.name as artist",
1646 "album.name as album",
1648 "song.artist_id as arid",
1649 "song.album_id as alid",
1653 "LEFT JOIN artist ON artist.id=song.artist_id",
1654 "LEFT JOIN album ON album.id=song.album_id",
1657 my @qw = ("present");
1660 my $limit = $conf{alllist_limit};
1661 my $s = $args{sort};
1662 my $filename_restriction = "/%";
1665 push @qj, "LEFT JOIN list_contents AS qlist ON qlist.song_id=song.id AND qlist.list_id=?";
1666 push @qja, $args{qlist};
1669 push @qs, "list_contents.list_id > 0 as in_list";
1670 push @qj, "LEFT JOIN list_contents ON list_contents.song_id=song.id AND list_contents.list_id=?";
1671 push @qja, $req->{list};
1673 if($args{any} =~ /\S/) {
1674 add_search_args(\@qw, \@qwa, \$s, $args{any},
1675 'artist.name', 'title', 'album.name');
1676 $cap = "Search any: $args{any}";
1678 if($args{artist} =~ /\S/) {
1679 add_search_args(\@qw, \@qwa, \$s, $args{artist}, 'artist.name');
1680 $cap = "Search Artist: $args{artist}";
1682 if($args{album} =~ /\S/) {
1683 add_search_args(\@qw, \@qwa, \$s, $args{album}, 'album.name');
1684 $cap = "Search Album: $args{album}";
1686 if($args{title} =~ /\S/) {
1687 add_search_args(\@qw, \@qwa, \$s, $args{title}, 'title');
1688 $cap = "Search Title: $args{title}";
1690 if($args{filename} =~ /\S/) {
1691 add_search_args(\@qw, \@qwa, \$s, lc($args{filename}), 'lcase(filename)');
1692 $cap = "Search Filename: $args{filename}";
1694 if($args{lyrics} =~ /\S/) {
1695 $s = "artist.name" unless $s;
1696 push @qj, "LEFT JOIN lyrics ON lyrics.id=song.id";
1697 add_search_args(\@qw, \@qwa, \$s, lc($args{lyrics}), 'lcase(lyrics)');
1698 $cap = "Search Title: $args{title}";
1701 if($args{artist_id}) {
1702 push @qw, "song.artist_id=?";
1703 push @qwa, $args{artist_id};
1704 $s = "artist.name" unless $s;
1705 $cap = sprintf($args{cap}, $args{artist_id});
1707 if($args{album_id}) {
1708 push @qw, "song.album_id=?";
1709 push @qwa, $args{album_id};
1710 $s = "album.name" unless $s;
1711 $cap = sprintf($args{cap}, $args{album_id});
1713 if($args{encoding}) {
1714 $s = "artist.name" unless $s;
1715 add_search_args(\@qw, \@qwa, \$s, $args{encoding}, 'encoding');
1716 $cap = "Search Encoding: $args{encoding}";
1719 $limit = 0 + $args{rand};
1722 if($args{cdda} =~ /^(\w+)/) {
1724 $filename_restriction = "cdda:%";
1725 $cap = "CD-DA ($1)";
1727 push @qw, "filename LIKE ?";
1728 push @qwa, $filename_restriction;
1730 if(!$s && $args{qlist}) {
1731 push @qw, "qlist.list_id";
1733 $s = "qlist.song_order";
1736 $s =~ s/^r_(.*)/\1 DESC/;
1737 if($s eq "encoding") { $s = "substring_index(encoding, '(', 1)"; }
1738 my $q = sprintf "SELECT %s FROM song %s WHERE %s ORDER BY %s",
1739 join(",", @qs), join(" ", @qj), join(" AND ", @qw),
1740 "$s,album.name,track,artist.name,title,time_added";
1741 print_alllist_table($req, \%session, $cap, $q, $limit, (@qja, @qwa));
1743 print "Error: No search terms specified.\n";
1745 # printf "[[%s]]\n", $bla;
1748 elsif($cmd eq 'albumlist') {
1750 printhdr($conf{allstyle});
1752 <center id=hdr>$args{cap}</center>
1754 print_seealso_row($req, $args{artist_id});
1755 print_albums_row($req, $req->{self}, $args{artist_id}, "Albums: ", 1);
1758 elsif($cmd eq 'sql') {
1759 require_write_access;
1761 if($args{limit} > 500) { $args{limit} = 500; }
1764 printhdr($conf{allstyle});
1766 printf <<EOF, enchtml($args{sql}), defined($args{limit})? $args{limit} : 500;
1768 <form action="$req->{self}" method=get>
1769 <input type=hidden name=cap value="SQL-query">
1770 <input type=hidden name=cmd value=sql>
1772 SELECT ... FROM ... WHERE ... AND <input type=text size=80 name=sql value="%s">
1773 Limit: <input type=text size=5 name=limit value=%d> (0=count only)
1774 <input type=submit value=Submit>
1780 if($args{limit} == 0) {
1781 my ($count) = $req->{dbh}->selectrow_array("SELECT COUNT(*) ".
1783 "LEFT JOIN artist ON artist.id=song.artist_id " .
1784 "LEFT JOIN album ON album.id=song.album_id " .
1785 "LEFT JOIN lyrics ON lyrics.id=song.id " .
1786 "WHERE present AND " .
1788 print "Count: $count\n";
1790 print_alllist_table($req, \%session, "User SQL-query",
1791 "SELECT artist.name as artist,album.name as album,song.*," .
1792 "song.artist_id as arid, song.album_id as alid, filename " .
1794 "LEFT JOIN artist ON artist.id=song.artist_id " .
1795 "LEFT JOIN album ON album.id=song.album_id " .
1796 "LEFT JOIN lyrics ON lyrics.id=song.id " .
1797 "WHERE present AND " .
1798 $args{sql} . " LIMIT $args{limit}");
1803 elsif($cmd eq 'recent') {
1805 printhdr($conf{allstyle});
1806 my $maxage = $args{days} * 86400;
1807 my $s = $args{sort} || "r_time_added";
1809 $s =~ s/^r_(.*)/\1 DESC/;
1810 $args{limit} <= 500 or $args{limit} = 500;
1811 print_alllist_table($req, \%session,
1812 "Most recent $args{limit} songs" . ($args{np}? " (never played yet)":""),
1813 "SELECT artist.name as artist,album.name as album,song.*," .
1814 "song.artist_id as arid, song.album_id as alid, filename" .
1815 " FROM song,artist,album WHERE present AND filename LIKE '/%'" .
1816 " AND song.artist_id=artist.id AND song.album_id=album.id" .
1817 " AND unix_timestamp(now()) - unix_timestamp(time_added) < $maxage" .
1818 ($args{np}? " AND unix_timestamp(last_played) = 0":"") .
1819 " ORDER BY $s,album.name,track,artist.name,title", $args{limit});
1822 elsif($cmd eq 'maint') {
1824 printhdr($conf{allstyle});
1827 <center id=hdr>Update Database</center>
1828 <form action="$req->{self}" method=get>
1829 <input type=hidden name=cmd value=update>
1830 <input type=submit value="Update Database"><br><br>
1831 Updating the database will take a while; don't press the 'stop' button on your browser if you want this to succeed.
1836 elsif($cmd eq 'update') {
1837 require_write_access;
1838 print $req->{cgiquery}->header(-type=>'text/plain');
1839 print `$progdir/soepkiptng_update 2>&1`;
1841 elsif($cmd eq 'edit') {
1843 printhdr($conf{editstyle});
1844 print_edit_page($req);
1847 elsif($cmd eq 'lyrics') {
1849 printhdr($conf{editstyle});
1850 print_lyrics_page($req);
1853 elsif($cmd eq 'googlelyrics') {
1854 print_lyrics_google_redir($req);
1856 elsif($cmd eq 'shoutcast') {
1858 printhdr($conf{allstyle});
1859 print_shoutcast_page($req);
1862 elsif($cmd eq 'download') {
1863 $args{id} =~ /(\d+)/;
1865 my ($file) = $req->{dbh}->selectrow_array("SELECT filename FROM song WHERE id=$id")
1866 or die "id $id not found in database\n";
1868 open F, $file or die "$file: $!\n";
1869 print $req->{cgiquery}->header(-type=>'application/octet-stream', -Content_length=>(-s F));
1870 while(read F, $_, 4096) { print; }
1873 elsif($cmd eq 'downloadset') {
1874 my @ids = ids_decode($args{ids});
1879 my ($file) = $req->{dbh}->selectrow_array("SELECT filename FROM song WHERE id=$_")
1880 or die "id $id not found in database\n";
1882 push @tarargs, "-C", $1, $file;
1884 print $req->{cgiquery}->header(-type=>'application/octet-stream');
1885 if(open(F, "-|") == 0) {
1886 setpriority 0, 0, 10;
1887 exec "tar", "cf", "-", "--numeric-owner", @tarargs;
1890 while(read F, $_, 4096) { print; }
1893 elsif($cmd eq 'lists') {
1895 printhdr($conf{allstyle});
1897 $req->{cookies}->{list} = $args{list};
1900 elsif($cmd eq 'cdda') {
1902 if(!$conf{cdda_prog}) {
1903 die "CD-DA not configured\n";
1905 $req->{dbh}->do("DELETE FROM song WHERE filename LIKE 'cdda:%'");
1906 open F, "$conf{cdda_prog}|";
1907 chop(my $cddbinfo = <F>);
1909 my $arid = get_id($req->{dbh}, "artist", $_) or die;
1911 my $alid = get_id($req->{dbh}, "album", $_) or die;
1917 $req->{dbh}->do("REPLACE INTO song SET title=?, filename=?, album_id=?, " .
1918 "artist_id=?, present=1, encoding='CD-DA', track=?, " .
1919 "length=?, time_added=NULL", undef,
1920 $_, "cdda:$cddbinfo:$nr", $alid, $arid, $nr, $len) or die;
1924 $args{cdda} = $cddbinfo;
1925 printredir($req, 'alllist', \%args);
1929 print "oei: $cmd\n";