Index: /trunk/src/main/org/lastpod/parser/ItunesStatsParser.java
===================================================================
--- /trunk/src/main/org/lastpod/parser/ItunesStatsParser.java (revision 91)
+++ /trunk/src/main/org/lastpod/parser/ItunesStatsParser.java (revision 93)
@@ -22,4 +22,5 @@
import org.lastpod.util.IoUtils;
+import org.lastpod.util.ItunesStatsFilter;
import java.io.BufferedInputStream;
@@ -113,5 +114,7 @@
playCountsBufferedIn = new BufferedInputStream(playCountsFileIn, 65535);
- return parseitunesStats(playCountsBufferedIn);
+ List trackList = parseitunesStats(playCountsBufferedIn);
+
+ return manufactureLastPlayed(trackList);
} catch (IOException e) {
String errorMsg =
@@ -145,6 +148,4 @@
IoUtils.skipFully(itunesStatsistream, 3); //skip rest of header
- Calendar calendar = Calendar.getInstance();
-
for (int i = 0; i < (numentries - 1); i++) {
itunesStatsistream.mark(1048576); //save beginning of entry location
@@ -164,7 +165,4 @@
TrackItem temptrack = (TrackItem) trackList.get(i);
temptrack.setPlaycount(playcount);
- calendar.add(Calendar.SECOND, -(int) temptrack.getLength());
- temptrack.setLastplayed(calendar.getTimeInMillis() / 1000);
-
recentPlays.add(trackList.get(i));
@@ -173,5 +171,5 @@
for (long j = 0; j < numberToManufacture; j++) {
- temptrack = manufactureTrack(temptrack, calendar);
+ temptrack = manufactureTrack(temptrack);
recentPlays.add(temptrack);
}
@@ -193,9 +191,6 @@
* @return A manufactured TrackItem.
*/
- private TrackItem manufactureTrack(TrackItem temptrack, Calendar calendar) {
+ private TrackItem manufactureTrack(TrackItem temptrack) {
TrackItem manufacturedTrack = new TrackItem(temptrack);
- calendar.add(Calendar.SECOND, -(int) manufacturedTrack.getLength());
- manufacturedTrack.setLastplayed(calendar.getTimeInMillis() / 1000);
-
manufacturedTrack.setPlaycount(1);
temptrack.setPlaycount(1);
@@ -203,3 +198,35 @@
return manufacturedTrack;
}
+
+ /**
+ * Manufactures the last played times for all the track items.
+ * @param trackItems The list of track items to modify.
+ * @return The modified list of track items.
+ */
+ private List manufactureLastPlayed(List trackItems) {
+ Calendar calendar = Calendar.getInstance();
+ TrackItem temptrack = null;
+
+ for (int i = trackItems.size() - 1; i >= 0; i--) {
+ temptrack = (TrackItem) trackItems.get(i);
+ calendar.add(Calendar.SECOND, -(int) temptrack.getLength());
+ temptrack.setLastplayed(calendar.getTimeInMillis() / 1000);
+ }
+
+ return trackItems;
+ }
+
+ /**
+ * Utility function to determine if the iTunesPath is that of an iPod shuffle.
+ * @param iTunesPath The path to the iTunes_Control directory.
+ * @return true if the iPod is a Shuffle.
+ */
+ public static boolean isIpodShuffle(String iTunesPath) {
+ /* Checks for the "iTunesStats" file. If it exists, switch to the iPod
+ * shuffle file. */
+ File file = new File(iTunesPath);
+ File[] itunesStatsFiles = file.listFiles(new ItunesStatsFilter());
+
+ return (itunesStatsFiles != null) && (itunesStatsFiles.length != 0);
+ }
}
Index: /trunk/src/main/org/lastpod/parser/ItunesDbParser.java
===================================================================
--- /trunk/src/main/org/lastpod/parser/ItunesDbParser.java (revision 92)
+++ /trunk/src/main/org/lastpod/parser/ItunesDbParser.java (revision 93)
@@ -29,6 +29,10 @@
import java.io.InputStream;
+import java.math.BigInteger;
+
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
@@ -45,4 +49,9 @@
/**
+ * The location of the iTunesSD file.
+ */
+ private String iTunesSdFile;
+
+ /**
* Stores a boolean value that will be passed into TrackItem.
*/
@@ -53,4 +62,9 @@
*/
String[] variousArtistsStrings;
+
+ /**
+ * Stores true if this iTunesDB is for an iPod shuffle.
+ */
+ boolean isShuffle = false;
/**
@@ -70,7 +84,8 @@
* @param variousArtistsStrings A String array containing the various artist
* strings that should be parsed.
+ * @param isShuffle true if this iTunesDB is for an iPod shuffle.
*/
public ItunesDbParser(String iTunesPath, boolean parseVariousArtists,
- String[] variousArtistsStrings) {
+ String[] variousArtistsStrings, boolean isShuffle) {
if (!iTunesPath.endsWith(File.separator)) {
iTunesPath += File.separator;
@@ -78,6 +93,8 @@
this.iTunesFile = iTunesPath + "iTunesDB";
+ this.iTunesSdFile = iTunesPath + "iTunesSD";
this.parseVariousArtists = parseVariousArtists;
this.variousArtistsStrings = variousArtistsStrings;
+ this.isShuffle = isShuffle;
}
@@ -95,5 +112,11 @@
itunesBufferedIn = new BufferedInputStream(itunesFileIn, 65535);
- return parseitunesdb(itunesBufferedIn);
+ List trackList = parseitunesdb(itunesBufferedIn);
+
+ if (!isShuffle) {
+ return trackList;
+ } else {
+ return readItunesSdAndReorderList(trackList);
+ }
} catch (IOException e) {
throw new RuntimeException("Error reading iTunes Database");
@@ -240,4 +263,69 @@
/**
+ * Reads the iTunesSD file and uses the ordering in this file to reorder
+ * the track list.
+ * @param trackList The track list from the iTunesDB
+ * @return A new java.util.List that is ordered per the
+ * iTunesSD order.
+ * @throws IOException Thrown if I/O errors occur.
+ */
+ private List readItunesSdAndReorderList(List trackList)
+ throws IOException {
+ InputStream itunesSdFileIn = null;
+ InputStream itunesSdBufferedIn = null;
+
+ try {
+ itunesSdFileIn = new FileInputStream(iTunesSdFile);
+ itunesSdBufferedIn = new BufferedInputStream(itunesSdFileIn, 65535);
+
+ /* Converts the trackList into a Map. */
+ Map trackMap = new HashMap();
+ TrackItem track = null;
+
+ for (int i = 0; i < trackList.size(); i++) {
+ track = (TrackItem) trackList.get(i);
+ trackMap.put(track.getLocation(), track);
+ }
+
+ List orderedTrackList = new ArrayList();
+
+ byte[] threeBytes = new byte[3];
+
+ itunesSdBufferedIn.read(threeBytes);
+
+ int numentries = (new BigInteger(threeBytes)).intValue();
+
+ IoUtils.skipFully(itunesSdBufferedIn, 15); //skip rest of header
+ assert (numentries == trackList.size());
+
+ for (int i = 0; i < (numentries - 1); i++) {
+ itunesSdBufferedIn.mark(1048576); //save beginning of entry location
+
+ itunesSdBufferedIn.read(threeBytes);
+
+ int entrylen = (new BigInteger(threeBytes)).intValue();
+
+ IoUtils.skipFully(itunesSdBufferedIn, 30);
+
+ byte[] data = new byte[522];
+ itunesSdBufferedIn.read(data);
+
+ /* Filename should have : characters instead of / characters. */
+ String filename = new String(data, "UTF-16LE").replace('/', ':').trim();
+
+ orderedTrackList.add(trackMap.get(filename));
+
+ itunesSdBufferedIn.reset();
+ IoUtils.skipFully(itunesSdBufferedIn, entrylen);
+ }
+
+ return orderedTrackList;
+ } finally {
+ IoUtils.cleanup(itunesSdFileIn, null);
+ IoUtils.cleanup(itunesSdBufferedIn, null);
+ }
+ }
+
+ /**
* Does nothing for this implementation.
* @param trackList Does nothing for this implementation.
Index: /trunk/src/main/org/lastpod/ModelImpl.java
===================================================================
--- /trunk/src/main/org/lastpod/ModelImpl.java (revision 90)
+++ /trunk/src/main/org/lastpod/ModelImpl.java (revision 93)
@@ -24,8 +24,5 @@
import org.lastpod.parser.TrackItemParser;
-import org.lastpod.util.ItunesStatsFilter;
import org.lastpod.util.MiscUtilities;
-
-import java.io.File;
import java.util.ArrayList;
@@ -101,17 +98,17 @@
History.getInstance(iTunesPath);
+ boolean isShuffle = ItunesStatsParser.isIpodShuffle(iTunesPath);
+
ItunesDbParser itunesDbParser =
- new ItunesDbParser(iTunesPath, parseVariousArtists, splitVariousArtistStrings);
-
- /* Defaults to the parser for non-shuffle iPods. */
- TrackItemParser playCountsParser = new PlayCountsParser(iTunesPath, parseMultiPlayTracks);
-
- /* Checks for the "iTunesStats" file. If it exists, switch to the iPod
- * shuffle parser. */
- File file = new File(iTunesPath);
- File[] itunesStatsFiles = file.listFiles(new ItunesStatsFilter());
-
- if ((itunesStatsFiles != null) && (itunesStatsFiles.length != 0)) {
+ new ItunesDbParser(iTunesPath, parseVariousArtists, splitVariousArtistStrings, isShuffle);
+
+ TrackItemParser playCountsParser = null;
+
+ /* If the iPod is a Shuffle, use the iPod shuffle parser. Otherwise
+ * use the non-shuffle parser. */
+ if (isShuffle) {
playCountsParser = new ItunesStatsParser(iTunesPath, parseMultiPlayTracks);
+ } else {
+ playCountsParser = new PlayCountsParser(iTunesPath, parseMultiPlayTracks);
}
Index: /trunk/src/main/org/lastpod/action/DeletePlayCounts.java
===================================================================
--- /trunk/src/main/org/lastpod/action/DeletePlayCounts.java (revision 89)
+++ /trunk/src/main/org/lastpod/action/DeletePlayCounts.java (revision 93)
@@ -22,5 +22,5 @@
import org.lastpod.UI;
-import org.lastpod.util.ItunesStatsFilter;
+import org.lastpod.parser.ItunesStatsParser;
import java.awt.event.ActionEvent;
@@ -91,10 +91,6 @@
playCountsFile = new File(iTunesPath + "Play Counts");
- /* Checks for the "iTunesStats" file. If it exists, switch to the iPod
- * shuffle file. */
- File file = new File(iTunesPath);
- File[] itunesStatsFiles = file.listFiles(new ItunesStatsFilter());
-
- if ((itunesStatsFiles != null) && (itunesStatsFiles.length != 0)) {
+ /* If the iPod is a Shuffle, switch to the shuffle delete logic. */
+ if (ItunesStatsParser.isIpodShuffle(iTunesPath)) {
playCountsFile = new File(iTunesPath + "iTunesStats");
putValue(SHORT_DESCRIPTION, "Removes the iTunesStats file from the iPod shuffle.");