From 69f51a85599e01c688f7c1718c5082868eed2146 Mon Sep 17 00:00:00 2001 From: mdb Date: Sun, 18 Mar 2001 06:58:55 +0000 Subject: [PATCH] Finished up the importer and fixed various things along the way. git-svn-id: https://samskivert.googlecode.com/svn/trunk@101 6335cc39-0255-0410-8fd6-9bcaacd3b74c --- projects/robodj/.cvsignore | 1 + projects/robodj/bin/runjava | 2 +- projects/robodj/build.xml | 59 ++++ projects/robodj/docs/notes.txt | 7 + projects/robodj/lib/.cvsignore | 1 + projects/robodj/src/java/robodj/Log.java | 38 +++ .../src/java/robodj/convert/EncodeTest.java | 33 ++ .../src/java/robodj/convert/LameEncoder.java | 104 ++++++ .../src/java/robodj/convert/RipTest.java | 5 +- .../java/robodj/importer/CDDBLookupPanel.java | 22 +- .../java/robodj/importer/CDInfoEditor.java | 5 +- .../src/java/robodj/importer/Importer.java | 38 ++- .../java/robodj/importer/ImporterFrame.java | 7 +- .../src/java/robodj/importer/Panel.java | 4 +- .../src/java/robodj/importer/RipPanel.java | 312 +++++++++++++++++- .../java/robodj/repository/Repository.java | 200 +++++------ 16 files changed, 689 insertions(+), 149 deletions(-) create mode 100644 projects/robodj/.cvsignore create mode 100644 projects/robodj/build.xml create mode 100644 projects/robodj/docs/notes.txt create mode 100644 projects/robodj/lib/.cvsignore create mode 100644 projects/robodj/src/java/robodj/Log.java create mode 100644 projects/robodj/src/java/robodj/convert/EncodeTest.java create mode 100644 projects/robodj/src/java/robodj/convert/LameEncoder.java diff --git a/projects/robodj/.cvsignore b/projects/robodj/.cvsignore new file mode 100644 index 00000000..27206ca4 --- /dev/null +++ b/projects/robodj/.cvsignore @@ -0,0 +1 @@ +extern diff --git a/projects/robodj/bin/runjava b/projects/robodj/bin/runjava index 5edd6850..29176914 100755 --- a/projects/robodj/bin/runjava +++ b/projects/robodj/bin/runjava @@ -52,7 +52,7 @@ if (defined $libpath) { } # put everything in our class path -my $classpath = "-classpath $jlib:$root/src/java"; +my $classpath = "-classpath $jlib:$root/dist/classes"; # any zip or jar files in our lib/ directory get added to the class path if (opendir(DIR, "$root/lib")) { diff --git a/projects/robodj/build.xml b/projects/robodj/build.xml new file mode 100644 index 00000000..ee216f6f --- /dev/null +++ b/projects/robodj/build.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/robodj/docs/notes.txt b/projects/robodj/docs/notes.txt new file mode 100644 index 00000000..a599d144 --- /dev/null +++ b/projects/robodj/docs/notes.txt @@ -0,0 +1,7 @@ +RoboDJ Notes +------------ + +- Catch database errors after the ripping process and allow use to retry + commit to database after fixing database unavailability. + +- Impersonate a registered CDDB client until we can get "approval". diff --git a/projects/robodj/lib/.cvsignore b/projects/robodj/lib/.cvsignore new file mode 100644 index 00000000..d392f0e8 --- /dev/null +++ b/projects/robodj/lib/.cvsignore @@ -0,0 +1 @@ +*.jar diff --git a/projects/robodj/src/java/robodj/Log.java b/projects/robodj/src/java/robodj/Log.java new file mode 100644 index 00000000..41220aea --- /dev/null +++ b/projects/robodj/src/java/robodj/Log.java @@ -0,0 +1,38 @@ +// +// $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); + } +} diff --git a/projects/robodj/src/java/robodj/convert/EncodeTest.java b/projects/robodj/src/java/robodj/convert/EncodeTest.java new file mode 100644 index 00000000..56856774 --- /dev/null +++ b/projects/robodj/src/java/robodj/convert/EncodeTest.java @@ -0,0 +1,33 @@ +// +// $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); + } + } +} diff --git a/projects/robodj/src/java/robodj/convert/LameEncoder.java b/projects/robodj/src/java/robodj/convert/LameEncoder.java new file mode 100644 index 00000000..0e6c930d --- /dev/null +++ b/projects/robodj/src/java/robodj/convert/LameEncoder.java @@ -0,0 +1,104 @@ +// +// $Id: LameEncoder.java,v 1.1 2001/03/18 06:58:55 mdb Exp $ + +package robodj.convert; + +import java.io.*; +import java.util.ArrayList; + +import gnu.regexp.*; + +/** + * 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("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 + RE regex; + try { + // a line of input looks like this + // 0/1273 ( 0%)| 0:00/ 0:00... + regex = new RE("^\\s*(\\d+)/(\\d+)\\s*\\(.*"); + } catch (REException ree) { + throw new ConvertException("Can't compile regexp?! " + ree); + } + + 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 + REMatch match = regex.getMatch(out); + if (match != null) { + int offset = -1, total = -1; + try { + offset = Integer.parseInt(match.toString(1)); + total = Integer.parseInt(match.toString(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); + } + } +} diff --git a/projects/robodj/src/java/robodj/convert/RipTest.java b/projects/robodj/src/java/robodj/convert/RipTest.java index 7fdc331d..835b3638 100644 --- a/projects/robodj/src/java/robodj/convert/RipTest.java +++ b/projects/robodj/src/java/robodj/convert/RipTest.java @@ -1,5 +1,5 @@ // -// $Id: RipTest.java,v 1.3 2001/02/06 08:18:00 mdb Exp $ +// $Id: RipTest.java,v 1.4 2001/03/18 06:58:55 mdb Exp $ package robodj.convert; @@ -24,6 +24,7 @@ public class RipTest int length = RipUtil.computeDiscLength(info); try { + System.out.println("connecting to: " + hostname); String rsp = cddb.connect(hostname); // set the timeout to 30 seconds @@ -80,7 +81,7 @@ public class RipTest } else { // try the CDDB lookup - lookupCDDB("us.cddb.com", info); + lookupCDDB("ca.freedb.org", info); // rip a track, for fun int track = 2; diff --git a/projects/robodj/src/java/robodj/importer/CDDBLookupPanel.java b/projects/robodj/src/java/robodj/importer/CDDBLookupPanel.java index 7b148519..b0f602ec 100644 --- a/projects/robodj/src/java/robodj/importer/CDDBLookupPanel.java +++ b/projects/robodj/src/java/robodj/importer/CDDBLookupPanel.java @@ -1,5 +1,5 @@ // -// $Id: CDDBLookupPanel.java,v 1.1 2000/12/10 07:02:09 mdb Exp $ +// $Id: CDDBLookupPanel.java,v 1.2 2001/03/18 06:58:55 mdb Exp $ package robodj.importer; @@ -34,11 +34,24 @@ public class CDDBLookupPanel 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); - add(_infoText); + // 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) @@ -82,7 +95,7 @@ public class CDDBLookupPanel public Object invoke () throws Exception { - return CDDBUtil.doCDDBLookup("us.cddb.com", info); + return CDDBUtil.doCDDBLookup("ca.freedb.org", info); } public boolean abort () @@ -97,7 +110,7 @@ public class CDDBLookupPanel CDDB.Detail[] details = (CDDB.Detail[])result; // replace the info text with the CD info editor - remove(_infoText); + remove(_spanel); _infoEditor = new CDInfoEditor(details, _info.length); add(_infoEditor); @@ -179,6 +192,7 @@ public class CDDBLookupPanel protected ImporterFrame _frame; protected JButton _next; + protected JPanel _spanel; protected JTextArea _infoText; protected CDInfoEditor _infoEditor; diff --git a/projects/robodj/src/java/robodj/importer/CDInfoEditor.java b/projects/robodj/src/java/robodj/importer/CDInfoEditor.java index 2989d77f..31ab8e32 100644 --- a/projects/robodj/src/java/robodj/importer/CDInfoEditor.java +++ b/projects/robodj/src/java/robodj/importer/CDInfoEditor.java @@ -1,5 +1,5 @@ // -// $Id: CDInfoEditor.java,v 1.1 2000/12/10 07:02:09 mdb Exp $ +// $Id: CDInfoEditor.java,v 1.2 2001/03/18 06:58:55 mdb Exp $ package robodj.importer; @@ -26,10 +26,11 @@ public class CDInfoEditor JLabel selLabel = new JLabel("Select..."); // create a string array for our combo box - String[] names = new String[details.length]; + String[] names = new String[details.length + 1]; for (int i = 0; i < details.length; i++) { names[i] = details[i].title; } + names[details.length] = ""; _selBox = new JComboBox(names); selPanel.add(selLabel, GroupLayout.FIXED); diff --git a/projects/robodj/src/java/robodj/importer/Importer.java b/projects/robodj/src/java/robodj/importer/Importer.java index c5917b9a..1d00072a 100644 --- a/projects/robodj/src/java/robodj/importer/Importer.java +++ b/projects/robodj/src/java/robodj/importer/Importer.java @@ -1,9 +1,15 @@ // -// $Id: Importer.java,v 1.1 2000/12/10 07:02:09 mdb Exp $ +// $Id: Importer.java,v 1.2 2001/03/18 06:58:55 mdb Exp $ package robodj.importer; +import java.sql.SQLException; +import java.util.Properties; + import com.samskivert.util.Log; +import com.samskivert.util.PropertiesUtil; + +import robodj.repository.Repository; /** * The importer is the GUI-based application for ripping, encoding and @@ -11,10 +17,40 @@ import com.samskivert.util.Log; */ public class Importer { + /** + * This is the string we use for the source field of entries created + * by the importer. + */ + public static final String ENTRY_SOURCE = "CD"; + public static Log log = new Log("importer"); + public static Properties config; + + public static Repository repository; + public static void main (String[] args) { + // set up our configuration by hand for now + config = new Properties(); + config.setProperty("repository.db.driver", "org.gjt.mm.mysql.Driver"); + config.setProperty("repository.db.username", "www"); + config.setProperty("repository.db.password", "Il0ve2PL@Y"); + config.setProperty("repository.db.url", + "jdbc:mysql://depravity:3306/robodj"); + config.setProperty("repository.basedir", "/export/robodj/repository"); + + // create an interface to the database repository + try { + Properties dbprops = + PropertiesUtil.getSubProperties(config, "repository.db"); + repository = new Repository(dbprops); + } catch (SQLException sqe) { + System.err.println("Unable to establish communication " + + "with music database: " + sqe); + System.exit(-1); + } + ImporterFrame frame = new ImporterFrame(); InsertCDPanel panel = new InsertCDPanel(); frame.setPanel(panel); diff --git a/projects/robodj/src/java/robodj/importer/ImporterFrame.java b/projects/robodj/src/java/robodj/importer/ImporterFrame.java index c1477044..89edb7cf 100644 --- a/projects/robodj/src/java/robodj/importer/ImporterFrame.java +++ b/projects/robodj/src/java/robodj/importer/ImporterFrame.java @@ -1,5 +1,5 @@ // -// $Id: ImporterFrame.java,v 1.1 2000/12/10 07:02:09 mdb Exp $ +// $Id: ImporterFrame.java,v 1.2 2001/03/18 06:58:55 mdb Exp $ package robodj.importer; @@ -24,7 +24,9 @@ public class ImporterFrame extends JFrame _top.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); // create a container for our control buttons - _buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + GroupLayout bgl = new HGroupLayout(GroupLayout.NONE); + bgl.setJustification(GroupLayout.RIGHT); + _buttonPanel = new JPanel(bgl); // stick it into the frame _top.add(_buttonPanel, GroupLayout.FIXED); @@ -51,7 +53,6 @@ public class ImporterFrame extends JFrame // then stick the new panel in there if (panel != null) { _panel = panel; - _panel.setBackground(Color.yellow); _top.add(_panel, 0); // let the panel know that it was added _panel.wasAddedToFrame(this); diff --git a/projects/robodj/src/java/robodj/importer/Panel.java b/projects/robodj/src/java/robodj/importer/Panel.java index a2093f3b..1d9a375f 100644 --- a/projects/robodj/src/java/robodj/importer/Panel.java +++ b/projects/robodj/src/java/robodj/importer/Panel.java @@ -1,8 +1,10 @@ // -// $Id: Panel.java,v 1.1 2000/12/10 07:02:09 mdb Exp $ +// $Id: Panel.java,v 1.2 2001/03/18 06:58:55 mdb Exp $ package robodj.importer; +import javax.swing.JPanel; + public class Panel extends JPanel { } diff --git a/projects/robodj/src/java/robodj/importer/RipPanel.java b/projects/robodj/src/java/robodj/importer/RipPanel.java index 1bdb747b..f66add90 100644 --- a/projects/robodj/src/java/robodj/importer/RipPanel.java +++ b/projects/robodj/src/java/robodj/importer/RipPanel.java @@ -1,11 +1,12 @@ // -// $Id: RipPanel.java,v 1.1 2000/12/10 07:02:09 mdb Exp $ +// $Id: RipPanel.java,v 1.2 2001/03/18 06:58:55 mdb Exp $ package robodj.importer; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.File; import java.net.URL; import java.util.ArrayList; @@ -16,6 +17,7 @@ import com.samskivert.swing.util.*; import com.samskivert.swing.*; import com.samskivert.util.StringUtil; +import robodj.Log; import robodj.convert.*; import robodj.repository.*; @@ -30,39 +32,324 @@ public class RipPanel setLayout(gl); // create our label for the left hand side - JLabel cdlabel = new JLabel("Ripping", JLabel.CENTER); + JLabel cdlabel = new JLabel("Converting", JLabel.CENTER); cdlabel.setFont(new Font("Helvetica", Font.BOLD, 24)); add(cdlabel, GroupLayout.FIXED); + // create a panel for the progress display + GroupLayout vgl = new VGroupLayout(GroupLayout.STRETCH); + vgl.setOffAxisPolicy(GroupLayout.STRETCH); + JPanel ppanel = new JPanel(vgl); + + // create the overall progress indicator + JLabel opl = new JLabel("Overall progress", JLabel.LEFT); + ppanel.add(opl, GroupLayout.FIXED); + // overall progress assumes equal time spent ripping and encoding + // each track on the disc. not entirely accurate, but reasonable + // for giving a general idea of how far along we are + _oprogress = new JProgressBar(0, 200*info.length); + ppanel.add(_oprogress, GroupLayout.FIXED); + + // create the task progress indicator + JLabel tpl = new JLabel("Track progress", JLabel.LEFT); + ppanel.add(tpl, GroupLayout.FIXED); + _progress = new JProgressBar(0, 100); + ppanel.add(_progress, GroupLayout.FIXED); + + // create the status log + JLabel sl = new JLabel("Status", JLabel.LEFT); + ppanel.add(sl, GroupLayout.FIXED); + _statusLog = new JTextArea(); + _statusLog.setLineWrap(true); + _statusLog.setEditable(false); + // make something for it to scroll around in + ppanel.add(new JScrollPane(_statusLog)); + + // add our panel to the main group + add(ppanel); + // save this stuff for later _info = info; _entry = entry; } + protected void postAsyncStatus (final String status) + { + SwingUtilities.invokeLater(new Runnable() { + public void run () + { + _statusLog.append(status + "\n"); + } + }); + } + + protected void postAsyncProgress (final int overallComplete, + final int taskComplete) + { + SwingUtilities.invokeLater(new Runnable() { + public void run () + { + // update our progress indicators + _oprogress.setValue(overallComplete); + _progress.setValue(taskComplete); + } + }); + } + + protected static String createTempPath (int trackno, String ext) + { + return "/tmp/track" + trackno + "." + ext; + } + + /** Handles the ripping and encoding of the tracks. */ + protected class RipperTask + implements Task, ConversionProgressListener + { + public RipperTask (Ripper.TrackInfo[] info) + { + _info = info; + } + + public Object invoke () + throws Exception + { + Ripper ripper = new CDParanoiaRipper(); + Encoder encoder = new LameEncoder(); + + for (int i = 0; i < _info.length; i++) { + _track = i+1; + + // rip the track + _encoding = false; + // it takes a second or so for the ripper to start + // reporting progress, so we clear out the progress + // indicator so that the 100% progress of the previous + // track doesn't look like it applies to this new track + // while we're spinning up + updateProgress(0); + postAsyncStatus("Ripping track " + _track + "..."); + String tpath = createTempPath(_track, "wav"); + ripper.ripTrack(_info, _track, tpath, this); + + // then encode it + _encoding = true; + updateProgress(0); + postAsyncStatus("Encoding track " + _track + "..."); + String dpath = createTempPath(_track, "mp3"); + encoder.encodeTrack(tpath, dpath, this); + + // remove the source wav file + File tfile = new File(tpath); + if (!tfile.delete()) { + postAsyncStatus("Unable to remove temp file " + + "for track " + _track + "."); + } + } + + return null; + } + + public boolean abort () + { + // no abort presently + return false; + } + + public void updateProgress (int percentComplete) + { + // first add completeness for tracks we've already done + int overallComplete = (_track-1) * 200; + // if we're encoding, then we finished ripping this track + if (_encoding) { + overallComplete += 100; + } + // finally add the percent complete for the current task + overallComplete += percentComplete; + // and pass it all on to the UI for display + postAsyncProgress(overallComplete, percentComplete); + } + + protected Ripper.TrackInfo[] _info; + protected int _track; + protected boolean _encoding; + } + + /** Commits the completed entry into the repository. */ + protected class CommitterTask + implements Task + { + public CommitterTask (Entry entry, Ripper.TrackInfo[] info) + { + _entry = entry; + _info = info; + } + + public Object invoke () + throws Exception + { + int entryid = -1; + + try { + // determine the target directory for the mp3 files + String rbase = + Importer.config.getProperty("repository.basedir"); + if (StringUtil.blank(rbase)) { + throw new Exception("No mp3 repository directory " + + "specified."); + } + + // ensure the repository directory exists + File rbf = new File(rbase); + if (!rbf.exists() || !rbf.isDirectory()) { + throw new Exception("Repository base directory '" + + rbase + "' is not valid."); + } + + postAsyncStatus("Creating repository directory."); + // create the hash directory + int hcode = _entry.title.hashCode(); + if (hcode < 0) { + hcode *= -1; + } + System.out.println(Integer.toHexString(255 % 0xFF)); + String hash = Integer.toHexString(hcode % 0xFF); + StringBuffer tpath = new StringBuffer(rbase); + if (tpath.charAt(tpath.length()-1) != '/') { + tpath.append("/"); + } + tpath.append(hash); + + File tdir = new File(tpath.toString()); + if (!tdir.exists()) { + if (!tdir.mkdir()) { + throw new Exception("Unable to create target " + + "directory: " + tpath); + } + + } else { + // make sure it's a directory + if (!tdir.isDirectory()) { + throw new Exception("Target directory '" + tpath + + "' is not valid."); + } + } + + // fill in the entry source since we ripped this from CD + _entry.source = Importer.ENTRY_SOURCE; + + // fill in blanks for the locations of the tunes because + // we won't know the name of the directory until we are + // assigned an entry id + for (int i = 0; i < _entry.songs.length; i++) { + _entry.songs[i].location = ""; + } + + postAsyncStatus("Creating database entry."); + // insert the entry into the repository so that we get the + // proper entry id which we'll need to put the tracks in their + // proper place in the repository + Importer.repository.insertEntry(_entry); + // track this so that we can clean up in case of failure + entryid = _entry.entryid; + + // now that we have an entry id, fully construct the + // target path + tpath.append("/").append(_entry.entryid); + tdir = new File(tpath.toString()); + if (!tdir.mkdir()) { + throw new Exception("Unable to create target " + + "directory: " + tpath); + } + + postAsyncStatus("Moving tracks into repository directory."); + // move the tracks into the target directory + tpath.append("/"); + for (int i = 0; i < _entry.songs.length; i++) { + int tno = i+1; + + // figure out the necessary paths + String npath = tpath.toString() + tno + ".mp3"; + String opath = createTempPath(tno, "mp3"); + + // move the file + File ofile = new File(opath); + File nfile = new File(npath); + if (!ofile.renameTo(nfile)) { + throw new Exception("Unable to move track " + tno + + " to target location: " + npath); + } + + // and update the song object + _entry.songs[i].location = npath; + } + + // finally update the entry in the database + Importer.repository.updateEntry(_entry); + // and clear out the entryid to indicate success + entryid = -1; + + postAsyncStatus("Import complete."); + return null; + + } finally { + // if we got halfway through creating our entry, remove it + // from the database + if (entryid != -1) { + Importer.repository.deleteEntry(_entry); + } + } + } + + public boolean abort () + { + // we don't support aborting. this shouldn't take long at all + return false; + } + + protected Entry _entry; + protected Ripper.TrackInfo[] _info; + } + public void wasAddedToFrame (ImporterFrame frame) { // keep this for later _frame = frame; - frame.addControlButton("Cancel", "cancel", this); + // add our next and cancel buttons + _cancel = frame.addControlButton("Cancel", "cancel", this); _next = frame.addControlButton("Next...", "next", this); _next.setEnabled(false); + + // create our info task and set it a running + Task ripTask = new RipperTask(_info); + TaskMaster.invokeTask("convert", ripTask, this); } public void taskCompleted (String name, Object result) { - if (name.equals("getinfo")) { + if (name.equals("convert")) { + // yay! we're all done. commit the entry to the repository + Task commitTask = new CommitterTask(_entry, _info); + TaskMaster.invokeTask("commit", commitTask, this); - } else if (name.equals("cddb_lookup")) { + } else if (name.equals("commit")) { + // we're all done. fix up the buttons and call it good + _cancel.setEnabled(false); + _next.setLabel("Exit"); + _next.setEnabled(true); } } public void taskFailed (String name, Throwable exception) { - if (name.equals("getinfo")) { - - } else if (name.equals("cddb_lookup")) { - } + String msg; + if (Exception.class.equals(exception.getClass())) { + msg = exception.getMessage(); + } else { + msg = exception.toString(); + } + _statusLog.append("Error: " + msg + "\n"); + Log.logStackTrace(exception); } public void actionPerformed (ActionEvent e) @@ -72,6 +359,7 @@ public class RipPanel System.exit(0); } else if (cmd.equals("next")) { + System.exit(0); } else { System.out.println("Unknown action event: " + cmd); @@ -80,6 +368,12 @@ public class RipPanel protected ImporterFrame _frame; protected JButton _next; + protected JButton _cancel; + protected JLabel _actionLabel; + + protected JProgressBar _oprogress; + protected JProgressBar _progress; + protected JTextArea _statusLog; protected Ripper.TrackInfo[] _info; protected Entry _entry; diff --git a/projects/robodj/src/java/robodj/repository/Repository.java b/projects/robodj/src/java/robodj/repository/Repository.java index 08263744..1cbf502d 100644 --- a/projects/robodj/src/java/robodj/repository/Repository.java +++ b/projects/robodj/src/java/robodj/repository/Repository.java @@ -1,5 +1,5 @@ // -// $Id: Repository.java,v 1.2 2001/02/13 05:48:29 mdb Exp $ +// $Id: Repository.java,v 1.3 2001/03/18 06:58:55 mdb Exp $ package robodj.repository; @@ -91,39 +91,30 @@ public class Repository extends MySQLRepository * and the song objects) will be filled in with the entryid of the * newly created entry. */ - public void insertEntry (Entry entry) + public void insertEntry (final Entry entry) throws SQLException { - try { - // insert the entry into the entry table - _etable.insert(entry); + execute(new Operation () { + public void invoke () throws SQLException + { + // insert the entry into the entry table + _etable.insert(entry); - // update the entryid now that it's known - entry.entryid = lastInsertedId(); + // update the entryid now that it's known + entry.entryid = lastInsertedId(); - // and insert all of it's songs into the songs table - if (entry.songs != null) { - for (int i = 0; i < entry.songs.length; i++) { - // insert the proper entryid - entry.songs[i].entryid = entry.entryid; - _stable.insert(entry.songs[i]); - // find out what songid was assigned - entry.songs[i].songid = lastInsertedId(); - } - } - - _session.commit(); - - } catch (SQLException sqe) { - // back out our changes if something got hosed - _session.rollback(); - throw sqe; - - } catch (RuntimeException rte) { - // back out our changes if something got hosed - _session.rollback(); - throw rte; - } + // and insert all of it's songs into the songs table + if (entry.songs != null) { + for (int i = 0; i < entry.songs.length; i++) { + // insert the proper entryid + entry.songs[i].entryid = entry.entryid; + _stable.insert(entry.songs[i]); + // find out what songid was assigned + entry.songs[i].songid = lastInsertedId(); + } + } + } + }); } /** @@ -136,57 +127,40 @@ public class Repository extends MySQLRepository * @see addSongToEntry * @see removeSongFromEntry */ - public void updateEntry (Entry entry) + public void updateEntry (final Entry entry) throws SQLException { - try { - // update the entry - _etable.update(entry); + execute(new Operation () { + public void invoke () throws SQLException + { + // update the entry + _etable.update(entry); - // and the entry's songs - if (entry.songs != null) { - for (int i = 0; i < entry.songs.length; i++) { - _stable.update(entry.songs[i]); - } - } - - _session.commit(); - - } catch (SQLException sqe) { - // back out our changes if something got hosed - _session.rollback(); - throw sqe; - - } catch (RuntimeException rte) { - // back out our changes if something got hosed - _session.rollback(); - throw rte; - } + // and the entry's songs + if (entry.songs != null) { + for (int i = 0; i < entry.songs.length; i++) { + _stable.update(entry.songs[i]); + } + } + } + }); } /** * Removes the entry (and all associated songs) from the database. */ - public void deleteEntry (Entry entry) + public void deleteEntry (final Entry entry) throws SQLException { - try { - // remove the entry from the entry table and the foreign key - // constraints will automatically remove all of the - // corresponding songs - _etable.delete(entry); - _session.commit(); - - } catch (SQLException sqe) { - // back out our changes if something got hosed - _session.rollback(); - throw sqe; - - } catch (RuntimeException rte) { - // back out our changes if something got hosed - _session.rollback(); - throw rte; - } + execute(new Operation () { + public void invoke () throws SQLException + { + // remove the entry from the entry table and the foreign + // key constraints will automatically remove all of the + // corresponding songs + _etable.delete(entry); + } + }); } /** @@ -201,81 +175,55 @@ public class Repository extends MySQLRepository * * @see updateSong */ - public void addSongToEntry (Entry entry, Song song) + public void addSongToEntry (final Entry entry, final Song song) throws SQLException { - try { - // fill in the appropriate entry id value - song.entryid = entry.entryid; + execute(new Operation () { + public void invoke () throws SQLException + { + // fill in the appropriate entry id value + song.entryid = entry.entryid; - // and stick the song into the database - _stable.insert(song); - // communicate the songid back to the caller - song.songid = lastInsertedId(); - _session.commit(); - - } catch (SQLException sqe) { - // back out our changes if something got hosed - _session.rollback(); - throw sqe; - - } catch (RuntimeException rte) { - // back out our changes if something got hosed - _session.rollback(); - throw rte; - } + // and stick the song into the database + _stable.insert(song); + // communicate the songid back to the caller + song.songid = lastInsertedId(); + } + }); } /** * Updates the specified song individually. The songid and entryid * parameters should already be set to the appropriate values. */ - public void updateSong (Song song) + public void updateSong (final Song song) throws SQLException { - try { - _stable.update(song); - _session.commit(); - - } catch (SQLException sqe) { - // back out our changes if something got hosed - _session.rollback(); - throw sqe; - - } catch (RuntimeException rte) { - // back out our changes if something got hosed - _session.rollback(); - throw rte; - } + execute(new Operation () { + public void invoke () throws SQLException + { + _stable.update(song); + } + }); } /** * Creates a category with the specified name and returns the id of * that new category. */ - public int createCategory (String name) + public int createCategory (final String name) throws SQLException { - try { - Category cat = new Category(); - cat.name = name; - _ctable.insert(cat); + execute(new Operation () { + public void invoke () throws SQLException + { + Category cat = new Category(); + cat.name = name; + _ctable.insert(cat); + } + }); - int categoryid = lastInsertedId(); - _session.commit(); - - return categoryid; - - } catch (SQLException sqe) { - // back out our changes if something got hosed - _session.rollback(); - throw sqe; - - } catch (RuntimeException rte) { - // back out our changes if something got hosed - _session.rollback(); - throw rte; - } + return lastInsertedId(); } /**