Skip to content

Commit

Permalink
chages to get the parser work properly for shuffles. The issue solved
Browse files Browse the repository at this point in the history
is the track ordering.  The iTunesSD file is now used on shuffles (it 
stores the order.)


git-svn-id: http://svn.lastpod.org/trunk@93 1be3d916-834f-4e11-84c0-b560dc06693e
  • Loading branch information
chris committed Jan 3, 2008
1 parent 13d78e2 commit e4f84ef
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 32 deletions.
21 changes: 9 additions & 12 deletions src/main/org/lastpod/ModelImpl.java
Expand Up @@ -23,11 +23,8 @@
import org.lastpod.parser.PlayCountsParser;
import org.lastpod.parser.TrackItemParser;

import org.lastpod.util.ItunesStatsFilter;
import org.lastpod.util.MiscUtilities;

import java.io.File;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
Expand Down Expand Up @@ -100,19 +97,19 @@ public void parsePlayCounts(UI userInterface) {
/* Initialize the history file. */
History.getInstance(iTunesPath);

ItunesDbParser itunesDbParser =
new ItunesDbParser(iTunesPath, parseVariousArtists, splitVariousArtistStrings);
boolean isShuffle = ItunesStatsParser.isIpodShuffle(iTunesPath);

/* Defaults to the parser for non-shuffle iPods. */
TrackItemParser playCountsParser = new PlayCountsParser(iTunesPath, parseMultiPlayTracks);
ItunesDbParser itunesDbParser =
new ItunesDbParser(iTunesPath, parseVariousArtists, splitVariousArtistStrings, isShuffle);

/* 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());
TrackItemParser playCountsParser = null;

if ((itunesStatsFiles != null) && (itunesStatsFiles.length != 0)) {
/* 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);
}

DbReader reader = new DbReader(itunesDbParser, playCountsParser);
Expand Down
10 changes: 3 additions & 7 deletions src/main/org/lastpod/action/DeletePlayCounts.java
Expand Up @@ -21,7 +21,7 @@
import org.lastpod.Model;
import org.lastpod.UI;

import org.lastpod.util.ItunesStatsFilter;
import org.lastpod.parser.ItunesStatsParser;

import java.awt.event.ActionEvent;

Expand Down Expand Up @@ -90,12 +90,8 @@ public DeletePlayCounts(UI userInterface, Model model, String text, ImageIcon ic
/* Defaults the file for non-shuffle iPods. */
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.");
}
Expand Down
92 changes: 90 additions & 2 deletions src/main/org/lastpod/parser/ItunesDbParser.java
Expand Up @@ -28,8 +28,12 @@
import java.io.IOException;
import java.io.InputStream;

import java.math.BigInteger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Parses the iTunes DB file from the iPod an creates a <code>List</code> of
Expand All @@ -43,6 +47,11 @@ public class ItunesDbParser implements TrackItemParser {
*/
private String iTunesFile;

/**
* The location of the iTunesSD file.
*/
private String iTunesSdFile;

/**
* Stores a boolean value that will be passed into <code>TrackItem</code>.
*/
Expand All @@ -53,6 +62,11 @@ public class ItunesDbParser implements TrackItemParser {
*/
String[] variousArtistsStrings;

/**
* Stores <code>true</code> if this iTunesDB is for an iPod shuffle.
*/
boolean isShuffle = false;

/**
* Default constructor should not be used.
*/
Expand All @@ -69,16 +83,19 @@ private ItunesDbParser() {
* Artists"
* @param variousArtistsStrings A String array containing the various artist
* strings that should be parsed.
* @param isShuffle <code>true</code> 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;
}

this.iTunesFile = iTunesPath + "iTunesDB";
this.iTunesSdFile = iTunesPath + "iTunesSD";
this.parseVariousArtists = parseVariousArtists;
this.variousArtistsStrings = variousArtistsStrings;
this.isShuffle = isShuffle;
}

/**
Expand All @@ -94,7 +111,13 @@ public List parse() {
itunesFileIn = new FileInputStream(iTunesFile);
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");
} finally {
Expand Down Expand Up @@ -238,6 +261,71 @@ public void parsemhod(TrackItem track, InputStream itunesistream)
IoUtils.skipFully(itunesistream, totalsize);
}

/**
* 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 <code>java.util.List</code> 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.
Expand Down
49 changes: 38 additions & 11 deletions src/main/org/lastpod/parser/ItunesStatsParser.java
Expand Up @@ -21,6 +21,7 @@
import org.lastpod.TrackItem;

import org.lastpod.util.IoUtils;
import org.lastpod.util.ItunesStatsFilter;

import java.io.BufferedInputStream;
import java.io.File;
Expand Down Expand Up @@ -112,7 +113,9 @@ public List parse() {
playCountsFileIn = new FileInputStream(iTunesStatsFile);
playCountsBufferedIn = new BufferedInputStream(playCountsFileIn, 65535);

return parseitunesStats(playCountsBufferedIn);
List trackList = parseitunesStats(playCountsBufferedIn);

return manufactureLastPlayed(trackList);
} catch (IOException e) {
String errorMsg =
"Error reading iTunesStats Database.\n"
Expand Down Expand Up @@ -144,8 +147,6 @@ private List parseitunesStats(InputStream itunesStatsistream)

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

Expand All @@ -163,16 +164,13 @@ private List parseitunesStats(InputStream itunesStatsistream)
if (playcount > 0) {
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));

if (parseMultiPlayTracks && (playcount > 1)) {
long numberToManufacture = playcount - 1;

for (long j = 0; j < numberToManufacture; j++) {
temptrack = manufactureTrack(temptrack, calendar);
temptrack = manufactureTrack(temptrack);
recentPlays.add(temptrack);
}
}
Expand All @@ -192,14 +190,43 @@ private List parseitunesStats(InputStream itunesStatsistream)
* @param temptrack The track to manufacture (if needed).
* @return A manufactured <code>TrackItem</code>.
*/
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);

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 <code>true</code> 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);
}
}

0 comments on commit e4f84ef

Please sign in to comment.