audiolink-0.05/0040755000175100017510000000000007764367145012014 5ustar AmitAmitaudiolink-0.05/Documentation/0040755000175100017510000000000007764367145014625 5ustar AmitAmitaudiolink-0.05/Documentation/CodingStyle.txt0100644000175100017510000000011507764367145017604 0ustar AmitAmit$Id: CodingStyle.txt,v 1.1.1.1 2003/09/11 08:14:29 amitshah Exp $ AudioLink audiolink-0.05/Documentation/design.txt0100644000175100017510000002355607764367145016647 0ustar AmitAmitAudioLink /* * $Id: design.txt,v 1.8 2003/11/16 18:01:02 amitshah Exp $ * * The design for the AudioLink software. * * Copyright (C) 2003 Amit Shah * * This file is part of AudioLink. * * AudioLink is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * AudioLink is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with AudioLink; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Authors: Amit Shah: initial design */ [read the readme.txt and motivation.txt files for background information] How is this tool supposed to work? As I see it now, there are two modules: 1) Go through the entire collection of songs / text and populate the database. This is tough... Songs would generally have information like the album, artist, etc. stored in the headers. But for texts, it is going to be difficult... Manually storing info will be the only way to get this right. 2) A nice user interface which will present all the search options, and the user then searches for something. A new directory will be created on a successful search with some data to work on: symlinks to the original files will be created in this directory. Design for code/alsearch (Module 2): I want to implement the 2nd module first, since I believe it will keep the project going and growing. A sample database of say, 5 songs, will be filled manually and worked on. This will give a result for me to see... The database: The sample database will contains fields like, "Album", "Year", "SongName", "Male Artist1", "Male Artist2", "Female Artist1", "Female Artist2", "Band", "Composer", "Lyricist", etc. ie, store more info than an ID3 tag (v1) stores. It will also contain the absolute location of the file. This database design does not handle the case where the user changes the structure of the file organization on the file system. For avoiding this: a) We could later write a small program by which the database updation for the new file path would be made simple. For example, a SQL query to the database to replace occurences of /mnt/songs/new to /mnt/songs/assorted for a particular file set. b) Another option would be to regenerate the database from scratch, but would be more time-, CPU-, disk-access consuming. c) A program that checks for validity/availablity of each file in the database. If a file doesn't exist, mark it as non-existant. On the next update, if a file with similar characteristics (like file name, artist, album, etc) is encountered, update the path and mark it as "existing" again. d) Have an interface within the program to facilitate moving of files anywhere in the filesystem: This way, we can move the file and also update the databae: best option, but not a great one for the user (why will she use our interface) (what if she forgets using this)... To make sure the user atleast considers using our tool, give a nice file manager front-end, so that the user just does a normal drag-and-drop of files, and we update the databae in the background! :-) [Later, we could design a dbfs, a database file system, which will make handling such things easier... The file system in this case will have to store extra information about the file in the metadata... and depending on the file type (from the magic number), specific fields in the metadata would stand for particular attributes, like: user_field_1 in the metadata for an MP3 would contain the album name, user_field_1 for a web page would contain the name of the author, etc Or, have a virtual fs sitting on top of a real fs... this virtual fs will behave as mentioned in the previous paragraph, but query our database on each access to the file's metadata that's stored in the database. This seems like a better option right now.] The front-end: A very simple search interface is to be presented first: either command-line driven or prompt-based. To start off, a prompt-based interface will be used. The user starts the application (don't know yet if perl supports accepting text by prompting the user -- if it doesn't, use C and then invoke the perl script) and enters the search criteria as desired. Based on the entries the database returns, create a directory based on the dir_name suggested by the user. The user is given two options: a) Create dir. name with the name of the artist b) Create dir. name specified by the user while giving the search criteria. In this directory, create symlinks to the original files. The symlinks could be organized according to the hierarchy that exists in the original file structure, or as all entries in the same directory. For example, if the file layout is such: / |-mnt |-songs |-English |-Hindi |-chalti_ka_naam_gaadi |-jhumroo and the user wants to listen to songs sung by Kishor Kumar, we could have the following two alternative layouts for our new directory: alternative layout 1: / |-mnt |-songs |-AudioLink | |-Kishor_Kumar | |-chalti_ka_naam_gaadi | | | |-jhumroo | | |-English |-Hindi |-chalti_ka_naam_gaadi |-jhumroo alternative layout 2: / |-mnt |-songs |-AudioLink | |-Kishor_Kumar | | |-English |-Hindi |-chalti_ka_naam_gaadi |-jhumroo One of these two layouts will be chosen by the user when she is presented with all the options at search-time. Ofcourse, the new path where the symlinks will be dumped is also configurable. config file: A config file will also be maintained to store the default settings, for ones like mentioned above: whether to create a file hierarchy based on the name of the album or in the base directory. This will keep a set of default options, and also bug the user lesser each time (this is especially needed in a prompt-based interface). This config file will be placed in ~/.audiolink/config. The directory is created to accomodate other files we might have later. A system-wide config file could be desirable too, if all users share the same database. In this case, we can update the schema if it has been updated in a newer version as part of the package upgrade process (atleast on Debian we can do this). A better option would be to add a new table in the database which will store AudioLink-specific data, like the schema version. This will help us upgrade the schema if new versions exist. This change can be a per-user change, so no system-wide config file would be necessary. Each of the scripts could then call a script which first checks the database schema, updates it if necessary and then the original script does its job. Design for the code/alfilldb script (Module 1): This script is supposed to do the following: * add info of song files to the database * update the information for existing entries in the database * update the ID3 tags for MP3s based on the values in the database (also Ogg Vorbis comments). * there should be level of questions like 'basic', 'limited' and 'all'. The script could be run in one of several modes: add-only: should just add new files: can be in interactive or passive modes: in the interactive mode, prompts can be made to put in info that doesn't exist in the file tags. upd-only: should just update existing files with info in the ID3 as well as info obtained from the user (interactive mode) addupd: another interactive mode: Add new files as well as update existing ones: combination of both the above modes. Design for the audiolink script: A top-level script, audiolink, which helps in the following ways: * Creation of database tables for a first-time user * Creation/modification of the config file * If we have a new database schema (detected from config file and new version), transparently update the schema for the user. * Invoke the alsearch/alfilldb scripts: should be flexible, in that the user may not even know the existence of these scripts. She should have access to all of the audiolink functionality through this one script. 1. Check if config file exists. 2. If not, prompt the user for creating one and fill in the values for the most used variables (user, pass, host for the DB; verbose mode; prompt mode and so on. 3. Design for the virtual filesystem discussed above: We basically want all filesystem operations like cp, mv, rm, etc. to go through our code, so that we can keep our database uptodate. An easy-to-use implementation could just involve one command: $ mount -oloop -tdbfs /home/songs /mnt/shared/songs This indicates that a local directory is (loopback) mounted in the local namespace. Any operations now done within /mnt/shared/songs will go through our dbfs. If a file is moved anywhere within /mnt/shared/songs, the database will be updated, and searches will always produce links/playlists to the right place. audiolink-0.05/Documentation/motivation.txt0100644000175100017510000000135007764367145017553 0ustar AmitAmit$Id: motivation.txt,v 1.1.1.1 2003/09/11 08:14:29 amitshah Exp $ AudioLink This project started with my need of searching for files on my local machine, be it music or any stored information in .txt, .html, .pdf formats. The main goal of the software is to make searching for _content_ on local file systems (or remote file systems mounted in the local namespace) easier. This differs from other search tools, which look for files, not content. You can't use traditional tools like grep to search for songs or a particular artist, for example. The project could further be improved upon to include a LAN crawler, which will sniff on NFS, SMB, FTP among other protocols to collect information on the files residing on other machines as well. audiolink-0.05/Documentation/tools_used.txt0100644000175100017510000000103307764367145017540 0ustar AmitAmit$Id: tools_used.txt,v 1.1.1.1 2003/09/11 08:14:29 amitshah Exp $ AudioLink Tools to be used: 1) MySQL as the database backend 2) Perl as the programming language 3) Command line Interface and KDE/Qt for the UI Why: 1) MySQL: no reason, could've used PostgreSQL too, but I like the name MySQL better. In the future, we could add support for PostgreSQL too. 2) Perl: I don't know perl yet and wanted to learn it... 3) CLI: Ofcourse, each app should have a CLI KDE/Qt: My preferred DE, and also, I'll get to learn Qt programming audiolink-0.05/Documentation/how_to_interpret_errors.txt0100644000175100017510000000167107764367145022357 0ustar AmitAmitIf AudioLink doesn't work as it's expected to, go through this doc for the errors you get (if any), and try to rectify the situation. If it doesn't work, file a bug report at: https://sourceforge.net/tracker/?group_id=89886&atid=591752 Try to include as many details as possible in your bug report. Thanks. "couldn't create symlink for file " 1. This would ususally mean that the symlink for the file already exists. 2. This could also mean that the filesystem you're running on doesn't support symlinks (eg., Windows). 3. This could also mean that changing to the target directory failed or some other error occured. If possible, gather more information about the problem and file a bug report. "can't create directory \n" 1. Check for permissions. Try creating a directory manually in the specified path and check if it fails. If it doesn't, file a bug report. audiolink-0.05/Documentation/alfilldb_doc.html0100644000175100017510000002372307764367145020115 0ustar AmitAmit alfilldb - Add/update information of music files in the AudioLink database
 alfilldb - Add/update information of music files in the AudioLink database


