root/trunk/src/main/org/lastpod/parser/ItunesDbParser.java

Revision 92, 7.7 kB (checked in by chris, 3 years ago)

ItunesDbParser? now parses and stores the TrackItem?'s location

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1 /*
2  * LastPod is an application used to publish one's iPod play counts to Last.fm.
3  * Copyright (C) 2007  Chris Tilden
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 package org.lastpod.parser;
20
21 import org.lastpod.TrackItem;
22
23 import org.lastpod.util.IoUtils;
24
25 import java.io.BufferedInputStream;
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30
31 import java.util.ArrayList;
32 import java.util.List;
33
34 /**
35  * Parses the iTunes DB file from the iPod an creates a <code>List</code> of
36  * <code>TrackItems</code>.  Note: the TrackItems returned do not contain play
37  * count information only things like track title, artist name, album name, etc.
38  * @author Chris Tilden
39  */
40 public class ItunesDbParser implements TrackItemParser {
41     /**
42      * The location of the iTunes database file.
43      */
44     private String iTunesFile;
45
46     /**
47      * Stores a boolean value that will be passed into <code>TrackItem</code>.
48      */
49     boolean parseVariousArtists;
50
51     /**
52      * Stores the strings used to parse various artists.
53      */
54     String[] variousArtistsStrings;
55
56     /**
57      * Default constructor should not be used.
58      */
59     private ItunesDbParser() {
60         /* Default constructor. */
61     }
62
63     /**
64      * Initializes the class with the locations of the iPod DB files.
65      *
66      * @param iTunesPath  Directory containing the iTunesDB and the corresponding
67      *                         Play Counts, including trailing "\" or "/".
68      * @param parseVariousArtists  If <code>true</code> then parses "Various
69      * Artists"
70      * @param variousArtistsStrings  A String array containing the various artist
71      * strings that should be parsed.
72      */
73     public ItunesDbParser(String iTunesPath, boolean parseVariousArtists,
74         String[] variousArtistsStrings) {
75         if (!iTunesPath.endsWith(File.separator)) {
76             iTunesPath += File.separator;
77         }
78
79         this.iTunesFile = iTunesPath + "iTunesDB";
80         this.parseVariousArtists = parseVariousArtists;
81         this.variousArtistsStrings = variousArtistsStrings;
82     }
83
84     /**
85      * Performs parsing.
86      * @return  A <code>List</code> containing <code>TrackItems</code>.  It
87      * contains all tracks from the iTunes database.
88      */
89     public List parse() {
90         InputStream itunesFileIn = null;
91         InputStream itunesBufferedIn = null;
92
93         try {
94             itunesFileIn = new FileInputStream(iTunesFile);
95             itunesBufferedIn = new BufferedInputStream(itunesFileIn, 65535);
96
97             return parseitunesdb(itunesBufferedIn);
98         } catch (IOException e) {
99             throw new RuntimeException("Error reading iTunes Database");
100         } finally {
101             IoUtils.cleanup(itunesFileIn, null);
102             IoUtils.cleanup(itunesBufferedIn, null);
103         }
104     }
105
106     /**
107      * Parses track information from the iTunesDB.
108      * @param itunesistream  A stream that reads the iTunes database file.
109      * @return  A <code>List</code> containing <code>TrackItems</code>.  It
110      * contains all tracks from the iTunes database.
111      * @throws IOException  Thrown if errors occur.
112      */
113     private List parseitunesdb(InputStream itunesistream)
114             throws IOException {
115         byte[] buf = new byte[1];
116         List trackList = new ArrayList();
117
118         //we seek one at a time because the mhit marker won't always be at a multiple of four
119         while (itunesistream.read(buf) != -1) {
120             if (buf[0] == 'm') { //Search for MHIT
121                 itunesistream.mark(1048576);
122                 buf = new byte[3];
123                 itunesistream.read(buf);
124
125                 if (new String(buf).equals("hit")) {
126                     trackList.add(parsemhit(itunesistream));
127                 } else {
128                     itunesistream.reset();
129                 }
130             }
131
132             buf = new byte[1];
133         }
134
135         return trackList;
136     }
137
138     /**
139      * Parses an MHIT object from the iTunes Database.
140      * @param itunesistream  A stream that reads the iTunes database file.
141      * @return Returns parsed track object.
142      * @throws IOException  Thrown if errors occur.
143      */
144     public TrackItem parsemhit(InputStream itunesistream)
145             throws IOException {
146         byte[] dword = new byte[4];
147         TrackItem track = new TrackItem();
148         track.setParseVariousArtists(parseVariousArtists);
149         track.setVariousArtistsStrings(variousArtistsStrings);
150
151         itunesistream.mark(1048576); //mark beginning of MHIT location
152
153         itunesistream.read(dword);
154
155         long headersize = IoUtils.littleEndianToBigInt(dword).longValue();
156
157         IoUtils.skipFully(itunesistream, 4);
158         itunesistream.read(dword);
159
160         long nummhods = IoUtils.littleEndianToBigInt(dword).longValue();
161
162         itunesistream.read(dword);
163         track.setTrackid(IoUtils.littleEndianToBigInt(dword).longValue());
164
165         IoUtils.skipFully(itunesistream, 20);
166         itunesistream.read(dword);
167         track.setLength(IoUtils.littleEndianToBigInt(dword).longValue() / 1000);
168
169         itunesistream.reset();
170         IoUtils.skipFully(itunesistream, headersize - 4); //skip to end of MHIT
171
172         for (long i = 0; i < nummhods; i++) {
173             parsemhod(track, itunesistream);
174         }
175
176         return track;
177     }
178
179     /**
180      * Parses an MHOD object and sets proper fields in the track item object.
181      * @param track  Track Item.
182      * @param itunesistream  A stream that reads the iTunes database file.
183      * @throws IOException  Thrown if errors occur.
184      */
185     public void parsemhod(TrackItem track, InputStream itunesistream)
186             throws IOException {
187         byte[] dword = new byte[4];
188
189         itunesistream.mark(1048576); //mark beginning of MHOD location
190
191         IoUtils.skipFully(itunesistream, 8);
192
193         itunesistream.read(dword);
194
195         long totalsize = IoUtils.littleEndianToBigInt(dword).longValue();
196
197         itunesistream.read(dword);
198
199         int mhodtype = IoUtils.littleEndianToBigInt(dword).intValue();
200
201         if ((mhodtype == 1) || (mhodtype == 2) || (mhodtype == 3) || (mhodtype == 4)) {
202             IoUtils.skipFully(itunesistream, 12);
203             itunesistream.read(dword);
204
205             int strlen = IoUtils.littleEndianToBigInt(dword).intValue();
206
207             IoUtils.skipFully(itunesistream, 8);
208
209             byte[] data = new byte[strlen];
210             itunesistream.read(data);
211
212             String stringdata = new String(data, "UTF-16LE");
213
214             switch (mhodtype) {
215             case 1:
216                 track.setTrack(stringdata);
217
218                 break;
219
220             case 2:
221                 track.setLocation(stringdata);
222
223                 break;
224
225             case 3:
226                 track.setAlbum(stringdata);
227
228                 break;
229
230             case 4:
231                 track.setArtist(stringdata);
232
233                 break;
234             }
235         }
236
237         itunesistream.reset();
238         IoUtils.skipFully(itunesistream, totalsize);
239     }
240
241     /**
242      * Does nothing for this implementation.
243      * @param trackList  Does nothing for this implementation.
244      */
245     public void setTrackList(List trackList) {
246         /* Do nothing. */
247     }
248 }
Note: See TracBrowser for help on using the browser.