Source code: org/esau/ptarmigan/impl/filter/PLSFilter.java
1 /* $Header: /cvsroot/ptarmigan/ptarmigan/src/java/org/esau/ptarmigan/impl/filter/PLSFilter.java,v 1.3 2002/09/19 03:48:00 reedesau Exp $ */
2
3 package org.esau.ptarmigan.impl.filter;
4
5 import java.io.BufferedReader;
6 import java.io.IOException;
7 import java.text.ParseException;
8
9 import org.xml.sax.SAXException;
10
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13
14 import org.esau.ptarmigan.util.HelperMisc;
15
16 /**
17 * PLSFilter -- transforms PLS playlist to ptarmigan playlist
18 * <p>
19 * TODO: recursively scan all playlists found.
20 *
21 * @author Reed Esau
22 * @version $Revision: 1.3 $ $Date: 2002/09/19 03:48:00 $
23 */
24 public final class PLSFilter extends SimplePlaylistFilter {
25
26 public PLSFilter() throws SAXException {
27 }
28
29 static final String PLS_MARKER = "[playlist]";
30
31 /** does the specified stream contain a marker for the tag? */
32 public boolean isMatch(String str) throws IOException {
33 log.debug("sniffing for " + PLS_MARKER);
34 return str.startsWith(PLS_MARKER);
35 }
36
37 /**
38 * Parse a document from a buffered source.
39 */
40 public void doParsePlaylist(BufferedReader reader)
41 throws SAXException, IOException, ParseException {
42
43 m_media_properties.setMimeType("audio/x-scpls");
44
45 log.debug("doParsePlaylist");
46
47 // first parse all the entries to an internal list
48
49 //TODO: do something with duration_total
50
51 int entry_count = 0;
52
53 startPlaylist();
54
55 if (readHeader( reader )) {
56 while (readEntry( reader )) {
57 entry_count++;
58 }
59 }
60
61 endPlaylist();
62 }
63
64 /** consume the header */
65 boolean readHeader(BufferedReader reader) throws IOException, SAXException {
66
67 final String PLAYLIST_NAME = "PlaylistName";
68
69 reader.mark(READ_AHEAD_LIMIT);
70
71 // consume all lines that start with [playlist] or PlaylistName
72 String line;
73 while ((line = reader.readLine()) != null) {
74 if (line.startsWith(PLS_MARKER)) {
75 reader.mark(READ_AHEAD_LIMIT);
76 continue;
77 }
78 if (line.startsWith(PLAYLIST_NAME)) {
79 this.setTitle ( line.substring(PLAYLIST_NAME.length() + 1) );
80 reader.mark(READ_AHEAD_LIMIT);
81 continue;
82 }
83 reader.reset(); // we're probably at the first entry; start reading them
84 break;
85 }
86
87 writeProperties();
88
89 return line != null;
90 }
91
92 /**
93 * Obtain the next Tune (or playlist?) object from the playlist.
94 *
95 * @return Next available Entry if one is available; otherwise returns null
96 * @exception IOException
97 */
98 boolean readEntry(BufferedReader reader) throws IOException, SAXException {
99
100 int index = 0;
101 boolean ready_to_commit = false;
102
103 // preserve the initial file position in case we encounter a
104 // subsequent .pls record (requiring a reset of the file position)
105 reader.mark(READ_AHEAD_LIMIT);
106
107 // Keep reading lines until we've read a complete entry (or end-
108 // of-file has been reached)
109
110 String line;
111 while ((line=reader.readLine()) != null ) {
112
113 if (log.isDebugEnabled())
114 log.debug("readEntry: line=" + line);
115
116 String trimmed = line.trim();
117
118 if (trimmed.length() == 0) // ignore all blank lines
119 continue;
120
121 // This is what a .pls entry looks like:
122 //File1=\download\02. Raindrops Keep Falling on My Head.mp3
123 //Title1=Burt Bacharach - Raindrops Keep Falling on My Head (from Butch Cassidy and the Sundance Kid)
124 //Length1=151
125 //
126 //or possibly with a url instead of a file:
127 //File1=http://192.168.100.105/02.%20Raindrops%20Keep%20Falling%20on%20My%20Head.mp3
128
129 for (int i = 0; i < PLS_KEYS.length; i++) {
130 String key = PLS_KEYS[i]; //File, Title or Length
131 if (line.startsWith(key) == false)
132 continue;
133
134 // extract N and value found in the keyN=value line
135 int rec_no_pos = key.length();
136 int equal_pos = line.indexOf(EQUALS, rec_no_pos);
137 String s_rec_no = line.substring(rec_no_pos, equal_pos);
138 int rec_no = HelperMisc.parseInt(s_rec_no, -1);
139 if (rec_no < 1)
140 continue;
141 String val = line.substring(equal_pos + 1);
142
143 if (index == 0) {
144 // if this is the first line of the record we're encountering...
145 index = rec_no;
146 }
147 else if (index != rec_no) {
148 // we're done with this entry if we've inadvertently
149 // swerved into a subsequent record
150 reader.reset(); // undo the recent read
151 ready_to_commit = true;
152 break;
153 }
154
155 switch (i) {
156 case PLS_TUNE_FILE:
157 setEntryPath(val);
158 break;
159 case PLS_TUNE_TITLE:
160 setEntryTitle(val);
161 break;
162 case PLS_TUNE_DURATION:
163 setEntryDuration(HelperMisc.parseInt(val, 0) * 1000); // millisecs
164 break;
165 }
166
167 break; // ready for the next line
168 }
169
170 if (ready_to_commit)
171 break;
172
173 // preserve the current file position in case we encounter a
174 // subsequent .pls record (requiring a reset of the file position)
175 reader.mark(READ_AHEAD_LIMIT);
176 }
177
178 writeEntry();
179
180 return line != null;
181 }
182
183 static final int READ_AHEAD_LIMIT = 1024;
184
185 static final String PLS_KEYS[] = { "File", "Title", "Length"};
186 static final int PLS_TUNE_FILE = 0;
187 static final int PLS_TUNE_TITLE = 1;
188 static final int PLS_TUNE_DURATION = 2;
189
190 static final int COMMA_CH = ',';
191 static final String EQUALS = "=";
192
193 /**
194 * logging object
195 */
196 static Log log = LogFactory.getLog(PLSFilter.class);
197 }
198
199 /*
200 PTARMIGAN MODIFIED BSD LICENSE
201
202 Copyright (c) 2002, Reed Esau (reed.esau@pobox.com) All rights reserved.
203
204 Redistribution and use in source and binary forms, with or without
205 modification, are permitted provided that the following conditions are
206 met:
207
208 Redistributions of source code must retain the above copyright notice,
209 this list of conditions and the following disclaimer.
210
211 Redistributions in binary form must reproduce the above copyright notice,
212 this list of conditions and the following disclaimer in the documentation
213 and/or other materials provided with the distribution.
214
215 Neither the name of the Ptarmigan Project
216 (http://ptarmigan.sourceforge.net) nor the names of its contributors may
217 be used to endorse or promote products derived from this software without
218 specific prior written permission.
219
220 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
221 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
222 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
223 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
224 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
225 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
226 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
227 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
228 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
229 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
230 POSSIBILITY OF SUCH DAMAGE.
231 */