Top


NAME

alfilldb - Add/update information of music files in the AudioLink database

Top


SYNOPSIS

alfilldb [OPTION]... /path/to/songs/...

alfilldb [OPTION]... --file=/path/to/song...

Top


DESCRIPTION

You can use this script to add or update information about your music files (MP3 or Ogg Vorbis) in the AudioLink database. This information will be used when you use the alsearch(1) program to search for particular music. This program is part of the audiolink(1) package.

The path given for the location of individual files or directories must be an absolute path (paths with ~ are allowed). Relative paths are not allowed. See the examples section for more information.

The user and password options have to be specified to gain access to the database. See the ``more information'' section in the audiolink(1) man page for the various ways in which you can specify them.

Options that are used repetitively during different invocations of the program (like the user, password options) may be put in the config file. See the audiolink(1) man page for details on the config file.

Top


OPTIONS

--add-only
Only the information about new songs will be added to the database; information about existing songs will not be updated.

--file=xxx
Works on just a single file instead of a directory. If this argument is given, the /path/to/dirs is not considered. (You can have more than one of these).

--help
Brief usage information

--host=xxx
Connects to the MySQL server on the given host. Default is localhost.

--no-prompt
Don't prompt for anything. Songs which do not have information for mandatory fields (eg., song title) will not be added to the database.

