summaryrefslogtreecommitdiff
path: root/scripts/gdserv.pm
diff options
context:
space:
mode:
authorlvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b>2004-05-28 15:30:48 +0000
committerlvw <lvw@e10066b5-e1e2-0310-b819-94efdf66514b>2004-05-28 15:30:48 +0000
commit2370a13b7f6512147550e5eec8a773e69d49119b (patch)
treedcf92a706d71b2c21cebef1969298c761f74de2c /scripts/gdserv.pm
parent616adfc77dc1d08f3bfcd79991a78c6350e4e2f6 (diff)
downloadvdr-plugin-muggle-2370a13b7f6512147550e5eec8a773e69d49119b.tar.gz
vdr-plugin-muggle-2370a13b7f6512147550e5eec8a773e69d49119b.tar.bz2
Merged and added import scripts
git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@99 e10066b5-e1e2-0310-b819-94efdf66514b
Diffstat (limited to 'scripts/gdserv.pm')
-rwxr-xr-xscripts/gdserv.pm3305
1 files changed, 3305 insertions, 0 deletions
diff --git a/scripts/gdserv.pm b/scripts/gdserv.pm
new file mode 100755
index 0000000..4dbd3fe
--- /dev/null
+++ b/scripts/gdserv.pm
@@ -0,0 +1,3305 @@
+##################################################
+#
+# GiantDisc mp3 Jukebox
+#
+# © 2000, Rolf Brugger
+#
+##################################################
+
+package gdserv;
+
+use lib '/home/andi/muggle/bin';
+use gdio;
+use myhash; # incremental hash calculation
+use gddb; # db modification routines
+use gdparams; # global variables, constants and parameters
+use gdgentools; # general tools
+use gdupdate;
+use gdsoundcard;
+
+use DBI;
+use strict;
+
+use FileHandle; # to set auto flush on pipe
+use File::Basename;
+
+
+### Init constants
+my $shrtFieldLen = 25;
+
+my $pl_list = 0; # play list
+my $rp_list = 1; # rip list
+my $co_list = 2; # compression list
+my $cr_list = 3; # cd recording list
+
+
+# player ouput constants
+my $audio0 = 0;
+my $audio1 = 1;
+my $speaker = 10;
+my $monitor = 11;
+
+# Player Types
+my $soundcard0 = 0; # main sound output of first soundcard
+my $soundcard1 = 1; # alternative sound output on second s9undcard (not supported)
+my $mp3streamer = 20; # mp3 streamer (barix exstreamer)
+
+# Audio out channel
+my $audchannel0 = 0;
+my $audchannel1 = 1;
+
+# Currently, there is only audio channel 0 supported, and it can be mapped to
+# either $soundcard0 or $mp3streamer
+
+
+
+
+
+
+
+
+
+
+
+
+### Global variables
+my $dbhost; # IP of database host (better use var in gdparams!?)
+my $dbh;
+my $playerid;
+#my $playertype; replaced by audiochannel
+
+my $rippipe_open=0;
+
+my $random_result_table; #gobal variable that holds random result list
+my $random_result_type; #gobal variable: what's in the table?
+
+
+BEGIN{
+}
+
+############################################################
+
+# Initialization
+sub init_server{
+ my $logtarget;
+ ($dbhost, $logtarget) = @_;
+
+ ### Open database connection
+ if($logtarget==2){ #stdout
+ print ("### Open database connection\n");
+ }
+
+ #print "dbhost is: $dbhost\n";
+ $dbh = DBI->connect("DBI:mysql:GiantDisc:".$dbhost, "music", undef)
+ or die "unable to connect to GiantDisc db";
+
+
+ if (gdupdate::db_check_update_132($dbh)){
+ print "\n Update to database structure version 1.32 required\n";
+ print " please run command 'gdupdatedb.pl'\n\n";
+ exit;
+ }
+
+ ### fix records (bug in recording routines prior to version 0.92)
+ #gdupdate::fix_leading_slash_bug($dbh);
+
+ ### UPDATE DATABASE to new versions
+# gdupdate::db_update_094($dbh);
+# gdupdate::db_update_095($dbh);
+# gdupdate::db_update_096($dbh);
+# gdupdate::db_update_097($dbh);
+# gdupdate::db_update_111($dbh);
+# gdupdate::db_update_112($dbh);
+# gdupdate::db_update_114($dbh);
+# gdupdate::db_update_131($dbh);
+}
+
+
+sub init_player{
+# Checks, if a player record with the same ipaddr and uichannel exists.
+# If it doesn't exist, a new record is created.
+# It returns the new generated player-ID
+#
+#
+#
+ my ($ipaddr, $uichannel, $logtarget, $cdripper,
+ $mp3encoder, $cdromdev, $cdrwdev) = @_;
+ $playerid = 0; #default
+ my $sth = $dbh->prepare(
+ "SELECT * FROM player "
+ ."WHERE ipaddr='$ipaddr' AND uichannel='$uichannel'" );
+ $sth->execute;
+
+ my $row;
+ if($row = $sth->fetchrow_hashref){
+ # matching player record found
+ $playerid = $row->{id};
+ }
+ else{
+ # get highest id in db
+ my $sth2 = $dbh->prepare(
+ "SELECT * FROM player ORDER BY id DESC LIMIT 1" );
+ $sth2->execute;
+ if($row = $sth2->fetchrow_hashref){
+ # matching player record found
+ $playerid = $row->{id}+1;
+ }
+ #else playerid has default value 0
+ $sth2->finish;
+
+ ### Create player record
+ my $retval = $dbh->do("INSERT INTO player SET "
+ ."ipaddr ='$ipaddr', "
+ ."uichannel ='$uichannel', "
+ ."id = $playerid, "
+ ."logtarget = $logtarget, "
+ ."cdripper ='$cdripper', "
+ ."mp3encoder='$mp3encoder', "
+ ."cdromdev ='$cdromdev', "
+ ."cdrwdev ='$cdrwdev'");
+ }
+ $sth->finish;
+
+ ### delete all tracklists (riplist, burnlist and comprlist) of this player
+ $dbh->do("DELETE FROM tracklistitem WHERE playerid=$playerid AND listtype=$rp_list"); #riplist
+ $dbh->do("DELETE FROM tracklistitem WHERE playerid=$playerid AND listtype=$co_list"); #compressionlist
+ $dbh->do("DELETE FROM tracklistitem WHERE playerid=$playerid AND listtype=$cr_list"); #cdrecordlist
+
+ return $playerid;
+}
+
+
+sub init_playerstate{
+#
+# global var: playerid
+
+ my ($playerhost, $snddevice, $playerapp, $ptlogger, $playertype);
+ my ($mp3playerparams, $oggplayerparams);
+ ($playerhost, $playertype, $snddevice, $playerapp, $mp3playerparams, $oggplayerparams, $ptlogger) = @_;
+ my $playerparams = join '\t', $mp3playerparams, $oggplayerparams;
+ my $audiochannel = $audchannel0;
+ gdgentools::pll_new_playstate($dbh, $playerid, $audiochannel,
+ $playertype, $snddevice, $playerapp, $playerparams, $ptlogger);
+
+ ### create message queue for this player-id/audio-channel
+ gdgentools::plmsg_newqueue($playerid, $audiochannel);
+
+ ### init soundcard driver
+ gdsoundcard::sndc_init($playertype, $playerhost, $audiochannel);
+}
+
+
+
+sub open_rippipe{
+ ### Open recording pipe
+ open(RIPPIPE, "| gdripcd.pl $dbhost $playerid $audchannel0")
+ or die "can't fork RIP-PIPE\n";
+ autoflush RIPPIPE 1;
+ $rippipe_open=1;
+}
+
+sub close_rippipe{
+ ### close recording pipe
+ close(RIPPIPE);
+ $rippipe_open=0;
+}
+
+############################################################
+### TOOL ROUTINES ###
+############################################################
+
+############################################################
+
+sub truncstr{
+### truncstr (string, length, noellipsis)
+ # If the string is longer than 'length' characters, it is truncated and
+ # '..' is appended => max length is 'length'+2
+ if (length($_[0]) > $_[1]){
+ $_[0] = substr ($_[0], 0, $_[1]);
+ if(!$_[2]){ # add ellipsis
+ $_[0] .= "..";
+ }
+ }
+}
+
+
+############################################################
+### DB CREATION & UPDATE ###
+############################################################
+
+############################################################
+### Creates/updates an album record
+sub replace_album_record{
+ my ($artist,$title,$cddbid) = @_;
+
+ my $base = gdparams::gdbase();
+
+ if(length($artist)==0){$artist="-";};
+ if(length($title)==0) {$title="-";};
+ if(length($cddbid)==0){print("ERROR: no CDDB-ID defined!\n");};
+ my $sqlcmd =
+ "REPLACE INTO album "
+ ."(artist,title,cddbid,modified) "
+ ."VALUES "
+ ."(".$dbh->quote($artist)
+ .",".$dbh->quote($title)
+ .",'$cddbid', CURDATE()) ";
+
+ #print("$sqlcmd \n");
+ $dbh->do($sqlcmd);
+}
+
+
+############################################################
+###
+sub record_cd_track{
+ my ($artist,$title,$genre1,$genre2,$year,
+ $lang,$type,$rating,$length,$source,$sourceid,
+ $tracknb,$audiofile,$condition,$voladjust, #$lengthfrm,$startfrm,$bpm,
+ $created,$id,$bitrate
+ ) = @_;
+
+ my $base = gdparams::gdbase();
+
+ ### Get an audio directory with enough space left (1GB)
+ my $ripdir=gdgentools::get_ripdirectory($gdparams::minfreehdspace); #1000 MBytes should be available
+
+ my $trid;
+ ### create a new track record
+ if($ripdir ne ""){
+ my ($audiofmt, $audioparam) = gdgentools::bitrate_str_to_format_param($bitrate); # split "mp3 128"
+ $trid = gddb::insert_track_record($dbh,$artist,$title,$genre1,$genre2,$year,
+ $lang,$type,$rating,$length,$source,$sourceid,
+ $tracknb,"tr0x".$sourceid."-".$tracknb.".".$audiofmt,
+ $condition,$voladjust,0,0,0, #$lengthfrm,$startfrm,$bpm,
+ $bitrate,
+ $created,$id);
+
+ unlink glob("$base/??/tr0x".$sourceid."-".$tracknb.".".$audiofmt); # delete old audio file
+ ### send rip command to pipe
+ if(!$rippipe_open){open_rippipe();}
+ print ("printing to RIPPIPE: tr=$tracknb id=$trid "
+ ."br=$bitrate len=$length "
+ ."dir=$base/$ripdir "
+ ."file=tr0x$sourceid art=$artist tit=$title\n");
+ gdgentools::tracklist_append_list($dbh, $playerid, $rp_list, $trid);
+ ### action is "rip"
+ print (RIPPIPE "rip\t$tracknb\t$trid\t$bitrate\t$length\t"
+ ."$base/$ripdir\ttr0x$sourceid\t$artist\t$title\n");
+ }
+ else{
+ print("Not enough space left on disc \n");
+ }
+}
+
+
+############################################################
+###
+sub import_audio_track{
+# creates a new record and moves the specified audio-file to a
+# music directory which has enough space left
+
+ my ($artist,$title,$genre1,$genre2,$year,
+ $lang,$type,$rating,$length,$source,$sourceid,
+ $tracknb,$audiofile,$condition,$voladjust,#$lengthfrm,$startfrm,$bpm,
+ $created,$id,$bitrate
+ ) = @_;
+
+ my $base = gdparams::gdbase();
+
+ ### Get an audio directory with enough space left (1GB)
+ my $ripdir=gdgentools::get_ripdirectory($gdparams::minfreehdspace);
+
+ my $trid;
+ ### create a new track record
+ if($ripdir ne ""){
+ ### put audio-file in music directory
+
+ my $origaudiofile = readlink "$base/inbox/$audiofile";
+
+ if(!$rippipe_open){open_rippipe();}
+
+ ### action is "move"
+ #format: "move, sourcefile, targetfile, linkname
+ print ("printing to RIPPIPE: move\t$origaudiofile\t$base/$ripdir/$audiofile\t$base/inbox/$audiofile\n");
+ print (RIPPIPE "move\t$origaudiofile\t$base/$ripdir/$audiofile\t$base/inbox/$audiofile\n");
+
+ ### create track record
+ $trid = gddb::insert_track_record($dbh,$artist,$title,$genre1,$genre2,$year,
+ $lang,$type,$rating,$length,$source,$sourceid,
+ $tracknb,$audiofile,$condition,$voladjust,
+ 0,0,0, #$lengthfrm,$startfrm,$bpm,
+ $bitrate,
+ $created,$id);
+
+ }
+ else{
+ print("Not enough space left on disc \n");
+ }
+}
+
+
+
+############################################################
+###
+# these variables are st by the routine, that sends track details to the
+# client. The value in there gives a hint, if the frequencies should be recalculated
+my ($trk_last_id, $trk_last_lang, $trk_last_genre1, $trk_last_genre2);
+
+sub update_track_record{
+ my ($artist,$title,$genre1,$genre2,$year,
+ $lang,$type,$rating,$length,$source,$sourceid,
+ $tracknb,$audiofile,$condition,$voladjust,#$lengthfrm,$startfrm,$bpm,
+ $created,$id,$bitrate
+ ) = @_;
+
+ my ($trid, $audiofpath);
+
+ ### always recalculate bitrate and track length
+ $audiofpath = gdgentools::get_full_audiofile_path($audiofile);
+ if(gdgentools::is_mp3stream($audiofile) || (length($audiofpath)>0)){
+ if(length($audiofpath)>0){ ## real mp3 file exists
+ $bitrate = gdgentools::get_bitrate_str("$audiofpath");
+ $length = gdgentools::audiofile_lengthsec("$audiofpath");
+ }
+ else{
+ $length = 0;
+ }
+
+ #print("UPDATING: $artist,$title,$genre1,$genre2,$year,"
+ # ."$lang,$type,$rating,$length,$source,$sourceid,"
+ # ."$tracknb,$audiofile,$condition,$voladjust,$created,$id\n");
+ $trid = gddb::insert_track_record($dbh,$artist,$title,$genre1,$genre2,$year,
+ $lang,$type,$rating,$length,$source,$sourceid,
+ $tracknb,$audiofile,$condition,$voladjust,
+ 0,0,0,#$lengthfrm,$startfrm,$bpm,
+ $bitrate,
+ $created,$id);
+
+ if ( $id != $trk_last_id
+ || $lang ne $trk_last_lang){ ### language changed?
+ system("killall -q gdtablefreq.pl");
+ system("nice gdtablefreq.pl --dbhost $dbhost --trklanguage&");
+# system("nice gdtablefreq.pl --dbhost $dbhost --trklanguage --verbose&");
+ }
+ if ( $id != $trk_last_id
+ || $genre1 ne $trk_last_genre1
+ || $genre2 ne $trk_last_genre2){ ### genre changed?
+ # recalculate genre frequencies
+ system("killall -q gdtablefreq.pl");
+ if (length($sourceid)>=8){
+ system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --albgenre $sourceid&");
+# system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --albgenre $sourceid --verbose&");
+ }
+ else{
+ system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre&");
+# system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --verbose&");
+ }
+ }
+ }
+ else{
+ print("Warning: can't update record (id=$id) because the associated mp3file does not exist, and no mp3 streaming url is specified\n");
+ }
+}
+
+
+
+sub recalc_table_frequencies{
+ system("killall -q gdtablefreq.pl");
+ system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --albgenre&");
+}
+
+############################################################
+### QUERIES ###
+############################################################
+
+
+############################################################
+###
+sub do_tr_query{ # returns a query handle
+
+ my $where = gddb::track_where_clause(@_);
+ my $order = gddb::track_order_clause(@_);
+
+ #print("WHERE clause: $where $order\n");
+ my $sth = $dbh->prepare( "SELECT * FROM tracks WHERE $where $order" );
+
+ my $rv = $sth->execute;
+ print("$rv records found\n");
+ return $sth;
+}
+
+############################################################
+###
+sub do_album_query{ # does an album query and returns a query handle
+ ###
+# EXAMPLE:
+# SELECT album.* FROM tracks JOIN album
+# WHERE album.cddbid=tracks.sourceid AND tracks.genre1='mdh'
+# AND album.title like "%de%"
+# GROUP BY album.cddbid;
+
+# my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto,
+# $lang,$type,$rating) = @_;
+
+ my $tmpcmd;
+
+ my $where = gddb::album_where_clause(@_);
+ my $order = gddb::album_order_clause(@_);
+
+#print "alb-order: $order\n\n";
+ if (length($order)<5){
+ $order = "ORDER BY album.artist";
+ }
+
+ my $sth = $dbh->prepare(
+# " SELECT album.* FROM tracks JOIN album "
+ " SELECT album.artist AS artist, album.title AS title, "
+ ."album.cddbid AS cddbid, album.genre AS genre, tracks.year AS year "
+ ."FROM tracks JOIN album "
+ ."WHERE album.cddbid=tracks.sourceid AND $where "
+ ."GROUP BY album.cddbid $order"
+ );
+
+ $sth->execute;
+ return $sth;
+}
+
+############################################################
+###
+sub do_playlist_query{ # does a playlist query and returns a query handle
+ ###
+ my ($author,$title,$genre1,$genre2,$yearfrom,$yearto,
+ $lang,$type,$rating) = @_;
+
+ my $where=" 1 "; # true AND ...
+
+ ### Artist
+ $where .= gddb::field_where_clause("author",$author);
+
+ ### Title
+ $where .= gddb::field_where_clause("title",$title);
+
+ my $sth = $dbh->prepare(
+ 'SELECT * FROM playlist '
+ ."WHERE $where"
+ );
+
+ $sth->execute;
+ return $sth;
+}
+
+############################################################
+###
+sub get_track{ # returns a query handle
+ my ($trackid) = @_;
+
+ my $sth = $dbh->prepare(
+ 'SELECT * FROM tracks '
+ ."WHERE id = $trackid"
+ );
+
+ $sth->execute;
+ return $sth;
+}
+
+############################################################
+###
+sub do_tracks_of_album_query{ # returns a query handle
+ my ($albumid) = @_;
+ my $sth = $dbh->prepare(
+ 'SELECT * FROM tracks '
+ ."WHERE sourceid = \"$albumid\" ORDER BY tracknb"
+ );
+
+ $sth->execute;
+ return $sth;
+}
+
+############################################################
+###
+sub do_tracks_of_playlist_query{ # returns a query handle
+ my ($playlistid) = @_;
+ my $sth = $dbh->prepare(
+ "SELECT * FROM playlistitem "
+ ."WHERE playlist = $playlistid ORDER BY tracknumber"
+ );
+
+ $sth->execute;
+ return $sth;
+}
+
+
+############################################################
+### LISTINGS ###
+############################################################
+
+############################################################
+### send tracks
+sub send_tracks_v1{ # send a record set to the serial line
+ # NEW VERSION
+ my ($sth) = @_;
+ my ($row);
+ my $stop=0;
+ while($row = $sth->fetchrow_hashref){
+ $stop = send_track($row);
+ last if ($stop);
+ }
+ if (!$stop){
+ gdio::putline ("");} # End of result
+ $sth->finish;
+}
+sub send_track{
+ my ($row)= @_;
+ my ($stop);
+ truncstr($row->{artist}, $shrtFieldLen);
+ truncstr($row->{title}, $shrtFieldLen);
+ print ("$row->{id}\t");
+ print ("$row->{artist}\t");
+ print ("$row->{title}\t");
+ print ("$row->{length}\t");
+ print ("$row->{genre1}\t");
+ #print ("$row->{genre2}\t");
+ print ("$row->{year}\t");
+ print ("$row->{type}\t");
+ print ("$row->{rating}\n");
+ $stop = gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t"
+ ."$row->{length}\t$row->{genre1}\t"
+ ."$row->{year}\t$row->{type}\t$row->{rating}");
+ return $stop
+}
+#sub send_tracks_v0{ # send a record set to the serial line
+# # OLD VERSION: will be obsolete when all
+# # routines are ported to 'send_tracks'
+# my ($sth) = @_;
+# my ($row);
+# my $stop=0;
+# while($row = $sth->fetchrow_hashref){
+# truncstr($row->{artist}, $shrtFieldLen);
+# truncstr($row->{title}, $shrtFieldLen);
+# print ("$row->{id}\t");
+# print ("$row->{artist}\t");
+# print ("$row->{title}\t");
+# print ("$row->{length}\n");
+# $stop = gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t"
+# ."$row->{length}\t$row->{tracknb}");
+# last if ($stop);
+# }
+# if (!$stop){
+# gdio::putline ("");} # End of result
+# $sth->finish;
+#}
+
+############################################################
+### Query tracks & List
+sub query_tracks_list{ #query tracks records and list them
+ my $sth = do_tr_query(1, @_); # perform database query
+ send_tracks_v1($sth);
+}
+
+sub query_streams_list{ #query tracks records and list them
+ my $sth = do_tr_query(2, @_); # perform database query
+ send_tracks_v1($sth);
+}
+
+############################################################
+### Generic Query tracks & List
+sub generic_track_query{ #query tracks records and list them
+ my ($where) = @_;
+ #print("WHERE clause: $where\n");
+ my $sth = $dbh->prepare( "SELECT * FROM tracks $where" );
+ my $rv = $sth->execute;
+ print("$rv records found\n");
+ send_tracks_v1($sth);
+}
+
+### Query a single tracks & List
+#sub query_single_track{ #query one trackfor a given id
+# my ($trackid) = @_;
+# my $sth = $dbh->prepare( "SELECT * FROM tracks WHERE id='$trackid'" );
+# $sth->execute;
+# send_tracks_v0($sth);
+#}
+
+
+############################################################
+### send artists
+sub send_artist_line_v1{ # send a single record to the serial line
+ my ($artist) = @_;
+ my $stop=0;
+ print ("\t"); # send an empty ID
+ truncstr($artist, 10+$shrtFieldLen, 1); # dont send ellipsis
+ print ("$artist\n");
+ $stop = gdio::putline("\t$artist");
+ return $stop;
+}
+
+#sub send_artist_line_v0{ # send a single record to the serial line
+# my ($artist) = @_;
+# my $stop=0;
+# print ("ESCn\t"); # ESC n: name (=artist)
+# print ("0\t"); # send an empty ID to be compatible with album record format
+# truncstr($artist, 10+$shrtFieldLen, 1); # dont send ellipsis
+# print ("$artist\t");
+# print (" \n"); # send an empty title to be compatible with album record format
+# $stop = gdio::putline("\en\t0\t$artist\t ");
+# return $stop;
+#}
+
+############################################################
+### Query artist & List
+sub query_artists_list{ #query track records and list their distinct artists
+ #my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto,
+ # $lang,$type,$rating) = @_;
+
+ ### Prepare Query
+ my $where = gddb::track_where_clause(1, @_);
+
+ #print("WHERE clause: $where\n");
+
+ generic_artists_query(
+ "SELECT DISTINCT artist FROM tracks WHERE $where ORDER BY artist" );
+}
+
+
+sub generic_artists_query{ #query track records and list their distinct artists
+ ### Prepare Query
+ my ($querystr) = @_;
+ my $sth = $dbh->prepare( $querystr );
+ my $rv = $sth->execute;
+ #print("$rv artists found\n");
+ ### Send results
+ my $row;
+ my $stop=0;
+ while($row = $sth->fetchrow_hashref){
+ $stop = send_artist_line_v1($row->{artist});
+ last if ($stop);
+ }
+ if (!$stop){
+ gdio::putline ("");} # End of result
+ $sth->finish;
+}
+
+
+############################################################
+### Query albums
+
+
+#sub query_single_album{
+ # query one albums with its tracks and list them
+ # the album is specified by the 'albumid'.
+# my ($albumid) = @_;
+# my $albsth;
+# $albsth = $dbh->prepare(
+# " SELECT * FROM album WHERE cddbid='$albumid'");
+# $albsth->execute;
+# send_albums_v0(1, $albsth); # 1: with tracks
+#}
+
+
+sub generic_album_query{
+ # query one albums with its tracks and list them
+ # the album is specified by the 'albumid'.
+ my ($querystr) = @_;
+ my $albsth;
+ my $albsth = $dbh->prepare( $querystr );
+ $albsth->execute;
+ send_albums_v1($albsth);
+}
+
+
+############################################################
+### Query albums & list
+sub query_albums{
+ # query albums and list them
+ my $albsth = do_album_query(@_); # perform database query
+ send_albums_v1($albsth);
+}
+
+
+############################################################
+### Query albums at random
+sub query_random_albums{
+ # prepares a ramdom query operation
+ # 1) performs an album query
+ # 2) selects album-ids and store them in a gobally accessible array
+ # 3) returns the number of albums found
+ my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto,
+ $lang,$type,$rating) = @_;
+ my $nbRecords;
+
+ my $where = gddb::album_where_clause(@_);
+# my $where=" 1 "; # true AND ...
+ ### Album: Artist
+# $where .= gddb::field_where_clause("album.artist",$artist);
+ ### Album: Title
+# $where .= gddb::field_where_clause("album.title",$title);
+ ### Track: genre, etc ...
+# $where.= gddb::attrib_where_clause($genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating);
+
+ my $sth = $dbh->prepare(
+ " SELECT album.cddbid,(ASCII(album.cddbid)*0)+RAND() AS randnum "
+ ."FROM tracks JOIN album "
+ ."WHERE album.cddbid=tracks.sourceid AND $where "
+ ."GROUP BY album.cddbid ORDER BY randnum"
+ ); # ORDER BY RAND() does not work in version 3.22
+ $nbRecords = $sth->execute;
+
+ ### store result in global array
+ $random_result_table = $sth->fetchall_arrayref;
+ $random_result_type = "alb";
+ # my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n");
+ $sth->finish;
+
+ ### Send number of records
+ print ("random album query: $nbRecords albums found\n");
+ gdio::putline ("$nbRecords\n");
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### Query tracks at random
+sub query_random_tracks{
+ # prepares a ramdom query operation
+ # 1) performs a track query
+ # 2) selects track-ids and store them in a gobally accessible array
+ # 3) returns the number of tracks found
+ my $nbRecords;
+
+ my $where = gddb::track_where_clause(@_);
+ my $sth = $dbh->prepare(
+ " SELECT id,(id*0)+RAND() AS randnum FROM tracks "
+ ."WHERE $where ORDER BY randnum"
+ ); # ORDER BY RAND() does not work in version 3.22
+ $nbRecords = $sth->execute;
+
+ ### store result in global array
+ $random_result_table = $sth->fetchall_arrayref;
+ $random_result_type = "trk";
+# my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n");
+ $sth->finish;
+
+ ### Send number of records
+ print ("random album query: $nbRecords tracks found\n");
+ gdio::putline ("$nbRecords\n");
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### Query artists at random
+sub query_random_artists{
+ # prepares a ramdom query operation
+ # 1) performs a track query
+ # 2) selects track-ids and store them in a gobally accessible array
+ # 3) returns the number of tracks found
+ my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto,
+ $lang,$type,$rating) = @_;
+ my $nbRecords;
+
+ my $where = gddb::track_where_clause(1, @_);
+ my $sth = $dbh->prepare(
+ " SELECT DISTINCT artist FROM tracks WHERE $where"
+ ); # can't "ORDER BY randnum" here!
+ $nbRecords = $sth->execute;
+
+ ### store result in global array
+ $random_result_table = $sth->fetchall_arrayref;
+ $random_result_type = "art";
+# my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n");
+ $sth->finish;
+ ### randomize it - because SQL query can't do it
+ shuffle_random_table();
+
+ ### Send number of records
+ print ("random artist query: $nbRecords artists found\n");
+ gdio::putline ("$nbRecords\n");
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### send next slice of random result table to the palm client
+sub collect_ids{
+ my ($position,$nbRecords,$idIdent) = @_;
+ my ($whereclause, $i);
+ $whereclause = "0";
+ # if $position < $#{$random_result_table}
+ for $i( $position .. ($position+$nbRecords-1)){
+ #print ($random_result_table->[$i][0]."\n");
+ $whereclause .= " OR $idIdent='".$random_result_table->[$i][0]."'";
+ }
+ return $whereclause;
+}
+
+sub shuffle_random_table{
+ my ($i, $j, $element);
+ for $i( 0 .. $#{$random_result_table}){
+ $j = int rand ($#{$random_result_table});
+# print ("exchange $i and $j\n");
+ $element = $random_result_table->[$i][0];
+ $random_result_table->[$i][0] = $random_result_table->[$j][0];
+ $random_result_table->[$j][0] = $element;
+ }
+}
+
+sub get_random_result{
+ # prepares a ramdom query operation
+ # 1) performs an album query
+ # 2) selects album-ids and store them in a gobally accessibe array
+ # 3) randomizes the order of the album-ids
+ # 4) returns the number of albums found
+ my ($position,$nbRecords) = @_;
+
+ my ($whereclause, $sth);
+ if ($random_result_type eq "alb"){
+ $whereclause = collect_ids ($position,$nbRecords, "tracks.sourceid=album.cddbid AND album.cddbid");
+ ### Perform the query
+ #print("SELECT album.artist AS artist, album.title AS title, "
+ # ."album.cddbid AS cddbid, album.genre AS genre, tracks.year AS year "
+ # ."FROM tracks JOIN album "
+ # ."WHERE $whereclause"
+ # ."GROUP BY album.cddbid\n");
+
+ $sth = $dbh->prepare(" SELECT album.artist AS artist, album.title AS title, "
+ ."album.cddbid AS cddbid, album.genre AS genre, tracks.year AS year "
+ ."FROM tracks JOIN album "
+ ."WHERE $whereclause"
+ ."GROUP BY album.cddbid");
+ $sth->execute;
+ send_albums_v1($sth);
+ $sth->finish;
+ }
+ elsif ($random_result_type eq "pll"){
+ $whereclause = collect_ids ($position,$nbRecords, "id");
+ ### Perform the query
+ print("SELECT * FROM playlist WHERE $whereclause\n");
+ $sth = $dbh->prepare("SELECT * FROM playlist WHERE $whereclause");
+ $sth->execute;
+ my $prow;
+ while($prow = $sth->fetchrow_hashref){
+ print("$prow->{id}\t$prow->{author}\t$prow->{title}\n");
+ gdio::putline("$prow->{id}\t$prow->{author}\t$prow->{title}");
+ }
+ gdio::putline (""); # End of result
+ $sth->finish;
+ }
+ elsif ($random_result_type eq "trk"){
+ $whereclause = collect_ids ($position,$nbRecords, "id");
+ ### Perform the query
+ print("SELECT * FROM tracks WHERE $whereclause\n");
+ $sth = $dbh->prepare("SELECT * FROM tracks WHERE $whereclause");
+ $sth->execute;
+ send_tracks_v1($sth);
+ $sth->finish;
+ }
+ elsif ($random_result_type eq "art"){
+ ### no additional query necessary with artists
+ my $i;
+ for $i( $position .. ($position+$nbRecords-1)){
+ truncstr($random_result_table->[$i][0], 10+$shrtFieldLen);
+ #print ($random_result_table->[$i][0]."\n");
+ send_artist_line_v1($random_result_table->[$i][0]);
+ }
+ gdio::putline (""); # End of result
+ }
+ else{
+ print ("Error: random_result_type '$random_result_type' not properly initialized");
+ }
+}
+
+
+############################################################
+### Query albums with or without tracks & list
+#sub send_albums_v0{
+# # sends the albums (specified by the record set handle '$albsth')
+# # if $withTracks is true, the according tracks are sent too
+#
+# my ($withTracks, $albsth) = @_;
+# my ($arow, $trow); # album and track row
+# my $trksth;
+# my $stop=0;
+#
+# while($arow = $albsth->fetchrow_hashref){
+# truncstr($arow->{artist}, $shrtFieldLen);
+# truncstr($arow->{title}, $shrtFieldLen);
+# print ("ESCa\t");
+# print ("$arow->{cddbid}\t");
+# print ("$arow->{artist}\t");
+# print ("$arow->{title}\n");
+# # print album (preceded by ESC a)
+# $stop = gdio::putline("\ea\t$arow->{cddbid}\t$arow->{artist}\t$arow->{title}");
+#
+# if ($withTracks){
+# ### get tracks of the album
+# my $trksth = do_tracks_of_album_query($arow->{cddbid});
+# while($trow = $trksth->fetchrow_hashref){
+# truncstr($trow->{artist}, $shrtFieldLen);
+# truncstr($trow->{title}, $shrtFieldLen);
+# print ("$trow->{id}\t");
+# print ("$trow->{artist}\t");
+# print ("$trow->{title}\t");
+# print ("$trow->{length}\n");
+# # print track
+# $stop = gdio::putline("$trow->{id}\t$trow->{artist}\t$trow->{title}\t"
+# ."$trow->{length}\t$trow->{tracknb}");
+# last if ($stop);
+# }
+# }
+# last if ($stop);
+# }
+# if (!$stop){
+# gdio::putline ("");} # End of result
+# $albsth->finish;
+#}
+
+sub send_albums_v1{
+ # sends the albums (specified by the record set handle '$albsth')
+ # if $withTracks is true, the according tracks are sent too
+
+ my ($albsth) = @_;
+ my ($arow, $trow); # album and track row
+ my $trksth;
+ my $stop=0;
+
+ while($arow = $albsth->fetchrow_hashref){
+ truncstr($arow->{artist}, $shrtFieldLen);
+ truncstr($arow->{title}, $shrtFieldLen);
+ print ("$arow->{cddbid}\t");
+ print ("$arow->{artist}\t");
+ print ("$arow->{title}\t");
+ print ("\t$arow->{genre}\t$arow->{year}\n"); # length,genre,year
+ # print album
+ $stop = gdio::putline("$arow->{cddbid}\t$arow->{artist}\t$arow->{title}\t\t$arow->{genre}\t$arow->{year}");
+ last if ($stop);
+ }
+ if (!$stop){
+ gdio::putline ("");} # End of result
+ $albsth->finish;
+}
+
+############################################################
+### Query playlists & list
+sub query_saved_playlists_list{
+ # query playlists and list them
+ #
+ # List format: id, author, title
+
+ my $pllsth = do_playlist_query(@_); # perform database query
+ my ($prow, $trow); # playlist and track row
+ my ($trksth, $trow, $irow, $plisth);
+ my $stop=0;
+
+ while($prow = $pllsth->fetchrow_hashref){
+ truncstr($prow->{artist}, $shrtFieldLen);
+ truncstr($prow->{title}, $shrtFieldLen);
+ print ("$prow->{id}\t");
+ print ("$prow->{author}\t");
+ print ("$prow->{title}\n");
+
+ $stop = gdio::putline("$prow->{id}\t$prow->{author}\t$prow->{title}");
+ last if ($stop);
+ }
+ if (!$stop){
+ gdio::putline ("");} # End of result
+ $pllsth->finish;
+}
+
+
+
+############################################################
+### Query playlists at random
+sub query_random_playlists{
+ # prepares a ramdom query operation
+ # 1) performs an playlist query
+ # 2) selects playlist-ids and store them in a gobally accessible array
+ # 3) returns the number of playlists found
+ my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto,
+ $lang,$type,$rating) = @_;
+ my $nbRecords;
+
+ my $where=" 1 "; # true AND ...
+
+ ### Artist
+ $where .= gddb::field_where_clause("author",$artist);
+ ### Title
+ $where .= gddb::field_where_clause("title",$title);
+
+ my $sth = $dbh->prepare(
+ 'SELECT id FROM playlist '
+ ."WHERE $where ORDER BY RAND()"
+ );
+
+ $nbRecords = $sth->execute;
+
+ ### store result in global array
+ $random_result_table = $sth->fetchall_arrayref;
+ $random_result_type = "pll";
+ # my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n");
+ $sth->finish;
+
+ ### Send number of records
+ print ("random album query: $nbRecords albums found\n");
+ gdio::putline ("$nbRecords\n");
+ gdio::putline (""); # End of result
+}
+
+
+sub tracks_of_saved_playlist{
+ # lists tracks of a saved playlist
+
+ my ($plid) = @_;
+
+ my ($trksth, $irow, $plisth, $row);
+ #my $stop=0;
+
+
+ ### get tracks of the playlist
+ $plisth = do_tracks_of_playlist_query($plid);
+ while($irow = $plisth->fetchrow_hashref){
+ $trksth = $dbh->prepare("SELECT * FROM tracks WHERE id=".$irow->{trackid});
+ $trksth->execute;
+ if($row = $trksth->fetchrow_hashref){
+ send_track($row);
+ }
+ $trksth->finish;
+ }
+ $plisth->finish;
+ gdio::putline (""); # End of result
+}
+
+
+
+############################################################
+### Query tracks and send result to Palm client
+sub simple_track_query{
+ my ($query_cmd) = @_;
+ my ($sth,$row);
+ my $stop=0;
+
+ my $sth = $dbh->prepare($query_cmd);
+ my $rv = $sth->execute;
+ print("$rv records found\n");
+
+ while($row = $sth->fetchrow_hashref){
+ truncstr($row->{artist}, $shrtFieldLen);
+ truncstr($row->{title}, $shrtFieldLen);
+ print ("$row->{id} $row->{artist} $row->{title}\t $row->{length}\n");
+ $stop = gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t"
+ ."$row->{length}\t$row->{tracknb}");
+ last if ($stop);
+ }
+ if (!$stop){
+ gdio::putline ("");} # End of result
+ $sth->finish;
+}
+
+
+
+############################################################
+### Play + Playlist commands ###
+############################################################
+
+
+############################################################
+### New Playlist commands
+
+sub pl_play{ gdgentools::pl_play ($dbh, $playerid, $audchannel0);}
+sub pl_play_at{gdgentools::pl_play_at($dbh, $playerid, $audchannel0, @_);}
+sub pl_stop{ gdgentools::pl_stop ($dbh, $playerid, $audchannel0);}
+sub pl_pause{ gdgentools::pl_pause ($dbh, $playerid, $audchannel0);}
+sub pl_rw{ gdgentools::pl_rw ($dbh, $playerid, $audchannel0);}
+sub pl_ff{ gdgentools::pl_ff ($dbh, $playerid, $audchannel0);}
+sub pl_goto{ gdgentools::pl_goto ($dbh, $playerid, $audchannel0, @_);}
+sub pl_prev{ gdgentools::pl_prev ($dbh, $playerid, $audchannel0);}
+sub pl_next{ gdgentools::pl_next ($dbh, $playerid, $audchannel0);}
+
+
+
+sub pl_append_tracks{
+# appends tracks to the playlist file
+# Parameters: list of track id's
+ gdgentools::tracklist_append_list($dbh, $playerid, $pl_list, @_);
+}
+
+sub pl_new_list{
+# Sets a new playlist. First, playing is stopped, then the current playlist
+# is emptied, the new playlist is written and the current track index is set
+# to zero.
+# An example, where this routine is used is the shuffle function (Palm
+# randomizes playlist order and sends new playlist).
+# Parameters: list of track id's
+ #my $tracklist = @_;
+ pl_empty();
+ pl_append_tracks(@_);
+}
+
+sub pl_empty{
+# empties (deletes) the playlist. The playstate is also reset
+ my $base = gdparams::gdbase();
+ # stop playing
+ gdgentools::stop_play_processes($dbh, $playerid, $audchannel0);
+ # reset list+state
+ gdgentools::tracklist_delete($dbh, $playerid, 0);
+ gdgentools::pll_write_playstate($dbh, $playerid, $audchannel0, 0, "st");
+ gdgentools::pll_write_playtime($dbh, $playerid, $audchannel0, 0, 0);
+ gdgentools::pll_write_shuffleparameters($dbh, $playerid, $audchannel0, "", "");
+}
+
+
+sub pl_remove_one{
+# Removes the specified playlist item (index) from the playlist
+# If the index corresponds to the currently played track, playing is
+# stopped first.
+# It the index is lower than the the index of the currently played track,
+# then nothing is done.
+# Parameter: index of the item to be removed
+
+ my ($delitem) = @_;
+ my ($trackind, $state, $frame, $shufflestat) = gdgentools::pll_get_playstate($dbh, $playerid, $audchannel0);
+ if ($delitem >= $trackind){
+ if (($delitem == $trackind) && $state ne "st" && $state ne "in"){
+ gdgentools::stop_play_processes($dbh, $playerid, $audchannel0);
+ gdgentools::pll_write_playstate($dbh, $playerid, $audchannel0, $trackind, "st");
+ }
+
+ ### delete it
+ gdgentools::tracklist_del_item($dbh, $playerid, 0, $delitem);
+ }
+}
+
+
+
+sub pl_reorder_one{
+# Reorders the playlist.
+# The specified playlist item (src) is moved to a new location (dest)
+# src and dest must be higher than the currently played track. They
+# therefore don't interfere with the track playing process.
+
+ my ($srcpos, $destpos) = @_;
+ ### move it
+ gdgentools::tracklist_reorder_item($dbh, $playerid, 0, $srcpos, $destpos);
+}
+
+
+############################################################
+### Shuffle play (query only)
+sub pl_calc_shufflestats{ # query tracks and send back statistics
+ my $where = gddb::track_where_clause(1, @_);
+ my $sth = $dbh->prepare(
+ "SELECT SUM(length) AS playlen, COUNT(*) AS nbtracks "
+ ."FROM tracks WHERE $where" );
+ $sth->execute;
+ my ($row, $nbtracks, $playlen);
+ if($row = $sth->fetchrow_hashref){
+ $nbtracks = $row->{nbtracks};
+ $playlen = $row->{playlen};
+ }
+ else{
+ print("Shuffle play: query error!\n");
+ }
+ $sth->finish;
+ return ($nbtracks, $playlen);
+}
+
+
+
+sub pl_check_shuffleplay{ # query tracks and send back statistics
+ my ($nbtracks, $playlen) = pl_calc_shufflestats(@_);
+
+ if($nbtracks > 10){
+ gdio::putline("$nbtracks tracks found. Total play length is "
+ . gdgentools::seconds_to_hm($playlen)
+ .". Play them in random order?");
+ }
+ else{
+ gdio::putline("Sorry! $nbtracks tracks are not enough to be played in shuffle mode.");
+ }
+ gdio::putline ("");
+}
+
+
+
+sub pl_start_shuffleplay{
+ pl_empty(); # stop playing, empty playlist and clear playstate
+ my ($nbtracks, $playlen) = pl_calc_shufflestats(@_);
+ my $shuffleparam = join ("\t",@_); ### Attention: does not join trailing empty parameters!
+ if (length($shuffleparam)==0){$shuffleparam ="\t\t";} ### join does not work on empty parameters -> cheat an empty parameter string!
+ gdgentools::pll_write_shuffleparameters($dbh, $playerid, $audchannel0,
+ $shuffleparam, "$nbtracks Tr [".gdgentools::seconds_to_hm($playlen)."]");
+ gdgentools::pl_play($dbh, $playerid, $audchannel0); # start playing - the player script knows how to handle the shuffle play parameters
+}
+
+
+############################################################
+
+
+# send current playlist to the client (tracklist with ID, artist, title, length)
+sub pl_reload_playlist{
+ my @playlist = gdgentools::tracklist_get_all($dbh, $playerid, 0);
+ my ($stop, $trackid, $sth, $trackrow, $row);
+
+ while($trackid = shift @playlist){
+ $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid");
+ $sth->execute;
+
+ if($row = $sth->fetchrow_hashref){
+ $stop = send_track($row);
+# truncstr($row->{artist}, $shrtFieldLen);
+# truncstr($row->{title}, $shrtFieldLen);
+# print ("$row->{id}\t$row->{artist}\t$row->{title}\t$row->{length}\n");
+# gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t"
+# ."$row->{length}\t$row->{tracknb}");
+ last if ($stop);
+ }
+ $sth->finish;
+ }
+ if (!$stop){
+ gdio::putline ("");} # End of result
+}
+
+
+# send current playstate to the client
+sub pl_get_curr_playstate{
+ # sends: pll-length, current-index, current-id, playtime, tracklength, state
+ use integer;
+
+print "get playstate for channel ".$gdgentools::audchannel1."\n";
+ my ($trackind, $state, $frame, $shufflestat) =
+ gdgentools::pll_get_playstate($dbh, $playerid, $audchannel0);
+ my ($played, $total) = gdgentools::pll_get_playtime($dbh, $playerid, $audchannel0);
+ my @playlist = gdgentools::tracklist_get_all($dbh, $playerid, 0);
+ my $playlistlen = @playlist;
+ my $pltime = $played/gdgentools::frames_per_second();
+ my $pllen = $total/gdgentools::frames_per_second();
+
+ #print ("Sending state: pl-len:$playlistlen ind:$trackind id:$playlist[$trackind] ptime:$pltime ptot:$pllen state:$state\n");
+ gdio::putline ("$playlistlen\t$trackind\t$playlist[$trackind]\t$pltime\t$pllen\t$state\t$shufflestat");
+ gdio::putline (""); # End of result
+}
+
+
+
+
+################################################################
+### Export Playlist:
+
+sub pl_export_empty{
+ my $base = gdparams::gdbase();
+
+ unlink <$base/outbox/*>;
+}
+
+sub pl_export_audiolinks{
+# exports the current playlist as links to mp3-files to the outbox
+ my $base = gdparams::gdbase();
+ my ($trackid, $row, $tracknum, $trh, $trkrow, $linkname, $mp3fp);
+ my ($audiofmt, $audioparam);
+
+ ### run through playlist
+ my $sth = $dbh->prepare(
+ "SELECT * FROM tracklistitem "
+ ."WHERE playerid=$playerid AND listtype=$pl_list "
+ ."ORDER BY tracknb " );
+ my $nbrec = $sth->execute;
+ while($row = $sth->fetchrow_hashref){
+ $trackid = $row->{trackid};
+
+ ### get track details
+ $trh = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid");
+ $trh->execute;
+ if($trkrow = $trh->fetchrow_hashref){
+ ####$trackid = $row->{trackid};
+ ### create link
+ $mp3fp = `ls $base/??/$trkrow->{mp3file}`; # get full filename and path
+ ($audiofmt, $audioparam) = gdgentools::bitrate_str_to_format_param($trkrow->{bitrate}); # split "mp3 128"
+ chop($mp3fp);
+ $linkname = gdgentools::ascii2filename(
+ gdgentools::iso2ascii($trkrow->{artist}." - ".$trkrow->{title}));
+ #print("exporting link: "."$mp3fp --- $base/outbox/$linkname.mp3"."\n");
+ symlink ("$mp3fp", "$base/outbox/$linkname.$audiofmt");
+ }
+ $trh->finish;
+
+ }
+ $sth->finish;
+}
+
+
+
+################################################################
+### Upload Playlist to mp3 Player:
+
+sub pl_upload_to_player_info{
+print("EXTMP3 is:".$gdparms::extmp3player."\n");
+ if (length($gdparms::extmp3player)>0){
+ pl_export_empty();
+ pl_export_audiolinks();
+print("CMD: gdupload2player.pl --$gdparms::extmp3player --info\n");
+ my $resline = `gdupload2player.pl --$gdparms::extmp3player --info`;
+ gdio::putline ($resline);
+ }
+ gdio::putline (""); # End of result
+}
+
+sub pl_upload_to_player{
+ my ($addtrks) = @_;
+
+ system( "gdupload2player.pl --$gdparms::extmp3player $addtrks&");
+}
+
+
+
+
+################################################################
+### Saved Playlists:
+
+sub pl_save_playlist{
+# saves the playlist with the specified name
+# Parameters: title, author, note, followed by list of track id's
+ my $title = shift(@_);
+ my $author = shift(@_);
+ my $note = shift(@_);
+ my ($playlistid, $trackid, $tracknum);
+
+ my $nbtracks = @_;
+ if (length($title)>0 && $nbtracks>0){
+ ### check if playlist with that name already exists
+ my $sth = $dbh->prepare("SELECT * FROM playlist WHERE title = ".$dbh->quote($title));
+ my $count = $sth->execute;
+ if ($count > 0){
+ ### A playlist with this name exists already, get it's ID
+ my $row = $sth->fetchrow_hashref;
+ $playlistid = $row->{id};
+ my $plh = $dbh->prepare(
+ "SELECT * FROM playlistitem WHERE playlist = ".$playlistid
+ ." ORDER BY tracknumber DESC LIMIT 1");
+ $count = $plh->execute;
+ if ($count>0){
+ $row = $plh->fetchrow_hashref;
+ $tracknum = $row->{tracknumber} +1;
+ }
+ else{
+ $tracknum = 1;
+ }
+ $plh->finish;
+
+ }
+ else{
+ ### A playlist with this name does not exist, create it
+ my $sqlcmd =
+ "INSERT INTO playlist (title,author,note,created) "
+ ."VALUES (".$dbh->quote($title).",".$dbh->quote($author)
+ .",'$note',CURDATE()) ";
+ $dbh->do($sqlcmd);
+ $playlistid = $dbh->{'mysql_insertid'};
+ $tracknum = 1;
+ }
+ $sth->finish;
+
+
+ ### Append tracks to playlist $playlistid starting with number $tracknum
+ while($trackid = shift(@_)){
+ #print("appending track-ID $trackid as itemnb $tracknum\n");
+ my $sqlcmd =
+ "INSERT INTO playlistitem (playlist,tracknumber,trackid) "
+ ."VALUES ($playlistid,$tracknum,$trackid) ";
+ $dbh->do($sqlcmd);
+ $tracknum++;
+ }
+ }
+ else{
+ print("Can't save playlist: title or playlist are empty\n");
+ }
+}
+
+
+sub pl_delete_playlist{
+# deletes the playlist with the specified id
+ my ($playlistid) = @_;
+ my $sqlcmd;
+
+ ### Delete all associated tracks
+ $sqlcmd = ("DELETE FROM playlistitem WHERE playlist = ".$playlistid);
+ $dbh->do($sqlcmd);
+
+ ### Delete playlist
+ $sqlcmd = ("DELETE FROM playlist WHERE id = ".$playlistid);
+ $dbh->do($sqlcmd);
+}
+
+
+
+############################################################
+### Play mp3 file commands
+
+### directly play a track (in inbox, before trimming, etc)
+sub cmd_direct_play_mp3_track{
+#parameters: filename of mp3 file (without path)
+# startframe (optional)
+# nbframes (optional)
+
+ my ($audiofile, $startframe, $nbframes) = @_;
+ my $base = gdparams::gdbase();
+
+ ### kill early, for /dev/dsp to recover
+ gdgentools::stop_play_processes($dbh, $playerid, $audchannel0);
+ system("sleep 1s"); # sleep a second
+
+ if($startframe > 0){
+ $startframe = "-k ".$startframe;
+ }
+ else{
+ $startframe = "-k 0";
+ }
+ if($nbframes > 0){
+ $nbframes = "-n ".$nbframes;
+ }
+ else{
+ $nbframes = "";
+ }
+
+ print("\nplaying inbox $audiofile $startframe $nbframes\n");
+ # play it in inbox or in 00, 01 ...
+ if ( gdgentools::audio_filetype($audiofile) eq "mp3"){
+ system( "mpg123 $startframe $nbframes $base/[0-9i][0-9n]*/$audiofile&");
+ }
+ elsif(gdgentools::audio_filetype($audiofile) eq "ogg"){
+ system( "ogg123 $base/[0-9i][0-9n]*/$audiofile&");
+ }
+}
+
+
+### Command stop playing
+sub cmd_stop_playing{ #
+ gdgentools::kill_all_play_processes();
+# gdgentools::stop_play_processes($dbh, $playerid, $audchannel0);
+ print("Playing stopped\n");
+}
+
+############################################################
+### Play CD track commands
+
+### Command stop playing cd
+sub cmd_stop_playing_cd{ #
+ system("cdplay stop");
+}
+
+### Command play CD track
+sub cmd_play_cd_track{ #
+ my ($tracknb) = @_;
+ system("cdplay play $tracknb");
+}
+
+
+############################################################
+### Listings ###
+############################################################
+
+############################################################
+### List used genres
+sub list_used_genres{ #get all used genres, count and list them
+
+ my $sth = $dbh->prepare(
+ 'SELECT id, genre, freq '
+ .'FROM genre '
+ .'ORDER BY genre '
+ );
+
+ $sth->execute;
+ my (@row, $stop);
+
+ # "no genre" first
+ print ("\t\t1\t0\t0\n");
+ gdio::putline ("\t\t1\t0\t0");
+
+ while(@row = $sth->fetchrow_array){
+ print ("$row[0]\t$row[1]\t$row[2]\t0\t0\n");
+ $stop = gdio::putline ("$row[0]\t$row[1]\t$row[2]\t0\t0");
+ last if ($stop);
+ }
+ gdio::putline (""); # End of result
+ $sth->finish;
+}
+
+############################################################
+### List used languages
+sub list_used_languages{ #get all used languages, count and list them
+ my $sth = $dbh->prepare(
+ 'SELECT id, language, freq '
+ .'FROM language '
+ .'ORDER BY freq DESC '
+ );
+
+ my $rv = $sth->execute;
+ my (@row, $stop);
+
+ # "no language" first
+ print ("\t\t1\n");
+ gdio::putline ("\t\t1");
+
+ while(@row = $sth->fetchrow_array){
+ print ("$row[0]\t$row[1]\t$row[2]\n");
+ $stop = gdio::putline ("$row[0]\t$row[1]\t$row[2]");
+ last if ($stop);
+ }
+ gdio::putline (""); # End of result
+ $sth->finish;
+}
+
+############################################################
+### List music types
+sub list_music_types{
+ my $sth = $dbh->prepare('SELECT * FROM musictype ORDER BY id ');
+ my $rv = $sth->execute;
+ my ($row, $stop);
+
+ while($row = $sth->fetchrow_hashref){
+ print ("$row->{musictype}\n");
+ $stop = gdio::putline ("$row->{musictype}");
+ last if ($stop);
+ }
+ gdio::putline (""); # End of result
+ $sth->finish;
+}
+
+############################################################
+### List music sources
+sub list_music_sources{
+ my $sth = $dbh->prepare('SELECT * FROM source ORDER BY id ');
+ my $rv = $sth->execute;
+ my ($row, $stop);
+
+ while($row = $sth->fetchrow_hashref){
+ print ("$row->{source}\n");
+ $stop = gdio::putline ("$row->{source}");
+ last if ($stop);
+ }
+ gdio::putline (""); # End of result
+ $sth->finish;
+}
+
+
+############################################################
+### Calculate language hash
+sub language_hash{ # returns the hash value of the language
+ # table
+# my $sth = $dbh->prepare( # get used languages only
+# 'SELECT language.id, language.language, COUNT(*) AS freq '
+# .'FROM tracks,language '
+# .'WHERE tracks.lang=language.id '
+# .'GROUP BY language.id '
+# .'ORDER BY freq DESC '
+# );
+
+ my $sth = $dbh->prepare(
+ 'SELECT id, language, freq '
+ .'FROM language '
+ .'ORDER BY freq DESC '
+ );
+
+ my $rv = $sth->execute;
+ my @row;
+ my $newval;
+
+ my $hashval = 0;
+
+ while(@row = $sth->fetchrow_array){
+ ### calc hash value
+ $newval = ord(chop($row[0]))<<8;
+ $newval += ord(chop($row[0]));
+ $hashval = myhash::addvaltohash($hashval, $newval);
+ }
+ $sth->finish;
+
+# my $sth = $dbh->prepare( # get all languages alphabetically
+# 'SELECT language.id '
+# .'FROM language '
+# .'ORDER BY language.id '
+# );
+#
+# my $rv = $sth->execute;
+# my @row;
+#
+# while(@row = $sth->fetchrow_array){
+# ### calc hash value
+# $newval = ord(chop($row[0]))<<8;
+# $newval += ord(chop($row[0]));
+# $hashval = myhash::addvaltohash($hashval, $newval);
+# }
+# $sth->finish;
+
+ return $hashval;
+}
+
+
+############################################################
+### Get language hash
+sub get_language_hash{ # sends the language hash val to th client
+ #
+
+ my $hashval = language_hash();
+
+ print("language hash value = $hashval \n");
+ gdio::putline ("$hashval"); # send hash value
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### calculate genre hash value
+sub genre_hash{ # returns hash value of the language
+ # table
+
+# my $sth = $dbh->prepare( # get used genres only
+# 'SELECT genre.id,genre.genre,COUNT(*) AS freq '
+# .'FROM tracks,genre '
+# .'WHERE tracks.genre1=genre.id OR tracks.genre2=genre.id '
+# .'GROUP BY genre.id '
+# .'ORDER BY freq '
+# );
+
+ my $sth = $dbh->prepare(
+ 'SELECT id, genre, freq '
+ .'FROM genre '
+ .'ORDER BY freq '
+ );
+
+ my $rv = $sth->execute;
+ my @row;
+ my $newval;
+
+ my $hashval = 0;
+
+ while(@row = $sth->fetchrow_array){
+ ### calc hash value
+ $newval = ord(chop($row[0]))<<16;
+ $newval += ord(chop($row[0]))<<8;
+ $newval += ord(chop($row[0]));
+ $hashval = myhash::addvaltohash($hashval, $newval);
+ }
+ $sth->finish;
+
+# my $sth = $dbh->prepare( # get all genres alphabetically
+# 'SELECT genre.id '
+# .'FROM genre '
+# .'ORDER BY genre.id '
+# );
+#
+# my $rv = $sth->execute;
+#
+# while(@row = $sth->fetchrow_array){
+# ### calc hash value
+# $newval = ord(chop($row[0]))<<8;
+# $newval += ord(chop($row[0]));
+# $hashval = myhash::addvaltohash($hashval, $newval);
+# }
+#
+# $sth->finish;
+
+ return $hashval;
+}
+
+
+
+############################################################
+### Get genre hash
+sub get_genre_hash{ # send genre hash value to client
+ #
+
+ my $hashval = genre_hash();
+
+ print("genre hash value = $hashval \n");
+ gdio::putline ("$hashval"); # send hash value
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### calculate types hash value
+sub types_hash{ # returns hash value of the
+ # musictype table
+
+ my $sth = $dbh->prepare('SELECT * FROM musictype ORDER BY id ');
+ my $rv = $sth->execute;
+ my $row;
+ my $hashval = 0;
+ my $newval;
+
+ while($row = $sth->fetchrow_hashref){
+ ### calc hash value
+ $newval = ord(chop($row->{musictype}))<<16;
+ $newval += ord(chop($row->{musictype}))<<8;
+ $newval += ord(chop($row->{musictype}));
+ $hashval = myhash::addvaltohash($hashval, $newval);
+ }
+ $sth->finish;
+
+ return $hashval;
+}
+
+############################################################
+### Get types hash
+sub get_types_hash{ # send types hash value to client
+ #
+
+ my $hashval = types_hash();
+
+ print("types hash value = $hashval \n");
+ gdio::putline ("$hashval"); # send hash value
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### calculate sources hash value
+sub sources_hash{ # returns hash value of the
+ # source table
+
+ my $sth = $dbh->prepare('SELECT * FROM source ORDER BY id ');
+ my $rv = $sth->execute;
+ my $row;
+ my $hashval = 0;
+ my $newval;
+
+ while($row = $sth->fetchrow_hashref){
+ ### calc hash value
+ $newval = ord(chop($row->{source}))<<16;
+ $newval += ord(chop($row->{source}))<<8;
+ $newval += ord(chop($row->{source}));
+ $hashval = myhash::addvaltohash($hashval, $newval);
+ }
+ $sth->finish;
+
+ return $hashval;
+}
+
+############################################################
+### Get sources hash
+sub get_sources_hash{ # send sources hash value to client
+ #
+
+ my $hashval = sources_hash();
+
+ print("sources hash value = $hashval \n");
+ gdio::putline ("$hashval"); # send hash value
+ gdio::putline (""); # End of result
+}
+
+############################################################
+### Get all hash
+sub get_all_hashes{ # send hash values to client
+ # of all dynamic tables:
+ # language, genre, type
+
+ my $languagehash = language_hash();
+ my $genrehash = genre_hash();
+ my $typeshash = types_hash();
+ my $sourceshash = sources_hash();
+
+ print("hash values: $languagehash $genrehash $typeshash $sourceshash\n");
+ gdio::putline ("$languagehash\t$genrehash\t$typeshash\t$sourceshash");
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### List all languages ordered
+sub list_all_languages_ordered{
+ #get all languages and list them ordered by their usage count
+# my $sth = $dbh->prepare(
+# 'SELECT language.id, language.language, '
+# .' (COUNT(*)-1) AS freq '
+# .'FROM language LEFT JOIN tracks '
+# .'ON tracks.lang=language.id '
+# .'GROUP BY language.id '
+# .'ORDER BY freq DESC, language.language ASC '
+# );
+ my $sth = $dbh->prepare(
+ 'SELECT id, language, freq '
+ .'FROM language '
+ .'ORDER BY freq DESC, language ASC '
+ );
+
+ my $rv = $sth->execute;
+ my (@row, $stop);
+
+ while(@row = $sth->fetchrow_array){
+ #if (length($row[2])==0){$row[2]=0;}
+ print ("$row[0]\t$row[1]\t$row[2]\n");
+ $stop = gdio::putline ("$row[0]\t$row[1]\t$row[2]");
+ last if ($stop);
+ }
+ gdio::putline (""); # End of result
+ $sth->finish;
+}
+
+
+############################################################
+### List all genres ordered directly
+sub list_all_genres_ordered{
+ # get all genres and list them ordered alphabetically by genre
+ # fields: id, genre, freq
+
+ ### Get all genre entries
+ my $sthA = $dbh->prepare(
+ 'SELECT id, genre FROM genre ORDER BY genre.id '
+ );
+ my $totcnt = $sthA->execute;
+
+ ### Get used entries only with count
+# my $sthB = $dbh->prepare(
+# 'SELECT genre.id,genre.genre,COUNT(*) AS freq '
+# .'FROM tracks,genre '
+# .'WHERE tracks.genre1=genre.id OR tracks.genre2=genre.id '
+# .'GROUP BY genre.id ORDER BY genre.id '
+# );
+ my $sthB = $dbh->prepare(
+ 'SELECT id, genre, freq '
+ .'FROM genre '
+ .'ORDER BY id '
+ );
+
+ my $usedcnt = $sthB->execute;
+
+### The following code should actually go to 'gdtablefreq.pl' !
+
+ ### Merge frequencies of list B into list A (both lists must be ordered by id!)
+ # -> build "hierarchical sum": freq(node) = sum-of each freq(subnode) + proper freq
+ my $rowB = $sthB->fetchrow_hashref;
+ my ($i, $rowA, @listA, $rec);
+ while ($rowA = $sthA->fetchrow_hashref){
+ $rec = {};
+ $rec->{"id"} = $rowA->{id};
+ $rec->{genre} = $rowA->{genre};
+ $rec->{freq} = 0; # init freq counter
+ if($rowA->{id} eq $rowB->{id}){
+ # copy frequency of B to A
+ $rec->{freq} = $rowB->{freq};
+ $rowB = $sthB->fetchrow_hashref; # get next entry from B
+ }
+ push @listA, $rec;
+ }
+
+ recalc_genre_freq_arrayofhash(@listA);
+
+ @listA = sort { $gdserv::a->{genre} cmp $gdserv::b->{genre} } @listA;
+
+ # "no genre" first: freq=1
+ print ("\t\t1\n");
+ gdio::putline ("\t\t1");
+
+ my $stop;
+ for $i (0 .. $#listA){
+ print ("$listA[$i]{id}\t$listA[$i]{genre}\t$listA[$i]{freq}\n");
+ $stop = gdio::putline
+ ("$listA[$i]{id}\t$listA[$i]{genre}\t$listA[$i]{freq}");
+ last if ($stop);
+ }
+ gdio::putline (""); # End of result
+ $sthA->finish;
+ $sthB->finish;
+}
+
+
+############################################################
+sub recalc_genre_freq_arrayofhash{
+ ### new version: works on array of hashes (id, genre, freq)
+ # requires that the array is ordered by id!
+
+ ### first pass, get maximal genre id length
+ my $arrlen = @_;
+ my $maxlen = 0;
+ my $i;
+
+ for $i( 0 .. $arrlen ){
+ if(length($_[$i]{id}) > $maxlen){
+ $maxlen = length($_[$i]{id});
+ }
+ }
+ ### next passes: cumulate freq of ID with maxlen to ID with maxlen-1
+ my ($parent, $ID);
+ while ($maxlen > 1){
+ for $i( 0 .. $arrlen ){
+ # 0:id, 1:genre, 2:freq
+ if(length($_[$i]{id}) == $maxlen){
+ ### add freq of this item to freq of parent item
+ $ID = $_[$i]{id};
+ chop($ID);
+ $parent=0;
+ while($parent<$arrlen && $_[$parent]{id} ne $ID){
+ $parent++;
+ }
+ if($_[$parent]{id} eq $ID){
+ $_[$parent]{freq} += $_[$i]{freq};
+ }
+ else{print("ERROR: cant' find parent ID $ID \n");exit;}
+ }
+ }
+ $maxlen--;
+ }
+}
+
+
+############################################################
+### Modify genres table
+
+############################################################
+### Modify Genres: Delete
+sub gen_delete{
+ ### Delete a genre and move all it's tracks to the supergenre
+ my($genreid) = @_; # the ID of the genre to be deleted
+
+ my $targetid = $genreid;
+ chop ($targetid);
+ print ("Delete genre id $genreid translate it to $targetid\n");
+
+ my ($retval1, $retval2, $gensdel);
+ $retval1 = $dbh->do('UPDATE tracks SET genre1="'.$targetid.'" WHERE genre1 LIKE "'.$genreid.'%"');
+ $retval2 = $dbh->do('UPDATE tracks SET genre2="'.$targetid.'" WHERE genre2 LIKE "'.$genreid.'%"');
+ $gensdel= $dbh->do('DELETE FROM genre WHERE id LIKE "'.$genreid.'%"');
+ print("$gensdel genre entries deleted. $retval1 + $retval2 track records changed to supergenre.\n");
+
+ if($retval1 eq '0E0'){$retval1=0;}
+ if($retval2 eq '0E0'){$retval2=0;}
+ gdio::putline ("$gensdel genre entries deleted. ".($retval1+$retval2)." track records changed.");
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### find a free genre for a given prefix. It returns only the suffix.
+sub gen_new_suffix{
+ my ($prefix) = @_;
+ my $suffix = "a";
+ ### search for a new id
+ my ($sth, $count);
+ while($suffix ne "z"){
+ $sth = $dbh->prepare("SELECT * FROM genre WHERE id = '".$prefix.$suffix."'");
+ $count = $sth->execute;
+ $sth->finish;
+ last if ($count eq '0E0');
+ $suffix++;
+ }
+ return $suffix;
+}
+
+############################################################
+### Modify Genres: Move
+sub gen_move{
+ ### Move a genre and all its subgenres to a new location in the hierarchy
+ my($genreidFrom, $genreidTo) = @_; # the two IDs
+
+ print ("Move genre $genreidFrom to $genreidTo\n");
+
+ my $suffix = gen_new_suffix($genreidTo); # new subgenre in target genre
+
+ if($suffix eq "z"){
+ print ("No genre moved! You can't put more than 26 subgenres in a genre. \n");
+ gdio::putline ("No genre moved! You can't put more than 26 subgenres in a genre.");
+ }
+ elsif( (length($genreidFrom) <= length($genreidTo))
+ && (substr($genreidTo, 0, length($genreidFrom)) eq $genreidFrom)
+ ){
+ print ("No genre moved! Target is a child of Source. \n");
+ gdio::putline ("No genre moved! Target is a child of Source.");
+ }
+ else{
+ ### replace all genre prefixes "$genreidFrom" by "$genreidTo.$suffix"
+ ### in tracks and genre table
+
+ my $retval1 = $dbh->do(
+ 'UPDATE tracks SET genre1=CONCAT("'.$genreidTo.'","'.$suffix.'",SUBSTRING(genre1, '.(length($genreidFrom)+1).')) '
+ .'WHERE genre1 LIKE "'.$genreidFrom.'%"');
+ my $retval2 = $dbh->do(
+ 'UPDATE tracks SET genre2=CONCAT("'.$genreidTo.'","'.$suffix.'",SUBSTRING(genre2, '.(length($genreidFrom)+1).')) '
+ .'WHERE genre2 LIKE "'.$genreidFrom.'%"');
+ my $retval3 = $dbh->do(
+ 'UPDATE genre SET id=CONCAT("'.$genreidTo.'","'.$suffix.'",SUBSTRING(id, '.(length($genreidFrom)+1).')) '
+ .'WHERE id LIKE "'.$genreidFrom.'%"');
+ gdio::putline (($retval1+$retval2)." track genres, $retval3 genre IDs moved.");
+# gdio::putline (" track genres moved.");
+ }
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### Modify Genres: Rename
+sub gen_rename{
+ ### Rename an existing genre
+ my($genreid, $newgenrename) = @_; # the ID and the new name of the genre
+ print ("Add genre $newgenrename to $genreid\n");
+
+ my $retval = $dbh->do("UPDATE genre SET genre=".$dbh->quote($newgenrename)." WHERE id = '".$genreid."'");
+
+ gdio::putline ("Genre renamed to $newgenrename.");
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### Modify Genres: Add
+sub gen_add{
+ ### Add a new genre
+ my($genreid, $newgenrename) = @_; # the ID of the genre where the new genre is added
+ print ("Add genre $newgenrename to $genreid\n");
+
+ my $suffix = gen_new_suffix($genreid);
+
+ if($suffix eq "z"){
+ print ("No genre added! You can't put more than 26 subgenres in a genre. \n");
+ gdio::putline ("No genre added! You can't put more than 26 subgenres in a genre.");
+ }
+ else{
+ my $retval = $dbh->do("INSERT INTO genre "
+ ."(id, id3genre, genre) "
+ ."VALUES('".$genreid.$suffix."',NULL,".$dbh->quote($newgenrename).")");
+ gdio::putline ("Genre $newgenrename added.");
+ }
+ gdio::putline (""); # End of result
+}
+
+
+
+
+############################################################
+### Modify music types table
+
+############################################################
+### returns the number of records in the musictype table
+sub typ_nb_records{
+ my ($sth, $count);
+ $sth = $dbh->prepare("SELECT * FROM musictype");
+ $count = $sth->execute;
+ $sth->finish;
+ return $count;
+}
+
+############################################################
+### Modify Types: Add
+sub typ_add{
+ ### Add a new type
+ my($newtypename) = @_;
+ print ("Add type $newtypename\n");
+
+ my $index = typ_nb_records() + 1;
+
+ my $retval = $dbh->do("INSERT INTO musictype "
+ ."(musictype, id) VALUES(".$dbh->quote($newtypename).",'".$index."')");
+ gdio::putline ("Music type $newtypename added.");
+ gdio::putline (""); # End of result
+}
+
+############################################################
+### Modify Types: Delete
+sub typ_delete{
+ ### Delete a type
+
+ my($delid) = @_; # the ID of the type to be deleted
+ my $lastid = typ_nb_records() - 1;
+
+ # sequence of id-numbers must be contiguous: -> "move" lastid over delid,
+ # overwriting delid.
+
+ # Note: track.type starts from 0, musictype.id starts from 1
+ # Bad, I know, but it's too late to change it. I ignore the num value
+ # of musictype.id, and only use it to define an order.
+ print ("Delete type id $delid, put $lastid to its place\n");
+
+ ### modify tracks table
+ my ($retval1, $retval2, $typsdel);
+ $retval1 = $dbh->do('UPDATE tracks SET type= 0 WHERE type = '.$delid);
+ $retval2 = $dbh->do('UPDATE tracks SET type='.$delid.' WHERE type = '.$lastid);
+
+ ### modify musictypes table
+ $typsdel= $dbh->do('DELETE FROM musictype WHERE id ='.($delid+1));
+ $retval2= $dbh->do('UPDATE musictype SET id='.($delid+1).' WHERE id = '.($lastid+1));
+
+ print("Type deleted. Type of ".$retval1." tracks set to NULL.\n");
+
+ if($retval1 eq '0E0'){$retval1=0;}
+ if($retval2 eq '0E0'){$retval2=0;}
+ gdio::putline ("Type deleted. Type of ".$retval1." tracks set to NULL.");
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### Send complete track
+sub send_track_row{
+ # Sends a complete track row obained from a SELECT * FROM tracks
+ # query.
+ # The parameter is a track row (record) obtained by a
+ # $trackset->fetchrow_hashref command
+
+ my ($trackrow) = @_;
+ ## if ($nbtrks > 0){
+ print(
+ $trackrow->{artist}.", "
+ .$trackrow->{title}.", "
+ .$trackrow->{genre1}.", "
+ .$trackrow->{genre2}.", "
+ .$trackrow->{year}.", "
+ .$trackrow->{lang}.", "
+ .$trackrow->{type}.", "
+ .$trackrow->{rating}.", "
+ .$trackrow->{length}.", "
+ .$trackrow->{source}.", "
+ .$trackrow->{sourceid}.", "
+ .$trackrow->{tracknb}.", "
+ .$trackrow->{mp3file}.", "
+ .$trackrow->{condition}.", "
+ .$trackrow->{voladjust}.", "
+ .$trackrow->{created}.", "
+ .$trackrow->{modified}.", "
+ .$trackrow->{id}.", "
+ .$trackrow->{bitrate}.", "
+ .$trackrow->{haslyrics}
+ ."\n"
+ );
+
+ gdio::putline (
+ $trackrow->{artist}."\t"
+ .$trackrow->{title}."\t"
+ .$trackrow->{genre1}."\t"
+ .$trackrow->{genre2}."\t"
+ .$trackrow->{year}."\t"
+ .$trackrow->{lang}."\t"
+ .$trackrow->{type}."\t"
+ .$trackrow->{rating}."\t"
+ .$trackrow->{length}."\t"
+ .$trackrow->{source}."\t"
+ .$trackrow->{sourceid}."\t"
+ .$trackrow->{tracknb}."\t"
+ .$trackrow->{mp3file}."\t"
+ .$trackrow->{condition}."\t"
+ .$trackrow->{voladjust}."\t"
+ .$trackrow->{created}."\t"
+ .$trackrow->{modified}."\t"
+ .$trackrow->{id}."\t"
+ .$trackrow->{bitrate}."\t"
+ .$trackrow->{haslyrics}
+ );
+
+ # These variables give a hint, if the frequencies should be recalculated (when a track is updated)
+ $trk_last_id = $trackrow->{id};
+ $trk_last_lang = $trackrow->{lang};
+ $trk_last_genre1 = $trackrow->{genre1};
+ $trk_last_genre2 = $trackrow->{genre2};
+}
+
+
+
+############################################################
+### Set CDDB_get parameters
+my %cddb_config;
+# following variables just need to be declared if different from defaults
+$cddb_config{CDDB_HOST}="freedb.freedb.org"; # set cddb host
+$cddb_config{CDDB_PORT}=8880; # set cddb port
+$cddb_config{CDDB_MODE}="cddb"; # set cddb mode: cddb or http
+$cddb_config{CD_DEVICE}="/dev/cdrom"; # set cd device
+# user interaction welcome?
+$cddb_config{input}=0; # 1: ask user if more than one possibility
+ # 0: no user interaction
+
+
+############################################################
+### Get and return id and toc of currently inserted CD
+sub get_cd_diskid{
+ use CDDB_get;
+ # get id and track lengths
+ my $diskid=CDDB_get::get_discids($cddb_config{CD_DEVICE});
+# $track[0]{cddbid} = sprintf "%lx", $diskid->[0]; # get hex notation
+ $diskid->[0] = sprintf "%08lx", $diskid->[0]; # get hex notation
+ return $diskid;
+}
+
+############################################################
+### Get and return directory of currently inserted CD
+sub get_cd_directory{
+# returns array of hashes: index 0: cddbid; index 1..: length (in seconds)
+# index 0: {cddbid (hex, without '0x')}, {title}, {artist}
+# other indexes: {tracklength-sec}, {title}, {artist}
+#
+# title and artist are only defined, if a cddb entry was found
+#
+# 1) if the system has internet access, a matching record is
+# searched at freedb.org
+# 2) searches for a matching cddb record in ~/cddb/*
+# 3) If a record couldn't be found, an array with default strings
+# is returned
+#
+
+ use CDDB_get;
+ #use Net::Ping; #can't use it because it requires root privileges
+
+ my @track;
+ my $diskid = get_cd_diskid();
+ my $base = gdparams::gdbase();
+ $track[0]{artist} = "Artist";
+ $track[0]{title} = "Album Title";
+ $track[0]{cddbid} = $diskid->[0];
+ my $nbtracks = $diskid->[1];
+ my $toc = $diskid->[2];
+ my $cddbid = $diskid->[0];
+ my ($lengthsec);
+ my $i=1;
+ while ($i <= $nbtracks) {#i<total incl. lead-out
+ $lengthsec = ($toc->[$i] ->{min}*60+$toc->[$i] ->{sec})
+ -($toc->[$i-1]->{min}*60+$toc->[$i-1]->{sec});
+ $track[$i]{track}= $i;
+ $track[$i]{length}= $lengthsec;
+ $track[$i]{artist}=" "; # some default values
+ $track[$i]{title} ="CD Track $i";
+ $i++;
+ }
+
+
+#print "\nBEGIN ping\n";
+#my $p = Net::Ping->new("icmp");
+#print "freedb.freedb.org is alive.\n" if $p->ping("freedb.freedb.org", 2);
+#$p->close();
+#print "END ping\n";
+
+ if ($gdparms::systemonline){# Are we online?
+ ##################################################################
+ ### We are online, Try to get matching freedb entry
+ my %cd=CDDB_get::get_cddb(\%cddb_config);
+
+ if(defined $cd{title}) {
+ $track[0]{artist} = $cd{artist};
+ $track[0]{title} = $cd{title};
+ #$track[0]{year} = $cd{year};
+ ### Add track info to @track
+ my $i=1;
+ while ($i <= $nbtracks) {
+ #print "$i: $cd{track}[$i-1] \n";
+ $track[$i]{artist} = $cd{artist};
+ $track[$i]{title} = $cd{track}[$i-1];
+ # ...{genre} = $cd{cat};
+ $i++;
+ }
+ }
+ else{
+ print "freedb: NOT FOUND cddbid: $cd{id}\n";
+ }
+ }
+ else{
+ ##################################################################
+ ### We are offline, try to find a cddb record on the local machine
+ my $cddb_file = `find $base/cddb -name $cddbid -print`;
+ $cddb_file = (split /\s+/, $cddb_file)[0]; # get first match only
+ if(length($cddb_file) > 0){
+ ### a cddb-file was found - get it
+ my @cddblst = get_cddb_rec_offline("$cddb_file");
+ $i = 0;
+ while ($i <= $nbtracks){
+ $track[$i]{artist} = $cddblst[$i]{artist};
+ $track[$i]{title} = $cddblst[$i]{title};
+ $i++;
+ }
+ }
+ }
+ return @track;
+}
+
+
+#sub get_cd__directory___OLD___{
+#sub get_cd__directory{
+# returns array of hashes: index 0: cddbid; index 1..: length (in seconds)
+# my $tmpout="/tmp/cdda2wav.index.out";
+# my $nbtracks = 0;
+# my @track;
+# my $discid;
+# my $line;
+#
+# system("rm $tmpout");
+# system("cdda2wav -D /dev/cdrom -N -d1 &> $tmpout");
+# open(TMPOUT, $tmpout);
+# while(<TMPOUT>){
+# $line = $_;
+# if($line=~m/total tracks/){
+# $line=~m/total tracks:([0-9]*)/;
+# $nbtracks = $1;
+# }
+# while($line=~m/[0-9]*\.\(\s*[0-9]*:[0-9]*\.[0-9]*\)/){
+# $line=~m/([0-9]*)\.\(\s*([0-9]*):([0-9]*)\.[0-9]*\)/;
+# $track[$1]{track}= $1;
+# $track[$1]{length}= ($2*60)+$3;
+#
+# #push @track, $rec;
+# $line=~s/[0-9]*\.\(\s*[0-9]*:[0-9]*\.[0-9]*\)/xx/;
+# }
+# if($line=~m/CDDB discid:\s*[0-9a-z]*/){
+# $line=~m/CDDB discid:\s*([0-9a-z]*)/;
+# #print "Disc ID: $1\n";
+# $discid=$1;
+# $discid =~ m/0x(\S*)/; # remove 0x
+# $track[0]{cddbid}= $1;
+# }
+# }
+# close(TMPOUT);
+# return @track;
+#}
+
+
+
+############################################################
+### Read the specified cddb-file and return its content
+sub get_cddb_rec_offline{
+# returns array: index 0: album artist, album title;
+# index 1..: track title;
+# examples: -> separator: '-' or '/'
+# DTITLE=Die Aerzte - Die Bestie in Menschengestalt
+# DTITLE=MC Lyte / Ain't No Other
+
+ my ($cddb_file) = @_;
+
+ my @track;
+ my $line;
+
+ open(CDDBF, $cddb_file) or die "can't open file $cddb_file\n";
+ while(<CDDBF>){
+ $line = $_;
+ chop($line);
+ if($line=~m/DTITLE=.+/){
+ if ($line=~m/DTITLE=(.+) \/ (.+)/){
+ $track[0]{artist}= $1;
+ $track[0]{title} = $2;
+ }
+ elsif($line=~m/DTITLE=(.+) \- (.+)/){
+ $track[0]{artist}= $1;
+ $track[0]{title} = $2;
+ }
+ elsif($line=~m/DTITLE=(.+)/){
+ $track[0]{artist}= $1;
+ $track[0]{title} = "";
+ }
+ }
+
+ if($line=~m/TTITLE([0-9]+)=(.+)/){
+ $track[$1+1]{artist}= $track[0]{artist};
+ $track[$1+1]{title} = $2;
+ }
+ }
+ close(CDDBF);
+
+ return @track;
+
+}
+
+############################################################
+### List directory of currently inserted CD plus informations from DB
+sub list_cd_directory_info{
+ # takes no parameter or the cddb-id
+
+ my ($cddbid) = @_;
+
+ my @track;
+ my $nbtracks;
+ my $i;
+ my $nbtrks;
+ my $nbalbs;
+ my $trseth; # track set handle
+ my @row;
+ my $trackrow;
+ my $base = gdparams::gdbase();
+ my $cddb_file="";
+ my @cddblst;
+
+ #########################################################
+ ### Set default values
+ @track = get_cd_directory();
+ $nbtracks = @track - 1;
+
+ #########################################################
+ ### Send album record first
+ $trseth = $dbh->prepare(
+ 'SELECT * FROM album WHERE cddbid ="'. $cddbid . '" ');
+ $nbalbs = $trseth->execute;
+ if($nbalbs > 0){
+ print("Matching album in database found");
+ $trackrow = $trseth->fetchrow_hashref;
+ $trackrow->{cddbid} = $cddbid;
+ print("$trackrow->{artist}\t$trackrow->{title}\t$cddbid\n");
+ gdio::putline("$trackrow->{artist}\t$trackrow->{title}\t$cddbid");
+ }
+ else{
+ if(defined($track[0]{title})){
+ ### a matching online freedb entry was found
+ print("$track[0]{artist}\t$track[0]{title}\t$track[0]{cddbid}\n");
+ gdio::putline("$track[0]{artist}\t$track[0]{title}\t$track[0]{cddbid}");
+ }
+ else{
+ print "Error: can't read CD directory at all\n";
+ }
+ }
+ $trseth->finish;
+
+ #########################################################
+ ### Send each track
+ for ($i=1; $i<=$nbtracks; $i++){
+ ### get first track with given cddb-id and track number
+
+ if (length($cddbid)>=8){
+ $trseth = $dbh->prepare(
+ 'SELECT * FROM tracks '
+ .'WHERE sourceid ="'. $cddbid . '" '
+ .' AND tracknb = '.$i );
+ $nbtrks = $trseth->execute;
+ $trackrow = $trseth->fetchrow_hashref;
+ }
+
+ if ((length($cddbid)<8) || ($nbtrks < 1)){
+ ### set default values
+ $nbtrks = 0;
+ $trackrow->{artist} = $track[$i]{artist};
+ $trackrow->{title} = $track[$i]{title};
+ $trackrow->{genre1} = "";
+ $trackrow->{genre2} = "";
+ $trackrow->{year} = "";
+ $trackrow->{lang} = "";
+ $trackrow->{type} = 0;
+ $trackrow->{rating} = 0;
+ $trackrow->{length} = $track[$i]{length};
+ $trackrow->{source} = 0;
+ $trackrow->{sourceid} = $track[0]{cddbid};
+ $trackrow->{tracknb} = $i;
+ $trackrow->{mp3file} = "";
+ $trackrow->{condition}= 0;
+ $trackrow->{voladjust}= 0;
+ #$trackrow->{created} =
+ #$trackrow->{modified} =
+ $trackrow->{bitrate} = $gdparms::defrecbitrate;
+ $trackrow->{haslyrics}="";
+ }
+
+ ### send all fields
+ send_track_row($trackrow);
+
+ $trseth->finish;
+ }# end for
+
+ gdio::putline (""); # End of result
+}
+############################################################
+### List directory of currently inserted CD plus informations from DB
+sub list_inbox_album_directory{
+ # takes as parameter the album directory (without /home/music/inbox prefix)
+
+ my ($path) = @_;
+
+ my ($curfile);
+ my $base = gdparams::gdbase();
+ my $fullpath = "$base/inbox/albums/$path";
+ print "import album at $fullpath\n";
+
+ unlink <$base/inbox/trxx*>;
+ unlink <$base/inbox/tmp-album-dir>;
+ system "cd \"$fullpath\"; rm -f trxx*";
+ opendir INBOX, "$fullpath";
+ my @inboxfile = readdir INBOX;
+ closedir INBOX;
+
+ ### generate new mp3-filename (get highest trxx... filename)
+ my $fileid = gdgentools::last_imported_tracknb($dbh);
+ $fileid += 1; # next available id
+ my $cddbid = sprintf("%08ld", $fileid);
+ my $tracknb = 1;
+
+ #########################################################
+ ### Send album record first
+
+ print "$path\t$path\t$cddbid\n";
+ gdio::putline("$path\t$path\t$cddbid");
+ #print "ln -s \"$fullpath\" \"$base/inbox/tmp-album-dir\"\n";
+ system "ln -s \"$fullpath\" \"$base/inbox/tmp-album-dir\"";
+
+ #########################################################
+ ### Send each track
+ foreach $curfile (@inboxfile){
+ ### check if file is audio format
+ if ( gdgentools::audio_filetype($curfile) eq "mp3"
+ || gdgentools::audio_filetype($curfile) eq "ogg"
+ || gdgentools::audio_filetype($curfile) eq "flac"){
+ send_inbox_track_info($curfile, $fileid, "$cddbid", $tracknb, "$fullpath");
+ $fileid += 1;
+ $tracknb += 1;
+ }
+ }# end foreach
+
+ gdio::putline (""); # End of result
+}
+
+
+
+############################################################
+### List directory of current inbox plus informations from the metatags
+sub list_inbox_directory_info{
+
+ my $base = gdparams::gdbase();
+
+ unlink <$base/inbox/trxx*>;
+ opendir INBOX, "$base/inbox";
+ my @ibfile = readdir INBOX;
+ closedir INBOX;
+
+ my ($curfile);
+
+
+ ### generate new mp3-filename (get highest trxx... filename)
+ my $fileid = gdgentools::last_imported_tracknb($dbh);
+
+ ### Send each track
+ foreach $curfile (@ibfile){
+ ### check if file is mp3 format
+ if ( gdgentools::audio_filetype($curfile) eq "mp3"
+ || gdgentools::audio_filetype($curfile) eq "ogg"
+ || gdgentools::audio_filetype($curfile) eq "flac"){
+ $fileid += 1; #next available id
+ send_inbox_track_info($curfile, $fileid, "", 1, "$base/inbox");
+ }
+ }# end foreach
+
+ gdio::putline (""); # End of result
+}
+
+############################################################
+### the file $curfile must be valid audio file
+# if it is a audiofile, all available metadata and other track
+# informations are sent to the server
+sub send_inbox_track_info{
+
+ my ($curfile, $fileid, $cddbid, $tracknb, $path) = @_;
+ my ($id3genre, $title, $audiofile, $trackrow);
+
+ my $base = gdparams::gdbase();
+
+ ### check if file has a legal audio format
+
+ if ( gdgentools::audio_filetype($curfile) eq "mp3"
+ || gdgentools::audio_filetype($curfile) eq "ogg"
+ || gdgentools::audio_filetype($curfile) eq "flac"){
+ print ("\ncurrentfile is: $curfile \n");
+
+ $audiofile = sprintf("trxx%08ld.%s", $fileid, gdgentools::audio_filetype($curfile));
+ symlink "$path/$curfile", "$base/inbox/$audiofile";
+
+ # get info from meta tags
+ $trackrow->{title} = gdgentools::audiofile_title("$path/$curfile"); # takes filename as default of no metatag present
+ $trackrow->{artist} = gdgentools::audiofile_artist("$path/$curfile");
+ $trackrow->{genre2} = "";
+ $trackrow->{year} = gdgentools::audiofile_year("$path/$curfile");
+ $trackrow->{lang} = "-";
+ $trackrow->{type} = 1; # medium
+ $trackrow->{rating} = 0;
+ $trackrow->{length} = gdgentools::audiofile_lengthsec("$path/$curfile");
+ $trackrow->{source} = 0; # CD
+ $trackrow->{sourceid} = $cddbid; # could also be empty
+ $trackrow->{tracknb} = $tracknb;
+ $trackrow->{mp3file} = $audiofile;
+ $trackrow->{condition} = 0; # OK
+ $trackrow->{voladjust} = 0;
+ $trackrow->{created} = "";
+ $trackrow->{modified} = "";
+ $trackrow->{id} = ""; # a new one will be automatically generated
+ $trackrow->{bitrate} = gdgentools::get_bitrate_str("$path/$curfile");
+
+ # get genre if available and translate to gd-genre
+ $trackrow->{genre1} = gdgentools::audiofile_genre($dbh, "$path/$curfile");
+# $id3genre = gdgentools::audiofile_genre("$path/$curfile");
+# $trackrow->{genre1} = gdgentools::genre_id3togd($dbh, $id3genre);
+
+ ### send all fields
+ $trackrow->{haslyrics} = "";
+ send_track_row($trackrow);
+
+ }# end if is mp3file
+}
+
+############################################################
+### import booklet/cover images
+sub import_cover_img{
+ # imports the jpeg images in a directory and associates them to an album
+ # the images are imported in lexical order.
+ # Naming scheme: trxx(cd-id)-(num).jpg, where num is an automatically
+ # incremented counter. The file imgxx(cd-id)-00.jpg is the front cover,
+ # the other are the following pages in a booklet.
+
+ # Parameters: 1) full directory path, 2) cd-id (like 0x10ac77e0, xx00001496)
+
+ gdgentools::import_cover_images($dbh, @_);
+}
+
+
+############################################################
+### Get one track and send all information details from DB
+sub get_track_details{
+ # takes as parameter the track-ID
+
+ my ($trackid) = @_;
+ my $base = gdparams::gdbase();
+
+
+ my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid");
+ $sth->execute;
+ my $trackrow = $sth->fetchrow_hashref;
+
+ if(length($trackrow->{mp3file})>0){
+ if(length($trackrow->{lyrics})>0){
+ $trackrow->{haslyrics} = "1";
+ }
+ }
+ ### send all fields
+ send_track_row($trackrow);
+
+ $sth->finish;
+
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### Delete specified track record and mp3 file
+sub delete_track{
+# deletes the playlist with the specified id
+ my ($trackid) = @_;
+ my $sqlcmd;
+ my $base = gdparams::gdbase();
+
+ ### Get track record
+ my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid");
+ $sth->execute;
+ my $trackrow = $sth->fetchrow_hashref;
+ my $trackfile = $trackrow->{mp3file};
+ truncstr($trackrow->{artist}, $shrtFieldLen);
+ truncstr($trackrow->{title}, $shrtFieldLen);
+ print("Deleting ($trackrow->{id}) $trackrow->{artist}, $trackrow->{title}\n");
+
+ ### Delete track record
+ $sqlcmd = ("DELETE FROM tracks WHERE id = ".$trackid);
+#print" \$dbh->do($sqlcmd);\n";
+ $dbh->do($sqlcmd);
+
+ ### Delete mp3 file
+#print("rm $base/[0-9][0-9]/$trackfile \n");
+ if (length($trackfile)>4){
+ system("rm $base/[0-9][0-9]/$trackfile");
+ }
+ $sth->finish;
+
+}
+
+
+############################################################
+### Delete specified album and all associated track records and mp3 files
+sub delete_album_tracks{
+# deletes the playlist with the specified id
+ my ($albumid) = @_;
+
+ if(length($albumid)>=6) {
+ ### Delete all associated tracks
+ ### get tracks of the album
+ my $trksth = do_tracks_of_album_query($albumid);
+ my $trow;
+ while($trow = $trksth->fetchrow_hashref){
+ delete_track($trow->{id});
+ }
+ $trksth->finish;
+
+ ### Delete album record
+ print("Deleting Album $albumid\n");
+ my $sqlcmd = ("DELETE FROM album WHERE cddbid = '".$albumid."'");
+#print"\$dbh->do($sqlcmd);\n"
+ $dbh->do($sqlcmd);
+ }
+}
+
+
+############################################################
+### Get one track and send lyrics (or empty text)
+sub get_track_lyrics{
+ # takes as parameter the track-ID
+
+ my ($trackid) = @_;
+
+ my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid");
+ $sth->execute;
+ my $trackrow = $sth->fetchrow_hashref;
+
+ ### insert one space into empty lines (protocol requires non empty content lines)
+ my $lyrics = $trackrow->{lyrics};
+
+ ### send lyrics
+ my @lyrlines = split /\n/, $trackrow->{lyrics};
+ chomp @lyrlines;
+ my $line;
+ print ("Sending lyrics: ".scalar(@lyrlines)." lines\n");
+ foreach $line (@lyrlines){ # line end is a \015\012 sequence
+ $line =~ s/\012$//; # chop off very last newline, if there is one
+ $line =~ s/\015$//; # chop off very last newline, if there is one
+ if (length($line)==0) {$line = " ";}
+ gdio::putline ($line);
+ }
+ gdio::putline (""); # End of result
+
+ $sth->finish;
+}
+
+############################################################
+### Update the lyrics of a track
+sub update_track_lyrics{
+ # parameters: track-ID, lyrics lines
+
+ my $trackid = shift @_;
+ my $lyrics = join "\n", @_;
+
+ print("Lyrics recieved for $trackid:\n$lyrics\n");
+ $dbh->do("UPDATE tracks "
+ ."SET lyrics=".$dbh->quote($lyrics)
+ ."WHERE id=$trackid");
+
+}
+
+
+############################################################
+### Trim mp3 file
+sub trim_mp3_file{
+ # parameters: track-ID, lyrics lines
+
+ my ($mp3file, $startframe, $endframe) = @_;
+ my ($mp3fpath, $mp3directory, $undofname);
+ my $base = gdparams::gdbase();
+
+ $mp3fpath = gdgentools::get_full_audiofile_path($mp3file);
+ $mp3directory = dirname($mp3fpath);
+ $undofname = $mp3directory."/TrimUndo-".$mp3file;
+
+
+ print ("rm $mp3directory/TrimUndo-*\n"); # remove old undofile
+ system("rm $mp3directory/TrimUndo-*");
+ print ("mv $mp3fpath $undofname\n"); # save original mp3file
+ system("mv $mp3fpath $undofname");
+
+ print ("gdmp3cutter $undofname $startframe $endframe $mp3fpath\n");
+ #system("gdmp3cutter $undofname $startframe $endframe $mp3fpath");
+ ### continuously send results, line by line
+ my ($res, $resline);
+ open CMDRES, "gdmp3cutter $undofname $startframe $endframe $mp3fpath |";
+ #autoflush CMDRES 1;
+ while($resline = <CMDRES>){
+ chop($resline);
+ print ("\"$resline\"\n");
+ gdio::putline ($resline);
+ }
+
+ print ("END\n");
+ gdio::putline (""); # End of result
+}
+
+############################################################
+### Get one album and send all information details from DB
+sub trim_mp3_file_undo{ # restores the trimmed mp3 file (if possible)
+ # parameters: track-ID, lyrics lines
+
+ my ($mp3file) = @_;
+ my ($mp3fpath, $mp3directory, $undofname);
+
+
+ my $base = gdparams::gdbase();
+ my $undofname = `ls $base/[0-9][0-9]/TrimUndo-$mp3file`; # get full path
+ chop($undofname);
+
+ $mp3directory = dirname($undofname);
+ $mp3fpath = $mp3directory."/".$mp3file;
+
+ #gdio::putline ("mv $undofname $mp3fpath");
+ print ("mv $undofname $mp3fpath \n"); # restore original mp3file
+ system("mv $undofname $mp3fpath");
+
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### Get one album and send all information details from DB
+sub get_album_details{
+ # takes as parameter the cddb-ID
+
+ my ($albumid) = @_;
+
+ if (length($albumid) > 8) { ### sometimes, a distorted album id preceeded by 4 trashy characters
+ # is passed to this routine (reason is unknown).
+ # this is just a workaround sent in by Frank (slightly modified by me)
+ print "warning: albumid preceeded by trash! fixing it.\n";
+ ($albumid) = substr($albumid,-8,8);
+ }
+
+ my $base = gdparams::gdbase();
+
+
+ my $sth = $dbh->prepare("SELECT * FROM album WHERE cddbid=\"$albumid\"");
+ $sth->execute;
+ my $row;
+ if($row = $sth->fetchrow_hashref){
+ ### send all fields
+ print("$row->{artist}\n");
+ print("$row->{title}\n");
+ gdio::putline ("$row->{artist}");
+ gdio::putline ("$row->{title}");
+ }
+ gdio::putline (""); # End of result
+
+ $sth->finish;
+}
+
+############################################################
+### check currently inserted CD and compare to GDDB
+sub check_current_cd{
+
+# This routine finds all distinct cddb-ID's in the GDDB that have
+# the same stem as the cddb-ID of the currently inserted CD.
+# The routine is needed to solve the problem of potentially ambiguous
+# cddb-ID's. It is usually called before grabbing a CD.
+
+# my @track = get_cd_directory();
+ #$track[0]{cddbid} =~ m/0x(\S*)/;
+ #my $cddbid = $track[0]{cddbid};
+ my $diskid = get_cd_diskid();
+ my $cddbid = $diskid->[0];
+
+ ### Get all cddb-id's with root $cddbid
+ my $sth = $dbh->prepare(
+ 'SELECT DISTINCT sourceid '
+ .'FROM tracks '
+ .'WHERE sourceid LIKE "'. $cddbid. '%" '
+ .'ORDER BY sourceid '
+ );
+
+ my $rv = $sth->execute;
+
+ ### Get first track for each distinct cddb-id
+ my $trseth; # track set handle
+ my $row;
+ my $trackrow;
+ while($row = $sth->fetchrow_hashref){
+ ### get first track with current cddb-id
+
+ $trseth = $dbh->prepare(
+ 'SELECT artist, title FROM tracks '
+ .'WHERE sourceid ="'. $row->{sourceid} . '" ORDER BY tracknb ');
+ $trseth->execute;
+ $trackrow = $trseth->fetchrow_hashref;
+
+ print("$row->{sourceid}\t$trackrow->{artist}, $trackrow->{title}\n");
+ gdio::putline ("$row->{sourceid}\t$trackrow->{artist}, "
+ ."$trackrow->{title}");
+ $trseth->finish;
+ }
+ if($rv > 0){ ### propose also new unambiguous key
+ ### bulletproof is to take biggest extension instead of $rv!!!
+
+ print ("$cddbid.$rv\tNone of these: New CD\n");
+ gdio::putline("$cddbid.$rv\tNone of these: New CD");
+ }
+ gdio::putline (""); # End of result
+
+ $sth->finish;
+}
+
+############################################################
+### read inbox album directories
+sub list_inbox_album_directories{
+
+# This routine finds all directoies in ~music/inbox/albums that
+# contain mp3 or ogg files. Each directory is considered as an
+# album
+
+ my $base = gdparams::gdbase();
+
+ my @filelist = `ls -R1 $base/inbox/albums`;
+ my ($curfile, $directory);
+
+ $directory="";
+
+ foreach $curfile (@filelist){
+ chop($curfile);
+ if ($curfile =~ m/\/inbox\/albums\/(.*):/ ){
+ #print "$curfile is a directory\n";
+ $directory = $1;
+ }
+ if(length($directory)>0 and
+ ( gdgentools::audio_filetype($curfile) eq "ogg"
+ or gdgentools::audio_filetype($curfile) eq "mp3"
+ or gdgentools::audio_filetype($curfile) eq "flac")
+ ){
+ # we have a audio file in a valid directory (directory not yet printed)
+ print "$directory\n";
+ gdio::putline "$directory";
+ $directory = ""; # mark as printed
+ }
+ }
+
+ gdio::putline (""); # End of result
+
+}
+
+
+############################################################
+### Open/close the CD tray
+sub open_cd_tray{
+ system("eject");
+}
+sub close_cd_tray{
+ system("eject -t");
+}
+
+############################################################
+### Get rip state
+sub get_rip_state{ # send CD rip status to client
+ my $base = gdparams::gdbase();
+ # get first item of rip-list
+ my $trid = gdgentools::tracklist_get_item($dbh, $playerid, $rp_list, 0);
+ if($trid != 0){
+ my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trid");
+ my $rv = $sth->execute;
+ my ($wavf, $track, $wavfsize);
+ if($track = $sth->fetchrow_hashref){
+ if($track->{mp3file} =~ /(.*)\.\w+/){
+ $wavf = `ls $base/[0-9][0-9]/$1.wav`;
+ chop($wavf);
+ }
+ else{print("getripstate: can't find wavfile\n");}
+ truncstr($track->{artist}, 20);
+ truncstr($track->{title}, 20);
+ print ("$track->{tracknb}. $track->{artist} - $track->{title}\n");
+ gdio::putline ("$track->{tracknb}. $track->{artist} - $track->{title}");
+
+ $wavfsize = (-s "$wavf");
+ my $wavtotsize = $track->{length} * 44100 * 4; # totalSize = length(sec)*sampRate*bytesPerSample
+ $wavtotsize++; # prevent from divison by zero
+ #print("length: $track->{length}, fsize: $wavfsize, soll: $wavtotsize\n");
+ my $percent = sprintf("%.2f%%", ($wavfsize * 100) / $wavtotsize);
+ print ("$percent percent\n");
+ gdio::putline ("$percent");
+ my $queuelen = gdgentools::tracklist_get_nb_items($dbh, $playerid, $rp_list);
+ $queuelen--;
+ print ("$queuelen tracks queued\n");
+ gdio::putline ("$queuelen tracks queued");
+ }
+ else{
+ print ("ERROR: can't find track in database\n");
+ gdio::putline ("ERROR: can't find track in database");
+ }
+ $sth->finish;
+ }
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### Get compress state
+sub get_compress_state{ # send track compress status to client
+ my $base = gdparams::gdbase();
+ # get first item of compression-list
+ my $trid = gdgentools::tracklist_get_item($dbh, $playerid, $co_list, 0);
+#print("top of compression list: track $trid\n");
+ if($trid != 0){
+ my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trid");
+ my $rv = $sth->execute;
+ my ($track, $audiofile, $currfilesize, $totalfilesize, $percent, $queuelen, $datarate);
+ my ($audiofmt, $audioparam);
+ if($track = $sth->fetchrow_hashref){
+ $audiofile = `ls $base/[0-9][0-9]/$track->{mp3file}`;
+ chop($audiofile);
+ truncstr($track->{artist}, 20);
+ truncstr($track->{title}, 20);
+ print ("$track->{tracknb}. $track->{artist} - $track->{title}\n");
+ gdio::putline ("$track->{tracknb}. $track->{artist} - $track->{title}");
+
+ $currfilesize = (-s "$audiofile");
+ ($audiofmt, $datarate) = gdgentools::bitrate_str_to_format_param($track->{bitrate}); # split "mp3 128"
+ if ($audiofmt eq "mp3" || $audiofmt eq "ogg"){
+ ### datarate is bitrate
+ $totalfilesize = ($track->{length} * $datarate * 1000) / 8; # totalSize = length*bitrate/BitsPerByte
+ }
+ if ($audiofmt eq "flac"){
+ ### datarate is sampling rate
+ $totalfilesize = ($track->{length} * $datarate * 1010 * 2); # totalSize = length*bitrate*BytesPerByte
+ }
+ $totalfilesize++; # prevent from divison by zero
+ #print("file: $audiofile, length: $track->{length}, bitrate: $datarate, fsize: $currfilesize, soll: $totalfilesize\n");
+ $percent = sprintf("%.2f%%", ($currfilesize * 100) / $totalfilesize);
+ print ("$percent\n");
+ gdio::putline ("$percent");
+
+ $queuelen = gdgentools::tracklist_get_nb_items($dbh, $playerid, $co_list);
+ $queuelen--;
+ print ("$queuelen wav-files queued\n");
+ gdio::putline ("$queuelen wav-files queued");
+ }
+ $sth->finish;
+ }
+ gdio::putline (""); # End of result
+}
+
+
+############################################################
+### Misc commands ###
+############################################################
+
+############################################################
+### command burn audio CD
+sub pl_burn_cd{ # Arguments in @_ are the
+
+ my ($trackid, $sth);
+ my $mp3list;
+ my $row;
+ my $base = gdparams::gdbase();
+
+ $mp3list = "";
+
+ ### Write track tiles+artists to file (for booklet printing etc.)
+ open(RECLIST, ">$base/tmp/gdburnlist.txt");
+ my $trkcnt = 1;
+
+ foreach $trackid (@_){
+ $sth = get_track($trackid); # perform database query
+
+ $row = $sth->fetchrow_hashref;
+ $sth->finish;
+ print("add to CD $base/??/$row->{mp3file} ($row->{title}) \n");
+ print(RECLIST "$trkcnt. $row->{artist} $row->{title} \n");
+ $mp3list = $mp3list . " " . "$base/??/" . $row->{mp3file};
+ $trkcnt ++;
+ }
+ close(RECLIST);
+ print("gdburn.sh " . $mp3list . "\n");
+ system("gdburn.sh " . $mp3list . "&");
+}
+
+
+
+############################################################
+### Update browse directory (external script)
+sub update_browse_directory{
+ my $base = gdparams::gdbase();
+ system("$base/bin/gdmakelinks.pl -s &"); # -s silent
+}
+
+### Update browse directory (external script)
+sub export_id3_tags{
+ my $base = gdparams::gdbase();
+# system("cd $base; $base/bin/gdexportdb.pl --id3 &");
+ system("cd $base; $base/bin/gdexportdb.pl --metatags &");
+}
+
+### Shut down entire server (external script)
+sub shut_down_server{
+ use vars qw($shutdowncmd);
+ print("Executing $gdparms::shutdowncmd \n");
+ system($gdparms::shutdowncmd);
+# system("/usr/sbin/usershutdown -h now &");
+}
+
+
+
+############################################################
+### General Query Command
+
+### General database query
+sub general_db_query{
+ my ($dbquery) = @_;
+ my ($sth, @row, $rowstr);
+
+ ### Get # tracks
+ $sth = $dbh->prepare($dbquery);
+ $sth->execute;
+ if(@row = $sth->fetchrow_array){
+ $rowstr = join "\t", @row;
+ print "$rowstr\n";
+ gdio::putline ($rowstr);
+ }
+ gdio::putline (""); # End of result
+ $sth->finish;
+}
+
+sub general_db_query_count{ # needed because older mysql-versions have a bug with COUNT(DISTINCT ...
+ my ($dbquery) = @_;
+ my ($sth, @row, $rowstr, $nbrec);
+
+ ### Get # tracks
+#print "Database Query(count): $dbquery\n";
+ $sth = $dbh->prepare($dbquery);
+ $nbrec = $sth->execute;
+ print "$nbrec\n";
+ gdio::putline ($nbrec);
+ gdio::putline (""); # End of result
+ $sth->finish;
+}
+
+
+############################################################
+### Soundcard/Volume Commands
+sub set_volume{
+ my ($vol) = @_;
+ gdsoundcard::sndc_set_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0), $vol);
+}
+
+sub get_volume{
+ my $vol = gdsoundcard::sndc_get_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0));
+
+ gdio::putline ($vol);
+ gdio::putline (""); # End of result
+}
+
+sub save_volume{
+ gdsoundcard::sndc_save_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0));
+}
+sub inc_volume{
+ gdsoundcard::sndc_inc_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0));
+}
+sub dec_volume{
+ gdsoundcard::sndc_dec_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0));
+}
+
+############################################################
+### General Shell Command
+
+### General shell command
+sub general_sh_command{
+ my ($shcommand) = @_;
+ system($shcommand);
+}
+
+### General shell command and send back result
+sub general_sh_command_res{
+ my ($shcommand) = @_;
+ my ($res, $resline);
+ $res = `$shcommand`;
+ my @reslines = split /\n/, $res;
+ while ($resline = shift @reslines){
+ print ("\"$resline\"\n");
+ gdio::putline ($resline);
+ }
+ gdio::putline (""); # End of result
+}
+
+sub general_sh_command_res_continuous{
+# continuously sends results, line by line
+ my ($shcommand) = @_;
+ my ($res, $resline);
+ open CMDRES, "$shcommand |";
+ #autoflush CMDRES 1;
+ while($resline = <CMDRES>){
+ chop($resline);
+ print ("\"$resline\"\n");
+ gdio::putline ($resline);
+ }
+ gdio::putline (""); # End of result
+}
+
+### General shell command and send back result
+sub gd_basedir{
+ my $base = gdparams::gdbase();
+ print ("$base\n");
+ gdio::putline ($base);
+ gdio::putline (""); # End of result
+}
+
+
+
+############################################################
+### Database and Disc statistics
+sub full_statistics{
+ my $base = gdparams::gdbase();
+ my ($sth, $row, $msg);
+
+ gdio::putline ("DB statistics");
+ ### Get # tracks
+ $sth = $dbh->prepare("SELECT COUNT(*) AS cnt FROM tracks");
+ $sth->execute;
+ if($row = $sth->fetchrow_hashref){
+ $msg = " ".$row->{cnt}." Tracks";
+ print "$msg \n";
+ gdio::putline ($msg);
+ }
+ $sth->finish;
+
+ ### Get # albums
+ $sth = $dbh->prepare("SELECT COUNT(*) AS cnt FROM album");
+ $sth->execute;
+ if($row = $sth->fetchrow_hashref){
+ $msg = " ".$row->{cnt}." Albums";
+ print "$msg \n";
+ gdio::putline ($msg);
+ }
+ $sth->finish;
+
+ gdio::putline (" ");
+ gdio::putline ("Disc statistics");
+ gdio::putline (" (dir: used / free)");
+
+ ### Get mp3 directories and check each directory
+ my @mdir = gdparams::mp3dirs();
+ my $i=0;
+ my (@dfres, $totused, $totfree);
+
+ $totused=0; $totfree=0;
+ while($i < @mdir){
+ if (-d "$base/$mdir[$i]"){
+ @dfres = split / +/, `df -m $base/$mdir[$i]|tail -1`;
+ $msg = " ".$mdir[$i].": ".$dfres[2]."M / ".$dfres[3]."M";
+ print "$msg \n";
+ gdio::putline ($msg);
+ $totused += $dfres[2];
+ $totfree += $dfres[3];
+ }
+ else{print "$base/$mdir[$i] is not a directory or does not exist\n";}
+ $i++;
+ }
+
+ $msg = " tot: ".$totused."M / ".$totfree."M";
+ print "$msg \n";
+ gdio::putline ($msg);
+
+ gdio::putline (""); # End of result
+
+ ### "Side Effect": print player-id and -type
+ print "\nStatus: playerid=$playerid, audiochannel=$audchannel0\n";
+
+}
+
+
+############################################################
+
+sub server_alive_test{ # Ping
+ print ("Ping: GiantDisc Server alive\n");
+ gdio::putline ("GiantDisc Server alive");
+ gdio::putline (""); # End of result
+}
+
+
+sub serial_test{
+ my $i;
+ for($i=0; $i<5; $i++){
+ print("$i ");
+ gdio::putline(" 123456789 123456789 123456789 123456789 123456789 123456789 123456789");
+ }
+ gdio::putline (""); # End of result
+ print("\n");
+}
+
+
+END{
+ ### close database connection
+ print("### close database connection\n");
+ $dbh->disconnect;
+
+ if($rippipe_open){close_rippipe();}
+}
+#
+1;
+#