################################################## # # 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[$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(){ # $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(){ $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 = ){ 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 = ){ 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; #