--pass=xxx
Password for accessing the database

--prompt=xxx
Prompt for input if there isn't enough information in the song (ID3 or Ogg Vorbis comments).

The parameters that prompt accepts are:

basic
Prompts just for the album name and the artist/band name

limited
Prompts for album, artist/band, genre and year fields

most
Prompt for all the fields except the Male, Female Artists and the Track number

paranoid
Prompt for all the fields, including Male Artist (1/2), Female Artist (1/2), Track Number.

-s, --na
Simulate or no-act mode: doesn't update the database.

--upd-only
Only entries in the database will be updated from the input the user gives. Make sure you don't use the --no-prompt option along with this one... else you won't get anything done!

--upd-song
Update the tags in the file, ie, ID3 for MP3, comments for Ogg Vorbis.

--user=xxx
Username for accessing the database

--verbose
Displays some extra information while processing files

Top


EXAMPLES

alfilldb --add-only /home/user/tmp/songs/
This invocation will scan the /home/user/tmp/songs directory recursively for new songs only. Songs already existing in the database will not be considered.

alfilldb --upd-only --prompt=most ~/tmp/songs/
This invocation will scan the $(HOME)/tmp/songs directory recursively for the current user for incomplete information in the database.

alfilldb ~user/tmp/songs/
This invocation will scan the /home/user/tmp/songs folder for music files; will add new entries to the database as well as update the existing ones.

alfilldb --file=~/tmp/songs/somefile.ogg --file=~/tmp/songs/otherfile.mp3
This invocation will just add (or update) information about the files $(HOME)/tmp/songs/somefile.ogg and $(HOME)/tmp/songs/otherfile.mp3.

Top


CAVEATS

If the --prompt option is not specified, alfilldb will just prompt for the title of the song being processed. The title information is asked only if the ID3 tag or the Vorbis comment doesn't contain the title. This behavior can be overriden by the --no-prompt option, and in this case, the entry for the file will not be made, since the song title is a mandatory field for storing song information in the datbase.

If neither of --add-only or --upd-only are specified, the default action is to add new entries as well as update existing ones.

If the ID3 tags or Vorbis comments for a file were updated after entries were made in the AudioLink database, they will not be reflected in the database. If you want to maintain consistency, it is advised that you keep the database updated (by using the --upd-only option) and then update the ID3 tag in the MP3 or the comment in the Ogg Vorbis file (by running alfilldb with the --upd-only option).

Top


SEE ALSO

audiolink(1), alsearch(1)

The current version of this man page is available on the AudioLink website at <http://audiolink.sourceforge.net/>.

Top


BUGS

Report bugs related to the AudioLink software or the man pages to the audiolink-devel mailing list <audiolink-devel@lists.sourceforge.net>.

Top


AUTHOR

This manual page is written and maintained by Amit Shah <amitshah@gmx.net>

Top


COPYRIGHT

The AudioLink package is Copyright (C) 2003, Amit Shah <amitshah@gmx.net>. All the programs and the documentation that come as part of AudioLink are licensed by the GNU General Public License v2 (GPLv2).

Top

 alfilldb - Add/update information of music files in the AudioLink database
