File Coverage

blib/lib/MusicRoom.pm
Criterion Covered Total %
statement 29 253 11.4
branch 2 146 1.3
condition 0 42 0.0
subroutine 10 26 38.4
pod n/a
total 41 467 8.7


line stmt bran cond sub pod time code
1             package MusicRoom;
2              
3 1     1   24109 use warnings;
  1         3  
  1         32  
4 1     1   6 use strict;
  1         2  
  1         78  
5              
6             =head1 NAME
7              
8             MusicRoom - Software for managing digital music
9              
10             =head1 VERSION
11              
12             Version 0.40
13              
14             =cut
15              
16             our $VERSION = '0.40';
17              
18              
19             =head1 SYNOPSIS
20              
21             Managing digital music files can be a complicated business,
22             converting between audio formats, ensuring tags are
23             consistent and locating extra data like cover art and lyrics.
24             This package provides a framework for performing these
25             tasks.
26              
27             The package has been designed to simplify the creation of
28             a collection of scripts to carry out the essential tasks
29             required to manage digital music. A complete set of sample
30             scripts has been distributed with the package. These can
31             be used directly or can be customised to meet your own
32             needs.
33              
34             =head1 NOTES
35              
36             Much of the work perfomed by the package is implemented using
37             other Perl modules and freely available programs.
38              
39             Extensively tested on Windows, all the facilities should
40             function under other environments, but there has not yet been
41             any attempt to test on other systems.
42              
43             This version of the system has been tested for many years
44             in a single setup. However it has not been widely tested
45             on lots of configurations. This means it should be treated
46             as Beta software for most users.
47              
48             =head1 OVERVIEW
49              
50             Managing your own music on computers can be complex. There are a number
51             of available programs that can help with particular tasks like format
52             conversion or playing music but keeping track of tags and manipulating auditory
53             data in a range of computers can become complex, especially when
54             music is accessed through a variety of mechanisms like DAAP servers and MP3
55             players.
56              
57             The MusicRoom package acts as the glue to tie together elements for
58             manipulating tag data, tracking down lyrics and cover art, converting
59             audio formats, keeping CSV duplicates of meta information and copying
60             music files from place to place.
61              
62             This is an initial version of the package, it has been extensively
63             tested at a single location but has not been widely used in many
64             different environments. If something here does not function how
65             you expect it to please tell me about it.
66              
67             =head2 The Directories
68              
69             Because the package ties together a number of elements there are three
70             directories that must be clearly defined:
71              
72             =over 4
73              
74             =item * The 'room' directory
75              
76             Holds the database of music tags, the list of valid artists and song names
77             and directories of any lyric files or cover art
78              
79             =item * The 'tools' directory
80              
81             Holds the external programs to manipulate audio data (such as C to encode
82             as C and C to adjust audio volume)
83              
84             =item * The 'scripts' directory
85              
86             Holds the Perl scripts that use the MusicRoom package.
87              
88             =back
89              
90             =head2 The Enabling Scripts
91              
92             Included in the release are a number of scripts that use the MusicRoom package
93             to perform various tasks. Some of these can be considered as essential elements
94             that perform key tasks, like setting up an inital directory structure, others
95             should best be thought of as "sample implementations" showing how the package
96             can be used.
97              
98             All scripts have been tested under Windows and most have also been tested on
99             Linux. There should not be anything that is OS specific in this software.
100              
101             =over 4
102              
103             =item * C
104              
105             This script will ask a set of questions, configure the system and create the
106             required database files.
107              
108             =item * C
109              
110             Scans a directory tree looking for music files and lists them in a CSV file.
111             The output file is deliberately structured to allow the fix_tags.pl script
112             to work on it.
113              
114             Typically the process of importing music requires three steps:
115             scanning a source; fixing the meta data; importing the data. This
116             script performs the first of these, identifying files of various audio
117             formats, extracting as many tags as it can find and reporting
118             what was found in a file that can be easily edited.
119              
120             =item * C
121              
122             Scan a CSV file that holds tags for a set of music files, check where the
123             tags need to be corrected and provide some automated tools to apply the
124             corrections. The idea is that once this script is happy with a set of
125             tags they are ready to add to the music collection.
126              
127             This script is just about usable but by no means complete.
128              
129             =item * C
130              
131             Perform the tasks required to take a validated set of tags in a CSV file
132             (usually generated by renaming a C file with C).
133              
134             This script automates the steps required to add music into the library.
135             It checks that the suggested tags meet all the restrictions (artist is
136             known, year is valid, cover art exists and so on), it then copies the
137             original files into the "best" directory, converts the format, normalises
138             the volume, adds all the tags and places a standard vesrion in the
139             "active" music directory.
140              
141             =item * C
142              
143             Every so often there are significant errors in the tags. This script
144             will regenerate "active" music files based on the latest set of tags.
145             It uses the "best" data as its source.
146              
147             The script relies on the bad active files and the entries in the
148             "active list" having been removed. It identifies which entries are
149             missing, locates the associated "best" auidio files and publishes
150             them to the active directory.
151              
152             =item * C
153              
154             List all the audio files that have a particular word in
155             their lyrics.
156              
157             =item * C
158              
159             List where the cover art files are to be found for music
160             in the collection.
161              
162             =item * C
163              
164             List where the lyric files are to be found for music
165             in the "active set". Produces a CSV file listing the
166             location of the text files that contain the lyrics.
167              
168             =item * C
169              
170             Scan a file of music that needs to have lyrics fetched
171             and uses a Perl module to download the appropriate
172             lyrics (if it can find them).
173              
174             The file used is essentially the lines identifying
175             missing lyrics from the C script.
176              
177             It uses C, so you must
178             install that module before attempting to run the script.
179              
180             =back
181              
182             =head2 The Modules
183              
184             There are a number of modules that the package is built on:
185              
186             =over 4
187              
188             =item * MusicRoom::LogicalModel
189              
190             Define the logical data structure that can be used to explore the information.
191              
192             =item * MusicRoom::Album, MusicRoom::Artist, MusicRoom::Track, MusicRoom::Song, MusicRoom::Zone
193              
194             These modules implement objects that come from the music database.
195              
196             =item * MusicRoom::Charts
197              
198             Routines for handling music charts
199              
200             =item * MusicRoom::CoverArt
201              
202             Routines for locating and attaching cover art for songs
203              
204             =item * MusicRoom::Lyrics
205              
206             Routines for locating and attaching cover art for songs
207              
208             =item * MusicRoom::File
209              
210             Handling files of various types, for example converting between audio
211             formats
212              
213             =item * MusicRoom::Date
214              
215             Handling dates, includes the ability to handle dates before the 1750s
216              
217             =item * MusicRoom::STN
218              
219             Generating and using random identifiers for elements such as items and
220             file names
221              
222             =item * MusicRoom::Context
223              
224             Handles the grouping of variables, and associated values
225              
226             =item * MusicRoom::Locate
227              
228             Using location specifiers in combination with songs (and similar things) to
229             identify associated files (containing for example cover art and lyrics).
230              
231             =item * MusicRoom::Text::CSV
232              
233             Handle comma seperated value files. Should be replaced by the real
234             Text::CSV package one day
235              
236             =item * MusicRoom::Text::Nearest
237              
238             Find the closest match for a name to a list of valid values
239              
240             =item * MusicRoom::Text::SoundexNG
241              
242             A specially tuned Soundex variant that identifies names close to
243             a given one
244              
245             =item * MusicRoom::InitialLists
246              
247             Some initial lists of valid artists, song titles and albums
248              
249             =item * MusicRoom::ValidAlbums, MusicRoom::ValidArtists, MusicRoom::ValidSongs
250              
251             Process the valid names lists
252              
253             =back
254              
255             =head1 SUBROUTINES/METHODS
256              
257             =cut
258              
259 1     1   5 use Carp;
  1         5  
  1         66  
