Migrated samskivert into its own, sensibly organized, repository.
git-svn-id: https://samskivert.googlecode.com/svn/trunk@1774 6335cc39-0255-0410-8fd6-9bcaacd3b74c
@@ -1,3 +0,0 @@
|
||||
extern
|
||||
build-stamp
|
||||
.jde-project.el
|
||||
@@ -1,31 +0,0 @@
|
||||
RoboDJ 1.2 (March 03, 2002)
|
||||
|
||||
NEW FEATURES
|
||||
|
||||
* (importer) Added support for adding ID3 tags to MP3 files after
|
||||
they are encoded.
|
||||
|
||||
* (chooser) Gave the UI a major facelift:
|
||||
- added icons for most buttons;
|
||||
- fixed layout to work more nicely;
|
||||
- made controls reflect actual state of the music daemon (paused
|
||||
versus playing, etc.).
|
||||
|
||||
* (importer) Added ability to back up from the CDDB panel to the
|
||||
"insert CD" panel.
|
||||
|
||||
RoboDJ 1.1 (February 22, 2002)
|
||||
|
||||
NEW FEATURES
|
||||
|
||||
* (chooser) Added support for removing an entire CD at at time
|
||||
from the playlist browser.
|
||||
|
||||
* (chooser) Added ability to search repository for entries where
|
||||
album, artist or song title contains a text string.
|
||||
|
||||
RoboDJ 1.0 (February 21, 2002)
|
||||
|
||||
INITIAL RELEASE
|
||||
|
||||
$Id: NEWS,v 1.2 2002/03/03 21:17:03 mdb Exp $
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# $Id: append,v 1.2 2001/03/22 03:00:25 mdb Exp $
|
||||
#
|
||||
# Appends the supplied song or songs to the playlist of a running MP3
|
||||
# daemon.
|
||||
|
||||
use strict;
|
||||
use IO::Socket;
|
||||
|
||||
my $usage = "Usage: $0 mp3_file [mp3_file ...]\n";
|
||||
my $path = shift or die $usage;
|
||||
|
||||
# determine our music daemon host and port
|
||||
my $host = "localhost";
|
||||
if (defined $ENV{"MUSICD_HOST"}) {
|
||||
$host = $ENV{"MUSICD_HOST"};
|
||||
}
|
||||
my $port = 2500;
|
||||
if (defined $ENV{"MUSICD_PORT"}) {
|
||||
$port = $ENV{"MUSICD_PORT"};
|
||||
}
|
||||
|
||||
# establish a socket connection with the server
|
||||
my $sock = new IO::Socket::INET(PeerAddr => $host,
|
||||
PeerPort => $port,
|
||||
Proto => "tcp");
|
||||
if (!defined $sock) {
|
||||
die "Unable to connect to music server on $host:$port.\n";
|
||||
}
|
||||
|
||||
do {
|
||||
# send the command
|
||||
print $sock "APPEND $path\n";
|
||||
# print the response
|
||||
print scalar <$sock>;
|
||||
} while ($path = shift);
|
||||
|
||||
close $sock;
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
`dirname $0`/runjava $* robodj.chooser.Chooser
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# $Id: delete,v 1.1 2002/03/03 04:56:43 mdb Exp $
|
||||
#
|
||||
# Deletes an entry from the repository. Must be run on the machine that
|
||||
# hosts the repository so that it can delete the files.
|
||||
|
||||
my $usage = "Usage: $0 repodir db_username db_password entryid\n";
|
||||
|
||||
my $repodir = shift or die $usage;
|
||||
my $username = shift or die $usage;
|
||||
my $password = shift or die $usage;
|
||||
my $entryid = shift or die $usage;
|
||||
|
||||
if (! -d $repodir) {
|
||||
die "'$repodir' not a valid repository directory.\n";
|
||||
}
|
||||
|
||||
invoke_sql("delete from entries where entryid=$entryid");
|
||||
invoke_sql("delete from songs where entryid=$entryid");
|
||||
system("rm -rf $repodir/*/$entryid");
|
||||
|
||||
sub invoke_sql {
|
||||
my ($sql) = @_;
|
||||
system("mysql -u $username -p$password -e \"$sql\" robodj") == 0
|
||||
or die "Error invoking 'mysql'.";
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
`dirname $0`/runjava robodj.importer.Importer
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# $Id: mpcmd,v 1.2 2001/03/22 03:00:25 mdb Exp $
|
||||
#
|
||||
# Appends the supplied song or songs to the playlist of a running MP3
|
||||
# daemon.
|
||||
|
||||
use strict;
|
||||
use IO::Socket;
|
||||
|
||||
my $usage = "Usage: $0 COMMAND [ARGUMENT ...]\n";
|
||||
my $command = shift or die $usage;
|
||||
|
||||
# determine our music daemon host and port
|
||||
my $host = "localhost";
|
||||
if (defined $ENV{"MUSICD_HOST"}) {
|
||||
$host = $ENV{"MUSICD_HOST"};
|
||||
}
|
||||
my $port = 2500;
|
||||
if (defined $ENV{"MUSICD_PORT"}) {
|
||||
$port = $ENV{"MUSICD_PORT"};
|
||||
}
|
||||
|
||||
# establish a socket connection with the server
|
||||
my $sock = new IO::Socket::INET(PeerAddr => $host,
|
||||
PeerPort => $port,
|
||||
Proto => "tcp");
|
||||
if (!defined $sock) {
|
||||
die "Unable to connect to music server on $host:$port.\n";
|
||||
}
|
||||
|
||||
# send the command
|
||||
print $sock "$command " . join(" ", @ARGV) . "\n";
|
||||
|
||||
# PLAYLIST has a multiline response, others are single line
|
||||
if ($command eq "PLAYLIST") {
|
||||
my $rsp = <$sock>;
|
||||
print $rsp;
|
||||
if ($rsp =~ m/200 Playlist songs: (\d+)/) {
|
||||
my $i;
|
||||
for ($i = 0; $i < $1; $i++) {
|
||||
print scalar <$sock>;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
print scalar <$sock>;
|
||||
}
|
||||
|
||||
close $sock;
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
`dirname $0`/runjava $* robodj.server.MusicDaemon
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# $Id: quickrip,v 1.1 2002/01/19 04:35:53 mdb Exp $
|
||||
#
|
||||
# Rips and encodes a CD and places it into the repository directory having
|
||||
# been given some necessary parameters.
|
||||
|
||||
my $usage = "$0 branchdir entryid [trackno]\n";
|
||||
my $root = "/export/robodj/repository";
|
||||
|
||||
# read our arguments
|
||||
my $branchdir = shift or die $usage;
|
||||
my $entryid = shift or die $usage;
|
||||
my $trackno = shift;
|
||||
|
||||
# figure out how many tracks there are to rip
|
||||
my $trackcount = 0;
|
||||
open(SCAN, "cdparanoia -Q 2>&1|") or die "Can't invoke cdparanoia: $!\n";
|
||||
while (<SCAN>) {
|
||||
# parse track info lines just to get the track counts
|
||||
if (/^\s*(\d+)\.\s+(\d+)\s\[\S*\]\s+(\d+)/) {
|
||||
$trackcount = $1;
|
||||
}
|
||||
}
|
||||
|
||||
# create the target directory
|
||||
my $target = "$root/$branchdir/$entryid";
|
||||
system("mkdir -p $target");
|
||||
|
||||
# do our main business
|
||||
if (defined $trackno) {
|
||||
# either rip a single track
|
||||
rip_track($trackno);
|
||||
|
||||
} else {
|
||||
# or rip the whole kit and kaboodle
|
||||
for ($i = 1; $i <= $trackcount; $i++) {
|
||||
rip_track($i);
|
||||
}
|
||||
}
|
||||
|
||||
sub rip_track
|
||||
{
|
||||
my ($track) = @_;
|
||||
my $tno = sprintf("%02d", $track);
|
||||
my $tmp = "/tmp/track$tno";
|
||||
# rip the track
|
||||
system("cdparanoia -w $track $tmp.wav\n");
|
||||
# encode it
|
||||
system("lame --nohist -v $tmp.wav $tmp.mp3\n");
|
||||
# move it into the repository
|
||||
system("mv $tmp.mp3 $target/$tno.mp3");
|
||||
# and clean up after ourselves
|
||||
unlink("$tmp.wav");
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# /usr/bin/rdjbrowse
|
||||
#
|
||||
# Launches the RoboDJ music browser and playlist manager
|
||||
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||
NAME=robodj
|
||||
DESC="Robodj game server"
|
||||
PIDFILE="/var/run/$NAME.pid"
|
||||
CLASS=robodj.chooser.Chooser
|
||||
LOGFILE=/var/log/$NAME/stdout.log
|
||||
ROBODJ_HOME=/usr/share/$NAME
|
||||
|
||||
# The following varibales can we overwritten in /etc/default/robodj
|
||||
MUSICD_HOST=localhost
|
||||
MUSICD_PORT=2500
|
||||
# The first existing directory is used for JAVA_HOME
|
||||
JDK_DIRS="/usr/local/jdk1.4 /usr/local/jdk1.3 /usr/lib/j2se/1.3"
|
||||
|
||||
# overwrite settings from default file
|
||||
if [ -f /etc/default/robodj ]; then
|
||||
. /etc/default/robodj
|
||||
fi
|
||||
|
||||
# Look for the right JVM to
|
||||
for jdir in $JDK_DIRS; do
|
||||
if [ -d "$jdir" -a -z "${JAVA_HOME}" ]; then
|
||||
JAVA_HOME=$jdir
|
||||
fi
|
||||
done
|
||||
JAVA="$JAVA_HOME/bin/java"
|
||||
|
||||
test -f $DAEMON || exit 0
|
||||
|
||||
# Start with the standard Java classes
|
||||
CLASSPATH="$JAVA_HOME/jre/lib/rt.jar"
|
||||
|
||||
# Add all JAR files in /usr/share/robodj/lib to the CLASSPATH
|
||||
for jar in $ROBODJ_HOME/lib/*.jar; do
|
||||
if [ -e $jar ]; then
|
||||
CLASSPATH=$jar:$CLASSPATH
|
||||
fi
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
$JAVA -Dmusicd_host=$MUSICD_HOST -Dmusicd_port=$MUSICD_PORT $CLASS
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# /usr/bin/rdjbrowse
|
||||
#
|
||||
# Launches the RoboDJ music browser and playlist manager
|
||||
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||
NAME=robodj
|
||||
DESC="Robodj game server"
|
||||
PIDFILE="/var/run/$NAME.pid"
|
||||
CLASS=robodj.importer.Importer
|
||||
LOGFILE=/var/log/$NAME/stdout.log
|
||||
ROBODJ_HOME=/usr/share/$NAME
|
||||
|
||||
# The following varibales can we overwritten in /etc/default/robodj
|
||||
|
||||
# The first existing directory is used for JAVA_HOME
|
||||
JDK_DIRS="/usr/local/jdk1.4 /usr/lib/j2se/1.3"
|
||||
|
||||
# overwrite settings from default file
|
||||
if [ -f /etc/default/robodj ]; then
|
||||
. /etc/default/robodj
|
||||
fi
|
||||
|
||||
# Look for the right JVM to
|
||||
for jdir in $JDK_DIRS; do
|
||||
if [ -d "$jdir" -a -z "${JAVA_HOME}" ]; then
|
||||
JAVA_HOME=$jdir
|
||||
fi
|
||||
done
|
||||
JAVA="$JAVA_HOME/bin/java"
|
||||
|
||||
test -f $DAEMON || exit 0
|
||||
|
||||
# Start with the standard Java classes
|
||||
CLASSPATH="$JAVA_HOME/jre/lib/rt.jar"
|
||||
|
||||
# Add all JAR files in /usr/share/robodj/lib to the CLASSPATH
|
||||
for jar in $ROBODJ_HOME/lib/*.jar; do
|
||||
if [ -e $jar ]; then
|
||||
CLASSPATH=$jar:$CLASSPATH
|
||||
fi
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
$JAVA $CLASS
|
||||
@@ -1,107 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
use Getopt::Std;
|
||||
|
||||
my $usage = "Usage: $0 [-p pid_file] [-r root_directory] args\n";
|
||||
|
||||
# locations of stuff
|
||||
chomp($location = `dirname $0`);
|
||||
|
||||
# get the server root by popping the /bin off of our parent directory
|
||||
@parts = split(/\//, $location);
|
||||
pop(@parts);
|
||||
my $root = join("/", @parts);
|
||||
|
||||
my $jhome = $ENV{"JAVA_HOME"};
|
||||
|
||||
# make sure JAVA_HOME is set
|
||||
if (!defined $jhome) {
|
||||
warn "$0: Error: No JAVA_HOME specified!\n";
|
||||
warn "\n";
|
||||
warn "You must set your JAVA_HOME environment variable to the\n";
|
||||
warn "the absolute path of your JDK installation. For example:\n";
|
||||
warn "\n";
|
||||
warn " % JAVA_HOME=/usr/local/jdk1.2\n";
|
||||
warn " % export JAVA_HOME\n";
|
||||
die "\n";
|
||||
}
|
||||
|
||||
# make sure it's set to a directory
|
||||
if (! -d $jhome) {
|
||||
die "$0: Can't find a java interpreter in '$jhome'.\n";
|
||||
}
|
||||
|
||||
my $java = "$jhome/bin/java";
|
||||
my $jlib = "$jhome/lib/classes.zip";
|
||||
|
||||
# determine our machine architecture
|
||||
my $ostype = `uname -s`;
|
||||
my $machtype = `uname -m`;
|
||||
chomp($ostype);
|
||||
chomp($machtype);
|
||||
my $arch = "$machtype-$ostype";
|
||||
|
||||
# add our native libraries to the runtime library path
|
||||
my $libs = "$root/lib/$arch";
|
||||
my $libpath = $ENV{"LD_LIBRARY_PATH"};
|
||||
|
||||
if (defined $libpath) {
|
||||
$ENV{"LD_LIBRARY_PATH"} = "$libs:$libpath";
|
||||
} else {
|
||||
$ENV{"LD_LIBRARY_PATH"} = $libs;
|
||||
}
|
||||
|
||||
# put everything in our class path
|
||||
my $classpath = "-classpath $root/dist/classes";
|
||||
|
||||
# add zip and jar files from our lib/ directory and the system lib/ directory
|
||||
my @dirs = ( "$root/lib", $ENV{"JAVA_LIBS"} );
|
||||
foreach $dir (@dirs) {
|
||||
next unless (defined $dir);
|
||||
if (opendir(DIR, $dir)) {
|
||||
foreach $lib (grep { /.(zip|jar)/ && -f "$dir/$_" } readdir(DIR)) {
|
||||
$classpath .= ":$dir/$lib";
|
||||
}
|
||||
closedir DIR;
|
||||
}
|
||||
}
|
||||
|
||||
# finally add the standard classes
|
||||
$classpath = "$classpath:$jlib";
|
||||
|
||||
# specify our application root (the resource manager needs this)
|
||||
my $rootarg = "-Dresource_url=file:$root/rsrc";
|
||||
|
||||
my $pid_file = undef;
|
||||
my $i = 0;
|
||||
|
||||
# strip out the -p and -r args (we'd use getopt() but the damned thing
|
||||
# provides no way of escaping arguments so that we can pass args to
|
||||
# runjava that get passed down to the JVM)
|
||||
for ($i = 0; $i < @ARGV; $i++) {
|
||||
my $arg = $ARGV[$i];
|
||||
|
||||
# stop when we see -- (and strip it out because Java don't dig --)
|
||||
if ($arg eq "--") {
|
||||
splice(@ARGV, $i, 1);
|
||||
last;
|
||||
}
|
||||
|
||||
if ($arg eq "-p") {
|
||||
$pid_file = $ARGV[$i+1];
|
||||
splice(@ARGV, $i, 2);
|
||||
$i -= 1; # decrement i so that things stay in sync
|
||||
|
||||
} elsif ($arg eq "-r") {
|
||||
$rootarg = "-Dapplication.root=" . $ARGV[$i+1];
|
||||
splice(@ARGV, $i, 2);
|
||||
$i -= 1; # decrement i so that things stay in sync
|
||||
}
|
||||
}
|
||||
|
||||
# log the pid file if requested to do so
|
||||
print `echo $$ > $pid_file` if (defined $pid_file);
|
||||
|
||||
my $cmd = "$java -mx256M $classpath $rootarg " . join(" ", @ARGV);
|
||||
# print "$cmd\n";
|
||||
exec($cmd);
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# $Id: setid3,v 1.3 2002/03/03 05:54:33 mdb Exp $
|
||||
#
|
||||
# A script for setting the id3 tags of the MP3 files in the RoboDJ
|
||||
# repository. The data from the database must be dumped into two files:
|
||||
# entries.txt and songs.txt using the following SQL:
|
||||
#
|
||||
# select * from ENTRIES into outfile 'entries.txt';
|
||||
# select * from SONGS into outfile 'songs.txt';
|
||||
#
|
||||
# and this script will operate on those files.
|
||||
|
||||
my $usage = "Usage: $0 repository_dir entries.txt songs.txt\n";
|
||||
my $repodir = shift or die $usage;
|
||||
my $entfile = shift or die $usage;
|
||||
my $songfile = shift or die $usage;
|
||||
|
||||
# unbuffered output
|
||||
$| = 1;
|
||||
|
||||
if (! -d $repodir) {
|
||||
die "'$repodir' not a valid repository directory.\n";
|
||||
}
|
||||
|
||||
my %entries;
|
||||
|
||||
open(ENTRIES, "$entfile") or die "Can't open '$entfile' for reading: $!\n";
|
||||
while (<ENTRIES>) {
|
||||
chomp;
|
||||
my ($entid, $title, $artist, $source) = split("\t");
|
||||
$entries{$entid} = [ $title, $artist ];
|
||||
}
|
||||
close(ENTRIES);
|
||||
|
||||
my $counter = 0;
|
||||
|
||||
open(SONGS, "$songfile") or die "Can't open '$songfile' for reading: $!\n";
|
||||
while (<SONGS>) {
|
||||
chomp;
|
||||
my ($songid, $entryid, $position, $title, $location, $duration) =
|
||||
split("\t");
|
||||
|
||||
# escape our arguments
|
||||
$album = $entries{$entryid}->[0];
|
||||
$artist = $entries{$entryid}->[1];
|
||||
|
||||
# invoke the command
|
||||
my @cmd = ("id3v2", "--song", "$title",
|
||||
"--artist", "$artist",
|
||||
"--album", "$album",
|
||||
"--track", "$position", "$location");
|
||||
warn "\nFailed to set tag on $location\n" unless (system(@cmd) == 0);
|
||||
|
||||
if ($counter++ % 100 == 99) {
|
||||
print ".";
|
||||
}
|
||||
}
|
||||
close(SONGS);
|
||||
|
||||
print "\nSet ID3 tags on $counter songs.\n";
|
||||
|
||||
sub escape {
|
||||
my ($text) = @_;
|
||||
$text =~ s:([^A-Za-z0-9]):\\$1:g;
|
||||
return $text;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#
|
||||
# $Id: build.properties,v 1.2 2002/11/11 18:23:36 mdb Exp $
|
||||
#
|
||||
# Build configuration for the RoboDJ client application.
|
||||
#
|
||||
|
||||
# the URI at which the application is canonically available
|
||||
application_uri = http://www.samskivert.com/code/robodj
|
||||
|
||||
# the jar file keystore
|
||||
sign.keystore = lib/keystore.dat
|
||||
|
||||
# the alias to use when signing the jar files
|
||||
sign.alias = samskivert
|
||||
|
||||
# the keystore password
|
||||
sign.storepass = monkeybiz
|
||||
|
||||
# the host on which the music daemon is running
|
||||
musicd_host = depravity
|
||||
@@ -1,145 +0,0 @@
|
||||
<!-- build configuration -->
|
||||
<project name="robodj" default="compile" basedir=".">
|
||||
|
||||
<!-- import properties -->
|
||||
<property file="build.properties"/>
|
||||
|
||||
<!-- things you may want to change -->
|
||||
<property name="app.name" value="robodj"/>
|
||||
<property name="build.compiler" value="jikes"/>
|
||||
<property name="web.home" value="/export/samskivert/pages/code"/>
|
||||
<property name="install.dir" value="${web.home}/${app.name}"/>
|
||||
<property name="narya.home" value="../../work/narya"/>
|
||||
|
||||
<!-- things you probably don't want to change -->
|
||||
<property name="src.dir" value="src/java"/>
|
||||
<property name="deploy.dir" value="dist"/>
|
||||
<property name="dist.jar" value="${app.name}.jar"/>
|
||||
<property name="javadoc.dir" value="${deploy.dir}/docs"/>
|
||||
<property name="doc.packages" value="robodj.*"/>
|
||||
<property name="doc.overview" value="robodj/overview.html"/>
|
||||
<property name="nartool.path" value="${narya.home}/bin"/>
|
||||
<property name="classes.dir" value="${deploy.dir}/classes"/>
|
||||
|
||||
<!-- declare our classpath -->
|
||||
<path id="classpath">
|
||||
<fileset dir="lib" includesfile="lib/CLIENT_LIBS"/>
|
||||
<pathelement location="${classes.dir}"/>
|
||||
</path>
|
||||
|
||||
<!-- generates .java files for all .dobj files -->
|
||||
<target name="gendobj">
|
||||
<apply executable="${nartool.path}/gendobj"
|
||||
failonerror="true" parallel="true">
|
||||
<srcfile/>
|
||||
<fileset dir="src/java" includes="**/*.dobj"/>
|
||||
</apply>
|
||||
</target>
|
||||
|
||||
<!-- generates marshaller and dispatcher classes for all invocation -->
|
||||
<!-- service declarations -->
|
||||
<target name="genservice">
|
||||
<!-- make sure the service class files are all compiled -->
|
||||
<javac srcdir="src/java" destdir="${classes.dir}"
|
||||
debug="on" optimize="${build.optimize}" deprecation="on">
|
||||
<classpath refid="classpath"/>
|
||||
<include name="**/*Service.java"/>
|
||||
</javac>
|
||||
<apply executable="${nartool.path}/genservice"
|
||||
failonerror="true" parallel="true">
|
||||
<arg value="--classpath"/>
|
||||
<arg value="${classes.dir}:lib/narya-distrib.jar:lib/narya-base.jar"/>
|
||||
<arg value="--sourcedir"/>
|
||||
<arg value="src/java"/>
|
||||
<fileset dir="src/java" includes="**/*Service.java"/>
|
||||
</apply>
|
||||
</target>
|
||||
|
||||
<!-- prepares the application directories -->
|
||||
<target name="prepare" depends="gendobj">
|
||||
<mkdir dir="${deploy.dir}"/>
|
||||
<mkdir dir="${classes.dir}"/>
|
||||
<mkdir dir="${javadoc.dir}"/>
|
||||
<!-- copy media and properties into the target directory -->
|
||||
<copy todir="${classes.dir}">
|
||||
<fileset dir="${src.dir}" includes="**/*.jpg"/>
|
||||
<fileset dir="${src.dir}" includes="**/*.png"/>
|
||||
<fileset dir="${src.dir}" includes="**/*.properties"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<!-- cleans out the built application -->
|
||||
<target name="clean">
|
||||
<delete dir="${deploy.dir}"/>
|
||||
</target>
|
||||
|
||||
<!-- cleans out the installed application -->
|
||||
<target name="purge" depends="clean">
|
||||
<delete dir="${install.dir}"/>
|
||||
</target>
|
||||
|
||||
<!-- build the java class files -->
|
||||
<target name="compile" depends="prepare">
|
||||
<javac srcdir="${src.dir}" destdir="${classes.dir}"
|
||||
debug="on" optimize="off" deprecation="off">
|
||||
<classpath refid="classpath"/>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!-- build the javadoc documentation -->
|
||||
<target name="javadoc" depends="prepare">
|
||||
<javadoc sourcepath="${src.dir}"
|
||||
packagenames="${doc.packages}"
|
||||
windowtitle="${app.name} API"
|
||||
doctitle="${app.name} API"
|
||||
overview="${src.dir}/${doc.overview}"
|
||||
destdir="${javadoc.dir}">
|
||||
<classpath refid="classpath"/>
|
||||
<link href="http://www.waywardgeeks.org/code/samskivert/javadoc"/>
|
||||
<link href="http://java.sun.com/products/jdk/1.3/docs/api"/>
|
||||
</javadoc>
|
||||
</target>
|
||||
|
||||
<!-- a target for rebuilding everything -->
|
||||
<target name="all" depends="clean,prepare,compile,javadoc,dist"/>
|
||||
|
||||
<!-- builds our distribution files (war and jar) -->
|
||||
<target name="dist" depends="prepare,compile">
|
||||
<jar destfile="${deploy.dir}/${dist.jar}"
|
||||
basedir="${classes.dir}"/>
|
||||
</target>
|
||||
|
||||
<!-- installs the JWS app -->
|
||||
<target name="install" depends="dist">
|
||||
<mkdir dir="${install.dir}"/>
|
||||
<!-- copy the jar files used by the client -->
|
||||
<copy todir="${install.dir}">
|
||||
<fileset dir="lib" includesfile="lib/CLIENT_LIBS"/>
|
||||
<fileset dir="dist" includes="*.jar"/>
|
||||
</copy>
|
||||
<!-- sign the jar files -->
|
||||
<signjar keystore="${sign.keystore}" alias="${sign.alias}"
|
||||
storepass="${sign.storepass}" lazy="true">
|
||||
<fileset dir="${install.dir}" includes="*.jar"/>
|
||||
</signjar>
|
||||
<!-- copy the jnlp file(s) and associated business into place -->
|
||||
<filter filtersfile="build.properties"/>
|
||||
<copy todir="${install.dir}" filtering="true">
|
||||
<fileset dir="etc" includes="**/*.jnlp"/>
|
||||
</copy>
|
||||
<copy todir="${install.dir}">
|
||||
<fileset dir="etc" includes="**/*.png"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<!-- mirrors the files to the production web server -->
|
||||
<target name="mirror" depends="install">
|
||||
<echo message="Mirroring files to ${musicd_host}..."/>
|
||||
<apply executable="rsync" parallel="true">
|
||||
<fileset dir="${install.dir}" includes="**"/>
|
||||
<arg line="-av"/>
|
||||
<srcfile/>
|
||||
<arg line="${musicd_host}:${install.dir}"/>
|
||||
</apply>
|
||||
</target>
|
||||
</project>
|
||||
@@ -1,2 +0,0 @@
|
||||
files
|
||||
robodj
|
||||
@@ -1,26 +0,0 @@
|
||||
robodj (1.2-2) unstable; urgency=low
|
||||
|
||||
* Upstream release updated with more features.
|
||||
|
||||
-- Michael Bayne <mdb@samskivert.com> Sun, 3 Mar 2002 12:59:12 -0800
|
||||
|
||||
robodj (1.2-1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Michael Bayne <mdb@samskivert.com> Sat, 2 Mar 2002 22:11:34 -0800
|
||||
|
||||
robodj (1.1-1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
* Added script for running importer.
|
||||
* Added defaults file for specifying music daemon host and other
|
||||
parameters.
|
||||
|
||||
-- Michael Bayne <mdb@samskivert.com> Thu, 22 Feb 2002 00:50:00 -0800
|
||||
|
||||
robodj (1.0-1) unstable; urgency=low
|
||||
|
||||
* Initial Release.
|
||||
|
||||
-- Michael Bayne <mdb@samskivert.com> Thu, 21 Feb 2002 23:00:58 -0800
|
||||
@@ -1,17 +0,0 @@
|
||||
Source: robodj
|
||||
Section: contrib/web
|
||||
Priority: optional
|
||||
Maintainer: Michael Bayne <mdb@samskivert.com>
|
||||
Build-Depends: debhelper (>= 3.0.40), ant (>= 1.3-1), j2sdk1.3 (>= 1.3.1-1.1)
|
||||
Standards-Version: 1.0.0
|
||||
|
||||
Package: robodj
|
||||
Architecture: all
|
||||
Section: contrib/web
|
||||
Priority: optional
|
||||
Depends: java-virtual-machine
|
||||
Description: Digital music management system
|
||||
robodj provides tools to convert music from CDs into MP3s and to manage
|
||||
the resulting music in categories. it stores music metadata in a SQL
|
||||
database and provides a music server that can be configured a network
|
||||
enabled client.
|
||||
@@ -1,19 +0,0 @@
|
||||
robodj - a digital music management system
|
||||
Copyright (C) 2002 Michael Bayne
|
||||
|
||||
This 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; either version 2, or (at your option) any later
|
||||
version.
|
||||
|
||||
This 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 with
|
||||
your Debian GNU/Linux system, in /usr/share/common-licenses/GPL, or with the
|
||||
lookuplet source package as the file COPYING. If not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
NEWS README
|
||||
@@ -1,5 +0,0 @@
|
||||
MUSICD_HOST=depravity
|
||||
MUSICD_PORT=2500
|
||||
|
||||
# The first existing directory is used for JAVA_HOME
|
||||
JDK_DIRS="/usr/local/jdk1.4 /usr/local/jdk1.3 /usr/lib/j2se/1.3"
|
||||
@@ -1,3 +0,0 @@
|
||||
etc/default
|
||||
usr/bin
|
||||
usr/share/robodj/lib
|
||||
@@ -1,65 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
# debian/rules file for robodj (uses debhelper V3)
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
# This is the debhelper compatability version to use.
|
||||
export DH_COMPAT=3
|
||||
|
||||
build: build-stamp
|
||||
build-stamp:
|
||||
dh_testdir
|
||||
# call ant to build RoboDJ in the dist directories
|
||||
ant dist
|
||||
touch build-stamp
|
||||
|
||||
clean:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
rm -f build-stamp
|
||||
ant clean
|
||||
dh_clean
|
||||
|
||||
install: build
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_clean -k
|
||||
dh_installdirs
|
||||
|
||||
# Install binaries, jars and configuration files
|
||||
install -m755 bin/rdj* debian/robodj/usr/bin
|
||||
install -m644 dist/robodj.jar debian/robodj/usr/share/robodj/lib/
|
||||
install -m644 lib/*.jar debian/robodj/usr/share/robodj/lib/
|
||||
|
||||
# Install defaults file
|
||||
install -m644 debian/robodj.default debian/robodj/etc/default/robodj
|
||||
|
||||
# Build architecture-independent files here.
|
||||
binary-indep: build install
|
||||
dh_testdir -i
|
||||
dh_testroot -i
|
||||
# dh_installdebconf -i
|
||||
# dh_installdocs -i
|
||||
# dh_installexamples -i build/conf
|
||||
# dh_installmenu -i
|
||||
# dh_installemacsen -i
|
||||
# dh_installpam -i
|
||||
# dh_installinit -i
|
||||
# dh_installcron -i
|
||||
# dh_installlogrotate -i
|
||||
# dh_installmanpages -i
|
||||
# dh_installinfo -i
|
||||
# dh_undocumented -i jspc.1
|
||||
dh_installchangelogs -i
|
||||
# dh_link -i
|
||||
dh_compress -i
|
||||
dh_fixperms -i
|
||||
dh_installdeb -i
|
||||
# dh_perl -i
|
||||
dh_gencontrol -i
|
||||
dh_md5sums -i
|
||||
dh_builddeb -i
|
||||
|
||||
binary: binary-indep
|
||||
.PHONY: build clean binary-indep binary-arch binary install
|
||||
@@ -1,35 +0,0 @@
|
||||
Robo DJ Design
|
||||
--------------
|
||||
|
||||
* Components
|
||||
|
||||
- Music database
|
||||
Schema
|
||||
OM classes
|
||||
|
||||
- Ripper and encoder
|
||||
Interface with tools (cdparanoia, etc.)
|
||||
Interface with CDDB
|
||||
Metadata entry/editing
|
||||
|
||||
- Music server
|
||||
Streams MP3 audio data to player (xmcd?)
|
||||
...
|
||||
|
||||
* Music database:
|
||||
|
||||
EID (int) - entry id
|
||||
PID (int) - parent id
|
||||
NAME (string) - entry name
|
||||
LOCATOR (string) - path to data (URL, maybe file: maybe other...)
|
||||
|
||||
Album
|
||||
Name
|
||||
Artist
|
||||
Record label
|
||||
Release date
|
||||
Genre
|
||||
Tracks
|
||||
Name
|
||||
Index
|
||||
Import date (date imported into Robo DJ system)
|
||||
@@ -1,29 +0,0 @@
|
||||
RoboDJ Notes
|
||||
------------
|
||||
|
||||
- Rewrite music daemon in Java, have the clients communicate with it using
|
||||
a more robust protocol (maybe Narya) so that they can be informed when
|
||||
shared state (the current track, etc.) changes
|
||||
|
||||
- Provide a protocol for uploading mp3 files via the music server to avoid
|
||||
having to do it by hand
|
||||
|
||||
- Use Java code to handle ID3 reading and updating
|
||||
|
||||
- Write a tool to import existing MP3 files into the system
|
||||
|
||||
- Play a sound when the ripping process is complete (or when an error
|
||||
occurs) because the user may be off doing something in another window
|
||||
|
||||
- Have musicd manage the playlist hierarchically so that if we add a whole
|
||||
CD to the playlist, we can remove it in the same way. Also pass the
|
||||
metadata (entryid, songid) to the musicd as well so that we don't have
|
||||
to reverse engineer things when we're displaying the contents of the
|
||||
playlist.
|
||||
|
||||
- Restructure ripping process so that the panel code drives the ripping of
|
||||
each track which would allow it to respond to ripping failures and
|
||||
provide the ability to retry or ignore a track that fails to rip.
|
||||
|
||||
- Add check to see if a disc is already in the repository and offer to
|
||||
re-rip it.
|
||||
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- JNLP File for RoboDJ music chooser -->
|
||||
|
||||
<!-- If you're seeing this in your web browser, you probably need to
|
||||
download and install the Sun JRE 1.4 and/or Java Web Start from
|
||||
http://java.sun.com/j2se/ -->
|
||||
|
||||
<jnlp spec="1.0+" codebase="@application_uri@/" href="chooser.jnlp">
|
||||
<information>
|
||||
<title>RoboDJ Chooser</title>
|
||||
<vendor>Seuss and Asseusciates</vendor>
|
||||
<homepage href="index.html"/>
|
||||
<icon href="icon.png"/>
|
||||
<description kind="short">
|
||||
Music choosing fun for the whole family!
|
||||
</description>
|
||||
<description>
|
||||
Provides a browser for the RoboDJ music repository and an interface
|
||||
by which that music can be queued up for playing on the music
|
||||
server.
|
||||
</description>
|
||||
<offline-allowed/>
|
||||
</information>
|
||||
<security>
|
||||
<all-permissions/>
|
||||
</security>
|
||||
<resources>
|
||||
<j2se version="1.4+" href="http://java.sun.com/products/autodl/j2se"/>
|
||||
<jar href="robodj.jar"/>
|
||||
<jar href="commons-io.jar"/>
|
||||
<jar href="commons-lang.jar"/>
|
||||
<jar href="mysql-connector-java-3.0.11-stable-bin.jar"/>
|
||||
<jar href="samskivert.jar"/>
|
||||
<jar href="narya-base.jar"/>
|
||||
<jar href="narya-distrib.jar"/>
|
||||
<property name="musicd_host" value="@musicd_host@"/>
|
||||
</resources>
|
||||
<application-desc main-class="robodj.chooser.Chooser"/>
|
||||
</jnlp>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -1,6 +0,0 @@
|
||||
commons-io.jar
|
||||
commons-lang.jar
|
||||
mysql-connector-java-3.0.11-stable-bin.jar
|
||||
samskivert.jar
|
||||
narya-base.jar
|
||||
narya-distrib.jar
|
||||
@@ -1,13 +0,0 @@
|
||||
Various third party libraries are needed by RoboDJ.
|
||||
|
||||
- Jakarta Commons Collections (org.apache.commons.*)
|
||||
http://jakarta.apache.org/commons
|
||||
|
||||
- Narya (com.threerings.narya.*)
|
||||
http://www.threerings.net/code/narya/
|
||||
|
||||
- samskivert (com.samskivert.*)
|
||||
http://www.samskivert.com/code/samskivert/
|
||||
|
||||
- The MySQL JDBC driver (though others might work as well)
|
||||
http://dev.mysql.com/downloads/connector/j/3.1.html
|
||||
@@ -1,38 +0,0 @@
|
||||
//
|
||||
// $Id: Log.java,v 1.1 2001/03/18 06:58:55 mdb Exp $
|
||||
|
||||
package robodj;
|
||||
|
||||
/**
|
||||
* A placeholder class that contains a reference to the log object used by
|
||||
* the robodj package.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
public static com.samskivert.util.Log log =
|
||||
new com.samskivert.util.Log("robodj");
|
||||
|
||||
/** Convenience function. */
|
||||
public static void debug (String message)
|
||||
{
|
||||
log.debug(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void info (String message)
|
||||
{
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void warning (String message)
|
||||
{
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void logStackTrace (Throwable t)
|
||||
{
|
||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
//
|
||||
// $Id: Version.java,v 1.2 2002/11/11 17:33:43 mdb Exp $
|
||||
|
||||
package robodj;
|
||||
|
||||
/**
|
||||
* Used to report version numbers in the client.
|
||||
*/
|
||||
public class Version
|
||||
{
|
||||
/** The application release version. */
|
||||
public static String RELEASE_VERSION = "1.3";
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// $Id: BrowsePanel.java,v 1.6 2004/01/26 16:33:40 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import com.samskivert.swing.GroupLayout;
|
||||
import com.samskivert.util.CollectionUtil;
|
||||
|
||||
import robodj.repository.*;
|
||||
|
||||
public class BrowsePanel extends JPanel
|
||||
{
|
||||
public BrowsePanel ()
|
||||
{
|
||||
setLayout(new BorderLayout(5, 5));
|
||||
|
||||
// obtain our categories
|
||||
ArrayList cats = new ArrayList();
|
||||
CollectionUtil.addAll(cats, Chooser.model.getCategories());
|
||||
// add an "Uncategorized" category
|
||||
cats.add(new Category(-1, "Uncategorized"));
|
||||
|
||||
final JList clist = new JList(cats.toArray());
|
||||
clist.getSelectionModel().setSelectionMode(
|
||||
ListSelectionModel.SINGLE_SELECTION);
|
||||
ListSelectionListener lsl = new ListSelectionListener() {
|
||||
public void valueChanged (ListSelectionEvent lse) {
|
||||
Category cat = (Category)clist.getSelectedValue();
|
||||
if (cat != null) {
|
||||
_hlabel.setText(cat.name);
|
||||
_category.setCategory(cat.categoryid);
|
||||
}
|
||||
}
|
||||
};
|
||||
clist.getSelectionModel().addListSelectionListener(lsl);
|
||||
add(new JScrollPane(clist), BorderLayout.WEST);
|
||||
|
||||
JPanel main = GroupLayout.makeVStretchBox(3);
|
||||
add(main, BorderLayout.CENTER);
|
||||
|
||||
JPanel header = GroupLayout.makeButtonBox(GroupLayout.CENTER);
|
||||
header.add(_hlabel = new JLabel());
|
||||
_hlabel.setFont(getFont().deriveFont(18f));
|
||||
main.add(header, GroupLayout.FIXED);
|
||||
|
||||
main.add(_category = new CategoryEntryList(-1));
|
||||
clist.setSelectedIndex(cats.size()-1);
|
||||
}
|
||||
|
||||
protected CategoryEntryList _category;
|
||||
protected JLabel _hlabel;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
//
|
||||
// $Id: CategoryEntryList.java,v 1.3 2004/01/26 16:10:55 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.swing.Controller;
|
||||
|
||||
import robodj.repository.Entry;
|
||||
|
||||
public class CategoryEntryList extends EntryList
|
||||
{
|
||||
/**
|
||||
* Constructs an entry list for entries in a particular category.
|
||||
*/
|
||||
public CategoryEntryList (int categoryId)
|
||||
{
|
||||
setCategory(categoryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates our category.
|
||||
*/
|
||||
public void setCategory (int categoryId)
|
||||
{
|
||||
if (categoryId != _categoryId) {
|
||||
_categoryId = categoryId;
|
||||
Controller.postAction(this, "refresh");
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected Controller createController ()
|
||||
{
|
||||
return new EntryController(this) {
|
||||
public Entry[] readEntries () throws PersistenceException {
|
||||
return Chooser.model.getEntries(_categoryId);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected String getEmptyString ()
|
||||
{
|
||||
return "No entries in this category.";
|
||||
}
|
||||
|
||||
/** The unique identifier of the category we are displaying. */
|
||||
protected int _categoryId;
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
//
|
||||
// $Id: Chooser.java,v 1.14 2004/01/26 16:33:40 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.jdbc.StaticConnectionProvider;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.samskivert.swing.util.SwingUtil;
|
||||
import com.samskivert.util.ConfigUtil;
|
||||
import com.samskivert.util.PropertiesUtil;
|
||||
import com.samskivert.util.RunAnywhere;
|
||||
|
||||
import robodj.Log;
|
||||
import robodj.repository.Model;
|
||||
import robodj.repository.Repository;
|
||||
import robodj.util.ErrorUtil;
|
||||
import robodj.util.RDJPrefs;
|
||||
import robodj.util.RDJPrefsPanel;
|
||||
|
||||
/**
|
||||
* The chooser is the GUI-based application for browsing the music
|
||||
* collection and managing the playlist.
|
||||
*/
|
||||
public class Chooser extends DJClient
|
||||
{
|
||||
public static Repository repository;
|
||||
|
||||
public static Model model;
|
||||
|
||||
public static ChooserFrame frame;
|
||||
|
||||
public void init ()
|
||||
{
|
||||
boolean error = false;
|
||||
|
||||
// try {
|
||||
// UIManager.setLookAndFeel(
|
||||
// RunAnywhere.isLinux() ?
|
||||
// "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" :
|
||||
// UIManager.getSystemLookAndFeelClassName());
|
||||
// } catch (Exception e) {
|
||||
// Log.info("Failed to set GTK look and feel: " + e);
|
||||
// }
|
||||
|
||||
// loop until the user provides us with a configuration that works
|
||||
// or requests to exit
|
||||
for (int ii = 0; ii < 100; ii++) {
|
||||
String repodir = RDJPrefs.getRepositoryDirectory();
|
||||
if (StringUtil.blank(repodir) || ii > 0) {
|
||||
// display the initial configuration wizard if we are not
|
||||
// yet properly configured
|
||||
RDJPrefsPanel.display(true);
|
||||
}
|
||||
|
||||
// create an interface to the database repository
|
||||
try {
|
||||
StaticConnectionProvider scp =
|
||||
new StaticConnectionProvider(RDJPrefs.getJDBCConfig());
|
||||
repository = new Repository(scp);
|
||||
model = new Model(repository);
|
||||
|
||||
} catch (PersistenceException pe) {
|
||||
String errmsg = "Unable to communicate with database:";
|
||||
if (ErrorUtil.reportError(errmsg, pe)) {
|
||||
System.exit(-1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!connectToServer()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void invokeLater (Runnable run)
|
||||
{
|
||||
SwingUtilities.invokeLater(run);
|
||||
}
|
||||
|
||||
protected void didInit ()
|
||||
{
|
||||
super.didInit();
|
||||
displayInterface();
|
||||
}
|
||||
|
||||
protected void displayInterface ()
|
||||
{
|
||||
if (frame == null) {
|
||||
// create our primary user interface frame, center the frame
|
||||
// in the screen and show it
|
||||
frame = new ChooserFrame();
|
||||
frame.setSize(660, frame.getToolkit().getScreenSize().height - 60);
|
||||
SwingUtil.centerWindow(frame);
|
||||
frame.setVisible(true);
|
||||
} else {
|
||||
frame.toFront();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
Chooser chooser = new Chooser();
|
||||
chooser.init();
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
//
|
||||
// $Id: ChooserController.java,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
import com.samskivert.swing.Controller;
|
||||
|
||||
import robodj.Log;
|
||||
|
||||
/**
|
||||
* Handles top-level chooser UI commands.
|
||||
*/
|
||||
public class ChooserController extends Controller
|
||||
{
|
||||
public ChooserController ()
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean handleAction (ActionEvent event)
|
||||
{
|
||||
String cmd = event.getActionCommand();
|
||||
if (cmd.equals("exit")) {
|
||||
Chooser.exit();
|
||||
|
||||
} else if (cmd.equals("skip")) {
|
||||
Chooser.djobj.skip();
|
||||
|
||||
} else if (cmd.equals("back")) {
|
||||
Chooser.djobj.back();
|
||||
|
||||
} else if (cmd.equals("pause")) {
|
||||
Chooser.djobj.pause();
|
||||
|
||||
} else if (cmd.equals("play")) {
|
||||
Chooser.djobj.play();
|
||||
|
||||
} else if (cmd.equals("stop")) {
|
||||
Chooser.djobj.stop();
|
||||
|
||||
} else {
|
||||
Log.warning("Unknown action event: " + cmd);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
//
|
||||
// $Id: ChooserFrame.java,v 1.13 2004/01/26 16:35:27 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import com.samskivert.swing.*;
|
||||
import com.samskivert.swing.util.*;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.presents.dobj.AttributeChangeListener;
|
||||
import com.threerings.presents.dobj.AttributeChangedEvent;
|
||||
|
||||
import robodj.Log;
|
||||
import robodj.Version;
|
||||
import robodj.repository.*;
|
||||
import robodj.util.ButtonUtil;
|
||||
import robodj.util.FancyPanel;
|
||||
import robodj.util.RDJPrefs;
|
||||
|
||||
public class ChooserFrame extends JFrame
|
||||
implements ControllerProvider, AttributeChangeListener
|
||||
{
|
||||
public ChooserFrame ()
|
||||
{
|
||||
super("RoboDJ Chooser " + Version.RELEASE_VERSION);
|
||||
|
||||
// quit gracefully if we're closed
|
||||
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
|
||||
addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing (WindowEvent e) {
|
||||
Chooser.exit();
|
||||
}
|
||||
});
|
||||
|
||||
// we create a top-level panel to manage everything
|
||||
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
JPanel top = new FancyPanel(gl, ButtonUtil.getImage(BACKGROUND_PATH));
|
||||
top.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
|
||||
// the top of the UI is the browser and the playlist manager
|
||||
JTabbedPane tpane = new JTabbedPane();
|
||||
tpane.setBackground(BGCOLOR);
|
||||
PlaylistPanel ppanel = new PlaylistPanel();
|
||||
String tip = "View and manipulate the playlist.";
|
||||
tpane.addTab("Playlist", null, ppanel, tip);
|
||||
|
||||
BrowsePanel bpanel = new BrowsePanel();
|
||||
tip = "Browse and select tunes to play.";
|
||||
tpane.addTab("Browse", null, bpanel, tip);
|
||||
|
||||
SearchPanel spanel = new SearchPanel();
|
||||
tip = "Search titles and artists.";
|
||||
tpane.addTab("Search", null, spanel, tip);
|
||||
top.add(tpane);
|
||||
|
||||
// the bottom is the control bar
|
||||
GroupLayout bgl = new HGroupLayout(GroupLayout.NONE);
|
||||
bgl.setJustification(GroupLayout.RIGHT);
|
||||
JPanel cbar = new JPanel(bgl);
|
||||
|
||||
// create a label and text field where the chooser user can
|
||||
// identify themselves
|
||||
cbar.add(new JLabel("Initials:"));
|
||||
cbar.add(_userField = new JTextField());
|
||||
Dimension ups = _userField.getPreferredSize();
|
||||
ups.width = 35;
|
||||
_userField.setPreferredSize(ups);
|
||||
cbar.add(new Spacer(100, 10));
|
||||
|
||||
// enforce a maximum of 3 letters in initials
|
||||
SwingUtil.setDocumentHelpers(
|
||||
_userField, new SwingUtil.DocumentValidator() {
|
||||
public boolean isValid (String text) {
|
||||
return (text.length() <= 3);
|
||||
}
|
||||
}, null);
|
||||
|
||||
// display any previously configured initials
|
||||
_userField.setText(RDJPrefs.getUser());
|
||||
|
||||
// add some fake control buttons for now
|
||||
cbar.add(ButtonUtil.createControlButton(
|
||||
BACK_TIP, "back", BACK_ICON_PATH, true));
|
||||
_stop = ButtonUtil.createControlButton(
|
||||
STOP_TIP, "stop", STOP_ICON_PATH, true);
|
||||
cbar.add(_stop);
|
||||
_pauseIcon = ButtonUtil.getIcon(PAUSE_ICON_PATH);
|
||||
_playIcon = ButtonUtil.getIcon(PLAY_ICON_PATH);
|
||||
_pause = ButtonUtil.createControlButton(
|
||||
"", "pause", _pauseIcon, true);
|
||||
cbar.add(_pause);
|
||||
cbar.add(ButtonUtil.createControlButton(
|
||||
SKIP_TIP, "skip", SKIP_ICON_PATH, true));
|
||||
cbar.add(new Spacer(50, 10));
|
||||
cbar.add(ButtonUtil.createControlButton(
|
||||
EXIT_TIP, "exit", EXIT_ICON_PATH, true));
|
||||
|
||||
// stick it into the frame
|
||||
top.add(cbar, GroupLayout.FIXED);
|
||||
|
||||
// now add our top-level panel (we'd not use this if we could set
|
||||
// a border on the content pane returned by the frame... alas)
|
||||
getContentPane().add(top, BorderLayout.CENTER);
|
||||
SwingUtil.applyToHierarchy(top, new SwingUtil.ComponentOp() {
|
||||
public void apply (Component comp) {
|
||||
if (comp instanceof JPanel || comp instanceof JButton) {
|
||||
((JComponent) comp).setOpaque(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// listen for pause and stop and whatnot
|
||||
Chooser.djobj.addListener(this);
|
||||
updateButtons();
|
||||
|
||||
// create our controller
|
||||
_controller = new ChooserController();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public Controller getController ()
|
||||
{
|
||||
return _controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string currently entered into the "user" field.
|
||||
*/
|
||||
public String getUser (boolean showError)
|
||||
{
|
||||
String user = _userField.getText();
|
||||
// if they haven't supplied a user, complain
|
||||
if (StringUtil.blank(user)) {
|
||||
if (!showError) {
|
||||
return null;
|
||||
}
|
||||
String errmsg = "The feature you have requested requires " +
|
||||
"that you identify yourself by entering your initials in " +
|
||||
"the 'Initials' box at the bottom of the window.";
|
||||
JOptionPane.showMessageDialog(this, errmsg, "Initials required",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return null;
|
||||
}
|
||||
|
||||
// if they typed in mixed or upper case, lower it for them
|
||||
user = user.toLowerCase();
|
||||
if (!user.equals(_userField.getText())) {
|
||||
_userField.setText(user);
|
||||
}
|
||||
|
||||
// make sure we've saved these initials values
|
||||
RDJPrefs.config.setValue(RDJPrefs.USER_KEY, user);
|
||||
return user;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void attributeChanged (AttributeChangedEvent event)
|
||||
{
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
protected void updateButtons ()
|
||||
{
|
||||
if (Chooser.djobj.playing == -1 || Chooser.djobj.paused) {
|
||||
_pause.setToolTipText(PLAY_TIP);
|
||||
_pause.setActionCommand("play");
|
||||
_pause.setIcon(_playIcon);
|
||||
|
||||
} else {
|
||||
_pause.setToolTipText(PAUSE_TIP);
|
||||
_pause.setActionCommand("pause");
|
||||
_pause.setIcon(_pauseIcon);
|
||||
}
|
||||
|
||||
_stop.setEnabled(Chooser.djobj.playing != -1);
|
||||
_pause.setEnabled(Chooser.djobj.playlist != null);
|
||||
}
|
||||
|
||||
/** Our top-level controller. */
|
||||
protected Controller _controller;
|
||||
|
||||
/** A field where the user can identify themselves. */
|
||||
protected JTextField _userField;
|
||||
|
||||
/** A reference to the play/pause button. */
|
||||
protected JButton _pause;
|
||||
|
||||
/** A reference to the stop button. */
|
||||
protected JButton _stop;
|
||||
|
||||
// used when toggling between play and pause
|
||||
protected ImageIcon _playIcon;
|
||||
protected ImageIcon _pauseIcon;
|
||||
|
||||
// icon paths
|
||||
protected static final String ICON_ROOT = "/robodj/chooser/images/";
|
||||
protected static final String PLAY_ICON_PATH = ICON_ROOT + "play.png";
|
||||
protected static final String PAUSE_ICON_PATH = ICON_ROOT + "pause.png";
|
||||
protected static final String STOP_ICON_PATH = ICON_ROOT + "stop.png";
|
||||
protected static final String SKIP_ICON_PATH = ICON_ROOT + "skip.png";
|
||||
protected static final String BACK_ICON_PATH = ICON_ROOT + "back.png";
|
||||
protected static final String EXIT_ICON_PATH = ICON_ROOT + "exit.png";
|
||||
|
||||
protected static final String BACKGROUND_PATH =
|
||||
ICON_ROOT + "background.png";
|
||||
|
||||
// button tips
|
||||
protected static final String PLAY_TIP = "Play";
|
||||
protected static final String PAUSE_TIP =
|
||||
"Pause the currently playing song";
|
||||
protected static final String STOP_TIP = "Stop the currently playing song";
|
||||
protected static final String SKIP_TIP = "Skip to the next song";
|
||||
protected static final String BACK_TIP = "Skip to the previous song";
|
||||
protected static final String EXIT_TIP = "Exit";
|
||||
|
||||
// our common background color
|
||||
protected static final Color BGCOLOR = new Color(0x9999CC);
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.ClientObserver;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.ObjectAccessException;
|
||||
import com.threerings.presents.dobj.Subscriber;
|
||||
import com.threerings.presents.net.UsernamePasswordCreds;
|
||||
|
||||
import robodj.client.DJService;
|
||||
import robodj.data.DJObject;
|
||||
import robodj.util.ErrorUtil;
|
||||
import robodj.util.RDJPrefs;
|
||||
import robodj.util.RDJPrefsPanel;
|
||||
|
||||
/**
|
||||
* Handles the common client business for RoboDJ.
|
||||
*/
|
||||
public abstract class DJClient
|
||||
implements Client.Invoker, ClientObserver, Subscriber
|
||||
{
|
||||
public static Client client;
|
||||
|
||||
public static DJObject djobj;
|
||||
|
||||
/**
|
||||
* Creates our {@link Client} instance and connects to the RoboDJ
|
||||
* server.
|
||||
*
|
||||
* @return true if the client was created successfully and the
|
||||
* connection process was initiated, false if it was not due to
|
||||
* missing or bogus configuration.
|
||||
*/
|
||||
public boolean connectToServer ()
|
||||
{
|
||||
try {
|
||||
String host = RDJPrefs.getMusicDaemonHost();
|
||||
if (StringUtil.blank(host)) {
|
||||
throw new IOException(
|
||||
"No music server host specified in configuration.");
|
||||
}
|
||||
// establish a connection with the music server
|
||||
Name name = new Name(RDJPrefs.getUser());
|
||||
client = new Client(new UsernamePasswordCreds(name, ""), this);
|
||||
client.addClientObserver(this);
|
||||
client.setServer(host, Client.DEFAULT_SERVER_PORT);
|
||||
client.logon();
|
||||
return true;
|
||||
|
||||
} catch (IOException ioe) {
|
||||
String errmsg = "Unable to communicate with music " +
|
||||
"server on host '" + RDJPrefs.getMusicDaemonHost() + "' ";
|
||||
if (ErrorUtil.reportError(errmsg, ioe)) {
|
||||
System.exit(-1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void clientDidLogon (Client lclient)
|
||||
{
|
||||
// subscribe to our DJObject
|
||||
DJService djsvc = (DJService)client.requireService(DJService.class);
|
||||
djsvc.getDJOid(client, new DJService.ResultListener() {
|
||||
public void requestProcessed (Object result) {
|
||||
int djoid = ((Integer)result).intValue();
|
||||
client.getDObjectManager().subscribeToObject(
|
||||
djoid, DJClient.this);
|
||||
}
|
||||
public void requestFailed (String cause) {
|
||||
ErrorUtil.reportError("Failed to fetch DJ oid: " + cause, null);
|
||||
System.exit(-1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void clientObjectDidChange (Client client)
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void clientDidLogoff (Client client)
|
||||
{
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void clientFailedToLogon (Client lclient, Exception cause)
|
||||
{
|
||||
if (ErrorUtil.reportError("Failed to connect to server", cause)) {
|
||||
System.exit(-1);
|
||||
} else {
|
||||
Thread t = new Thread() {
|
||||
public void run () {
|
||||
RDJPrefsPanel.display(true);
|
||||
client.setServer(RDJPrefs.getMusicDaemonHost(),
|
||||
Client.DEFAULT_SERVER_PORT);
|
||||
client.logon();
|
||||
}
|
||||
};
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void clientConnectionFailed (Client client, Exception cause)
|
||||
{
|
||||
ErrorUtil.reportError("Lost connection to server", cause);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean clientWillLogoff (Client client)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void objectAvailable (DObject object)
|
||||
{
|
||||
djobj = (DJObject)object;
|
||||
didInit();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void requestFailed (int oid, ObjectAccessException cause)
|
||||
{
|
||||
ErrorUtil.reportError("Failed to fetch DJ object", cause);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we have successfully connected to the server and
|
||||
* subscribed to our {@link DJObject}.
|
||||
*/
|
||||
protected void didInit ()
|
||||
{
|
||||
}
|
||||
|
||||
public static void exit ()
|
||||
{
|
||||
client.logoff(false);
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
//
|
||||
// $Id: EditDialog.java,v 1.3 2001/09/21 03:09:01 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import javax.swing.*;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.swing.*;
|
||||
import com.samskivert.swing.util.*;
|
||||
|
||||
import robodj.Log;
|
||||
import robodj.repository.Entry;
|
||||
import robodj.repository.EntryEditor;
|
||||
|
||||
public class EditDialog
|
||||
extends JDialog
|
||||
implements TaskObserver, ActionListener
|
||||
{
|
||||
public EditDialog (Entry entry)
|
||||
{
|
||||
setTitle("Edit " + entry.title);
|
||||
|
||||
// keep this around for later
|
||||
_entry = entry;
|
||||
|
||||
// we create a top-level panel to manage everything
|
||||
JPanel top = new JPanel();
|
||||
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
top.setLayout(gl);
|
||||
|
||||
// give ourselves a wee bit of a border
|
||||
top.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
|
||||
// create our entry editor
|
||||
_editor = new EntryEditor(Chooser.model, entry);
|
||||
top.add(_editor);
|
||||
|
||||
// create some control buttons
|
||||
gl = new HGroupLayout(GroupLayout.NONE);
|
||||
gl.setJustification(GroupLayout.RIGHT);
|
||||
JPanel btnPanel = new JPanel(gl);
|
||||
btnPanel.add(createControlButton("Update", "update"));
|
||||
btnPanel.add(createControlButton("Revert", "revert"));
|
||||
btnPanel.add(createControlButton("Cancel", "cancel"));
|
||||
top.add(btnPanel, GroupLayout.FIXED);
|
||||
|
||||
getContentPane().add(top, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public void actionPerformed (ActionEvent e)
|
||||
{
|
||||
String cmd = e.getActionCommand();
|
||||
if (cmd.equals("update")) {
|
||||
// flush the edits to the entry
|
||||
_editor.applyToEntry();
|
||||
// do the update in a separate task
|
||||
TaskMaster.invokeMethodTask("updateEntry", this, this);
|
||||
|
||||
} else if (cmd.equals("revert")) {
|
||||
_editor.reset();
|
||||
|
||||
} else if (cmd.equals("cancel")) {
|
||||
// get the hell out of dodge
|
||||
dispose();
|
||||
|
||||
} else {
|
||||
System.out.println("Unknown action event: " + cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the entry in the repository.
|
||||
*/
|
||||
public void updateEntry ()
|
||||
throws PersistenceException
|
||||
{
|
||||
Chooser.model.updateEntry(_entry);
|
||||
}
|
||||
|
||||
public void taskCompleted (String name, Object result)
|
||||
{
|
||||
if (name.equals("updateEntry")) {
|
||||
// we're done updating, we can go away now
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void taskFailed (String name, Throwable exception)
|
||||
{
|
||||
String msg;
|
||||
if (Exception.class.equals(exception.getClass())) {
|
||||
msg = exception.getMessage();
|
||||
} else {
|
||||
msg = exception.toString();
|
||||
}
|
||||
JOptionPane.showMessageDialog(this, msg, "Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
Log.logStackTrace(exception);
|
||||
}
|
||||
|
||||
protected JButton createControlButton (String label, String action)
|
||||
{
|
||||
JButton btn = new JButton(label);
|
||||
btn.setActionCommand(action);
|
||||
btn.addActionListener(this);
|
||||
return btn;
|
||||
}
|
||||
|
||||
protected Entry _entry;
|
||||
protected EntryEditor _editor;
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
//
|
||||
// $Id: EntryController.java,v 1.3 2004/01/26 16:10:55 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import javax.swing.event.AncestorEvent;
|
||||
import javax.swing.event.AncestorListener;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.swing.util.SwingUtil;
|
||||
import com.samskivert.swing.util.TaskMaster;
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
|
||||
import robodj.Log;
|
||||
import robodj.data.PlaylistEntry;
|
||||
import robodj.repository.Entry;
|
||||
import robodj.repository.Song;
|
||||
|
||||
/**
|
||||
* Handles default entry list behavior.
|
||||
*/
|
||||
public abstract class EntryController extends ItemController
|
||||
implements AncestorListener
|
||||
{
|
||||
public EntryController (EntryList list)
|
||||
{
|
||||
super(list);
|
||||
_list = list;
|
||||
_list.addAncestorListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the entries from the database.
|
||||
*/
|
||||
public void readSongs ()
|
||||
throws PersistenceException
|
||||
{
|
||||
Chooser.repository.populateSongs(_entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the entries from the database and plays them all.
|
||||
*/
|
||||
public void readAndPlay ()
|
||||
throws PersistenceException
|
||||
{
|
||||
Chooser.repository.populateSongs(_entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the category for the displayed entry.
|
||||
*/
|
||||
public void recategorizeEntry ()
|
||||
throws PersistenceException
|
||||
{
|
||||
Chooser.model.recategorize(_entry, _newcatid);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void taskCompleted (String name, Object result)
|
||||
{
|
||||
if (name.equals("readEntries")) {
|
||||
// the result should be an array of entry objects with which
|
||||
// we can populate our button list
|
||||
_entries = (Entry[])result;
|
||||
_list.populateEntries(_entries);
|
||||
|
||||
} else if (name.equals("readAndPlay")) {
|
||||
appendToPlaylist(_entry.songs);
|
||||
|
||||
} else if (name.equals("readSongs")) {
|
||||
_list.populateSong(_entry);
|
||||
|
||||
} else {
|
||||
super.taskCompleted(name, result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the entries from the database. This will be called on a
|
||||
* separate thread and should be prepared to be called repeatedly
|
||||
* without undue overhead. When the user browses into a song and back
|
||||
* up again, this method is called to repopulate the entries.
|
||||
*/
|
||||
public abstract Entry[] readEntries ()
|
||||
throws PersistenceException;
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean handleAction (ActionEvent e)
|
||||
{
|
||||
String cmd = e.getActionCommand();
|
||||
if (cmd.equals(EntryItem.BROWSE)) {
|
||||
_entry = EntryItem.getEntry(e.getSource());
|
||||
// start up the task that reads this CDs songs from the database
|
||||
TaskMaster.invokeMethodTask("readSongs", this, this);
|
||||
|
||||
} else if (cmd.equals(EntryItem.PLAY)) {
|
||||
_entry = EntryItem.getEntry(e.getSource());
|
||||
// start up the task that reads this CDs songs from the database
|
||||
TaskMaster.invokeMethodTask("readAndPlay", this, this);
|
||||
|
||||
} else if (cmd.equals(SongItem.PLAY)) {
|
||||
Song song = SongItem.getSong(e.getSource());
|
||||
appendToPlaylist(new Song[] { song });
|
||||
|
||||
} else if (cmd.equals("refresh")) {
|
||||
// re-read the category
|
||||
TaskMaster.invokeMethodTask("readEntries", this, this);
|
||||
|
||||
} else if (cmd.equals("edit")) {
|
||||
EditDialog dialog = new EditDialog(_entry);
|
||||
dialog.setSize(400, 400);
|
||||
SwingUtil.centerWindow(dialog);
|
||||
dialog.show();
|
||||
|
||||
} else if (cmd.equals("categorize")) {
|
||||
JComboBox cb = (JComboBox)e.getSource();
|
||||
String catname = (String)cb.getSelectedItem();
|
||||
_newcatid = Chooser.model.getCategoryId(catname);
|
||||
// do the recategorization in a separate task
|
||||
TaskMaster.invokeMethodTask("recategorizeEntry", this, this);
|
||||
|
||||
} else {
|
||||
return super.handleAction(e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void ancestorAdded (AncestorEvent e)
|
||||
{
|
||||
// stick a "loading" label in the list to let the user know
|
||||
// what's up
|
||||
_list._bpanel.add(new JLabel("Loading..."));
|
||||
|
||||
// we need to revalidate the component because we added a child
|
||||
_list.revalidate();
|
||||
_list.repaint();
|
||||
|
||||
// start up the task that reads the CD info from the database
|
||||
TaskMaster.invokeMethodTask("readEntries", this, this);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void ancestorRemoved (AncestorEvent e)
|
||||
{
|
||||
// clear out our entry ui elements
|
||||
_list._bpanel.removeAll();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void ancestorMoved (AncestorEvent e)
|
||||
{
|
||||
// nothing doing
|
||||
}
|
||||
|
||||
protected void appendToPlaylist (Song[] songs)
|
||||
{
|
||||
PlaylistEntry[] plist = Chooser.djobj.playlist;
|
||||
if (plist == null) {
|
||||
plist = new PlaylistEntry[0];
|
||||
}
|
||||
for (int i = 0; i < songs.length; i++) {
|
||||
if (songs[i].isHated()) {
|
||||
continue;
|
||||
}
|
||||
PlaylistEntry entry = new PlaylistEntry(
|
||||
songs[i].entryid, songs[i].songid, songs[i].title);
|
||||
plist = (PlaylistEntry[])ArrayUtil.append(plist, entry);
|
||||
}
|
||||
Chooser.djobj.setPlaylist(plist);
|
||||
}
|
||||
|
||||
protected EntryList _list;
|
||||
protected Entry[] _entries;
|
||||
protected Entry _entry;
|
||||
protected int _newcatid;
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
//
|
||||
// $Id: EntryItem.java,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import com.samskivert.swing.HGroupLayout;
|
||||
|
||||
import robodj.repository.Entry;
|
||||
import robodj.util.ButtonUtil;
|
||||
|
||||
/**
|
||||
* Displays an entry in the browser.
|
||||
*/
|
||||
public class EntryItem extends Item
|
||||
{
|
||||
public static final String BROWSE = "entry:browse";
|
||||
|
||||
public static final String PLAY = "entry:play";
|
||||
|
||||
public EntryItem (Entry entry)
|
||||
{
|
||||
_entry = entry;
|
||||
|
||||
// set up our layout manager
|
||||
HGroupLayout gl = new HGroupLayout(HGroupLayout.NONE);
|
||||
gl.setOffAxisPolicy(HGroupLayout.STRETCH);
|
||||
gl.setJustification(HGroupLayout.LEFT);
|
||||
setLayout(gl);
|
||||
|
||||
// create a browse and a play button
|
||||
JButton button;
|
||||
|
||||
// add a browse button
|
||||
button = ButtonUtil.createControlButton(
|
||||
BROWSE_ENTRY_TIP, BROWSE, _browseIcon, true);
|
||||
button.putClientProperty(ENTRY_PROP, entry);
|
||||
add(button, HGroupLayout.FIXED);
|
||||
|
||||
// add a play all button
|
||||
button = ButtonUtil.createControlButton(
|
||||
PLAY_ENTRY_TIP, PLAY, _playIcon, true);
|
||||
button.putClientProperty(ENTRY_PROP, entry);
|
||||
add(button, HGroupLayout.FIXED);
|
||||
|
||||
// add the entry title
|
||||
JLabel entryLabel = new JLabel(entry.title);
|
||||
entryLabel.setFont(_nameFont);
|
||||
entryLabel.setToolTipText(entry.title + " (" + entry.entryid + ")");
|
||||
add(entryLabel);
|
||||
}
|
||||
|
||||
public static Entry getEntry (Object source)
|
||||
{
|
||||
return (Entry)((JButton)source).getClientProperty(ENTRY_PROP);
|
||||
}
|
||||
|
||||
protected Entry _entry;
|
||||
|
||||
protected static final String ENTRY_PROP = "entry";
|
||||
|
||||
protected static final String BROWSE_ENTRY_TIP =
|
||||
"Browse the songs in this album";
|
||||
protected static final String PLAY_ENTRY_TIP =
|
||||
"Append this album to the playlist";
|
||||
|
||||
protected static ImageIcon _browseIcon =
|
||||
ButtonUtil.getIcon(ICON_ROOT + "browse.png");
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
//
|
||||
// $Id: EntryList.java,v 1.15 2004/01/26 16:33:40 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.Font;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.swing.*;
|
||||
import com.samskivert.swing.util.*;
|
||||
|
||||
import robodj.Log;
|
||||
import robodj.repository.*;
|
||||
import robodj.util.ButtonUtil;
|
||||
|
||||
public abstract class EntryList extends JSplitPane
|
||||
implements ControllerProvider
|
||||
{
|
||||
public EntryList ()
|
||||
{
|
||||
super(JSplitPane.VERTICAL_SPLIT);
|
||||
|
||||
setLeftComponent(new JScrollPane(_bpanel = createPanel()));
|
||||
setRightComponent(new JScrollPane(_epanel = createPanel()));
|
||||
|
||||
// use a special font for our name buttons
|
||||
_titleFont = new Font("Helvetica", Font.BOLD, 14);
|
||||
|
||||
// create our controller
|
||||
_controller = createController();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void doLayout ()
|
||||
{
|
||||
super.doLayout();
|
||||
|
||||
// we only want to do this once so as not to mess with subsequent
|
||||
// adjustments by the user
|
||||
if (!_adjusted) {
|
||||
setDividerLocation(0.5f);
|
||||
_adjusted = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a panel configured for displaying entries or songs.
|
||||
*/
|
||||
protected JPanel createPanel ()
|
||||
{
|
||||
GroupLayout gl = new VGroupLayout(GroupLayout.NONE);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
gl.setJustification(GroupLayout.TOP);
|
||||
gl.setGap(2);
|
||||
JPanel panel = new ScrollablePanel() {
|
||||
// make the playlist fit the width of the scrolling viewport
|
||||
public boolean getScrollableTracksViewportWidth () {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
panel.setLayout(gl);
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
return panel;
|
||||
}
|
||||
|
||||
public Controller getController ()
|
||||
{
|
||||
return _controller;
|
||||
}
|
||||
|
||||
/** Creates the controller for this entry list. */
|
||||
protected abstract Controller createController ();
|
||||
|
||||
/** The string to display when there are no matching results. */
|
||||
protected abstract String getEmptyString ();
|
||||
|
||||
/**
|
||||
* Creates the proper buttons, etc. for each entry.
|
||||
*/
|
||||
protected void populateEntries (Entry[] entries)
|
||||
{
|
||||
// clear out any existing children
|
||||
_bpanel.removeAll();
|
||||
|
||||
// sort our entries
|
||||
Comparator ecomp = new Comparator() {
|
||||
public int compare (Object o1, Object o2) {
|
||||
Entry e1 = (Entry)o1, e2 = (Entry)o2;
|
||||
int rv = e1.artist.compareTo(e2.artist);
|
||||
return rv == 0 ? e1.title.compareTo(e2.title) : rv;
|
||||
}
|
||||
public boolean equals (Object o1) {
|
||||
return o1.equals(this);
|
||||
}
|
||||
};
|
||||
Arrays.sort(entries, ecomp);
|
||||
|
||||
// and add buttons for every entry
|
||||
String artist = null;
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
if (!entries[i].artist.equals(artist)) {
|
||||
artist = entries[i].artist;
|
||||
JLabel label = new JLabel(entries[i].artist);
|
||||
_bpanel.add(label);
|
||||
}
|
||||
|
||||
// create an entry item for this entry
|
||||
_bpanel.add(new EntryItem(entries[i]));
|
||||
}
|
||||
|
||||
// if there were no entries, stick a label in to that effect
|
||||
if (entries.length == 0) {
|
||||
_bpanel.add(new JLabel(getEmptyString()));
|
||||
}
|
||||
|
||||
// reset our scroll position
|
||||
clearScrollPosition((JScrollPane)getLeftComponent());
|
||||
|
||||
// we've removed and added components so we need to revalidate
|
||||
SwingUtil.refresh(_bpanel);
|
||||
}
|
||||
|
||||
protected void populateSong (Entry entry)
|
||||
{
|
||||
// clear out any existing children
|
||||
_epanel.removeAll();
|
||||
|
||||
GroupLayout gl = new HGroupLayout(HGroupLayout.STRETCH);
|
||||
gl.setJustification(GroupLayout.LEFT);
|
||||
JPanel header = new JPanel(gl);
|
||||
|
||||
// add the album title
|
||||
JLabel label = new JLabel(entry.title);
|
||||
label.setToolTipText(entry.title);
|
||||
label.setFont(_titleFont);
|
||||
header.add(label);
|
||||
|
||||
// create an edit button
|
||||
JButton ebtn = ButtonUtil.createControlButton(
|
||||
EDIT_TIP, "edit", ButtonUtil.getIcon(EDIT_ICON_PATH), true);
|
||||
header.add(ebtn, GroupLayout.FIXED);
|
||||
|
||||
// add a combo box for categorizing
|
||||
JComboBox catcombo =
|
||||
new JComboBox(ModelUtil.catBoxNames(Chooser.model));
|
||||
header.add(catcombo, GroupLayout.FIXED);
|
||||
|
||||
// configure the combo box
|
||||
catcombo.addActionListener(Controller.DISPATCHER);
|
||||
catcombo.setActionCommand("categorize");
|
||||
int catid = Chooser.model.getCategory(entry.entryid);
|
||||
int catidx = ModelUtil.getCategoryIndex(Chooser.model, catid);
|
||||
catcombo.setSelectedIndex(catidx+1);
|
||||
|
||||
_epanel.add(header, GroupLayout.FIXED);
|
||||
|
||||
// sort the songs by position
|
||||
Comparator scomp = new Comparator() {
|
||||
public int compare (Object o1, Object o2) {
|
||||
Song s1 = (Song)o1, s2 = (Song)o2;
|
||||
return s1.position - s2.position;
|
||||
}
|
||||
public boolean equals (Object o1) {
|
||||
return o1.equals(this);
|
||||
}
|
||||
};
|
||||
Arrays.sort(entry.songs, scomp);
|
||||
|
||||
// and add buttons for every song
|
||||
for (int i = 0; i < entry.songs.length; i++) {
|
||||
_epanel.add(new SongItem(entry.songs[i], SongItem.BROWSER));
|
||||
}
|
||||
|
||||
// we've removed and added components so we need to revalidate
|
||||
SwingUtil.refresh(_epanel);
|
||||
}
|
||||
|
||||
protected void clearScrollPosition (JScrollPane pane)
|
||||
{
|
||||
BoundedRangeModel model = pane.getVerticalScrollBar().getModel();
|
||||
model.setValue(model.getMinimum());
|
||||
}
|
||||
|
||||
protected Controller _controller;
|
||||
protected JPanel _bpanel;
|
||||
protected JPanel _epanel;
|
||||
|
||||
protected boolean _adjusted;
|
||||
protected Font _titleFont;
|
||||
|
||||
protected static final String UP_TIP =
|
||||
"Back up to albums listing";
|
||||
protected static final String EDIT_TIP =
|
||||
"Edit the album information";
|
||||
|
||||
protected static final String ICON_ROOT = "/robodj/chooser/images/";
|
||||
protected static final String UP_ICON_PATH = ICON_ROOT + "up.png";
|
||||
protected static final String EDIT_ICON_PATH = ICON_ROOT + "edit.png";
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
//
|
||||
// $Id: Item.java,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Graphics;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import com.samskivert.swing.util.SwingUtil;
|
||||
|
||||
import robodj.util.ButtonUtil;
|
||||
|
||||
/**
|
||||
* The base class for items displayed in an entry list or playlist.
|
||||
*/
|
||||
public class Item extends JPanel
|
||||
{
|
||||
// documentation inherited
|
||||
protected void paintChildren (Graphics g)
|
||||
{
|
||||
Graphics2D gfx = (Graphics2D)g;
|
||||
Object key = SwingUtil.activateAntiAliasing(gfx);
|
||||
super.paintChildren(g);
|
||||
SwingUtil.restoreAntiAliasing(gfx, key);
|
||||
}
|
||||
|
||||
protected static Font _nameFont = new Font("Dialog", Font.PLAIN, 12);
|
||||
protected static Font _hasVotesFont =
|
||||
new Font("Dialog", Font.ITALIC, 12);
|
||||
|
||||
protected static final String ICON_ROOT = "/robodj/chooser/images/";
|
||||
|
||||
protected static ImageIcon _playIcon =
|
||||
ButtonUtil.getIcon(ICON_ROOT + "play.png");
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
//
|
||||
// $Id: ItemController.java,v 1.2 2003/10/10 21:31:57 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.swing.Controller;
|
||||
import com.samskivert.swing.util.TaskMaster;
|
||||
import com.samskivert.swing.util.TaskObserver;
|
||||
|
||||
import robodj.Log;
|
||||
import robodj.repository.Song;
|
||||
import robodj.util.ErrorUtil;
|
||||
|
||||
/**
|
||||
* Handles standard commands when displaying lists of items.
|
||||
*/
|
||||
public abstract class ItemController extends Controller
|
||||
implements TaskObserver
|
||||
{
|
||||
public ItemController (JComponent panel)
|
||||
{
|
||||
_panel = panel;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean handleAction (ActionEvent e)
|
||||
{
|
||||
String cmd = e.getActionCommand();
|
||||
if (cmd.equals(SongItem.TOGGLE_VOTE)) {
|
||||
JButton button = (JButton)e.getSource();
|
||||
SongItem item = (SongItem)button.getParent();
|
||||
_song = item.getSong();
|
||||
|
||||
String who = Chooser.frame.getUser(true);
|
||||
switch (_song.hasVoted(who)) {
|
||||
case -1: _song.addVote(who, true); break;
|
||||
case 1: _song.clearVote(who); break;
|
||||
default:
|
||||
case 0: _song.addVote(who, false); break;
|
||||
}
|
||||
item.update();
|
||||
TaskMaster.invokeMethodTask("updateSong", this, this);
|
||||
|
||||
} else {
|
||||
Log.warning("Unknown action event: " + cmd);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the song referenced by {@link #_song} in the repository.
|
||||
*/
|
||||
public void updateSong ()
|
||||
{
|
||||
if (_song != null) {
|
||||
try {
|
||||
Chooser.repository.updateSong(_song);
|
||||
} catch (PersistenceException pe) {
|
||||
ErrorUtil.reportError(
|
||||
"Failure updating song '" + _song + "'", pe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void taskCompleted (String name, Object result)
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void taskFailed (String name, Throwable exception)
|
||||
{
|
||||
String msg;
|
||||
if (Exception.class.equals(exception.getClass())) {
|
||||
msg = exception.getMessage();
|
||||
} else {
|
||||
msg = exception.toString();
|
||||
}
|
||||
JOptionPane.showMessageDialog(_panel, msg, "Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
Log.logStackTrace(exception);
|
||||
}
|
||||
|
||||
protected JComponent _panel;
|
||||
protected Song _song;
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
//
|
||||
// $Id: PlaylistController.java,v 1.3 2004/02/24 12:40:24 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.samskivert.swing.util.SwingUtil;
|
||||
import com.samskivert.swing.util.TaskAdapter;
|
||||
import com.samskivert.swing.util.TaskMaster;
|
||||
|
||||
import com.threerings.presents.dobj.AttributeChangeListener;
|
||||
import com.threerings.presents.dobj.AttributeChangedEvent;
|
||||
|
||||
import robodj.Log;
|
||||
import robodj.data.DJObject;
|
||||
import robodj.data.PlaylistEntry;
|
||||
import robodj.repository.Entry;
|
||||
import robodj.repository.Song;
|
||||
|
||||
/**
|
||||
* Handles playlist UI commands.
|
||||
*/
|
||||
public class PlaylistController extends ItemController
|
||||
implements AttributeChangeListener
|
||||
{
|
||||
public PlaylistController (PlaylistPanel panel)
|
||||
{
|
||||
super(panel);
|
||||
_panel = panel;
|
||||
|
||||
// listen to the DJ object
|
||||
Chooser.djobj.addListener(this);
|
||||
}
|
||||
|
||||
public void wasAdded ()
|
||||
{
|
||||
super.wasAdded();
|
||||
|
||||
// start up the task that reads the CD info from the database
|
||||
TaskMaster.invokeMethodTask("readPlaylist", this, this);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean handleAction (ActionEvent e)
|
||||
{
|
||||
String cmd = e.getActionCommand();
|
||||
if (cmd.equals(PlaylistPanel.CLEAR)) {
|
||||
Chooser.djobj.clear();
|
||||
|
||||
} else if (cmd.equals(SongItem.SKIP_TO)) {
|
||||
JButton button = (JButton)e.getSource();
|
||||
SongItem item = (SongItem)button.getParent();
|
||||
Chooser.djobj.play(((Integer)item.extra).intValue());
|
||||
|
||||
} else if (cmd.equals(SongItem.REMOVE)) {
|
||||
JButton button = (JButton)e.getSource();
|
||||
SongItem item = (SongItem)button.getParent();
|
||||
int sidx = ((Integer)item.extra).intValue();
|
||||
Chooser.djobj.remove(sidx, 1);
|
||||
|
||||
} else if (cmd.equals(PlaylistPanel.REMOVE_ALL)) {
|
||||
JButton src = (JButton)e.getSource();
|
||||
PlaylistPanel.PLE entry =
|
||||
(PlaylistPanel.PLE)src.getClientProperty("entry");
|
||||
|
||||
// remove all entries starting with this one until we get to
|
||||
// one that has a different entryid
|
||||
int start = -1, length = 1;
|
||||
for (int ii = 0; ii < _panel.plist.size(); ii++) {
|
||||
PlaylistPanel.PLE pe = (PlaylistPanel.PLE)_panel.plist.get(ii);
|
||||
if (entry == pe) {
|
||||
start = ii;
|
||||
} else if (start != -1) {
|
||||
if (pe.entry.entryid == entry.entry.entryid) {
|
||||
length++;
|
||||
} else {
|
||||
// we hit an entry that doesn't match, bail
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (start != -1) {
|
||||
Chooser.djobj.remove(start, length);
|
||||
}
|
||||
|
||||
} else if (cmd.equals(PlaylistPanel.SHUFFLE)) {
|
||||
// shuffle!
|
||||
Chooser.djobj.shuffle();
|
||||
|
||||
} else {
|
||||
return super.handleAction(e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void attributeChanged (AttributeChangedEvent event)
|
||||
{
|
||||
if (event.getName().equals(DJObject.PLAYLIST)) {
|
||||
TaskMaster.invokeMethodTask("readPlaylist", this, this);
|
||||
|
||||
} else if (event.getName().equals(DJObject.PLAYING)) {
|
||||
playingUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
public void readPlaylist ()
|
||||
throws PersistenceException
|
||||
{
|
||||
// clear out any previous playlist
|
||||
_panel.plist.clear();
|
||||
|
||||
int pcount = (Chooser.djobj.playlist == null) ? 0 :
|
||||
Chooser.djobj.playlist.length;
|
||||
for (int ii = 0; ii < pcount; ii++) {
|
||||
PlaylistEntry pentry = Chooser.djobj.playlist[ii];
|
||||
Entry entry = Chooser.model.getEntry(pentry.entryId);
|
||||
if (entry != null) {
|
||||
Song song = entry.getSong(pentry.songId);
|
||||
if (song != null) {
|
||||
_panel.plist.add(new PlaylistPanel.PLE(entry, song));
|
||||
} else {
|
||||
Log.warning("No song for entry? [pentry=" + pentry + "].");
|
||||
}
|
||||
} else {
|
||||
Log.warning("Unable to load entry? [pentry=" + pentry + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void playingUpdated ()
|
||||
{
|
||||
// unhighlight whoever is playing now
|
||||
if (_playing != null) {
|
||||
_playing.item.setIsPlaying(false);
|
||||
_playing = null;
|
||||
}
|
||||
|
||||
int pidx = Chooser.djobj.playing;
|
||||
if (pidx >= 0 && _panel.plist.size() > pidx) {
|
||||
_playing = (PlaylistPanel.PLE)_panel.plist.get(pidx);
|
||||
}
|
||||
|
||||
if (_playing != null) {
|
||||
_playing.item.setIsPlaying(true);
|
||||
}
|
||||
_panel.repaint();
|
||||
}
|
||||
|
||||
public void taskCompleted (String name, Object result)
|
||||
{
|
||||
if (name.equals("readPlaylist")) {
|
||||
_panel.populatePlaylist(_panel.plist);
|
||||
playingUpdated();
|
||||
} else {
|
||||
super.taskCompleted(name, result);
|
||||
}
|
||||
}
|
||||
|
||||
protected PlaylistPanel _panel;
|
||||
protected PlaylistPanel.PLE _playing;
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
//
|
||||
// $Id: PlaylistPanel.java,v 1.18 2004/02/24 12:40:24 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import javax.swing.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.swing.*;
|
||||
import com.samskivert.swing.util.*;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import robodj.Log;
|
||||
import robodj.repository.*;
|
||||
import robodj.util.ButtonUtil;
|
||||
|
||||
public class PlaylistPanel extends ControlledPanel
|
||||
{
|
||||
public static class PLE
|
||||
{
|
||||
public Entry entry;
|
||||
|
||||
public Song song;
|
||||
|
||||
public SongItem item;
|
||||
|
||||
public PLE (Entry entry, Song song)
|
||||
{
|
||||
this.entry = entry;
|
||||
this.song = song;
|
||||
}
|
||||
|
||||
public String toString ()
|
||||
{
|
||||
return StringUtil.fieldsToString(this);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String CLEAR = "clear";
|
||||
|
||||
public static final String REMOVE_ALL = "remove_all";
|
||||
|
||||
public static final String SHUFFLE = "shuffle";
|
||||
|
||||
public ArrayList plist = new ArrayList();
|
||||
|
||||
public PlaylistPanel ()
|
||||
{
|
||||
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
setLayout(gl);
|
||||
|
||||
// create the pane that will hold the buttons
|
||||
gl = new VGroupLayout(GroupLayout.NONE);
|
||||
gl.setJustification(GroupLayout.TOP);
|
||||
gl.setGap(2);
|
||||
_bpanel = new SmartPanel();
|
||||
_bpanel.setLayout(gl);
|
||||
|
||||
// give ourselves a wee bit of a border
|
||||
_bpanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
|
||||
// put it into a scrolling pane
|
||||
JScrollPane bscroll = new JScrollPane(_bpanel);
|
||||
add(bscroll);
|
||||
|
||||
GroupLayout bgl = new HGroupLayout(GroupLayout.NONE);
|
||||
bgl.setJustification(GroupLayout.RIGHT);
|
||||
JPanel cbar = new JPanel(bgl);
|
||||
|
||||
// add our control buttons
|
||||
_clearbut = ButtonUtil.createControlButton(
|
||||
CLEAR_TIP, CLEAR, CLEAR_ICON_PATH);
|
||||
cbar.add(_clearbut);
|
||||
cbar.add(ButtonUtil.createControlButton(
|
||||
SHUFFLE_TIP, SHUFFLE, SHUFFLE_ICON_PATH));
|
||||
add(cbar, GroupLayout.FIXED);
|
||||
|
||||
// use a special font for our name buttons
|
||||
_nameFont = new Font("Helvetica", Font.PLAIN, 12);
|
||||
|
||||
// create our icons
|
||||
_removeEntryIcon = ButtonUtil.getIcon(REMOVE_ENTRY_ICON_PATH);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void addNotify ()
|
||||
{
|
||||
super.addNotify();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected Controller createController ()
|
||||
{
|
||||
return new PlaylistController(this);
|
||||
}
|
||||
|
||||
protected void prepareForRefresh ()
|
||||
{
|
||||
// stick a "loading" label in the list to let the user know
|
||||
// what's up
|
||||
_bpanel.removeAll();
|
||||
_bpanel.add(new JLabel("Loading..."));
|
||||
// swing doesn't automatically validate after adding/removing
|
||||
// children
|
||||
_bpanel.revalidate();
|
||||
}
|
||||
|
||||
protected void populatePlaylist (ArrayList plist)
|
||||
{
|
||||
// clear out any existing children
|
||||
_bpanel.removeAll();
|
||||
|
||||
// adjust our layout policy
|
||||
GroupLayout gl = (GroupLayout)_bpanel.getLayout();
|
||||
gl.setOffAxisPolicy(GroupLayout.EQUALIZE);
|
||||
gl.setOffAxisJustification(GroupLayout.LEFT);
|
||||
|
||||
int current = Chooser.djobj.playing;
|
||||
|
||||
// add buttons for every entry
|
||||
String title = null;
|
||||
for (int ii = 0; ii < plist.size(); ii++) {
|
||||
PLE entry = (PLE)plist.get(ii);
|
||||
JButton button;
|
||||
|
||||
// add record/artist indicators when the record and artist
|
||||
// changes
|
||||
if (!entry.entry.title.equals(title)) {
|
||||
JPanel tpanel = new JPanel();
|
||||
gl = new HGroupLayout(GroupLayout.NONE);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
gl.setJustification(GroupLayout.LEFT);
|
||||
tpanel.setLayout(gl);
|
||||
|
||||
title = entry.entry.title;
|
||||
JLabel label = new JLabel(entry.entry.title + " - " +
|
||||
entry.entry.artist);
|
||||
tpanel.add(label);
|
||||
tpanel.add(newButton(REMOVE_ENTRY_TIP, REMOVE_ALL,
|
||||
_removeEntryIcon, entry));
|
||||
|
||||
_bpanel.add(tpanel);
|
||||
}
|
||||
|
||||
// create a song item to display this playlist entry
|
||||
entry.item = new SongItem(entry.song, SongItem.PLAYLIST);
|
||||
entry.item.setIsPlaying(current == ii);
|
||||
entry.item.extra = new Integer(ii);
|
||||
_bpanel.add(entry.item);
|
||||
|
||||
// let the bpanel know that we want to scroll the active track
|
||||
// label into place once we're all laid out
|
||||
if (current == ii) {
|
||||
_bpanel.setScrollTarget(entry.item);
|
||||
}
|
||||
}
|
||||
|
||||
// if there were no entries, stick a label in to that effect
|
||||
if (plist.size() == 0) {
|
||||
_bpanel.add(new JLabel("Nothing playing."));
|
||||
}
|
||||
|
||||
// swing doesn't automatically validate after adding/removing
|
||||
// children
|
||||
_bpanel.revalidate();
|
||||
}
|
||||
|
||||
protected JButton newButton (String tooltip, String command,
|
||||
ImageIcon icon, Object clientProperty)
|
||||
{
|
||||
JButton button = ButtonUtil.createControlButton(
|
||||
tooltip, command, icon, true);
|
||||
button.putClientProperty("entry", clientProperty);
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* A panel that can be made to scroll a particular child into view
|
||||
* when it becomes visible.
|
||||
*/
|
||||
protected static class SmartPanel extends ScrollablePanel
|
||||
{
|
||||
public void setScrollTarget (JComponent comp)
|
||||
{
|
||||
_target = comp;
|
||||
}
|
||||
|
||||
public void doLayout ()
|
||||
{
|
||||
super.doLayout();
|
||||
|
||||
if (_target != null) {
|
||||
Rectangle bounds = _target.getBounds();
|
||||
_target = null;
|
||||
// this seems to sometimes call layout(), so we need to
|
||||
// prevent recursion
|
||||
scrollRectToVisible(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
// make the playlist fit the width of the scrolling viewport
|
||||
public boolean getScrollableTracksViewportWidth ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected JComponent _target;
|
||||
}
|
||||
|
||||
protected SmartPanel _bpanel;
|
||||
protected JButton _clearbut;
|
||||
|
||||
protected Font _nameFont;
|
||||
|
||||
protected ImageIcon _skiptoIcon;
|
||||
protected ImageIcon _removeSongIcon;
|
||||
protected ImageIcon _removeEntryIcon;
|
||||
|
||||
// icon paths
|
||||
protected static final String ICON_ROOT = "/robodj/chooser/images/";
|
||||
protected static final String CLEAR_ICON_PATH = ICON_ROOT + "clear.png";
|
||||
protected static final String SHUFFLE_ICON_PATH = ICON_ROOT + "shuffle.png";
|
||||
protected static final String SKIPTO_ICON_PATH = ICON_ROOT + "skip.png";
|
||||
protected static final String REMOVE_ENTRY_ICON_PATH =
|
||||
ICON_ROOT + "remove_entry.png";
|
||||
protected static final String REMOVE_SONG_ICON_PATH =
|
||||
ICON_ROOT + "remove_song.png";
|
||||
|
||||
// button tips
|
||||
protected static final String CLEAR_TIP =
|
||||
"Clear all songs from the playlist";
|
||||
protected static final String SHUFFLE_TIP =
|
||||
"Shuffle the songs in the playlist";
|
||||
protected static final String SKIPTO_TIP = "Skip to this song";
|
||||
protected static final String REMOVE_ENTRY_TIP =
|
||||
"Remove all songs in this entry from the playlist";
|
||||
protected static final String REMOVE_SONG_TIP =
|
||||
"Remove this song from the playlist";
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
//
|
||||
// $Id: QueryEntryList.java,v 1.2 2003/05/04 18:16:06 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.swing.Controller;
|
||||
|
||||
import robodj.repository.Entry;
|
||||
|
||||
public class QueryEntryList extends EntryList
|
||||
{
|
||||
/**
|
||||
* Constructs an entry list to display entries that match a query.
|
||||
*/
|
||||
public QueryEntryList (String query)
|
||||
{
|
||||
_query = query;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected Controller createController ()
|
||||
{
|
||||
return new EntryController(this) {
|
||||
public Entry[] readEntries () throws PersistenceException {
|
||||
if (_entries == null) {
|
||||
_entries = Chooser.repository.matchEntries(_query);
|
||||
}
|
||||
return _entries;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected String getEmptyString ()
|
||||
{
|
||||
return "No matches to your query.";
|
||||
}
|
||||
|
||||
/** The query with which we're looking up entries. */
|
||||
protected String _query;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import com.samskivert.util.Queue;
|
||||
|
||||
/**
|
||||
* Provides the ability to perform some basic RoboDJ commands from the
|
||||
* command line.
|
||||
*/
|
||||
public class Remote extends DJClient
|
||||
{
|
||||
public void invoke (String command)
|
||||
{
|
||||
_command = command;
|
||||
connectToServer();
|
||||
}
|
||||
|
||||
protected void didInit ()
|
||||
{
|
||||
super.didInit();
|
||||
|
||||
if (_command.equalsIgnoreCase("play")) {
|
||||
djobj.play();
|
||||
|
||||
} else if (_command.equalsIgnoreCase("pause")) {
|
||||
djobj.pause();
|
||||
|
||||
} else if (_command.equalsIgnoreCase("stop")) {
|
||||
djobj.stop();
|
||||
|
||||
} else {
|
||||
System.err.println("Unknown command: " + _command);
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void invokeLater (Runnable run)
|
||||
{
|
||||
_queue.append(run);
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
if (args.length == 0) {
|
||||
System.out.println("Usage: Remote [play|pause|stop]");
|
||||
System.exit(-1);
|
||||
}
|
||||
Remote remote = new Remote();
|
||||
remote.invoke(args[0]);
|
||||
|
||||
Runnable run;
|
||||
while ((run = (Runnable)_queue.get()) != null) {
|
||||
run.run();
|
||||
}
|
||||
}
|
||||
|
||||
protected String _command;
|
||||
protected static Queue _queue = new Queue();
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
//
|
||||
// $Id: SearchPanel.java,v 1.3 2002/03/03 20:56:12 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import javax.swing.*;
|
||||
|
||||
import com.samskivert.swing.*;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import robodj.repository.*;
|
||||
|
||||
public class SearchPanel extends JPanel
|
||||
implements ActionListener
|
||||
{
|
||||
public SearchPanel ()
|
||||
{
|
||||
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
gl.setJustification(GroupLayout.TOP);
|
||||
setLayout(gl);
|
||||
|
||||
// give ourselves a wee bit of a border
|
||||
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
|
||||
add(new JLabel("Enter a word to be matched against the " +
|
||||
"artists, titles and songs:"), GroupLayout.FIXED);
|
||||
|
||||
gl = new HGroupLayout(GroupLayout.STRETCH);
|
||||
JPanel qpanel = new JPanel(gl);
|
||||
_qbox = new JTextField();
|
||||
_qbox.addActionListener(this);
|
||||
_qbox.setActionCommand("query");
|
||||
qpanel.add(_qbox);
|
||||
|
||||
JButton qbutton = new JButton("Query");
|
||||
qbutton.addActionListener(this);
|
||||
qbutton.setActionCommand("query");
|
||||
qpanel.add(qbutton, GroupLayout.FIXED);
|
||||
add(qpanel, GroupLayout.FIXED);
|
||||
}
|
||||
|
||||
public void actionPerformed (ActionEvent e)
|
||||
{
|
||||
if (e.getActionCommand().equals("query")) {
|
||||
String text = _qbox.getText();
|
||||
if (StringUtil.blank(text) || text.equals(_query)) {
|
||||
// ignore empty text or repeat queries
|
||||
return;
|
||||
}
|
||||
|
||||
// make a note of the query
|
||||
_query = text;
|
||||
|
||||
// remove any previous query results
|
||||
if (_qlist != null) {
|
||||
remove(_qlist);
|
||||
_qlist = null;
|
||||
}
|
||||
|
||||
// create and add a query entry list for the query
|
||||
_qlist = new QueryEntryList(_query);
|
||||
add(_qlist);
|
||||
|
||||
// we need to revalidate ourselves and repaint because we've
|
||||
// added a component
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/** The most recent query. */
|
||||
protected String _query;
|
||||
|
||||
/** The query text box. */
|
||||
protected JTextField _qbox;
|
||||
|
||||
/** The entry list that displays the results of our query. */
|
||||
protected QueryEntryList _qlist;
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
//
|
||||
// $Id: SongItem.java,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||
|
||||
package robodj.chooser;
|
||||
|
||||
import java.awt.Color;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import com.samskivert.swing.HGroupLayout;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import robodj.repository.Song;
|
||||
import robodj.util.ButtonUtil;
|
||||
|
||||
/**
|
||||
* Displays a particular song (used by the playlist and the entry list).
|
||||
*/
|
||||
public class SongItem extends Item
|
||||
{
|
||||
/** Configures this song item for playlist mode. */
|
||||
public static final int PLAYLIST = 0;
|
||||
|
||||
/** Configures this song item for browser mode. */
|
||||
public static final int BROWSER = 1;
|
||||
|
||||
// playlist mode actions
|
||||
public static final String SKIP_TO = "song:skip_to";
|
||||
public static final String REMOVE = "song:remove";
|
||||
|
||||
// browser mode actions
|
||||
public static final String PLAY = "song:play";
|
||||
|
||||
// ubiquitous actions
|
||||
public static final String TOGGLE_VOTE = "song:toggle_vote";
|
||||
|
||||
/** Used depending on where this song is being displayed. */
|
||||
public Object extra;
|
||||
|
||||
/**
|
||||
* Creates a song item configured with the supplied song and specified
|
||||
* mode.
|
||||
*/
|
||||
public SongItem (Song song, int mode)
|
||||
{
|
||||
_song = song;
|
||||
|
||||
// configure our layout manager
|
||||
HGroupLayout gl = new HGroupLayout(HGroupLayout.NONE);
|
||||
gl.setJustification(HGroupLayout.LEFT);
|
||||
setLayout(gl);
|
||||
|
||||
// create our control buttons
|
||||
JButton button;
|
||||
switch (mode) {
|
||||
case PLAYLIST:
|
||||
// add a button for skipping to this song
|
||||
button = ButtonUtil.createControlButton(
|
||||
SKIP_TO_TIP, SKIP_TO, _skipToIcon, true);
|
||||
button.putClientProperty("song", _song);
|
||||
add(button, HGroupLayout.FIXED);
|
||||
|
||||
// add a button for removing this song
|
||||
button = ButtonUtil.createControlButton(
|
||||
REMOVE_TIP, REMOVE, _removeIcon, true);
|
||||
button.putClientProperty("song", _song);
|
||||
add(button, HGroupLayout.FIXED);
|
||||
|
||||
break;
|
||||
|
||||
case BROWSER:
|
||||
// add a button for playing the song
|
||||
button = ButtonUtil.createControlButton(
|
||||
PLAY_SONG_TIP, PLAY, _playIcon, true);
|
||||
button.putClientProperty(SONG_PROP, _song);
|
||||
add(button, HGroupLayout.FIXED);
|
||||
break;
|
||||
|
||||
default:
|
||||
System.err.println("Unknown song mode: " + mode);
|
||||
break;
|
||||
}
|
||||
|
||||
// add a vote button
|
||||
_voteButton = ButtonUtil.createControlButton(
|
||||
TOGGLE_VOTE_TIP, TOGGLE_VOTE, _noVoteIcon, true);
|
||||
_voteButton.putClientProperty(SONG_PROP, song);
|
||||
add(_voteButton, HGroupLayout.FIXED);
|
||||
|
||||
// add the song title
|
||||
_trackLabel = new JLabel(_song.title);
|
||||
add(_trackLabel);
|
||||
|
||||
// update our display based on our votes
|
||||
update();
|
||||
}
|
||||
|
||||
public void setIsPlaying (boolean isPlaying)
|
||||
{
|
||||
_trackLabel.setForeground(isPlaying ? Color.red : Color.black);
|
||||
}
|
||||
|
||||
public void update ()
|
||||
{
|
||||
if (!StringUtil.blank(_song.votes)) {
|
||||
_trackLabel.setFont(_hasVotesFont);
|
||||
_trackLabel.setToolTipText(
|
||||
"<html><table><tr><td>" + _song.title + "</td></tr>" +
|
||||
"<tr><td>Votes: " + _song.votes + "</td></tr>" +
|
||||
"</table></html>");
|
||||
} else {
|
||||
_trackLabel.setFont(_nameFont);
|
||||
_trackLabel.setToolTipText(_song.title);
|
||||
}
|
||||
|
||||
switch (_song.hasVoted(Chooser.frame.getUser(false))) {
|
||||
case -1: _voteButton.setIcon(_nayVoteIcon); break;
|
||||
case 1: _voteButton.setIcon(_yeaVoteIcon); break;
|
||||
default:
|
||||
case 0: _voteButton.setIcon(_noVoteIcon); break;
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
public Song getSong ()
|
||||
{
|
||||
return _song;
|
||||
}
|
||||
|
||||
public static Song getSong (Object source)
|
||||
{
|
||||
return (Song)((JButton)source).getClientProperty(SONG_PROP);
|
||||
}
|
||||
|
||||
protected Song _song;
|
||||
protected JButton _voteButton;
|
||||
protected JLabel _trackLabel;
|
||||
|
||||
protected static final String SONG_PROP = "song";
|
||||
|
||||
protected static final String SKIP_TO_TIP =
|
||||
"Skip to this song";
|
||||
protected static final String REMOVE_TIP =
|
||||
"Remove this song from the playlist";
|
||||
|
||||
protected static final String PLAY_SONG_TIP =
|
||||
"Append this song to the playlist";
|
||||
|
||||
protected static final String TOGGLE_VOTE_TIP =
|
||||
"Toggle your vote for this song";
|
||||
|
||||
protected static ImageIcon _skipToIcon =
|
||||
ButtonUtil.getIcon(ICON_ROOT + "skip.png");
|
||||
protected static ImageIcon _removeIcon =
|
||||
ButtonUtil.getIcon(ICON_ROOT + "remove_song.png");
|
||||
|
||||
protected static ImageIcon _yeaVoteIcon =
|
||||
ButtonUtil.getIcon(ICON_ROOT + "yea_vote.png");
|
||||
protected static ImageIcon _nayVoteIcon =
|
||||
ButtonUtil.getIcon(ICON_ROOT + "nay_vote.png");
|
||||
protected static ImageIcon _noVoteIcon =
|
||||
ButtonUtil.getIcon(ICON_ROOT + "no_vote.png");
|
||||
}
|
||||
|
Before Width: | Height: | Size: 572 B |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 992 B |
|
Before Width: | Height: | Size: 873 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 243 B |
|
Before Width: | Height: | Size: 820 B |
|
Before Width: | Height: | Size: 336 B |
|
Before Width: | Height: | Size: 367 B |
|
Before Width: | Height: | Size: 382 B |
|
Before Width: | Height: | Size: 255 B |
|
Before Width: | Height: | Size: 727 B |
|
Before Width: | Height: | Size: 563 B |
|
Before Width: | Height: | Size: 575 B |
|
Before Width: | Height: | Size: 510 B |
|
Before Width: | Height: | Size: 340 B |
|
Before Width: | Height: | Size: 681 B |
@@ -1,16 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package robodj.client;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
/**
|
||||
* Defines the main RoboDJ services.
|
||||
*/
|
||||
public interface DJService extends InvocationService
|
||||
{
|
||||
/** Used to obtain the oid of the DJ object. */
|
||||
public void getDJOid (Client client, ResultListener listener);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#
|
||||
# $Id: config.properties,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||
#
|
||||
# Default RoboDJ configuration properties
|
||||
|
||||
#
|
||||
# The directory in which RoboDJ will store its ripped music
|
||||
|
||||
# repository.basedir = /export/robodj/repository
|
||||
|
||||
#
|
||||
# The directory in which the importer will store temp files while ripping
|
||||
|
||||
# repository.tmpdir = /export/robodj/tmp
|
||||
|
||||
#
|
||||
# The JDBC configuration for accessing the music database
|
||||
|
||||
jdbc.default.driver = org.gjt.mm.mysql.Driver
|
||||
jdbc.default.username = _USERNAME_
|
||||
jdbc.default.password = _PASSWORD_
|
||||
jdbc.default.url = jdbc:mysql://_DBHOST_:3306/_DBNAME_
|
||||
|
||||
#
|
||||
# The CDDB host to which to connect
|
||||
|
||||
cddb.host = freedb.freedb.org
|
||||
|
||||
#
|
||||
# The music server host and port information
|
||||
|
||||
# musicd.host = hostname
|
||||
musicd.port = 2500
|
||||
@@ -1,204 +0,0 @@
|
||||
//
|
||||
// $Id: CDParanoiaRipper.java,v 1.9 2003/10/24 18:34:28 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
/**
|
||||
* A ripper implementation that uses cdparanoia to do it's job.
|
||||
*/
|
||||
public class CDParanoiaRipper implements Ripper
|
||||
{
|
||||
public TrackInfo[] getTrackInfo ()
|
||||
throws ConvertException
|
||||
{
|
||||
// an input line that we're interested in looks something like
|
||||
// this:
|
||||
//
|
||||
// 1. 17980 [03:59.55] 0 [00:00.00] no no 2
|
||||
Pattern regex;
|
||||
try {
|
||||
regex = Pattern.compile(
|
||||
"^\\s*\\d+\\.\\s+(\\d+)\\s\\[\\S*\\]\\s+(\\d+)");
|
||||
} catch (PatternSyntaxException pse) {
|
||||
throw new ConvertException("Can't compile regexp?! " + pse);
|
||||
}
|
||||
|
||||
try {
|
||||
// fork off a cdparanoia process to read the TOC
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
Process tocproc = rt.exec("cdparanoia -Q");
|
||||
|
||||
InputStream in = tocproc.getErrorStream();
|
||||
BufferedInputStream bin = new BufferedInputStream(in);
|
||||
DataInputStream din = new DataInputStream(bin);
|
||||
|
||||
String inline;
|
||||
StringBuffer input = new StringBuffer();
|
||||
ArrayList flist = new ArrayList();
|
||||
|
||||
while ((inline = din.readLine()) != null) {
|
||||
// skip blank lines and lines that are in the header
|
||||
if (inline.trim().length() == 0 ||
|
||||
inline.indexOf("xiph.org") != -1 ||
|
||||
inline.indexOf("mit.edu") != -1 ||
|
||||
inline.indexOf("cdparanoia") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// keep track of all of the input in case we need to
|
||||
// report an error later
|
||||
input.append(inline).append("\n");
|
||||
|
||||
// see if we match our regular expression
|
||||
Matcher match = regex.matcher(inline);
|
||||
if (match.find()) {
|
||||
flist.add(inline.substring(match.start(1), match.end(1)));
|
||||
flist.add(inline.substring(match.start(2), match.end(2)));
|
||||
}
|
||||
}
|
||||
|
||||
// check the return value of the process
|
||||
try {
|
||||
int retval = tocproc.waitFor();
|
||||
if (retval != 0) {
|
||||
// ship off the error output from cdparanoia
|
||||
throw new ConvertException(input.toString());
|
||||
}
|
||||
|
||||
} catch (InterruptedException ie) {
|
||||
// why we were interrupted I can only speculate, but we'll
|
||||
// go ahead and freak out anyway
|
||||
throw new ConvertException("Interrupted while waiting for " +
|
||||
"cdparanoia process to exit.");
|
||||
}
|
||||
|
||||
// parse the frame offsets and stick them in an array
|
||||
TrackInfo[] frames = new TrackInfo[flist.size()/2];
|
||||
for (int i = 0; i < frames.length; i++) {
|
||||
try {
|
||||
frames[i] = new TrackInfo();
|
||||
frames[i].length =
|
||||
Integer.parseInt((String)flist.get(2*i));
|
||||
frames[i].offset =
|
||||
Integer.parseInt((String)flist.get(2*i+1));
|
||||
|
||||
// for some reason, cdparanoia reports track offsets
|
||||
// starting from zero but CDDB assumes they start at 2
|
||||
// seconds, so we have to adjust... sigh.
|
||||
frames[i].offset += 2 * RipUtil.FRAMES_PER_SECOND;
|
||||
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ConvertException(
|
||||
"Bogus frame value for track " + (i+1) + ": " +
|
||||
nfe.getMessage() + "\n\n" + input);
|
||||
}
|
||||
}
|
||||
|
||||
return frames;
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new ConvertException(
|
||||
"Error communicating with ripper:\n" + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public void ripTrack (TrackInfo[] info, int index, String target,
|
||||
ConversionProgressListener listener)
|
||||
throws ConvertException
|
||||
{
|
||||
StringBuffer cmd = new StringBuffer("nice cdparanoia");
|
||||
cmd.append(" -w"); // request output in WAV format
|
||||
cmd.append(" -e"); // request progress information to stderr
|
||||
cmd.append(" ").append(index); // add track number
|
||||
cmd.append(" ").append(target); // add output file name
|
||||
|
||||
// we'll need this for later
|
||||
Pattern regex;
|
||||
try {
|
||||
regex = Pattern.compile("^##: -?\\d+ \\[(\\S+)\\] @ (\\d+)");
|
||||
} catch (PatternSyntaxException pse) {
|
||||
throw new ConvertException("Can't compile regexp?! " + pse);
|
||||
}
|
||||
|
||||
try {
|
||||
// fork off a cdparanoia process to read the TOC
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
Process ripproc = rt.exec(cmd.toString());
|
||||
|
||||
InputStream in = ripproc.getErrorStream();
|
||||
BufferedInputStream bin = new BufferedInputStream(in);
|
||||
DataInputStream din = new DataInputStream(bin);
|
||||
|
||||
// read output from the subprocess
|
||||
String out;
|
||||
|
||||
// figure out what our progress boundaries are
|
||||
long tstart = info[index-1].offset * MAGIC_FACTOR;
|
||||
long tlength = info[index-1].length * MAGIC_FACTOR;
|
||||
int lastReported = 0;
|
||||
|
||||
while ((out = din.readLine()) != null) {
|
||||
// if they don't care about the output then neither do we
|
||||
if (listener == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse the output
|
||||
Matcher match = regex.matcher(out);
|
||||
if (match.matches()) {
|
||||
String action = out.substring(match.start(1), match.end(1));
|
||||
long offset = -1L;
|
||||
try {
|
||||
offset = Long.parseLong(
|
||||
out.substring(match.start(2), match.end(2)));
|
||||
} catch (NumberFormatException nfe) {
|
||||
// System.err.println("Malformed pos. info: " + out);
|
||||
continue;
|
||||
}
|
||||
|
||||
int pctDone = (int)(100 * (offset - tstart) / tlength);
|
||||
if (pctDone - lastReported >= 1) {
|
||||
listener.updateProgress(pctDone);
|
||||
lastReported = pctDone;
|
||||
}
|
||||
|
||||
} else {
|
||||
// System.out.println("Couldn't match: " + out);
|
||||
}
|
||||
}
|
||||
|
||||
// check the return value of the process
|
||||
try {
|
||||
int retval = ripproc.waitFor();
|
||||
if (retval != 0) {
|
||||
// ship off the error output from cdparanoia
|
||||
throw new ConvertException(
|
||||
"Ripper failed: " + retval);
|
||||
}
|
||||
|
||||
} catch (InterruptedException ie) {
|
||||
// why we were interrupted I can only speculate, but we'll
|
||||
// go ahead and freak out anyway
|
||||
throw new ConvertException("Interrupted while waiting for " +
|
||||
"ripper process to exit.");
|
||||
}
|
||||
|
||||
// if everything was successful, report completion
|
||||
listener.updateProgress(100);
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new ConvertException(
|
||||
"Error communicating with ripper:\n" + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
// cdparanoia reports length in sectors and progress in
|
||||
protected static final int MAGIC_FACTOR = 1176;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
//
|
||||
// $Id: ConversionProgressListener.java,v 1.2 2001/02/06 08:18:00 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
/**
|
||||
* This interface is used to communicate ripping progress from a ripper or
|
||||
* encoder implementation to code using the conversion services (and
|
||||
* hopefully ultimately to the end user).
|
||||
*/
|
||||
public interface ConversionProgressListener
|
||||
{
|
||||
/**
|
||||
* Informs the listener that the converter has completed the specified
|
||||
* percentage of the conversion process. The percentage should reflect
|
||||
* only the conversion being performed by the converter at that time
|
||||
* (ripping or converting a single track). The information provided
|
||||
* herein will be mapped to a global progress indication by the
|
||||
* calling software (in the case of converting an entire CD).
|
||||
*/
|
||||
public void updateProgress (int percentComplete);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// $Id: ConvertException.java,v 1.1 2000/10/30 22:21:11 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
/**
|
||||
* A convert exception can be thrown to indicate that some problem was
|
||||
* encountered during some part of the conversion process (ripping or
|
||||
* encoding).
|
||||
*/
|
||||
public class ConvertException extends Exception
|
||||
{
|
||||
public ConvertException (String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// $Id: EncodeTest.java,v 1.1 2001/03/18 06:58:55 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
import com.samskivert.net.cddb.*;
|
||||
|
||||
public class EncodeTest
|
||||
{
|
||||
public static void main (String[] args)
|
||||
{
|
||||
if (args.length < 2) {
|
||||
System.err.println("Usage: EncodeTest source.wav dest.mp3");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
try {
|
||||
Encoder encoder = new LameEncoder();
|
||||
ConversionProgressListener listener =
|
||||
new ConversionProgressListener() {
|
||||
public void updateProgress (int percentDone)
|
||||
{
|
||||
System.out.println("Percent done: " +
|
||||
percentDone + "%");
|
||||
}
|
||||
};
|
||||
encoder.encodeTrack(args[0], args[1], listener);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
//
|
||||
// $Id: Encoder.java,v 1.1 2000/10/30 22:21:11 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
/**
|
||||
* The encoder interface is used to manipulate particular MP3 encoding
|
||||
* software in the ways needed by the RoboDJ system.
|
||||
*/
|
||||
public interface Encoder
|
||||
{
|
||||
/**
|
||||
* Instructs the encoder to encode the specified track.
|
||||
*
|
||||
* @param source the path to the source file that should be encoded.
|
||||
* @param dest the path to the destination file which should be
|
||||
* created by the encoder.
|
||||
* @param listener a callback object that should be called to
|
||||
* communicate encoding progress. If the listener parameter is null,
|
||||
* the caller doesn't want to hear about progress (shame on them).
|
||||
*
|
||||
* @exception ConvertException can be thrown if anything fails during
|
||||
* the encoding process.
|
||||
*/
|
||||
public void encodeTrack (String source, String dest,
|
||||
ConversionProgressListener listener)
|
||||
throws ConvertException;
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
//
|
||||
// $Id: ID3Tagger.java,v 1.3 2002/11/11 17:04:56 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
/**
|
||||
* A tagger implementation that uses 'id3v2' to do it's job.
|
||||
*/
|
||||
public class ID3Tagger implements Tagger
|
||||
{
|
||||
// documentation inherited
|
||||
public void idTrack (String target, String artist, String album,
|
||||
String title, int trackNo)
|
||||
throws ConvertException
|
||||
{
|
||||
String[] cmdarray = new String[] {
|
||||
"id3v2", "--song", title, "--artist", artist, "--album", album,
|
||||
"--track", Integer.toString(trackNo), target };
|
||||
|
||||
try {
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
Process ripproc = rt.exec(cmdarray);
|
||||
|
||||
InputStream in = ripproc.getErrorStream();
|
||||
String output = IOUtils.toString(in);
|
||||
|
||||
// check the return value of the process
|
||||
try {
|
||||
int retval = ripproc.waitFor();
|
||||
if (retval != 0) {
|
||||
// ship off the error output from id3
|
||||
throw new ConvertException(
|
||||
"id3v2 failed: " + output);
|
||||
}
|
||||
|
||||
} catch (InterruptedException ie) {
|
||||
// why we were interrupted I can only speculate, but we'll
|
||||
// go ahead and freak out anyway
|
||||
throw new ConvertException("Interrupted while waiting for " +
|
||||
"encoder process to exit.");
|
||||
}
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new ConvertException(
|
||||
"Error communicating with encoder:\n" + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
ID3Tagger tagger = new ID3Tagger();
|
||||
if (args.length < 5) {
|
||||
System.err.println("Usage: ID3Tagger target artist album " +
|
||||
"title trackNo");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
int trackNo = 0;
|
||||
try {
|
||||
trackNo = Integer.parseInt(args[4]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.err.println("Invalid track number: " + args[4]);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
try {
|
||||
tagger.idTrack(args[0], args[1], args[2], args[3], trackNo);
|
||||
} catch (ConvertException ce) {
|
||||
ce.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
//
|
||||
// $Id: L3Encoder.java,v 1.2 2003/10/10 19:33:16 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* An encoder implementation that uses the Fraunhofer-IIS L3ENC mp3
|
||||
* encoding program.
|
||||
*/
|
||||
public class L3Encoder implements Encoder
|
||||
{
|
||||
public void encodeTrack (String source, String dest,
|
||||
ConversionProgressListener listener)
|
||||
throws ConvertException
|
||||
{
|
||||
StringBuffer cmd = new StringBuffer("nice l3enc");
|
||||
cmd.append(" ").append(source); // add input file name
|
||||
cmd.append(" ").append(dest); // add output file name
|
||||
cmd.append(" -br 128000"); // request 128kbps encoding
|
||||
|
||||
try {
|
||||
// fork off a cdparanoia process to read the TOC
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
Process encproc = rt.exec(cmd.toString());
|
||||
|
||||
InputStream in = encproc.getErrorStream();
|
||||
BufferedInputStream bin = new BufferedInputStream(in);
|
||||
DataInputStream din = new DataInputStream(bin);
|
||||
|
||||
// read output from the subprocess and chuck it for now
|
||||
while (din.readLine() != null) {
|
||||
// la la la
|
||||
}
|
||||
|
||||
// check the return value of the process
|
||||
try {
|
||||
int retval = encproc.waitFor();
|
||||
if (retval != 0) {
|
||||
// ship off the error output from cdparanoia
|
||||
throw new ConvertException(
|
||||
"Encoder failed: " + retval);
|
||||
}
|
||||
|
||||
} catch (InterruptedException ie) {
|
||||
// why we were interrupted I can only speculate, but we'll
|
||||
// go ahead and freak out anyway
|
||||
throw new ConvertException("Interrupted while waiting for " +
|
||||
"encoder process to exit.");
|
||||
}
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new ConvertException(
|
||||
"Error communicating with encoder:\n" + ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
//
|
||||
// $Id: LameEncoder.java,v 1.4 2003/10/24 18:34:28 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
/**
|
||||
* An encoder implementation that uses LAME to do it's job.
|
||||
*/
|
||||
public class LameEncoder implements Encoder
|
||||
{
|
||||
public void encodeTrack (String source, String dest,
|
||||
ConversionProgressListener listener)
|
||||
throws ConvertException
|
||||
{
|
||||
StringBuffer cmd = new StringBuffer("nice lame");
|
||||
cmd.append(" --nohist"); // we don't want histogram output
|
||||
cmd.append(" -v"); // request variable bitrate encoding
|
||||
cmd.append(" ").append(source); // add source file name
|
||||
cmd.append(" ").append(dest); // add output file name
|
||||
|
||||
// we'll need this for later
|
||||
Pattern regex;
|
||||
try {
|
||||
// a line of input looks like this
|
||||
// 0/1273 ( 0%)| 0:00/ 0:00...
|
||||
regex = Pattern.compile("^\\s*(\\d+)/(\\d+)\\s*\\(.*");
|
||||
} catch (PatternSyntaxException pse) {
|
||||
throw new ConvertException("Can't compile regexp?! " + pse);
|
||||
}
|
||||
|
||||
try {
|
||||
// fork off a cdparanoia process to read the TOC
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
Process ripproc = rt.exec(cmd.toString());
|
||||
|
||||
InputStream in = ripproc.getErrorStream();
|
||||
BufferedInputStream bin = new BufferedInputStream(in);
|
||||
DataInputStream din = new DataInputStream(bin);
|
||||
|
||||
// read output from the subprocess
|
||||
String out;
|
||||
int lastReported = 0;
|
||||
|
||||
while ((out = din.readLine()) != null) {
|
||||
// if they don't care about the output then neither do we
|
||||
if (listener == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse the output
|
||||
Matcher match = regex.matcher(out);
|
||||
if (match.matches()) {
|
||||
int offset = -1, total = -1;
|
||||
try {
|
||||
offset = Integer.parseInt(
|
||||
out.substring(match.start(1), match.end(1)));
|
||||
total = Integer.parseInt(
|
||||
out.substring(match.start(2), match.end(2)));
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.err.println("Malformed position info: " + out);
|
||||
continue;
|
||||
}
|
||||
|
||||
int pctDone = (100 * offset) / total;
|
||||
if (pctDone - lastReported >= 1) {
|
||||
listener.updateProgress(pctDone);
|
||||
lastReported = pctDone;
|
||||
}
|
||||
|
||||
} else {
|
||||
// System.out.println("Couldn't match: " + out);
|
||||
}
|
||||
}
|
||||
|
||||
// check the return value of the process
|
||||
try {
|
||||
int retval = ripproc.waitFor();
|
||||
if (retval != 0) {
|
||||
// ship off the error output from cdparanoia
|
||||
throw new ConvertException(
|
||||
"Encoder failed: " + retval);
|
||||
}
|
||||
|
||||
} catch (InterruptedException ie) {
|
||||
// why we were interrupted I can only speculate, but we'll
|
||||
// go ahead and freak out anyway
|
||||
throw new ConvertException("Interrupted while waiting for " +
|
||||
"encoder process to exit.");
|
||||
}
|
||||
|
||||
// if everything was successful, report completion
|
||||
if (lastReported < 100) {
|
||||
listener.updateProgress(100);
|
||||
}
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new ConvertException(
|
||||
"Error communicating with encoder:\n" + ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
//
|
||||
// $Id: RipTest.java,v 1.4 2001/03/18 06:58:55 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
import com.samskivert.net.cddb.*;
|
||||
|
||||
public class RipTest
|
||||
{
|
||||
public static void lookupCDDB (String hostname, Ripper.TrackInfo[] info)
|
||||
throws Exception
|
||||
{
|
||||
CDDB cddb = new CDDB();
|
||||
|
||||
long discid = RipUtil.computeDiscId(info);
|
||||
String cdid = Long.toString(discid, 0x10);
|
||||
|
||||
// create an array with the track offsets
|
||||
int[] offsets = new int[info.length];
|
||||
for (int i = 0; i < info.length; i++) {
|
||||
offsets[i] = info[i].offset;
|
||||
}
|
||||
|
||||
int length = RipUtil.computeDiscLength(info);
|
||||
|
||||
try {
|
||||
System.out.println("connecting to: " + hostname);
|
||||
String rsp = cddb.connect(hostname);
|
||||
|
||||
// set the timeout to 30 seconds
|
||||
cddb.setTimeout(30*1000);
|
||||
|
||||
// try a test query
|
||||
CDDB.Entry[] entries = cddb.query(cdid, offsets, length);
|
||||
|
||||
if (entries == null || entries.length == 0) {
|
||||
System.out.println("No match for " + cdid + ".");
|
||||
|
||||
} else {
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
System.out.println("Match " + entries[i].category + "/" +
|
||||
entries[i].cdid + "/" +
|
||||
entries[i].title);
|
||||
}
|
||||
|
||||
CDDB.Detail detail = cddb.read(entries[0].category,
|
||||
entries[0].cdid);
|
||||
System.out.println("Title: " + detail.title);
|
||||
for (int i = 0; i < detail.trackNames.length; i++) {
|
||||
System.out.println(pad(i) + ": " + detail.trackNames[i]);
|
||||
}
|
||||
System.out.println("Extended data: " + detail.extendedData);
|
||||
for (int i = 0; i < detail.extendedTrackData.length; i++) {
|
||||
System.out.println(pad(i) + ": " +
|
||||
detail.extendedTrackData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
cddb.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected static String pad (int value)
|
||||
{
|
||||
return ((value > 9) ? "" : " ") + value;
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
try {
|
||||
Ripper ripper = new CDParanoiaRipper();
|
||||
Ripper.TrackInfo[] info = ripper.getTrackInfo();
|
||||
for (int i = 0; i < info.length; i++) {
|
||||
System.out.println((i+1) + ": " + info[i].offset + ", " +
|
||||
info[i].length);
|
||||
}
|
||||
|
||||
if (info.length == 0) {
|
||||
System.out.println("Matched no tracks?!");
|
||||
|
||||
} else {
|
||||
// try the CDDB lookup
|
||||
lookupCDDB("ca.freedb.org", info);
|
||||
|
||||
// rip a track, for fun
|
||||
int track = 2;
|
||||
System.out.println("Ripping track " + track +
|
||||
" [" + info[track-1].offset +
|
||||
", " + info[track-1].length + "]...");
|
||||
|
||||
// report our progress
|
||||
ConversionProgressListener listener =
|
||||
new ConversionProgressListener() {
|
||||
public void updateProgress (int percentDone)
|
||||
{
|
||||
System.out.println("Percent done: " +
|
||||
percentDone + "%");
|
||||
}
|
||||
};
|
||||
ripper.ripTrack(info, track, "/tmp/track" + track + ".wav",
|
||||
listener);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
//
|
||||
// $Id: RipUtil.java,v 1.3 2003/10/24 18:34:28 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* This class contains utility functions related to ripping.
|
||||
*/
|
||||
public class RipUtil
|
||||
{
|
||||
/**
|
||||
* Compact discs contain this many frames per second.
|
||||
*/
|
||||
public static int FRAMES_PER_SECOND = 75;
|
||||
|
||||
/**
|
||||
* Computes the CDDB disc id for the specified CD using the supplied
|
||||
* track info.
|
||||
*/
|
||||
public static long computeDiscId (Ripper.TrackInfo[] info)
|
||||
{
|
||||
// first we sum all the digits in the numbers that represent the
|
||||
// number of seconds for each track
|
||||
long digits = 0;
|
||||
for (int i = 0; i < info.length; i++) {
|
||||
int secs = info[i].offset/FRAMES_PER_SECOND;
|
||||
digits += addDigits(secs);
|
||||
}
|
||||
|
||||
// determine the total number of seconds of all the tracks on the
|
||||
// CD (doing so by subtracting the offset of the end of the disc
|
||||
// from the offset of the beginning which is how the CDDB
|
||||
// algorithm does it)
|
||||
long totsecs = (long)computeDiscLength(info);
|
||||
|
||||
// finally combine these two values with the number of tracks on
|
||||
// the disc into the actual disc id
|
||||
return ((digits & 0xFF) << 24) | (totsecs << 8) | info.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the length of the CD (in seconds) as computed from the
|
||||
* supplied frame information.
|
||||
*/
|
||||
public static int computeDiscLength (Ripper.TrackInfo[] info)
|
||||
{
|
||||
if (info == null || info.length == 0) {
|
||||
String errmsg = "Must have at least one track to compute disc " +
|
||||
"length [info=" + StringUtil.toString(info) + "]";
|
||||
throw new IllegalArgumentException(errmsg);
|
||||
}
|
||||
return (info[info.length-1].offset +
|
||||
info[info.length-1].length -
|
||||
info[0].offset)/FRAMES_PER_SECOND;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the sum of the digits in the supplied number (eg. 6 for 15,
|
||||
* 8 for 251, etc.).
|
||||
*/
|
||||
protected static int addDigits (int value)
|
||||
{
|
||||
int rv = 0;
|
||||
while (value > 0) {
|
||||
rv += value%10;
|
||||
value /= 10;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
//
|
||||
// $Id: Ripper.java,v 1.3 2001/02/06 08:18:00 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
/**
|
||||
* The ripper interface is used to manipulate particular CD ripping
|
||||
* software in the ways needed by the RoboDJ system.
|
||||
*/
|
||||
public interface Ripper
|
||||
{
|
||||
/**
|
||||
* Instances of this object are used to communicate the track
|
||||
* information of a CD back to the caller.
|
||||
*/
|
||||
public class TrackInfo
|
||||
{
|
||||
/**
|
||||
* The offset of the start of this track (in frames).
|
||||
*/
|
||||
public int offset;
|
||||
|
||||
/**
|
||||
* The length of this track (in frames).
|
||||
*/
|
||||
public int length;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should return an array with an entry for each track
|
||||
* on the CD (in order) containing the frame offset and length of that
|
||||
* track. This function will be called in another thread and has 30
|
||||
* seconds to do its job or it will be declared delinquent and
|
||||
* ignored.
|
||||
*
|
||||
* @exception ConvertException can be thrown if some problem occurs
|
||||
* trying to read the CD table of contents (like lack of access to the
|
||||
* CDROM device or any other errors).
|
||||
*/
|
||||
public TrackInfo[] getTrackInfo ()
|
||||
throws ConvertException;
|
||||
|
||||
/**
|
||||
* Instructs the ripper to rip the track with the supplied index into
|
||||
* the file specified by target. If the ripper supports progress
|
||||
* notification, it should communicate it to the supplied progress
|
||||
* listener. The track should be ripped in WAV format.
|
||||
*
|
||||
* @param info the track info as returned by
|
||||
* <code>getTrackInfo</code>. This is necessary to compute progress.
|
||||
* @param index the track number of the track to rip (starting at 1
|
||||
* since CDs start counting tracks at 1).
|
||||
* @param target the path to the file into which the track should be
|
||||
* ripped.
|
||||
* @param listener a callback object that should be called to
|
||||
* communicate ripping progress. If the listener parameter is null,
|
||||
* the caller doesn't want to hear about progress (shame on them).
|
||||
*
|
||||
* @exception ConvertException can be thrown if some problem occurs
|
||||
* trying to rip the specified track (like lack of access to the CDROM
|
||||
* device or any other errors).
|
||||
*/
|
||||
public void ripTrack (TrackInfo[] info, int index, String target,
|
||||
ConversionProgressListener listener)
|
||||
throws ConvertException;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// $Id: Tagger.java,v 1.1 2002/03/03 05:25:10 mdb Exp $
|
||||
|
||||
package robodj.convert;
|
||||
|
||||
/**
|
||||
* The tagger interface is used to set the ID3 tags in MP3 files.
|
||||
*/
|
||||
public interface Tagger
|
||||
{
|
||||
/**
|
||||
* Sets the ID3 tags for the specified target MP3 file for the
|
||||
* supplied information: artist, album name, track title and track
|
||||
* number.
|
||||
*/
|
||||
public void idTrack (String target, String artist, String album,
|
||||
String title, int trackNo)
|
||||
throws ConvertException;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package robodj.data;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService.ResultListener;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.dobj.InvocationResponseEvent;
|
||||
import robodj.client.DJService;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link DJService} interface
|
||||
* that marshalls the arguments and delivers the request to the provider
|
||||
* on the server. Also provides an implementation of the response listener
|
||||
* interfaces that marshall the response arguments and deliver them back
|
||||
* to the requesting client.
|
||||
*/
|
||||
public class DJMarshaller extends InvocationMarshaller
|
||||
implements DJService
|
||||
{
|
||||
/** The method id used to dispatch {@link #getDJOid} requests. */
|
||||
public static final int GET_DJOID = 1;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void getDJOid (Client arg1, ResultListener arg2)
|
||||
{
|
||||
ResultMarshaller listener2 = new ResultMarshaller();
|
||||
listener2.listener = arg2;
|
||||
sendRequest(arg1, GET_DJOID, new Object[] {
|
||||
listener2
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package robodj.data;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.ListUtil;
|
||||
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
|
||||
import robodj.Log;
|
||||
|
||||
/**
|
||||
* Contains the shared state of RoboDJ.
|
||||
*/
|
||||
public class DJObject extends DObject
|
||||
{
|
||||
/** The current playlist. */
|
||||
public PlaylistEntry[] playlist;
|
||||
|
||||
/** The index of the currently playing track. */
|
||||
public int playing = -1;
|
||||
|
||||
/** Whether or not we're paused. */
|
||||
public boolean paused;
|
||||
|
||||
/**
|
||||
* Returns the currently playing track.
|
||||
*/
|
||||
public PlaylistEntry getPlaying ()
|
||||
{
|
||||
return (playing < 0) ? null : getEntry(playing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the playlist entry for the next song in the playlist.
|
||||
*/
|
||||
public PlaylistEntry getNext ()
|
||||
{
|
||||
return (playing < 0) ? null : getEntry(playing + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the playlist entry with the current index or null if the
|
||||
* index is not valid.
|
||||
*/
|
||||
public PlaylistEntry getEntry (int index)
|
||||
{
|
||||
return havePlaylist() ? playlist[index % playlist.length] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the specified track in the current playlist.
|
||||
*/
|
||||
public void play (int index)
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
setPlaying(index % playlist.length);
|
||||
}
|
||||
}
|
||||
|
||||
public void play ()
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
if (playing == -1) {
|
||||
setPlaying(0);
|
||||
}
|
||||
if (paused) {
|
||||
setPaused(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void skip ()
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
play(playing + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void back ()
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
play(playing + playlist.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop ()
|
||||
{
|
||||
setPlaying(-1);
|
||||
}
|
||||
|
||||
public void pause ()
|
||||
{
|
||||
setPaused(!paused);
|
||||
}
|
||||
|
||||
public void remove (int start, int length)
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
setPlaylist((PlaylistEntry[])ArrayUtil.splice(
|
||||
playlist, start, length));
|
||||
if (playing >= (start+length)) {
|
||||
setPlaying(playing-length);
|
||||
} else if (playing > start) {
|
||||
setPlaying(start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void shuffle ()
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
PlaylistEntry entry = getPlaying();
|
||||
ArrayUtil.shuffle(playlist);
|
||||
setPlaylist(playlist);
|
||||
setPlaying(ListUtil.indexOf(playlist, entry));
|
||||
}
|
||||
}
|
||||
|
||||
public void clear ()
|
||||
{
|
||||
setPlaying(-1);
|
||||
setPlaylist(null);
|
||||
}
|
||||
|
||||
/** Helper function. */
|
||||
protected boolean havePlaylist ()
|
||||
{
|
||||
return (playlist != null && playlist.length > 0);
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package robodj.data;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.ListUtil;
|
||||
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
|
||||
import robodj.Log;
|
||||
|
||||
/**
|
||||
* Contains the shared state of RoboDJ.
|
||||
*/
|
||||
public class DJObject extends DObject
|
||||
{
|
||||
/** The field name of the <code>playlist</code> field. */
|
||||
public static final String PLAYLIST = "playlist";
|
||||
|
||||
/** The field name of the <code>playing</code> field. */
|
||||
public static final String PLAYING = "playing";
|
||||
|
||||
/** The field name of the <code>paused</code> field. */
|
||||
public static final String PAUSED = "paused";
|
||||
|
||||
/** The current playlist. */
|
||||
public PlaylistEntry[] playlist;
|
||||
|
||||
/** The index of the currently playing track. */
|
||||
public int playing = -1;
|
||||
|
||||
/** Whether or not we're paused. */
|
||||
public boolean paused;
|
||||
|
||||
/**
|
||||
* Returns the currently playing track.
|
||||
*/
|
||||
public PlaylistEntry getPlaying ()
|
||||
{
|
||||
return (playing < 0) ? null : getEntry(playing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the playlist entry for the next song in the playlist.
|
||||
*/
|
||||
public PlaylistEntry getNext ()
|
||||
{
|
||||
return (playing < 0) ? null : getEntry(playing + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the playlist entry with the current index or null if the
|
||||
* index is not valid.
|
||||
*/
|
||||
public PlaylistEntry getEntry (int index)
|
||||
{
|
||||
return havePlaylist() ? playlist[index % playlist.length] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the specified track in the current playlist.
|
||||
*/
|
||||
public void play (int index)
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
setPlaying(index % playlist.length);
|
||||
}
|
||||
}
|
||||
|
||||
public void play ()
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
if (playing == -1) {
|
||||
setPlaying(0);
|
||||
}
|
||||
if (paused) {
|
||||
setPaused(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void skip ()
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
play(playing + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void back ()
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
play(playing + playlist.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop ()
|
||||
{
|
||||
setPlaying(-1);
|
||||
}
|
||||
|
||||
public void pause ()
|
||||
{
|
||||
setPaused(!paused);
|
||||
}
|
||||
|
||||
public void remove (int start, int length)
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
setPlaylist((PlaylistEntry[])ArrayUtil.splice(
|
||||
playlist, start, length));
|
||||
if (playing >= (start+length)) {
|
||||
setPlaying(playing-length);
|
||||
} else if (playing > start) {
|
||||
setPlaying(start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void shuffle ()
|
||||
{
|
||||
if (havePlaylist()) {
|
||||
PlaylistEntry entry = getPlaying();
|
||||
ArrayUtil.shuffle(playlist);
|
||||
setPlaylist(playlist);
|
||||
setPlaying(ListUtil.indexOf(playlist, entry));
|
||||
}
|
||||
}
|
||||
|
||||
public void clear ()
|
||||
{
|
||||
setPlaying(-1);
|
||||
setPlaylist(null);
|
||||
}
|
||||
|
||||
/** Helper function. */
|
||||
protected boolean havePlaylist ()
|
||||
{
|
||||
return (playlist != null && playlist.length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>playlist</code> field be set to the specified
|
||||
* value. The local value will be updated immediately and an event
|
||||
* will be propagated through the system to notify all listeners that
|
||||
* the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
public void setPlaylist (PlaylistEntry[] playlist)
|
||||
{
|
||||
requestAttributeChange(PLAYLIST, playlist);
|
||||
this.playlist = playlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>index</code>th element of
|
||||
* <code>playlist</code> field be set to the specified value. The local
|
||||
* value will be updated immediately and an event will be propagated
|
||||
* through the system to notify all listeners that the attribute did
|
||||
* change. Proxied copies of this object (on clients) will apply the
|
||||
* value change when they received the attribute changed notification.
|
||||
*/
|
||||
public void setPlaylistAt (PlaylistEntry value, int index)
|
||||
{
|
||||
requestElementUpdate(PLAYLIST, value, index);
|
||||
this.playlist[index] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>playing</code> field be set to the specified
|
||||
* value. The local value will be updated immediately and an event
|
||||
* will be propagated through the system to notify all listeners that
|
||||
* the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
public void setPlaying (int playing)
|
||||
{
|
||||
requestAttributeChange(PLAYING, new Integer(playing));
|
||||
this.playing = playing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>paused</code> field be set to the specified
|
||||
* value. The local value will be updated immediately and an event
|
||||
* will be propagated through the system to notify all listeners that
|
||||
* the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
public void setPaused (boolean paused)
|
||||
{
|
||||
requestAttributeChange(PAUSED, new Boolean(paused));
|
||||
this.paused = paused;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package robodj.data;
|
||||
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
|
||||
import robodj.repository.Entry;
|
||||
import robodj.repository.Song;
|
||||
|
||||
/**
|
||||
* Defines an entry currently in the playlist.
|
||||
*/
|
||||
public class PlaylistEntry extends SimpleStreamableObject
|
||||
implements DSet.Entry
|
||||
{
|
||||
/** The album with which we are associated. */
|
||||
public int entryId;
|
||||
|
||||
/** The id of the song represented by this entry. */
|
||||
public int songId;
|
||||
|
||||
/** The title of this entry (duplicated from the Song to simplify
|
||||
* playlist display. */
|
||||
public String title;
|
||||
|
||||
/** Zero argument constructor for streaming. */
|
||||
public PlaylistEntry ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a playlist entry from the specified entry and song. */
|
||||
public PlaylistEntry (Entry entry, Song song)
|
||||
{
|
||||
this(entry.entryid, song.songid, song.title);
|
||||
}
|
||||
|
||||
/** Creates a playlist entry from the specified information. */
|
||||
public PlaylistEntry (int entryId, int songId, String title)
|
||||
{
|
||||
this.entryId = entryId;
|
||||
this.songId = songId;
|
||||
this.title = title;
|
||||
_key = entryId + ":" + songId;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Comparable getKey ()
|
||||
{
|
||||
return _key;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
if (other instanceof PlaylistEntry) {
|
||||
return _key.equals(((PlaylistEntry)other)._key);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public int hashCode ()
|
||||
{
|
||||
return _key.hashCode();
|
||||
}
|
||||
|
||||
protected String _key;
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
//
|
||||
// $Id: CDDBLookupPanel.java,v 1.7 2003/05/04 18:16:07 mdb Exp $
|
||||
|
||||
package robodj.importer;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import com.samskivert.net.cddb.CDDB;
|
||||
import com.samskivert.swing.util.*;
|
||||
import com.samskivert.swing.*;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import robodj.convert.*;
|
||||
import robodj.repository.*;
|
||||
|
||||
public class CDDBLookupPanel
|
||||
extends ImporterPanel
|
||||
implements ActionListener, TaskObserver
|
||||
{
|
||||
public CDDBLookupPanel ()
|
||||
{
|
||||
GroupLayout gl = new HGroupLayout(GroupLayout.STRETCH);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
setLayout(gl);
|
||||
|
||||
// create our label for the left hand side
|
||||
JLabel cdlabel = new JLabel("CDDB Lookup", JLabel.CENTER);
|
||||
cdlabel.setFont(new Font("Helvetica", Font.BOLD, 24));
|
||||
add(cdlabel, GroupLayout.FIXED);
|
||||
|
||||
// on the right hand side we want a status display to let the user
|
||||
// know how the CDDB lookup process is going
|
||||
GroupLayout vgl = new VGroupLayout(GroupLayout.STRETCH);
|
||||
vgl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
_spanel = new JPanel(vgl);
|
||||
|
||||
JLabel sl = new JLabel("Status", JLabel.LEFT);
|
||||
_spanel.add(sl, GroupLayout.FIXED);
|
||||
|
||||
// create the "insert cd" text label for the right hand side
|
||||
_infoText = new JTextArea("Reading CD information...");
|
||||
_infoText.setLineWrap(true);
|
||||
_infoText.setEditable(false);
|
||||
// make something for it to scroll around in
|
||||
_spanel.add(new JScrollPane(_infoText));
|
||||
|
||||
// add our panel to the main group
|
||||
add(_spanel);
|
||||
}
|
||||
|
||||
public void wasAddedToFrame (ImporterFrame frame, boolean popped)
|
||||
{
|
||||
// keep this for later
|
||||
_frame = frame;
|
||||
|
||||
frame.addControlButton("Cancel", "cancel", this);
|
||||
frame.addControlButton("Back", "back", this);
|
||||
_next = frame.addControlButton("Next...", "next", this);
|
||||
_next.setEnabled(popped);
|
||||
|
||||
// if we are being shown for the first time...
|
||||
if (!popped) {
|
||||
// create our info task and set it a running
|
||||
TaskMaster.invokeTask("getinfo", new TaskAdapter() {
|
||||
public Object invoke () throws Exception {
|
||||
Ripper ripper = new CDParanoiaRipper();
|
||||
return ripper.getTrackInfo();
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
public void taskCompleted (String name, Object result)
|
||||
{
|
||||
if (name.equals("getinfo")) {
|
||||
_info = (Ripper.TrackInfo[])result;
|
||||
final Ripper.TrackInfo[] info = _info;
|
||||
_infoText.append("\nRead info for " + _info.length +
|
||||
" tracks.\nDoing CDDB lookup...");
|
||||
|
||||
// create our cddb lookup task and start it up
|
||||
Task infoTask = new Task()
|
||||
{
|
||||
public Object invoke ()
|
||||
throws Exception
|
||||
{
|
||||
return CDDBUtil.doCDDBLookup("freedb.freedb.org", info);
|
||||
}
|
||||
|
||||
public boolean abort ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
TaskMaster.invokeTask("cddb_lookup", infoTask, this);
|
||||
|
||||
} else if (name.equals("cddb_lookup")) {
|
||||
// get our hands on the CDDB details
|
||||
CDDB.Detail[] details = (CDDB.Detail[])result;
|
||||
|
||||
// replace the info text with the CD info editor
|
||||
remove(_spanel);
|
||||
_infoEditor = new CDInfoEditor(details, _info.length);
|
||||
add(_infoEditor);
|
||||
|
||||
// enable the next button
|
||||
_next.setEnabled(true);
|
||||
|
||||
// relay everything out
|
||||
validate();
|
||||
}
|
||||
}
|
||||
|
||||
public void taskFailed (String name, Throwable exception)
|
||||
{
|
||||
if (name.equals("getinfo")) {
|
||||
_infoText.append("\n\nUnable to read CD info:\n\n" +
|
||||
exception.getMessage());
|
||||
|
||||
} else if (name.equals("cddb_lookup")) {
|
||||
_infoText.append("\n\nCDDB lookup failed: " +
|
||||
exception.getMessage());
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void actionPerformed (ActionEvent e)
|
||||
{
|
||||
String cmd = e.getActionCommand();
|
||||
if (cmd.equals("cancel")) {
|
||||
System.exit(0);
|
||||
|
||||
} else if (cmd.equals("back")) {
|
||||
// pop back to the last panel
|
||||
_frame.popPanel();
|
||||
|
||||
} else if (cmd.equals("next")) {
|
||||
extractEntryAndContinue();
|
||||
|
||||
} else {
|
||||
System.out.println("Unknown action event: " + cmd);
|
||||
}
|
||||
}
|
||||
|
||||
protected void extractEntryAndContinue ()
|
||||
{
|
||||
Entry entry = new Entry();
|
||||
entry.title = _infoEditor.getTitle();
|
||||
if (!validate(entry.title, "Title cannot be blank.")) {
|
||||
return;
|
||||
}
|
||||
|
||||
entry.artist = _infoEditor.getArtist();
|
||||
if (!validate(entry.artist, "Artist cannot be blank.")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] names = _infoEditor.getTrackNames();
|
||||
entry.songs = new Song[names.length];
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
if (!validate(names[i], "Track " + (i+1) +
|
||||
" cannot be blank.")) {
|
||||
return;
|
||||
}
|
||||
Song song = (entry.songs[i] = new Song());
|
||||
song.title = names[i];
|
||||
song.position = i+1;
|
||||
song.votes = "";
|
||||
}
|
||||
|
||||
// create the rip panel and pass the entry and info along
|
||||
_frame.pushPanel(new RipPanel(_info, entry));
|
||||
}
|
||||
|
||||
protected boolean validate (String value, String errmsg)
|
||||
{
|
||||
if (StringUtil.blank(value)) {
|
||||
JOptionPane.showMessageDialog(SwingUtilities.getRootPane(this),
|
||||
errmsg, "Invalid data provided",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected ImporterFrame _frame;
|
||||
protected JButton _next;
|
||||
protected JPanel _spanel;
|
||||
protected JTextArea _infoText;
|
||||
protected CDInfoEditor _infoEditor;
|
||||
|
||||
protected Ripper.TrackInfo[] _info;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
//
|
||||
// $Id: CDDBUtil.java,v 1.1 2000/12/10 07:02:09 mdb Exp $
|
||||
|
||||
package robodj.importer;
|
||||
|
||||
import com.samskivert.net.cddb.*;
|
||||
import robodj.convert.Ripper;
|
||||
import robodj.convert.RipUtil;
|
||||
|
||||
public class CDDBUtil
|
||||
{
|
||||
/**
|
||||
* Does a CDDB lookup for the supplied CD information and returns an
|
||||
* array of disc info objects containing the name information returned
|
||||
* from CDDB.
|
||||
*
|
||||
* @return An array of detail objects, one per match or a zero length
|
||||
* array if no matches were found.
|
||||
*/
|
||||
public static CDDB.Detail[] doCDDBLookup (String hostname,
|
||||
Ripper.TrackInfo[] info)
|
||||
throws Exception
|
||||
{
|
||||
CDDB cddb = new CDDB();
|
||||
|
||||
long discid = RipUtil.computeDiscId(info);
|
||||
String cdid = Long.toString(discid, 0x10);
|
||||
|
||||
// create an array with the track offsets
|
||||
int[] offsets = new int[info.length];
|
||||
for (int i = 0; i < info.length; i++) {
|
||||
offsets[i] = info[i].offset;
|
||||
}
|
||||
|
||||
int length = RipUtil.computeDiscLength(info);
|
||||
|
||||
try {
|
||||
String rsp = cddb.connect(hostname);
|
||||
|
||||
// set the timeout to 30 seconds
|
||||
cddb.setTimeout(30*1000);
|
||||
|
||||
// try a test query
|
||||
CDDB.Entry[] entries = cddb.query(cdid, offsets, length);
|
||||
CDDB.Detail[] details;
|
||||
|
||||
if (entries == null || entries.length == 0) {
|
||||
details = new CDDB.Detail[0];
|
||||
|
||||
} else {
|
||||
details = new CDDB.Detail[entries.length];
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
details[i] = cddb.read(entries[i].category,
|
||||
entries[i].cdid);
|
||||
}
|
||||
}
|
||||
|
||||
return details;
|
||||
|
||||
} finally {
|
||||
cddb.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
//
|
||||
// $Id: CDInfoEditor.java,v 1.3 2001/06/05 17:41:00 mdb Exp $
|
||||
|
||||
package robodj.importer;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.*;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import com.samskivert.swing.*;
|
||||
import com.samskivert.net.cddb.CDDB;
|
||||
|
||||
public class CDInfoEditor
|
||||
extends JPanel
|
||||
{
|
||||
public CDInfoEditor (CDDB.Detail[] details, int numTracks)
|
||||
{
|
||||
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
setLayout(gl);
|
||||
|
||||
// create the selection components
|
||||
JPanel selPanel = new JPanel(new HGroupLayout(GroupLayout.STRETCH));
|
||||
JLabel selLabel = new JLabel("Select...");
|
||||
|
||||
// create a string array for our combo box
|
||||
String[] names = new String[details.length + 1];
|
||||
for (int i = 0; i < details.length; i++) {
|
||||
names[i] = details[i].title;
|
||||
}
|
||||
names[details.length] = "<blank>";
|
||||
|
||||
_selBox = new JComboBox(names);
|
||||
selPanel.add(selLabel, GroupLayout.FIXED);
|
||||
selPanel.add(_selBox);
|
||||
|
||||
_selBox.addActionListener(new ActionListener ()
|
||||
{
|
||||
public void actionPerformed (ActionEvent e)
|
||||
{
|
||||
JComboBox cb = (JComboBox)e.getSource();
|
||||
selectDetail(cb.getSelectedIndex());
|
||||
}
|
||||
});
|
||||
|
||||
add(selPanel, GroupLayout.FIXED);
|
||||
|
||||
// create the table model because we need it to listen to the
|
||||
// title and artist text fields
|
||||
_tmodel = new DetailTableModel(details, numTracks);
|
||||
|
||||
// create the title editing components
|
||||
JPanel titPanel = new JPanel(new HGroupLayout(GroupLayout.STRETCH));
|
||||
JLabel titLabel = new JLabel("Title");
|
||||
_titText = new JTextField();
|
||||
_titText.getDocument().addDocumentListener(_tmodel);
|
||||
_titText.getDocument().putProperty("name", "title");
|
||||
titPanel.add(titLabel, GroupLayout.FIXED);
|
||||
titPanel.add(_titText);
|
||||
add(titPanel, GroupLayout.FIXED);
|
||||
|
||||
// create the artist editing components
|
||||
JPanel artPanel = new JPanel(new HGroupLayout(GroupLayout.STRETCH));
|
||||
JLabel artLabel = new JLabel("Artist");
|
||||
_artText = new JTextField();
|
||||
_artText.getDocument().addDocumentListener(_tmodel);
|
||||
_artText.getDocument().putProperty("name", "artist");
|
||||
artPanel.add(artLabel, GroupLayout.FIXED);
|
||||
artPanel.add(_artText);
|
||||
add(artPanel, GroupLayout.FIXED);
|
||||
|
||||
// create the track table
|
||||
_trackTable = new JTable(_tmodel);
|
||||
TableColumn tcol = _trackTable.getColumnModel().getColumn(0);
|
||||
// i wish this fucking worked
|
||||
// tcol.sizeWidthToFit();
|
||||
// then i wouldn't have do this nasty hack
|
||||
tcol.setMaxWidth(20);
|
||||
|
||||
// make something for it to scroll around in
|
||||
JScrollPane tscroll = new JScrollPane(_trackTable);
|
||||
add(tscroll);
|
||||
|
||||
JPanel butPanel = new JPanel(new HGroupLayout(GroupLayout.NONE));
|
||||
JButton fixCaseBtn = new JButton("Canonicalize text");
|
||||
fixCaseBtn.addActionListener(new ActionListener () {
|
||||
public void actionPerformed (ActionEvent ae) {
|
||||
fixCase();
|
||||
}
|
||||
});
|
||||
butPanel.add(fixCaseBtn);
|
||||
add(butPanel, GroupLayout.FIXED);
|
||||
|
||||
selectDetail(0);
|
||||
}
|
||||
|
||||
public String getTitle ()
|
||||
{
|
||||
return _tmodel.getTitle();
|
||||
}
|
||||
|
||||
public String getArtist ()
|
||||
{
|
||||
return _tmodel.getArtist();
|
||||
}
|
||||
|
||||
public String[] getTrackNames ()
|
||||
{
|
||||
// commit any uncommitted edits the user might have in progress
|
||||
TableCellEditor editor = _trackTable.getCellEditor();
|
||||
if (editor != null) {
|
||||
editor.stopCellEditing();
|
||||
}
|
||||
return _tmodel.getTrackNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the case of the track, title and artist text. This
|
||||
* currently means capitalizing every word and performing some case
|
||||
* fixups according to an exception list.
|
||||
*/
|
||||
public void fixCase ()
|
||||
{
|
||||
// clean up the title
|
||||
String title = fixCase(_tmodel.getTitle());
|
||||
_tmodel.setTitle(title);
|
||||
_titText.setText(title);
|
||||
|
||||
// clean up the artist
|
||||
String artist = fixCase(_tmodel.getArtist());
|
||||
_tmodel.setArtist(artist);
|
||||
_artText.setText(artist);
|
||||
|
||||
// clean up the tracks
|
||||
String[] names = getTrackNames();
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
_tmodel.setTrackName(i, fixCase(names[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the case of the supplied string according to our case fixing
|
||||
* rules.
|
||||
*/
|
||||
protected String fixCase (String text)
|
||||
{
|
||||
StringTokenizer tok = new StringTokenizer(text);
|
||||
StringBuffer ntbuf = new StringBuffer();
|
||||
String excep;
|
||||
|
||||
for (int i = 0; tok.hasMoreTokens(); i++) {
|
||||
String token = tok.nextToken();
|
||||
if (i > 0) {
|
||||
ntbuf.append(" ");
|
||||
}
|
||||
|
||||
if ((excep = getException(token)) != null) {
|
||||
// if this token should be overridden explicitly, do so
|
||||
ntbuf.append(excep);
|
||||
|
||||
} else if (Character.isLowerCase(token.charAt(0))) {
|
||||
// if the first character is lower case, fix it
|
||||
ntbuf.append(Character.toUpperCase(token.charAt(0)));
|
||||
if (token.length() > 1) {
|
||||
ntbuf.append(token.substring(1));
|
||||
}
|
||||
|
||||
} else {
|
||||
// otherwise don't mess with it
|
||||
ntbuf.append(token);
|
||||
}
|
||||
}
|
||||
|
||||
// report modifications for now
|
||||
String ntext = ntbuf.toString();
|
||||
// if (!ntext.equals(text)) {
|
||||
// System.out.println("Adjusted case '" + text + "' to '" +
|
||||
// ntext + "'.");
|
||||
// }
|
||||
|
||||
return ntext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exception string for this token if there is one
|
||||
* otherwise null.
|
||||
*/
|
||||
protected String getException (String token)
|
||||
{
|
||||
// we could toLowerCase() the token and keep all this stuff in a
|
||||
// hashtable, but the list is small and we'd rather not create a
|
||||
// zillion objects with all those toLowerCase() calls, so we just
|
||||
// search the exception list linearly
|
||||
for (int i = 0; i < OVERRIDE_KEYS.length; i++) {
|
||||
if (token.equalsIgnoreCase(OVERRIDE_KEYS[i])) {
|
||||
return OVERRIDE_VALS[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void selectDetail (int index)
|
||||
{
|
||||
_tmodel.selectDetail(index);
|
||||
_titText.setText(_tmodel.getTitle());
|
||||
_artText.setText(_tmodel.getArtist());
|
||||
}
|
||||
|
||||
protected JComboBox _selBox;
|
||||
protected JTextField _titText;
|
||||
protected JTextField _artText;
|
||||
protected JTable _trackTable;
|
||||
|
||||
protected DetailTableModel _tmodel;
|
||||
|
||||
// used in case fixups
|
||||
protected static String[] OVERRIDE_KEYS = {
|
||||
"vs.", "dj", "/"
|
||||
};
|
||||
protected static String[] OVERRIDE_VALS = {
|
||||
"vs.", "DJ", "-"
|
||||
};
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
//
|
||||
// $Id: DetailTableModel.java,v 1.4 2004/01/07 01:57:10 mdb Exp $
|
||||
|
||||
package robodj.importer;
|
||||
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import com.samskivert.net.cddb.CDDB;
|
||||
import robodj.Log;
|
||||
|
||||
public class DetailTableModel
|
||||
extends AbstractTableModel
|
||||
implements DocumentListener
|
||||
{
|
||||
public DetailTableModel (CDDB.Detail[] details, int numTracks)
|
||||
{
|
||||
_details = details;
|
||||
_numTracks = numTracks;
|
||||
_names = new String[_numTracks];
|
||||
|
||||
// copy in the names from the first detail record (or empty
|
||||
// strings if there is no first detail record)
|
||||
copyInfo(0);
|
||||
}
|
||||
|
||||
public void selectDetail (int index)
|
||||
{
|
||||
// copy the track names from the specified detail record into the
|
||||
// names array
|
||||
copyInfo(index);
|
||||
|
||||
// fire a data changed event to let the table know what's up
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public String getTitle ()
|
||||
{
|
||||
return _title;
|
||||
}
|
||||
|
||||
public String getArtist ()
|
||||
{
|
||||
return _artist;
|
||||
}
|
||||
|
||||
// this does not cause the UI displaying the title to update because
|
||||
// it isn't listening to the model, we're only listening to it
|
||||
public void setTitle (String title)
|
||||
{
|
||||
_title = title;
|
||||
}
|
||||
|
||||
// this does not cause the UI displaying the artist to update because
|
||||
// it isn't listening to the model, we're only listening to it
|
||||
public void setArtist (String artist)
|
||||
{
|
||||
_artist = artist;
|
||||
}
|
||||
|
||||
// this does cause the track display to update
|
||||
public void setTrackName (int index, String name)
|
||||
{
|
||||
_names[index] = name;
|
||||
fireTableCellUpdated(index, TITLE_COLUMN);
|
||||
}
|
||||
|
||||
public String[] getTrackNames ()
|
||||
{
|
||||
return _names;
|
||||
}
|
||||
|
||||
public void insertUpdate (DocumentEvent e)
|
||||
{
|
||||
updateText(e.getDocument());
|
||||
}
|
||||
|
||||
public void removeUpdate (DocumentEvent e)
|
||||
{
|
||||
updateText(e.getDocument());
|
||||
}
|
||||
|
||||
public void changedUpdate (DocumentEvent e)
|
||||
{
|
||||
updateText(e.getDocument());
|
||||
}
|
||||
|
||||
protected void updateText (Document doc)
|
||||
{
|
||||
try {
|
||||
if (doc.getProperty("name").equals("title")) {
|
||||
_title = doc.getText(0, doc.getLength());
|
||||
} else if (doc.getProperty("name").equals("artist")) {
|
||||
_artist = doc.getText(0, doc.getLength());
|
||||
}
|
||||
|
||||
} catch (BadLocationException ble) {
|
||||
Log.warning("Can't extract text from text field?!");
|
||||
Log.logStackTrace(ble);
|
||||
}
|
||||
}
|
||||
|
||||
protected void copyInfo (int index)
|
||||
{
|
||||
if (_details.length > index) {
|
||||
// extract the artist and title from the combined title
|
||||
// provided by the detail record
|
||||
String src = _details[index].title;
|
||||
int sidx = src.indexOf("/");
|
||||
if (sidx != -1) {
|
||||
_artist = src.substring(0, sidx).trim();
|
||||
_title = src.substring(sidx+1).trim();
|
||||
} else {
|
||||
_title = src;
|
||||
_artist = "Unknown";
|
||||
}
|
||||
|
||||
for (int i = 0; i < _names.length; i++) {
|
||||
_names[i] = _details[index].trackNames[i];
|
||||
}
|
||||
|
||||
} else {
|
||||
_title = "Unknown";
|
||||
_artist = "Unknown";
|
||||
|
||||
for (int i = 0; i < _names.length; i++) {
|
||||
_names[i] = "Track " + (i+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getRowCount ()
|
||||
{
|
||||
return _numTracks;
|
||||
}
|
||||
|
||||
public int getColumnCount ()
|
||||
{
|
||||
return COLUMN_NAMES.length;
|
||||
}
|
||||
|
||||
public Object getValueAt (int rowIndex, int columnIndex)
|
||||
{
|
||||
switch (columnIndex) {
|
||||
case TITLE_COLUMN:
|
||||
return _names[rowIndex];
|
||||
|
||||
default:
|
||||
case 0:
|
||||
return new Integer(rowIndex+1);
|
||||
}
|
||||
}
|
||||
|
||||
public String getColumnName (int column)
|
||||
{
|
||||
return COLUMN_NAMES[column];
|
||||
}
|
||||
|
||||
public Class getColumnClass (int column)
|
||||
{
|
||||
return COLUMN_CLASSES[column];
|
||||
}
|
||||
|
||||
public boolean isCellEditable (int row, int column)
|
||||
{
|
||||
return column == TITLE_COLUMN;
|
||||
}
|
||||
|
||||
public void setValueAt (Object value, int row, int column)
|
||||
{
|
||||
if (column == TITLE_COLUMN) {
|
||||
_names[row] = (String)value;
|
||||
fireTableCellUpdated(row, column);
|
||||
}
|
||||
}
|
||||
|
||||
protected CDDB.Detail[] _details;
|
||||
protected String _title;
|
||||
protected String _artist;
|
||||
|
||||
protected String[] _names;
|
||||
protected int _numTracks;
|
||||
|
||||
protected static final String[] COLUMN_NAMES = { "T#", "Title" };
|
||||
protected static final Class[] COLUMN_CLASSES = {
|
||||
Integer.class, String.class };
|
||||
|
||||
protected static final int TITLE_COLUMN = 1;
|
||||
}
|
||||