audiolink-0.05/Documentation/alsearch_doc.html0100644000175100017510000002162107764367145020121 0ustar AmitAmit alsearch - Search the AudioLink database for music
 alsearch - Search the AudioLink database for music


Top


NAME

alsearch - Search the AudioLink database for music

Top


SYNOPSIS

alsearch [OPTION]... search_option... --td=/some/path/to/create/links/

alsearch [OPTION]... search_option... -s

Top


DESCRIPTION

You can use this script to search for songs in the AudioLink database. You can specify one or several options for the artist, composer, lyricist, album, etc., as the search criteria.

You can specify several search_option options to search for particular music files. See the section on search options for the list of options.

The search is not case sensitive. alsearch looks for strings as well as sub-strings in the fields being searched. See the examples section for more information.

Specifying the --td (target directory) option is mandatory for creating links to the actual files. This directory will contain the symbolic links to the actual audio files. The directory will be created if it doesn't exist.

Creating symbolic links to the actual files is analogous to creating playlists in audio-playing software. Symbolic links are actually just point to the actual files on the hard disk. This way of creating and storing playlists is very useful in several ways, some of which are:

  1. Compatible across various audio players
  2. Since the ``playlists'' are actually files represented on your hard disk, you can add whole directories generated by the alsearch program in the playlist of your audio software. If you switch to another music player for whatever reason, you still have your playlists. You don't have to bother about compatibility between the playlist formats of the two players.

  3. Can be seen and operated upon in a file browser
  4. As the symbolic links are present on a file-system, your playlist collection can be viewed by using normal file operations in the shell or using a GUI-based file browsing program. If the file browsing program is supports file traversals and symlinks, it'll show you the information of the actual song.

Top


OPTIONS

--help
Brief usage information

--host=xxx
Connects to the MySQL server on the target host. Default is localhost.

--pass=xxx
Password for the database

-s, --na
Just displays the search results, doesn't create links (simulation mode)

--td=xxx
Target-directory in which to create links

--user=xxx
Username for the database

-v, --verbose
Displays some extra information. Useful for spotting errors and sending debug information.

Top


SEARCH OPTIONS

--album=xxx
Search in the ``album'' field

--artist=xxx
Search in the artist/band/performers fields. You can give this option multiple times, for example:

alsearch --artist=kishore --artist=asha --td=/songs/asha_kishore

--comment=xxx
Search in the ``comment'' field

-c, --composer=xxx
Search in the ``composer'' field

--genre=xxx
Search in the ``genre'' field

-l, --lyricist=xxx
Search in the ``lyricist'' field

--title=xxx
Search in the ``title'' field

Top


EXAMPLES

The options that take arguments can be specified in two ways:

alsearch --artist="kishore kumar" --td=/songs/kishore

alsearch --artist "kishore kumar" --td /songs/kishore

That is, the option and the argument can be separated with a ' ' (space) or an '=' (equal) sign.

In case you want to search for a string that has spaces, enclose the string in `` ''.

alsearch --artist=kishore --composer=burman --lyricist=bakshi

Will search for songs sung by *Kishore*, composed by *Burman* and written by *Bakshi*. Any name in the composer field which have ``burman'' in them will be matched. This means, it'll find songs composed by RD Burman, SD Burman, etc. This is true for all search fields.

Top


SEE ALSO

audiolink(1), alfilldb(1)

The current version of this man page is available on the AudioLink website at <http://audiolink.sourceforge.net/>.

Top


BUGS

Report bugs related to the AudioLink software or the man pages to the audiolink-devel mailing list <audiolink-devel@lists.sourceforge.net>.

Top


AUTHOR

This manual page is written and maintained by Amit Shah <amitshah@gmx.net>

Top


COPYRIGHT

The AudioLink package is Copyright (C) 2003, Amit Shah <amitshah@gmx.net>. All the programs and the documentation that come as part of AudioLink are licensed by the GNU General Public License v2 (GPLv2).

Top

 alsearch - Search the AudioLink database for music
audiolink-0.05/Documentation/audiolink_doc.html0100644000175100017510000001547307764367145020326 0ustar AmitAmit audiolink - Create AudioLink config file, databases and tables
 audiolink - Create AudioLink config file, databases and tables


Top


NAME

audiolink - Create AudioLink config file, databases and tables

Top


SYNOPSIS

audiolink [OPTION]...

Top


DESCRIPTION

AudioLink is a set of programs which help you manage your music collection. It makes searching for music on your local storage media easier and faster. Your searches can include a variety of criteria, like male artists, female artists, band, genre, etc.

It supports music files of MP3 and Ogg Vorbis formats.