260 1     1   6 use Cwd;
  1         2  
  1         70  
261 1     1   949 use IO::File;
  1         12206  
  1         133  
262 1     1   2397 use DBI;
  1         21273  
  1         95  
263              
264             my($phase,%config,$dir,$conf_file);
265             my(%databases);
266              
267 1     1   11 use constant MUSICROOM_DIR => "MUSICROOM_DIR";
  1         2  
  1         62  
268 1     1   5 use constant MUSICROOM_CONF => "musicroom.conf";
  1         2  
  1         40  
269 1     1   5 use constant MUSICROOM_VERSION => "0.01";
  1         1  
  1         3969  
270              
271             # This would turn on tracing in the DBI code
272             # DBI->trace(2);
273              
274             $phase = "configure";
275             read_conf();
276              
277             # All these need the configuration to be loaded first, so they
278             # have to be "required" after the read_conf()
279             require MusicRoom::File;
280             require MusicRoom::Date;
281             require MusicRoom::LogicalModel;
282             require MusicRoom::Text::CSV;
283             require MusicRoom::Text::Nearest;
284             require MusicRoom::STN;
285             require MusicRoom::CoverArt;
286             require MusicRoom::Lyrics;
287             require MusicRoom::Charts;
288              
289             sub is_running
290             {
291 0 0   0   0 return "" if($phase eq "configure");
292 0         0 return 1;
293             }
294              
295             sub check_ready
296             {
297 0 0   0   0 if($phase eq "configure")
298             {
299 0         0 croak("Must configure MusicRoom before using it, run setup.pl");
300             }
301 0 0       0 if($phase ne "active")
302             {
303 0         0 croak("Phase has bad value in MusicRoom");
304             }
305             }
306              
307             sub read_conf
308             {
309             # We can only call this once
310 1 50   1   6 if($phase ne "configure")
311             {
312 0         0 carp("Can only call MusicRoom::init() at startup");
313 0         0 return;
314             }
315              
316 1 50       206 croak("Must set environment variable MUSICROOM_DIR to use MusicRoom")
317             if(!defined $ENV{MUSICROOM_DIR});
318              
319 0           $dir = $ENV{MUSICROOM_DIR};
320              
321             # If we are on Windows then switch over to using / not \
322 0           $dir =~ s/\\/\//g;
323              
324 0 0         $dir .= "/" if(!($dir =~ m#/$#));
325 0           $conf_file = $dir.MUSICROOM_CONF;
326              
327             # If the configuration file has not yet been created we must
328             # wait for it to be set up
329 0 0         return if(!-r $conf_file);
330              
331             # Read values into the config hash
332 0           my $fh = IO::File->new($conf_file);
333 0 0         croak("Cannot read $config{config_file}\n")
334             if(!defined $fh);
335 0           my $got_version = "";
336              
337 0           while(my $line = <$fh>)
338             {
339 0           chomp $line;
340 0           $line =~ s/\cZ//g;
341 0 0         next if($line =~ /^\s*$/);
342 0 0         next if($line =~ /^\s*#/);
343 0 0         if($line =~ /^\s*(version)\s*\=\s*\"([^\"]*)\"/)
344             {
345 0           $config{version} = $2;
346 0           $got_version = 1;
347 0 0         croak("Configuration is for wrong version ($config{version} not ".
348             MUSICROOM_VERSION.")") if($config{version} ne MUSICROOM_VERSION);
349             }
350 0 0         if($line =~ /^\s*(\w+)\s*\=\s*\"([^\"]*)\"/)
    0          
    0          
    0          
    0          
351             {
352 0           $config{$1} = $2;
353             }
354             elsif($line =~ /^\s*(\w+)\s*\=\s*\'([^\']*)\'/)
355             {
356 0           $config{$1} = $2;
357             }
358             elsif($line =~ /^\s*(\w+)\s*\=\s*\|([^\|]*)\|/)
359             {
360 0           $config{$1} = $2;
361             }
362             elsif($line =~ /^\s*(\w+)\s*\=\s*\/([^\/]*)\//)
363             {
364 0           $config{$1} = $2;
365             }
366             elsif($line =~ /^\s*(\w+)\s*\=\s*(\S.+)/)
367             {
368 0           $config{$1} = $2;
369             }
370             else
371             {
372 0           carp("Cannot parse config file \"$line\"");
373             }
374             }
375 0           $fh->close();
376              
377 0 0         croak("Missing version spec in file")
378             if(!$got_version);
379 0           open_database("core");
380 0           $phase = "active";
381             }
382              
383             sub configure
384             {
385             # This is where we store the config, the values set here are the ones
386             # we need to get to the config file
387 0 0   0     croak("The MusicRoom system is already configured")
388             if($phase ne "configure");
389              
390 0 0         croak("Cannot find directory $dir (from \$MUSICROOM_DIR)")
391             if(!-d $dir);
392              
393 0 0         croak("Do not have permission to write to $dir")
394             if(!-w $dir);
395              
396 0 0         croak("File $conf_file already exists")
397             if(-r $conf_file);
398              
399             my %config_vars =
400             (
401             # Setting a default_value and read_only is a good way
402             # to nail a config var value
403             version =>
404             {
405             default_value => MUSICROOM_VERSION,
406             },
407             data_location_file =>
408             {
409             default_value => ".musicroom_dir",
410             },
411             db_type =>
412             {
413             default_value => "SQLite",
414             },
415             core_db_name =>
416             {
417             default_value => "mrm_core.dat",
418             },
419             coverart_subdir =>
420             {
421             default_value => "art",
422             },
423             lyrics_subdir =>
424             {
425             default_value => "lyrics",
426             },
427             tools_dir =>
428             {
429             configure => \&configure_var,
430             name => "Path to directory containing format conversion tools",
431             after_set_fun => sub
432 0     0     {
433             # Convert to absolute path if it was relative
434             },
435             },
436 0           room_name =>
437             {
438             configure => \&configure_var,
439             name => "Music Library Name",
440             value_type => "text",
441             },
442             object_file =>
443             {
444             # Definitions for the database objects
445             name => "Object Definition File",
446             value_type => "text",
447             },
448             wav_disabled =>
449             {
450             default_value => "",
451             },
452             mp3_disabled =>
453             {
454             default_value => "",
455             },
456             );
457            
458 0           foreach my $var (keys %config_vars)
459             {
460 0 0         next if(!defined $config_vars{$var}->{default_value});
461            
462 0 0         &{$config_vars{$var}->{before_set_fun}}($var)
  0            
463             if(defined $config_vars{$var}->{before_set_fun});
464 0           $config{$var} = $config_vars{$var}->{default_value};
465 0 0         &{$config_vars{$var}->{after_set_fun}}($var)
  0            
466             if(defined $config_vars{$var}->{after_set_fun});
467             }
468              
469 0           local($|);
470              
471 0           $|=1;
472 0           my $called_one;
473 0           foreach my $var (sort keys %config_vars)
474             {
475 0 0         if(defined $config_vars{$var}->{configure})
476             {
477 0 0         print "MusicRoom needs to be configured\n"
478             if(!defined $called_one);
479 0           $called_one = 1;
480 0           &{$config_vars{$var}->{configure}}($var,%config_vars);
  0            
481             }
482             }
483              
484 0           save_conf();
485 0           create_database("core");
486              
487             # Now that we are ready to go lets get started
488 0           read_conf();
489             }
490              
491             sub set_conf
492             {
493 0     0     my($var,$val) = @_;
494              
495 0           check_ready();
496             # Set and save into the file
497 0 0         if(!defined $config{$var})
498             {
499 0           carp("Cannot set configuration var \"$var\"");
500 0           return undef;
501             }
502              
503             # Check that the value can be written into the file
504 0 0 0       if($val =~ /\"/ && $val =~ /\'/ && $val =~ /\|/ && $val =~ /\//)
      0        
      0        
505             {
506 0           carp("Cannot have <\"> and <\'> and <\|> and <\/> in single conf value");
507 0           $val =~ s/\|/!/g;
508             }
509 0           $config{$var} = $val;
510 0           save_conf();
511             }
512              
513             sub get_conf
514             {
515 0     0     my($var,$silent) = @_;
516             # Get the value
517 0           check_ready();
518              
519             # Magic value to get to the directory
520 0 0         return $dir
521             if(lc($var) eq "dir");
522              
523             # Look up in the configuration
524 0 0         if(!defined $config{$var})
525             {
526 0 0 0       carp("No value for configuration var \"$var\"")
527             if(!defined $silent || !$silent);
528 0           return undef;
529             }
530 0           return $config{$var};
531             }
532              
533             sub read_config
534             {
535 0 0   0     croak("Cannot find $conf_file")
536             if(!-r $conf_file);
537              
538             # Read values into the config hash
539 0           my $fh = IO::File->new($conf_file);
540 0 0         croak("Cannot read $conf_file\n")
541             if(!defined $fh);
542 0           while(my $line = <$fh>)
543             {
544 0           chomp $line;
545 0           $line =~ s/\cZ//g;
546 0 0         next if($line =~ /^\s*$/);
547 0 0         next if($line =~ /^\s*#/);
548 0 0         if($line =~ /^\s*(\w+)\s*\=\s*\"([^\"]*)\"/)
    0          
    0          
    0          
    0          
549             {
550 0           $config{$1} = $2;
551             }
552             elsif($line =~ /^\s*(\w+)\s*\=\s*\'([^\']*)\'/)
553             {
554 0           $config{$1} = $2;
555             }
556             elsif($line =~ /^\s*(\w+)\s*\=\s*\|([^\|]*)\|/)
557             {
558 0           $config{$1} = $2;
559             }
560             elsif($line =~ /^\s*(\w+)\s*\=\s*\/([^\/]*)\//)
561             {
562 0           $config{$1} = $2;
563             }
564             elsif($line =~ /^\s*(\w+)\s*\=\s*(\S.+)/)
565             {
566 0           $config{$1} = $2;
567             }
568             else
569             {
570 0           carp("Cannot parse config file \"$line\"");
571             }
572             }
573 0           $fh->close();
574             }
575              
576             sub save_conf
577             {
578 0     0     my $fh = IO::File->new(">$conf_file");
579 0 0         if(!defined $fh)
580             {
581 0           croak("Cannot write to $conf_file");
582             }
583              
584 0           my $date_str = MusicRoom::Date::text(undef);
585 0           print $fh <<"EndHeader";
586             # Configuration file for MusicRoom
587             # Saved: $date_str
588             # Program: $0
589             #
590             EndHeader
591 0           foreach my $key (sort keys %config)
592             {
593 0           my $val = $config{$key};
594              
595 0 0         if(!($val =~ /\"/))
    0          
    0          
    0          
596             {
597 0           print $fh "$key=\"$val\"\n";
598             }
599             elsif(!($val =~ /\'/))
600             {
601 0           print $fh "$key=\'$val\'\n";
602             }
603             elsif(!($val =~ /\|/))
604             {
605 0           print $fh "$key=\|$val\|\n";
606             }
607             elsif(!($val =~ /\//))
608             {
609 0           print $fh "$key=\/$val\/\n";
610             }
611             else
612             {
613 0           carp("Bad setting for $key ($val)");
614             }
615             }
616 0           $fh->close();
617             }
618              
619             sub configure_var
620             {
621 0     0     my($var,%config_vars) = @_;
622              
623 0 0 0       if(!defined $var)
    0          
    0          
624             {
625 0           carp("configure_var called without variable name");
626 0           return;
627             }
628             elsif(!defined $config_vars{$var})
629             {
630 0           carp("Attempt to configure unknown var $var");
631 0           return;
632             }
633             elsif(!defined $config_vars{$var}->{value_type} ||
634             $config_vars{$var}->{value_type} eq "text")
635             {
636 0           my $name = $config_vars{$var}->{name};
637 0 0         $name = $var if(!defined $name);
638 0           print "Define a value for \"$name\": ";
639 0           my $val = <>;
640 0           chomp $val;
641 0           $config{$var} = $val;
642             }
643             else
644             {
645 0           carp("No method defined for $config_vars{$var}->{value_type} vars yet");
646             }
647             }
648              
649             sub open_database
650             {
651 0     0     my($part) = @_;
652              
653 0           my $dbfile = $config{"${part}_db_name"};
654 0           my $dbtype = $config{db_type};
655              
656 0 0         croak("Cannot find db_name for \"${part}\"")
657             if(!defined $dbfile);
658              
659 0 0         $databases{$part} = {} if(!defined $databases{$part});
660 0           $databases{$part}->{handle} = DBI->connect(
661             "dbi:$dbtype:$dir/$dbfile", "", "",
662             {RaiseError => 1, AutoCommit => 1});
663             }
664              
665             sub create_database
666             {
667             # We have to create a database with the appropriate tables in
668 0     0     my($part) = @_;
669              
670 0           open_database($part);
671              
672 0           foreach my $table (MusicRoom::LogicalModel::list_physical_tables($part))
673             {
674 0           my $stmt = "CREATE TABLE \"$table\" ( ";
675              
676 0           my $id;
677 0           foreach my $col (MusicRoom::LogicalModel::get_physical_columns($part,$table))
678             {
679 0 0         $id = "id" if($col eq "id");
680 0 0 0       $id = "name" if(!defined $id && $col eq "name");
681              
682 0           my $spec = MusicRoom::LogicalModel::get_physical_column($part,$table,$col);
683              
684 0           $stmt .= "\"$col\" $spec, ";
685             }
686 0 0         croak("Must have an id or name in every table")
687             if(!defined $id);
688              
689             # $stmt =~ s/, $/) /;
690 0           $stmt .= "PRIMARY KEY ( \"$id\" ));";
691 0           my $table = $databases{$part}->{handle}->prepare($stmt);
692 0 0         if(!defined $table)
693             {
694 0           carp("Failed to prepare $stmt");
695 0           next;
696             }
697 0           $table->execute();
698             }
699             # The loading up of data is done in the setup.pl script, if it was
700             # here then the complete initial list of valid items would be loaded
701             # into every script that used MusicRoom and that would just be silly
702              
703             # But we do need to close the database so that the read_conf can open
704             # it again
705 0           shutdown_database($part);
706             }
707              
708             sub select
709             {
710             # Do an SQL statement
711 0     0     my($part,$table,$cols,$where_clause) = @_;
712              
713 0 0 0       if(!defined $databases{$part} ||
714             !defined $databases{$part}->{handle})
715             {
716 0           carp("Must open database \"$part\" before attempting to use it");
717 0           return undef;
718             }
719 0 0         if(ref($cols) ne "ARRAY")
720             {
721 0           carp("Must supply an array of column names");
722 0           return undef;
723             }
724 0 0         if($#{$cols} < 0)
  0            
725             {
726 0           carp("Must supply at least one column to select");
727 0           return undef;
728             }
729              
730 0           my $stmt = "SELECT ".join(',',@{$cols})." FROM $table";
  0            
731 0 0 0       if(defined $where_clause && $where_clause ne "")
732             {
733 0           $stmt .= " WHERE ".$where_clause;
734             }
735 0           $stmt .= ";";
736              
737 0           my $sth = $databases{$part}->{handle}->prepare($stmt);
738 0 0         if(!defined $sth)
739             {
740 0           carp("Failed to prepare \"$stmt\"");
741 0           return ();
742             }
743 0           my @result;
744 0           my $rows_affected = $sth->execute();
745              
746 0           while(1)
747             {
748 0           my $ret = $sth->fetchrow_arrayref();
749 0 0 0       return @result if(!defined $ret || ref($ret) ne "ARRAY" || !@{$ret});
  0   0        
750              
751             # Need to copy the result, otherwise the next fetchrow_arrayref() will
752             # overwrite it
753 0           my @result_arry = @{$ret};
  0            
754 0           push @result,\@result_arry;
755             }
756             }
757              
758             sub insert
759             {
760 0     0     my($part,$table,$cols,$values) = @_;
761              
762 0 0 0       if(!defined $databases{$part} ||
763             !defined $databases{$part}->{handle})
764             {
765 0           carp("Must open database \"$part\" before attempting to use it");
766 0           return undef;
767             }
768 0 0         if(ref($cols) ne "ARRAY")
769             {
770 0           carp("Must supply an array of column names");
771 0           return undef;
772             }
773 0 0         if(ref($values) ne "ARRAY")
774             {
775 0           carp("Must supply an array of values");
776 0           return undef;
777             }
778 0 0         if($#{$cols} < 0)
  0            
779             {
780 0           carp("Must supply at least one column to insert");
781 0           return undef;
782             }
783 0 0         if($#{$cols} != $#{$values})
  0            
  0            
784             {
785 0           carp("Supplied ".($#{$values}+1)." values for ".($#{$cols}+1)." slots");
  0            
  0            
786 0           return undef;
787             }
788              
789 0           my @vals;
790 0           foreach my $val (@{$values})
  0            
791             {
792 0           push @vals,quoteSQL($part,$val);
793             }
794 0           my $stat = "INSERT INTO $table (".join(',',@{$cols}).
  0            
795             ") VALUES (".join(',',@vals).");";
796              
797 0           my $count = $databases{$part}->{handle}->do($stat);
798 0 0         if($count != 1)
799             {
800 0           carp("Got return value of \"$count\" from \"$stat\"");
801 0           return undef;
802             }
803 0           return 1;
804             }
805              
806             sub doSQL
807             {
808             # Do an SQL statement
809 0     0     my($part,$stmt) = @_;
810              
811 0 0 0       if(!defined $databases{$part} ||
812             !defined $databases{$part}->{handle})
813             {
814 0           carp("Must open database \"$part\" before attempting to use it");
815 0           return undef;
816             }
817 0           return $databases{$part}->{handle}->do($stmt);
818             }
819              
820             sub quoteSQL
821             {
822             # Convert a string to a form that SQL can manage
823 0     0     my($part,$string) = @_;
824              
825 0 0 0       if(!defined $databases{$part} ||
826             !defined $databases{$part}->{handle})
827             {
828 0           carp("Must open database \"$part\" before attempting to use it");
829 0           return undef;
830             }
831             # Special cases
832 0 0 0       return "\'".$string."\'"
833             if(lc($string) eq "true" || lc($string) eq "false");
834              
835              
836 0           return $databases{$part}->{handle}->quote($string);
837             }
838              
839             sub shutdown_database
840             {
841             # Close down database handles
842 0     0     my($part) = @_;
843              
844 0 0         if(defined $part)
845             {
846 0 0         $databases{$part}->{handle}->disconnect()
847             if(defined $databases{$part}->{handle});
848 0           $databases{$part}->{handle} = undef;
849 0           return;
850             }
851 0           foreach my $each_part (keys %databases)
852             {
853 0           shutdown_database($each_part);
854             }
855             }
856              
857             =head1 AUTHOR
858              
859             Steve Hawtin, C<< >>
860              
861             =head1 BUGS
862              
863             Please report any bugs or feature requests to C, or through
864             the web interface at L. I will be
865             notified, and then you'll automatically be notified of progress on your bug as I make changes.
866              
867             =head1 SUPPORT
868              
869             You can find documentation for this module with the perldoc command.
870              
871             perldoc MusicRoom
872              
873             You can also look for information at:
874              
875             =over 4
876              
877             =item * RT: CPAN's request tracker
878              
879             L
880              
881             =item * AnnoCPAN: Annotated CPAN documentation
882              
883             L
884              
885             =item * CPAN Ratings
886              
887             L
888              
889             =item * Search CPAN
890              
891             L
892              
893             =back
894              
895              
896             =head1 ACKNOWLEDGEMENTS
897              
898              
899             =head1 LICENSE AND COPYRIGHT
900              
901             Copyright 2007-2010 Steve Hawtin.
902              
903             This program is free software; you can redistribute it and/or modify it
904             under the terms of either: the GNU General Public License as published
905             by the Free Software Foundation; or the Artistic License.
906              
907             See http://dev.perl.org/licenses/ for more information.
908              
909              
910             =cut
911              
912             1; # End of MusicRoom