diff --git a/projects/robodj/src/java/robodj/importer/CDDBLookupPanel.java b/projects/robodj/src/java/robodj/importer/CDDBLookupPanel.java new file mode 100644 index 00000000..7b148519 --- /dev/null +++ b/projects/robodj/src/java/robodj/importer/CDDBLookupPanel.java @@ -0,0 +1,186 @@ +// +// $Id: CDDBLookupPanel.java,v 1.1 2000/12/10 07:02:09 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); + + // 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); + } + + public void wasAddedToFrame (ImporterFrame frame) + { + // keep this for later + _frame = frame; + + frame.addControlButton("Cancel", "cancel", this); + _next = frame.addControlButton("Next...", "next", this); + _next.setEnabled(false); + + // create our info task and set it a running + Task infoTask = new Task() + { + public Object invoke () + throws Exception + { + Ripper ripper = new CDParanoiaRipper(); + return ripper.getTrackInfo(); + } + + public boolean abort () + { + return false; + } + }; + TaskMaster.invokeTask("getinfo", infoTask, 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("us.cddb.com", 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(_infoText); + _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()); + } + } + + public void actionPerformed (ActionEvent e) + { + String cmd = e.getActionCommand(); + if (cmd.equals("cancel")) { + System.exit(0); + + } 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; + } + + // create the rip panel and pass the entry and info along + RipPanel panel = new RipPanel(_info, entry); + _frame.setPanel(panel); + } + + 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 JTextArea _infoText; + protected CDInfoEditor _infoEditor; + + protected Ripper.TrackInfo[] _info; +} diff --git a/projects/robodj/src/java/robodj/importer/CDDBUtil.java b/projects/robodj/src/java/robodj/importer/CDDBUtil.java new file mode 100644 index 00000000..4f61ec67 --- /dev/null +++ b/projects/robodj/src/java/robodj/importer/CDDBUtil.java @@ -0,0 +1,64 @@ +// +// $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(); + } + } +} diff --git a/projects/robodj/src/java/robodj/importer/CDInfoEditor.java b/projects/robodj/src/java/robodj/importer/CDInfoEditor.java new file mode 100644 index 00000000..2989d77f --- /dev/null +++ b/projects/robodj/src/java/robodj/importer/CDInfoEditor.java @@ -0,0 +1,121 @@ +// +// $Id: CDInfoEditor.java,v 1.1 2000/12/10 07:02:09 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 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]; + for (int i = 0; i < details.length; i++) { + names[i] = details[i].title; + } + + _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); + + 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(); + } + + 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; +} diff --git a/projects/robodj/src/java/robodj/importer/DetailTableModel.java b/projects/robodj/src/java/robodj/importer/DetailTableModel.java new file mode 100644 index 00000000..f851471f --- /dev/null +++ b/projects/robodj/src/java/robodj/importer/DetailTableModel.java @@ -0,0 +1,172 @@ +// +// $Id: DetailTableModel.java,v 1.1 2000/12/10 07:02:09 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.util.Log; +import com.samskivert.net.cddb.CDDB; + +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; + } + + 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) { + Importer.log.warning("Can't extract text from text field?!"); + Importer.log.logStackTrace(Log.WARNING, 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 = ""; + _artist = ""; + + for (int i = 0; i < _names.length; i++) { + _names[i] = ""; + } + } + } + + 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; +} diff --git a/projects/robodj/src/java/robodj/importer/Importer.java b/projects/robodj/src/java/robodj/importer/Importer.java new file mode 100644 index 00000000..c5917b9a --- /dev/null +++ b/projects/robodj/src/java/robodj/importer/Importer.java @@ -0,0 +1,23 @@ +// +// $Id: Importer.java,v 1.1 2000/12/10 07:02:09 mdb Exp $ + +package robodj.importer; + +import com.samskivert.util.Log; + +/** + * The importer is the GUI-based application for ripping, encoding and + * importing CDs and other music into the RoboDJ system. + */ +public class Importer +{ + public static Log log = new Log("importer"); + + public static void main (String[] args) + { + ImporterFrame frame = new ImporterFrame(); + InsertCDPanel panel = new InsertCDPanel(); + frame.setPanel(panel); + frame.setVisible(true); + } +} diff --git a/projects/robodj/src/java/robodj/importer/ImporterFrame.java b/projects/robodj/src/java/robodj/importer/ImporterFrame.java new file mode 100644 index 00000000..c1477044 --- /dev/null +++ b/projects/robodj/src/java/robodj/importer/ImporterFrame.java @@ -0,0 +1,86 @@ +// +// $Id: ImporterFrame.java,v 1.1 2000/12/10 07:02:09 mdb Exp $ + +package robodj.importer; + +import java.awt.*; +import java.awt.event.ActionListener; +import javax.swing.*; + +import com.samskivert.swing.*; + +public class ImporterFrame extends JFrame +{ + public ImporterFrame () + { + super("RoboDJ CD Importer"); + + _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 a container for our control buttons + _buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + + // stick it into the frame + _top.add(_buttonPanel, 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); + } + + public void setPanel (ImporterPanel panel) + { + boolean repack = true; + + // clear out any old panel + if (_panel != null) { + _top.remove(_panel); + // don't repack if there was an old panel + repack = false; + } + + // clear out old control buttons + clearControlButtons(); + + // 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); + } + + // and possibly repack ourselves + if (repack) { + pack(); + } else { + validate(); + } + } + + public JButton addControlButton (String label, String command, + ActionListener target) + { + JButton abutton = new JButton(label); + abutton.setActionCommand(command); + abutton.addActionListener(target); + _buttonPanel.add(abutton); + return abutton; + } + + public void clearControlButtons () + { + _buttonPanel.removeAll(); + } + + protected ImporterPanel _panel; + protected JPanel _top; + protected JPanel _buttonPanel; +} diff --git a/projects/robodj/src/java/robodj/importer/ImporterPanel.java b/projects/robodj/src/java/robodj/importer/ImporterPanel.java new file mode 100644 index 00000000..8b3594eb --- /dev/null +++ b/projects/robodj/src/java/robodj/importer/ImporterPanel.java @@ -0,0 +1,25 @@ +// +// $Id: ImporterPanel.java,v 1.1 2000/12/10 07:02:09 mdb Exp $ + +package robodj.importer; + +import java.awt.LayoutManager; +import javax.swing.JPanel; + +public abstract class ImporterPanel extends JPanel +{ + public ImporterPanel () + { + } + + public ImporterPanel (LayoutManager layout) + { + super(layout); + } + + /** + * When an importer panel is added to the importer frame, it is + * notified so that it can create the appropriate control buttons. + */ + public abstract void wasAddedToFrame (ImporterFrame frame); +} diff --git a/projects/robodj/src/java/robodj/importer/InsertCDPanel.java b/projects/robodj/src/java/robodj/importer/InsertCDPanel.java new file mode 100644 index 00000000..c30fbad8 --- /dev/null +++ b/projects/robodj/src/java/robodj/importer/InsertCDPanel.java @@ -0,0 +1,61 @@ +// +// $Id: InsertCDPanel.java,v 1.1 2000/12/10 07:02:09 mdb Exp $ + +package robodj.importer; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.URL; +import javax.swing.*; + +public class InsertCDPanel + extends ImporterPanel + implements ActionListener +{ + public InsertCDPanel () + { + super(new GridLayout(1, 2)); + + // create our image for the left hand side + ClassLoader cl = getClass().getClassLoader(); + URL cdurl = cl.getResource("robodj/importer/cd.jpg"); + ImageIcon cdicon = new ImageIcon(cdurl); + JLabel cdlabel = new JLabel(cdicon); + add(cdlabel); + + // create the "insert cd" text label for the right hand side + JLabel ilabel = new JLabel("Please insert the CD...", + JLabel.CENTER); + add(ilabel); + + // give ourselves a wee bit of a border + setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + } + + public void wasAddedToFrame (ImporterFrame frame) + { + frame.addControlButton("Cancel", "cancel", this); + frame.addControlButton("Next...", "next", this); + + // keep a handle on this for later + _frame = frame; + } + + public void actionPerformed (ActionEvent e) + { + String cmd = e.getActionCommand(); + if (cmd.equals("cancel")) { + System.exit(0); + + } else if (cmd.equals("next")) { + CDDBLookupPanel panel = new CDDBLookupPanel(); + _frame.setPanel(panel); + + } else { + System.out.println("Unknown action event: " + cmd); + } + } + + protected ImporterFrame _frame; +} diff --git a/projects/robodj/src/java/robodj/importer/Makefile b/projects/robodj/src/java/robodj/importer/Makefile new file mode 100644 index 00000000..8cf51249 --- /dev/null +++ b/projects/robodj/src/java/robodj/importer/Makefile @@ -0,0 +1,17 @@ +# +# $Id: Makefile,v 1.1 2000/12/10 07:02:09 mdb Exp $ + +ROOT = ../.. + +SRCS = \ + CDDBLookupPanel.java \ + CDDBUtil.java \ + CDInfoEditor.java \ + DetailTableModel.java \ + Importer.java \ + ImporterFrame.java \ + ImporterPanel.java \ + InsertCDPanel.java \ + RipPanel.java \ + +include $(ROOT)/build/Makefile.java diff --git a/projects/robodj/src/java/robodj/importer/Panel.java b/projects/robodj/src/java/robodj/importer/Panel.java new file mode 100644 index 00000000..a2093f3b --- /dev/null +++ b/projects/robodj/src/java/robodj/importer/Panel.java @@ -0,0 +1,8 @@ +// +// $Id: Panel.java,v 1.1 2000/12/10 07:02:09 mdb Exp $ + +package robodj.importer; + +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 new file mode 100644 index 00000000..1bdb747b --- /dev/null +++ b/projects/robodj/src/java/robodj/importer/RipPanel.java @@ -0,0 +1,86 @@ +// +// $Id: RipPanel.java,v 1.1 2000/12/10 07:02:09 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 RipPanel + extends ImporterPanel + implements ActionListener, TaskObserver +{ + public RipPanel (Ripper.TrackInfo[] info, Entry entry) + { + GroupLayout gl = new HGroupLayout(GroupLayout.STRETCH); + gl.setOffAxisPolicy(GroupLayout.STRETCH); + setLayout(gl); + + // create our label for the left hand side + JLabel cdlabel = new JLabel("Ripping", JLabel.CENTER); + cdlabel.setFont(new Font("Helvetica", Font.BOLD, 24)); + add(cdlabel, GroupLayout.FIXED); + + // save this stuff for later + _info = info; + _entry = entry; + } + + public void wasAddedToFrame (ImporterFrame frame) + { + // keep this for later + _frame = frame; + + frame.addControlButton("Cancel", "cancel", this); + _next = frame.addControlButton("Next...", "next", this); + _next.setEnabled(false); + } + + public void taskCompleted (String name, Object result) + { + if (name.equals("getinfo")) { + + } else if (name.equals("cddb_lookup")) { + } + } + + public void taskFailed (String name, Throwable exception) + { + if (name.equals("getinfo")) { + + } else if (name.equals("cddb_lookup")) { + } + } + + public void actionPerformed (ActionEvent e) + { + String cmd = e.getActionCommand(); + if (cmd.equals("cancel")) { + System.exit(0); + + } else if (cmd.equals("next")) { + + } else { + System.out.println("Unknown action event: " + cmd); + } + } + + protected ImporterFrame _frame; + protected JButton _next; + + protected Ripper.TrackInfo[] _info; + protected Entry _entry; +} diff --git a/projects/robodj/src/java/robodj/importer/cd.jpg b/projects/robodj/src/java/robodj/importer/cd.jpg new file mode 100644 index 00000000..aa58cb6a Binary files /dev/null and b/projects/robodj/src/java/robodj/importer/cd.jpg differ