audiolink assists you in creating a configuration file for oft-used options passed to the AudioLink programs and creating the MySQL database and tables which the AudioLink programs, alfilldb(1) and alsearch(1) use.

The options specified on the command prompt override the options specified in the config file.

Top


OPTIONS

--help
Brief usage information

--host=xxx
Connects to the MySQL server on the target host. Default is localhost.

--pass=xxx
Password for the database

--user=xxx
Username for the database

--verbose
Display extra information about what's going on

Top


MORE INFORMATION

The user and password fields for the database have to be specified. There are three ways of doing this:

  1. Command-line arguments
  2. By specifying them via the --user and --pass command-line arguments to the programs:

    alfilldb --user=mysql_username --pass=mysql_password --prompt=basic /songs/

    alsearch --user=mysql_username --pass=mysql_password --artist=kishore --td=/songs/kishore

  3. Environment variables
  4. Setting the DBI_USER and DBI_PASS environment variables:

    If you are using bash, ksh, zsh or sh do this:

    export DBI_USER=mysql_username

    export DBI_PASS=mysql_password

    If you are using csh or tcsh, do this:

    setenv DBI_USER mysql_username

    setenv DBI_USER mysql_password

    Consult the man page of the respective shell interpreter that you use for help on environment variables.

  5. Config file
  6. Specifying them in the config file (the most convenient). The config file is stored in each user's home directory in the ~/.audiolink/config location.

Top


CONFIGURATION FILE

The AudioLink configuration file config resides in the .audiolink/ directory in the user's home directory ($HOME/.audiolink/config).

The format of the current config file is pretty easy to understand: Just put in a = b on separate lines for all the options you want the AudioLink scripts to use by default. Here, a is the option name, like user, password, host, etc., and b is the value you wish to associate the option with. For example, to set the username for accessing the database to 'root', you would put this in the config file:

user = root

Top


SEE ALSO

alfilldb(1), alsearch(1)

The current version of this man page is available on the AudioLink website at <http://audiolink.sourceforge.net/>.

Top


BUGS

Report bugs related to the AudioLink software or the man pages to the audiolink-devel mailing list <audiolink-devel@lists.sourceforge.net>.

Top


AUTHOR

This manual page is written and maintained by Amit Shah <amitshah@gmx.net>

Top


COPYRIGHT

The AudioLink package is Copyright (C) 2003, Amit Shah <amitshah@gmx.net>. All the programs and the documentation that come as part of AudioLink are licensed by the GNU General Public License v2 (GPLv2).

Top

 audiolink - Create AudioLink config file, databases and tables
audiolink-0.05/gui/0040755000175100017510000000000007764367145012600 5ustar AmitAmitaudiolink-0.05/gui/qt/0040755000175100017510000000000007764367145013224 5ustar AmitAmitaudiolink-0.05/code/0040755000175100017510000000000007764367145012726 5ustar AmitAmitaudiolink-0.05/code/alfilldb0100755000175100017510000005151707764367145014433 0ustar AmitAmit#!/usr/bin/perl # AudioLink # # $Id: alfilldb,v 1.33 2003/12/05 12:31:42 amitshah Exp $ # # alfilldb: Implements the 1st module of the AudioLink software. This # script crawls through a local collection of music files and # populates the database based on the tag information found in the # files. # # Copyright (C) 2003, Amit Shah # # This file is part of AudioLink. # # AudioLink is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License, or # (at your option) any later version. # # AudioLink is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with AudioLink; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use DBI; use MP3::Info; use File::Find; use Getopt::Long; use Ogg::Vorbis::Header; use Pod::Usage; # Options $help = 0; # Display usage information and quit. $verbose = 0; # Some extra information to be displayed $no_act = 0; # Do not put the stuff in the database $prompt = 0; # Prompt for user input if there isn't enf info in ID3 or Vorbis $dontbug = 0; # Don't prompt if title is NULL. Skips song addition in # the database in this case. $addmode = 0; # If the script was invoked as add-only (or w/o add-only # and upd-only) (new song entries in the datbase # allowed). $updmode = 0; # If the script was invoked as upd-only (or w/o add-only # and upd-only) (update existing entries in the # database). $updsong = 0; # Update the song info (ID3 tags for MP3, Vorbis comments). $mp3_ogg = 0; # 1 = MP3, 2 = Ogg Vorbis @files; # Multiple pathnames given on the command prompt $file = ""; # Pathname given on the command prompt to be operated on. @guessed_parts; # parts of the guessed names: artist, album, etc. $guessed = 0; # have we shown the 'guesses' list already? @guessed_nr; # [0]: number of guessed values; [1]: 0+tags $update_db = 0; # If there exists an entry in the database for any # song, we need to update it. # Options for the database $user = undef; $password = undef; $host = "localhost"; # use local mysql server by default # show the tags read from the file and the db. sub show_file_tags { my $counter = $guessed_nr[0]; while ($counter <= $guessed_nr[1]) { print "\t\t[" . $counter . "] " . $guessed_parts[$counter++] . "\n"; } } # guess the values of artist, album, etc. from the filenames # parameter: the filename sub guess_guessed { # just look between '-' for possible values. @guessed_parts = split(/-/, $_[0]); my $part; my $counter = 0; foreach $part (@guessed_parts) { # strip leading and trailing whitespaces $guessed_parts[$counter++] =~ s/(^\s*)(((\w+)(\s*))*(\w+))(\s*$)/$2/; # 2 stupid bugs above: # emacs: emacs interprets the ending $) as a variable and # doesn't have a closing bracket to get the indentation right # perl: if a space is put between $ and ), the regexp doesn't work } } # print the guessed values of artist, album, etc. sub show_guessed { $guessed = 1; # we've shown this list... my $counter = 0; unless ($dontbug) { my $part; print "NOTE: Use gg to select a particular entry. Replace by the corresponding number.\n"; print "\tChoices for fields in the song guessed from the filename are: \n"; foreach $part (@guessed_parts) { print "\t\t[$counter] $guessed_parts[$counter]\n"; $counter++; } $guessed_nr[0] = $counter; $guessed_parts[$counter++] = $album if $album; $guessed_parts[$counter++] = $artist if $artist; $guessed_parts[$counter++] = $title if $title; $guessed_parts[$counter++] = $comment if $comment; $guessed_parts[$counter++] = $genre if $genre; $guessed_parts[$counter++] = $year if $year; $guessed_parts[$counter++] = $track if $track; $guessed_nr[1] = $counter - 1; # show the entries from the db/file tags print "\tFields from the tags/db:\n"; show_file_tags($guessed_nr[0]); } } # see if the field is empty. If it is, ask for a value. # parameters: 0: the field to be compared # 1: the name of the field. # 2: the pathname of the song. sub get_input_fields { my $input; my $done = 0; if ($_[0]) { return $_[0]; } unless ($guessed) { show_guessed(); } while (not $done) { print "Enter " . $_[1] . " for song ". $_[2] . ": "; chomp($input = ); if ($input =~ /^gg(\d{1,2})$/) { if ($1 > $guessed_nr[1]) { print "Invalid input; try again\n"; next; } $input = $1; $done = 1; # valid guessed_field was given } else { $done = 2; # valid input was given } } if ($done == 1) { if ($input <= $guessed_nr[1]) { # if we guessed from file name / tag $input = $guessed_parts[$input]; } } return $input; } # the 'find' command executes this subroutine for each file found. sub wanted { my $updating = 0; # are we updating an entry? my $pathname; # stores the absolute file name, # ie. /path/to/file.ogg my $dirname; # stores the dirname, ie., /path/to/ my $filename; # stores the filename, ie, file.ogg my @fileparts; # stores all the parts of a file; ie, /path, # /to, file.ogg. my $filname; # filename w/o extension if ($file) { # filename given on the command-line $pathname = $file; } else { # directory name given on the command-line $pathname = $File::Find::name; $dirname = $File::Find::dir; } @fileparts = split(/\//, $pathname); $filename = $fileparts[-1]; # get the filename from the last item # in the array. if ($filename =~ /\.mp3/i) { # case-insensitive search $mp3_ogg = 1; $filname = $`; } elsif ($filename =~ /\.ogg/i) { $mp3_ogg = 2; $filname = $`; } else { # supporting MP3 and Ogg Vorbis only return; } # the order below is important, as the fields are used in an array # (indexed by numbers) below. If new fields are added, put them # after the last field below. my $statement = qq(SELECT song_nr, song, album, year, ma1, ma2, fa1, fa2, composer, lyricist, band, genre, track, comment FROM aldb WHERE path = "$pathname"); my $sth = $dbh->prepare($statement); $sth->execute or die "\n$0: Error: Can't execute search query\n"; if ($sth->rows) { if($updmode) { # update the entry here $updating = 1; } else { print "Duplicate entry found; not updating\n"; $update_db++; return; } } if ($mp3_ogg == 1) { # we have an MP3 file to work on my $tag = get_mp3tag($pathname) or print "Couldn't get mp3 tag for file $pathname\n"; $artist = $tag->{"ARTIST"}; $title = $tag->{"TITLE"}; $comment = $tag->{"COMMENT"}; $genre = $tag->{"GENRE"}; $year = $tag->{"YEAR"}; $album = $tag->{"ALBUM"}; $track = $tag->{"TRACK"}; } elsif ($mp3_ogg == 2) { # we have an Ogg file to work on eval { $ogg = Ogg::Vorbis::Header->new($pathname); }; if ($@) { warn "Couldn't open Vorbis file $pathname; skipping...\n"; return; } eval { foreach my $tag ($ogg->comment_tags) { foreach my $field ($ogg->comment($tag)) { if ($tag =~ /title/i) { $title = $field; } elsif ($tag =~ /album/i) { $album = $field; } elsif ($tag =~ /tracknumber/i) { $track = $field; } elsif ($tag =~ /genre/i) { $genre = $field; } elsif ($tag =~ /performer/i) { $composer = $field; } elsif ($tag =~ /lyricist/i) { $lyricist = $field; } elsif ($tag =~ /date/i) { $year = $field; } elsif ($tag =~ /description/i) { $comment = $field; } elsif ($tag =~ /artist/i) { if (not $artist) { $artist = $field; } elsif (not $band) { $band = $field; } elsif (not $ma1) { $ma1 = $field; } elsif (not $ma2) { $ma2 = $field; } elsif (not $fa1) { $fa1 = $field; } elsif (not $fa1) { $fa2 = $field; } } } } }; if ($@) { warn "WARNING: Possibly bad Vorbis file, $pathname; skipping...\n"; return; } } # Now, guess the album, artist, etc. from the filename. # $filname is filled in when we detected whether we had # an MP3 or an Ogg Vorbis file. guess_guessed($filname); if ($updating) { my @row = $sth->fetchrow_array; $title = $row[1]; $album = $row[2]; $year = $row[3]; $ma1 = $row[4]; $ma2 = $row[5]; $fa1 = $row[6]; $fa2 = $row[7]; $composer = $row[8]; $lyricist = $row[9]; $band = $row[10]; $genre = $row[11]; $track = $row[12]; $comment = $row[13]; } if ($verbose) { print "About to add/update info from $pathname:\n"; show_guessed(); } # Inserting title is mandatory 'cos we create links based on song name unless ($title) { if ($dontbug) { print "No-prompt mode and no title found in audio file: skipping entry $pathname\n"; return; } $title = get_input_fields($title, "title", $pathname); } if ($prompt_mode > 0) { # either of 'basic', 'limited', 'most', 'paranoid' modes $album = get_input_fields($album, "album", $pathname); #FIXME: for the artist, accept also if the artist is # a band, ma1, ma2, fa1, fa2, etc. $artist = get_input_fields($artist, "artist/band", $pathname); } if ($prompt_mode > 1) { # either of 'limited', 'most', 'paranoid' modes $genre = get_input_fields($genre, "genre", $pathname); $year = get_input_fields($year, "year", $pathname); } if ($prompt_mode > 2) { # either of 'most', 'paranoid' modes $composer = get_input_fields($composer, "composer", $pathname); $lyricist = get_input_fields($lyricist, "lyricist", $pathname); $comment = get_input_fields($comment, "comment", $pathname); } if ($prompt_mode > 3) { # 'paranoid' mode $ma1 = get_input_fields($ma1, "Male Artist-1", $pathname); $ma2 = get_input_fields($ma2, "Male Artist-2", $pathname); $fa1 = get_input_fields($fa1, "Female Artist-1", $pathname); $fa2 = get_input_fields($fa2, "Female Artist-2", $pathname); $track = get_input_fields($track, "Track", $pathname); } unless ($no_act) { if ($updating) { $statement = qq(UPDATE aldb SET song = "$title", album = "$album", band = "$artist", genre = "$genre", year = "$year", comment = "$comment", ma1 = "$ma1", ma2 = "$ma2", fa1 = "$fa1", fa2 = "$fa2", composer = "$composer", lyricist = "$lyricist", track = "$track" WHERE path = "$pathname"); } else { $statement = qq(INSERT INTO aldb (song, album, band, path, genre, track, year, comment, ma1, ma2, fa1, fa2, composer, lyricist) VALUES ("$title", "$album", "$artist", "$pathname", "$genre", "$track", "$year", "$comment", "$ma1", "$ma2", "$fa1", "$fa2", "$composer", "$lyricist")); } my $sth = $dbh->prepare($statement); $sth->execute or print "$0: Couldn't insert data for $pathname\n"; if ($updsong) { # update ID3 in MP3 or comment in Ogg Vorbis if ($mp3_ogg == 1 ) { eval { set_mp3tag($pathname, $title, $artist, $album, $year, $comment, $genre, $track); }; if ($@) { warn "WARNING: Couldn't write info back to MP3 ID3 for $pathname.\n\tDo you have write permissions?\n"; } } elsif ($mp3_ogg == 2) { eval { $ogg->add_comments("ARTIST", $artist) if $artist; $ogg->add_comments("ARTIST", $ma1) if $ma1; $ogg->add_comments("ARTIST", $ma2) if $ma2; $ogg->add_comments("ARTIST", $fa1) if $fa1; $ogg->add_comments("ARTIST", $fa2) if $fa2; $ogg->add_comments("ARTIST", $band) if $band; $ogg->add_comments("TITLE", $title) if $title; $ogg->add_comments("ALBUM", $album) if $album; $ogg->add_comments("PERFORMER", $composer) if $composer; $ogg->add_comments("LYRICIST", $lyricist) if $lyricist; $ogg->add_comments("DESCRIPTION", $comment) if $comment; $ogg->add_comments("DATE", $year) if $year; $ogg->add_comments("GENRE", $genre) if $genre; $ogg->add_comments("TRACKNUMBER", $track) if $band; $ogg->write_vorbis; }; if ($@) { warn "WARNING: Couldn't write info back to Ogg Vorbis for $pathname.\n\tDo you have write permissions?\n"; } } } } } # execution starts here # check for command-line arguments if (not @ARGV) { pod2usage(); } GetOptions( 'help' => \$help, 'verbose' => \$verbose, 'na|s' => \$no_act, 'prompt=s' => \$prompt, 'no-prompt' => \$dontbug, 'user=s' => \$user, 'pass=s' => \$password, 'host=s' => \$host, 'add-only' => \$addmode, 'upd-only' => \$updmode, 'upd-song' => \$updsong, 'file=s' => \@files ) or pod2usage(); if ($help) { pod2usage(); } if (not @ARGV and not $files[0]) { print "No directory or files specified\n"; pod2usage(); } if ($prompt =~ /basic/i) { $prompt_mode = 1; } elsif ($prompt =~ /limited/i) { $prompt_mode = 2; } elsif ($prompt =~ /most/i) { $prompt_mode = 3; } elsif ($prompt =~ /paranoid/i) { $prompt_mode = 4; } elsif (not $prompt) { $prompt_mode = 0; } else { die "\n$0: Error: Invalid argument \'$prompt\' passed to \'prompt\'. See the alfilldb(1) man page for more information.\n"; } if ($addmode and $updmode) { die "\n$0: Error: Conflicting modes specified, please choose one of " . " --add-only or --upd-only\n"; } if ($addmode) { $updmode = 0; } elsif ($updmode) { $addmode = 0; } else { # no option specified, default to add and update $addmode = $updmode = 1; } $config_file = "$ENV{HOME}/.audiolink/config"; if (-e $config_file) { open(CONFFILE, $config_file); # go through the config file while () { if (/^\s*user\s*\=+\s*(\w+)/) { chomp($user = $1) unless $user; } elsif ( /^\s*pass(word)?\s*=\s*(\w+)/) { chomp($password = $2) unless $password; } elsif ( /^\s*host\s*=\s*(\w+)/) { chomp($host = $1) unless $host; } } } else { warn "WARNING: config file not found. Use the audiolink script to create one.\n"; } # connect to the database. $dbi_string = "DBI:mysql:aldb:$host"; # using MySQL $dbh = DBI->connect($dbi_string,$user,$password) or die "\n$0: Error: Could not connect to the database!\ Check the user, password and host fields for the MySQL connection."; if (not $files[0]) { foreach $path (@ARGV) { # Check if the user has given a path format other than an absolute # path or a path that starts with ~. If yes, bail out. unless ($path =~ /^~/ or $path =~ /^\//) { print "WARNING: pathname doesn't consist of an absolute pathname.\n"; print "Skipping entry $path\n"; next; } find (\&wanted, $path); } } else { # File name specified on the command prompt foreach $file (@files) { # perl magic from perl cookbook (1st ed.), recipe 7.3 # expand tilde in filename $file =~ s{ ^ ~ ( [^/]* ) } { $1 ? (getpwnam($1))[7] : ($ENV{HOME} || $ENV{LOGDIR} || (getpwduid($>))[7]) }ex; # Check if the user has given a path format other than an absolute # path or a path that starts with ~. If yes, bail out. # Since we've already expanded the ~ case, just check against /. unless ($file =~ /^\//) { print "WARNING: pathname doesn't consist of an absolute pathname.\n"; print "Skipping entry $file\n"; next; } wanted(); } } if ($update_db > 0) { print "$update_db entries in the database were not updated. Use the --upd-only option to update them.\n"; } =pod =head1 NAME alfilldb - Add/update information of music files in the AudioLink database =head1 SYNOPSIS B [I