1 | package org.farng.mp3.lyrics3; |
2 | |
3 | import org.farng.mp3.AbstractMP3Tag; |
4 | import org.farng.mp3.InvalidTagException; |
5 | import org.farng.mp3.TagException; |
6 | import org.farng.mp3.TagNotFoundException; |
7 | import org.farng.mp3.TagOptionSingleton; |
8 | import org.farng.mp3.id3.AbstractID3v2Frame; |
9 | import org.farng.mp3.id3.ID3v1; |
10 | import org.farng.mp3.id3.ID3v2_4; |
11 | |
12 | import java.io.IOException; |
13 | import java.io.RandomAccessFile; |
14 | import java.util.HashMap; |
15 | import java.util.Iterator; |
16 | import java.util.Map; |
17 | |
18 | /** |
19 | * <TABLE border=0> <TBODY> <TR> <TD class=h2>What is Lyrics3 v2.00?</TD></TR></TBODY></TABLE> <TABLE border=0> <TBODY> |
20 | * <TR vAlign=top> <TD> <P>The Lyrics3 v2.00 tag is more complicated than the previous Lyrics3 tag but has a lot more |
21 | * capabilities. Just like the old Lyrics3 tag it resides between the audio and the ID3 tag, which must be present. The |
22 | * tag uses only text for everything from content to size descriptors, which are represented as numerical strings. This |
23 | * makes it easier to implement a Lyrics3 v2.00 reader. At least if BASIC is your language of choice.</P> |
24 | * <p/> |
25 | * <P>The Lyrics3 block, after the MP3 audio and before the ID3 tag, begins with the word "LYRICSBEGIN" after which a |
26 | * number of field records follows. The Lyrics3 block ends with a six character size descriptor and the string |
27 | * "LYRICS200". The size value includes the "LYRICSBEGIN" and "LYRICS200" strings.</P> |
28 | * <p/> |
29 | * <P>Lyrics2 v2.00 uses somthing called fields to represent information. The data in a field can consist of ASCII |
30 | * characters in the range 01 to 254 according to the standard. As the ASCII character map is only defined from 00 to |
31 | * 128 ISO-8859-1 might be assumed. Numerical fields are 5 or 6 characters long, depending on location, and are padded |
32 | * with zeroes. |
33 | * <p/> |
34 | * <P>Only the size of the tag sets the limit for how many fields may be present. All fields uses a simple structure |
35 | * that includes a three character field ID, six characters describing the size of the information and the actual |
36 | * information. This makes it possible to read unknown fields and write them back when saving the tag. There are no |
37 | * required fields in the tag, but at least one field must exist. Fields can appear in any order in the tag, except the |
38 | * indication field which must be the first field if used. Fields that include more then one line uses [CR][LF] |
39 | * delimiters between lines.</P></TD> </TR></TBODY></TABLE> <BR> <TABLE border=0> <TBODY> <TR> <TD |
40 | * class=h2>Defined fields</TD></TR></TBODY></TABLE> <TABLE border=0> <TBODY> <TR vAlign=top> <TD> <P>The following list |
41 | * is a list of currently defined field IDs. More fields might be added if needed on newer versions of the Lyrics3 v2.00 |
42 | * specifications. Unknown fields should be ignored.</P> <TABLE> <TBODY> <TR> <TD><U>ID</U></TD> <TD><U>Max |
43 | * size</U></TD> <TD><U>Description</U></TD></TR> <TR vAlign=top> <TD><B>IND</B></TD> <TD>00002</TD> <TD>Indications |
44 | * field. This is always two characters big in v2.00, but might be bigger in a future standard. The first byte indicates |
45 | * wether or not a lyrics field is present. "1" for present and "0" for otherwise. The second character indicates if |
46 | * there is a timestamp in the lyrics. Again "1" for yes and "0" for no.</TD></TR> <TR vAlign=top> <TD><B>LYR</B></TD> |
47 | * <TD>99999</TD> <TD>Lyrics multi line text. Timestamps can be used anywhere in the text in any order. Timestamp format |
48 | * is [mm:ss] (no spaces allowed in the timestamps).</TD></TR> <TR vAlign=top> <TD><B>INF</B></TD> <TD>99999</TD> |
49 | * <TD>Additional information multi line text.</TD></TR> <TR vAlign=top> <TD><B>AUT</B></TD> <TD>00250</TD> |
50 | * <TD>Lyrics/Music Author name.</TD></TR> <TR vAlign=top> <TD><B>EAL</B></TD> <TD>00250</TD> <TD>Extended Album |
51 | * name.</TD></TR> <TR vAlign=top> <TD><B>EAR</B></TD> <TD>00250</TD> <TD>Extended Artist name.</TD></TR> <TR |
52 | * vAlign=top> <TD><B>ETT</B></TD> <TD>00250</TD> <TD>Extended Track Title.</TD></TR> <TR vAlign=top> |
53 | * <TD><B>IMG</B></TD> <TD>99999</TD> <TD>Link to an image files (BMP or JPG format). Image lines include filename, |
54 | * description and timestamp separated by delimiter - two ASCII chars 124 ("||"). Description and timestamp are |
55 | * optional, but if timestamp is used, and there is no description, two delimiters ("||||") should be used between the |
56 | * filename and the timestamp. Multiple images are allowed by using a [CR][LF] delimiter between each image line. No |
57 | * [CR][LF] is needed after the last image line. Number of images is not limited (except by the field |
58 | * size).<BR><B>Filename</B> can be in one of these formats: <UL> <LI>Filename only - when the image is located in the |
59 | * same path as the MP3 file (preferred, since if you move the mp3 file this will still be correct) <LI>Relative Path + |
60 | * Filename - when the image is located in a subdirectory below the MP3 file (i.e. images\cover.jpg) <LI>Full path + |
61 | * Filename - when the image is located in a totally different path or drive. This will not work if the image is moved |
62 | * or drive letters has changed, and so should be avoided if possible (i.e. c:\images\artist.jpg)</LI></UL><B>Description</B> |
63 | * can be up to 250 chars long.<BR><B>Timestamp</B> must be formatted like the lyrics timestamp which is "[mm:ss]". If |
64 | * an image has a timestamp, then the visible image will automatically switch to that image on the timestamp play time, |
65 | * just the same as the selected lyrics line is switched based on timestamps.</TD></TR></TBODY></TABLE> |
66 | * </TD></TR></TBODY></TABLE> <TABLE border=0> <TBODY> <TR vAlign=top> <TD> <P>The extended Album, Artist and |
67 | * Track are an extension to the fields in the ID3v1 tag - which are limited to 30 chars. If these extended fields |
68 | * exist, make sure their first 30 chars are exactly the same as the ones in the ID3v1 tag. If they are the same, |
69 | * display the extended field. If not, display the one from the ID tag. These 'mismatched' extended fields, should be |
70 | * removed when saving the lyrics tag.</P> |
71 | * <p/> |
72 | * <P>When saving the extended fields, make sure to copy the first 30 chars of each field to the ID3 tag matching |
73 | * fields. It is recommended NOT to save extended fields at all, if they are not larger then 30 |
74 | * chars.</P></TD></TR></TBODY></TABLE> |
75 | * |
76 | * @author Eric Farng |
77 | * @version $Revision: 1.5 $ |
78 | */ |
79 | public class Lyrics3v2 extends AbstractLyrics3 { |
80 | |
81 | private Map fieldMap = new HashMap(8); |
82 | |
83 | /** |
84 | * Creates a new Lyrics3v2 object. |
85 | */ |
86 | public Lyrics3v2() { |
87 | super(); |
88 | } |
89 | |
90 | /** |
91 | * Creates a new Lyrics3v2 object. |
92 | */ |
93 | public Lyrics3v2(final Lyrics3v2 copyObject) { |
94 | super(copyObject); |
95 | final Iterator iterator = copyObject.fieldMap.keySet().iterator(); |
96 | String oldIdentifier; |
97 | String newIdentifier; |
98 | Lyrics3v2Field newObject; |
99 | while (iterator.hasNext()) { |
100 | oldIdentifier = iterator.next().toString(); |
101 | newIdentifier = oldIdentifier; |
102 | newObject = new Lyrics3v2Field((Lyrics3v2Field) copyObject.fieldMap.get(newIdentifier)); |
103 | fieldMap.put(newIdentifier, newObject); |
104 | } |
105 | } |
106 | |
107 | /** |
108 | * Creates a new Lyrics3v2 object. |
109 | */ |
110 | public Lyrics3v2(final AbstractMP3Tag mp3tag) { |
111 | super(); |
112 | if (mp3tag != null) { |
113 | // upgrade the tag to lyrics3v2 |
114 | if (mp3tag instanceof Lyrics3v2) { |
115 | throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument"); |
116 | } else if (mp3tag instanceof Lyrics3v1) { |
117 | final Lyrics3v1 lyricOld = (Lyrics3v1) mp3tag; |
118 | final Lyrics3v2Field newField = new Lyrics3v2Field(new FieldBodyLYR(lyricOld.getLyric())); |
119 | fieldMap.put(newField.getIdentifier(), newField); |
120 | } else { |
121 | Lyrics3v2Field newField; |
122 | final Iterator iterator; |
123 | iterator = (new ID3v2_4(mp3tag)).iterator(); |
124 | while (iterator.hasNext()) { |
125 | try { |
126 | newField = new Lyrics3v2Field((AbstractID3v2Frame) iterator.next()); |
127 | this.fieldMap.put(newField.getIdentifier(), newField); |
128 | } catch (TagException ex) { |
129 | //invalid frame to create lyrics3 field. ignore and |
130 | // keep going |
131 | } |
132 | } |
133 | } |
134 | } |
135 | } |
136 | |
137 | /** |
138 | * Creates a new Lyrics3v2 object. |
139 | */ |
140 | public Lyrics3v2(final RandomAccessFile file) throws TagNotFoundException, IOException { |
141 | this.read(file); |
142 | } |
143 | |
144 | public void setField(final Lyrics3v2Field field) { |
145 | if (field.getBody() != null) { |
146 | this.fieldMap.put(field.getIdentifier(), field); |
147 | } |
148 | } |
149 | |
150 | /** |
151 | * Gets the value of the frame identified by identifier |
152 | * |
153 | * @param identifier The three letter code |
154 | * |
155 | * @return The value associated with the identifier |
156 | */ |
157 | public Lyrics3v2Field getField(final String identifier) { |
158 | return (Lyrics3v2Field) this.fieldMap.get(identifier); |
159 | } |
160 | |
161 | public int getFieldCount() { |
162 | return this.fieldMap.size(); |
163 | } |
164 | |
165 | public String getIdentifier() { |
166 | return "Lyrics3v2.00"; |
167 | } |
168 | |
169 | public int getSize() { |
170 | int size = 0; |
171 | final Iterator iterator = this.fieldMap.values().iterator(); |
172 | Lyrics3v2Field field; |
173 | while (iterator.hasNext()) { |
174 | field = (Lyrics3v2Field) iterator.next(); |
175 | size += field.getSize(); |
176 | } |
177 | |
178 | // include LYRICSBEGIN, but not 6 char size or LYRICSEND |
179 | return 11 + size; |
180 | } |
181 | |
182 | public void append(final AbstractMP3Tag tag) { |
183 | final Lyrics3v2 oldTag = this; |
184 | final Lyrics3v2 newTag; |
185 | if (tag != null) { |
186 | if (tag instanceof Lyrics3v2) { |
187 | newTag = (Lyrics3v2) tag; |
188 | } else { |
189 | newTag = new Lyrics3v2(tag); |
190 | } |
191 | Iterator iterator = newTag.fieldMap.values().iterator(); |
192 | Lyrics3v2Field field; |
193 | AbstractLyrics3v2FieldBody body; |
194 | while (iterator.hasNext()) { |
195 | field = (Lyrics3v2Field) iterator.next(); |
196 | if (oldTag.hasField(field.getIdentifier()) == false) { |
197 | oldTag.setField(field); |
198 | } else { |
199 | body = (AbstractLyrics3v2FieldBody) oldTag.getField(field.getIdentifier()).getBody(); |
200 | final boolean save = TagOptionSingleton.getInstance().getLyrics3SaveField(field.getIdentifier()); |
201 | if ((body.getSize() == 0) && save) { |
202 | oldTag.setField(field); |
203 | } |
204 | } |
205 | } |
206 | |
207 | // reset tag options to save all current fields. |
208 | iterator = oldTag.fieldMap.keySet().iterator(); |
209 | String id; |
210 | while (iterator.hasNext()) { |
211 | id = (String) iterator.next(); |
212 | TagOptionSingleton.getInstance().setLyrics3SaveField(id, true); |
213 | } |
214 | } |
215 | } |
216 | |
217 | public boolean equals(final Object obj) { |
218 | if ((obj instanceof Lyrics3v2) == false) { |
219 | return false; |
220 | } |
221 | final Lyrics3v2 lyrics3v2 = (Lyrics3v2) obj; |
222 | if (this.fieldMap.equals(lyrics3v2.fieldMap) == false) { |
223 | return false; |
224 | } |
225 | return super.equals(obj); |
226 | } |
227 | |
228 | public boolean hasField(final String identifier) { |
229 | return this.fieldMap.containsKey(identifier); |
230 | } |
231 | |
232 | public Iterator iterator() { |
233 | return this.fieldMap.values().iterator(); |
234 | } |
235 | |
236 | public void overwrite(final AbstractMP3Tag tag) { |
237 | final Lyrics3v2 oldTag = this; |
238 | final Lyrics3v2 newTag; |
239 | if (tag != null) { |
240 | if (tag instanceof Lyrics3v2) { |
241 | newTag = (Lyrics3v2) tag; |
242 | } else { |
243 | newTag = new Lyrics3v2(tag); |
244 | } |
245 | Iterator iterator = newTag.fieldMap.values().iterator(); |
246 | Lyrics3v2Field field; |
247 | while (iterator.hasNext()) { |
248 | field = (Lyrics3v2Field) iterator.next(); |
249 | if (TagOptionSingleton.getInstance().getLyrics3SaveField(field.getIdentifier())) { |
250 | oldTag.setField(field); |
251 | } |
252 | } |
253 | |
254 | // reset tag options to save all current fields. |
255 | iterator = oldTag.fieldMap.keySet().iterator(); |
256 | String id; |
257 | while (iterator.hasNext()) { |
258 | id = (String) iterator.next(); |
259 | TagOptionSingleton.getInstance().setLyrics3SaveField(id, true); |
260 | } |
261 | } |
262 | } |
263 | |
264 | public void read(final RandomAccessFile file) throws TagNotFoundException, IOException { |
265 | final long filePointer; |
266 | final int lyricSize; |
267 | if (seek(file)) { |
268 | lyricSize = seekSize(file); |
269 | } else { |
270 | throw new TagNotFoundException("Lyrics3v2.00 Tag Not Found"); |
271 | } |
272 | |
273 | // reset file pointer to the beginning of the tag; |
274 | seek(file); |
275 | filePointer = file.getFilePointer(); |
276 | this.fieldMap = new HashMap(); |
277 | Lyrics3v2Field lyric; |
278 | |
279 | // read each of the fields |
280 | while ((file.getFilePointer() - filePointer) < (lyricSize - 11)) { |
281 | try { |
282 | lyric = new Lyrics3v2Field(file); |
283 | setField(lyric); |
284 | } catch (InvalidTagException ex) { |
285 | // keep reading until we're done |
286 | } |
287 | } |
288 | } |
289 | |
290 | public void removeField(final String identifier) { |
291 | this.fieldMap.remove(identifier); |
292 | } |
293 | |
294 | public boolean seek(final RandomAccessFile file) throws IOException { |
295 | final byte[] buffer = new byte[11]; |
296 | String lyricEnd; |
297 | final String lyricStart; |
298 | long filePointer; |
299 | final long lyricSize; |
300 | |
301 | // check right before the ID3 1.0 tag for the lyrics tag |
302 | file.seek(file.length() - 128 - 9); |
303 | file.read(buffer, 0, 9); |
304 | lyricEnd = new String(buffer, 0, 9); |
305 | if (lyricEnd.equals("LYRICS200")) { |
306 | filePointer = file.getFilePointer(); |
307 | } else { |
308 | // check the end of the file for a lyrics tag incase an ID3 |
309 | // tag wasn't placed after it. |
310 | file.seek(file.length() - 9); |
311 | file.read(buffer, 0, 9); |
312 | lyricEnd = new String(buffer, 0, 9); |
313 | if (lyricEnd.equals("LYRICS200")) { |
314 | filePointer = file.getFilePointer(); |
315 | } else { |
316 | return false; |
317 | } |
318 | } |
319 | |
320 | // read the 6 bytes for the length of the tag |
321 | filePointer -= (9 + 6); |
322 | file.seek(filePointer); |
323 | file.read(buffer, 0, 6); |
324 | lyricSize = Integer.parseInt(new String(buffer, 0, 6)); |
325 | |
326 | // read the lyrics begin tag if it exists. |
327 | file.seek(filePointer - lyricSize); |
328 | file.read(buffer, 0, 11); |
329 | lyricStart = new String(buffer, 0, 11); |
330 | return lyricStart.equals("LYRICSBEGIN") == true; |
331 | } |
332 | |
333 | public String toString() { |
334 | final Iterator iterator = this.fieldMap.values().iterator(); |
335 | Lyrics3v2Field field; |
336 | String str = getIdentifier() + " " + this.getSize() + "\n"; |
337 | while (iterator.hasNext()) { |
338 | field = (Lyrics3v2Field) iterator.next(); |
339 | str += (field.toString() + "\n"); |
340 | } |
341 | return str; |
342 | } |
343 | |
344 | public void updateField(final String identifier) { |
345 | Lyrics3v2Field lyrField; |
346 | if (identifier.equals("IND")) { |
347 | final boolean lyricsPresent = this.fieldMap.containsKey("LYR"); |
348 | boolean timeStampPresent = false; |
349 | if (lyricsPresent) { |
350 | lyrField = (Lyrics3v2Field) this.fieldMap.get("LYR"); |
351 | final FieldBodyLYR lyrBody = (FieldBodyLYR) lyrField.getBody(); |
352 | timeStampPresent = lyrBody.hasTimeStamp(); |
353 | } |
354 | lyrField = new Lyrics3v2Field(new FieldBodyIND(lyricsPresent, timeStampPresent)); |
355 | setField(lyrField); |
356 | } |
357 | } |
358 | |
359 | public void write(final AbstractMP3Tag tag) { |
360 | final Lyrics3v2 oldTag = this; |
361 | final Lyrics3v2 newTag; |
362 | if (tag != null) { |
363 | if (tag instanceof Lyrics3v2) { |
364 | newTag = (Lyrics3v2) tag; |
365 | } else { |
366 | newTag = new Lyrics3v2(tag); |
367 | } |
368 | final Iterator iterator = newTag.fieldMap.values().iterator(); |
369 | Lyrics3v2Field field; |
370 | oldTag.fieldMap.clear(); |
371 | while (iterator.hasNext()) { |
372 | field = (Lyrics3v2Field) iterator.next(); |
373 | oldTag.setField(field); |
374 | } |
375 | } |
376 | } |
377 | |
378 | public void write(final RandomAccessFile file) throws IOException { |
379 | int offset = 0; |
380 | final long filePointer; |
381 | final byte[] buffer = new byte[6 + 9]; |
382 | String str; |
383 | Lyrics3v2Field field; |
384 | final Iterator iterator; |
385 | ID3v1 id3v1tag = new ID3v1(); |
386 | id3v1tag = id3v1tag.getID3tag(file); |
387 | delete(file); |
388 | file.seek(file.length()); |
389 | filePointer = file.getFilePointer(); |
390 | str = "LYRICSBEGIN"; |
391 | for (int i = 0; i < str.length(); i++) { |
392 | buffer[i] = (byte) str.charAt(i); |
393 | } |
394 | file.write(buffer, 0, str.length()); |
395 | |
396 | // IND needs to go first. lets create/update it and write it first. |
397 | updateField("IND"); |
398 | field = (Lyrics3v2Field) this.fieldMap.get("IND"); |
399 | field.write(file); |
400 | iterator = this.fieldMap.values().iterator(); |
401 | while (iterator.hasNext()) { |
402 | field = (Lyrics3v2Field) iterator.next(); |
403 | final String id = field.getIdentifier(); |
404 | final boolean save = TagOptionSingleton.getInstance().getLyrics3SaveField(id); |
405 | if ((id.equals("IND") == false) && save) { |
406 | field.write(file); |
407 | } |
408 | } |
409 | final long size; |
410 | size = file.getFilePointer() - filePointer; |
411 | str = Long.toString(size); |
412 | for (int i = 0; i < (6 - str.length()); i++) { |
413 | buffer[i] = (byte) '0'; |
414 | } |
415 | offset += (6 - str.length()); |
416 | for (int i = 0; i < str.length(); i++) { |
417 | buffer[i + offset] = (byte) str.charAt(i); |
418 | } |
419 | offset += str.length(); |
420 | str = "LYRICS200"; |
421 | for (int i = 0; i < str.length(); i++) { |
422 | buffer[i + offset] = (byte) str.charAt(i); |
423 | } |
424 | offset += str.length(); |
425 | file.write(buffer, 0, offset); |
426 | if (id3v1tag != null) { |
427 | id3v1tag.write(file); |
428 | } |
429 | } |
430 | |
431 | private int seekSize(final RandomAccessFile file) throws IOException { |
432 | final byte[] buffer = new byte[11]; |
433 | String lyricEnd; |
434 | long filePointer; |
435 | |
436 | // check right before the ID3 1.0 tag for the lyrics tag |
437 | file.seek(file.length() - 128 - 9); |
438 | file.read(buffer, 0, 9); |
439 | lyricEnd = new String(buffer, 0, 9); |
440 | if (lyricEnd.equals("LYRICS200")) { |
441 | filePointer = file.getFilePointer(); |
442 | } else { |
443 | // check the end of the file for a lyrics tag incase an ID3 |
444 | // tag wasn't placed after it. |
445 | file.seek(file.length() - 9); |
446 | file.read(buffer, 0, 9); |
447 | lyricEnd = new String(buffer, 0, 9); |
448 | if (lyricEnd.equals("LYRICS200")) { |
449 | filePointer = file.getFilePointer(); |
450 | } else { |
451 | return -1; |
452 | } |
453 | } |
454 | |
455 | // read the 6 bytes for the length of the tag |
456 | filePointer -= (9 + 6); |
457 | file.seek(filePointer); |
458 | file.read(buffer, 0, 6); |
459 | return Integer.parseInt(new String(buffer, 0, 6)); |
460 | } |
461 | |
462 | public String getSongTitle() { |
463 | String title = ""; |
464 | Lyrics3v2Field field = getField("ETT"); |
465 | if (field != null) { |
466 | FieldBodyETT body = (FieldBodyETT) field.getBody(); |
467 | title = body.getTitle(); |
468 | } |
469 | return title.trim(); |
470 | } |
471 | |
472 | public String getLeadArtist() { |
473 | String artist = ""; |
474 | Lyrics3v2Field field = getField("EAR"); |
475 | if (field != null) { |
476 | FieldBodyEAR body = (FieldBodyEAR) field.getBody(); |
477 | artist = body.getArtist(); |
478 | } |
479 | return artist.trim(); |
480 | } |
481 | |
482 | public String getAlbumTitle() { |
483 | String album = ""; |
484 | Lyrics3v2Field field = getField("EAL"); |
485 | if (field != null) { |
486 | FieldBodyEAL body = (FieldBodyEAL) field.getBody(); |
487 | album = body.getAlbum(); |
488 | } |
489 | return album.trim(); |
490 | } |
491 | |
492 | public String getYearReleased() { |
493 | throw new UnsupportedOperationException("This tag does not contain that information"); |
494 | } |
495 | |
496 | public String getSongComment() { |
497 | String additionalInformation = ""; |
498 | Lyrics3v2Field field = getField("INF"); |
499 | if (field != null) { |
500 | FieldBodyINF body = (FieldBodyINF) field.getBody(); |
501 | additionalInformation = body.getAdditionalInformation(); |
502 | } |
503 | return additionalInformation.trim(); |
504 | } |
505 | |
506 | public String getSongGenre() { |
507 | throw new UnsupportedOperationException("This tag does not contain that information"); |
508 | } |
509 | |
510 | public String getTrackNumberOnAlbum() { |
511 | throw new UnsupportedOperationException("This tag does not contain that information"); |
512 | } |
513 | |
514 | public String getSongLyric() { |
515 | String lyrics = ""; |
516 | Lyrics3v2Field field = getField("LYR"); |
517 | if (field != null) { |
518 | FieldBodyLYR body = (FieldBodyLYR) field.getBody(); |
519 | lyrics = body.getLyric(); |
520 | } |
521 | return lyrics.trim().trim(); |
522 | } |
523 | |
524 | public String getAuthorComposer() { |
525 | String author = ""; |
526 | Lyrics3v2Field field = getField("AUT"); |
527 | if (field != null) { |
528 | FieldBodyAUT body = (FieldBodyAUT) field.getBody(); |
529 | author = body.getAuthor(); |
530 | } |
531 | return author.trim(); |
532 | } |
533 | |
534 | public void setSongTitle(String songTitle) { |
535 | Lyrics3v2Field field = getField("ETT"); |
536 | if (field == null) { |
537 | field = new Lyrics3v2Field(new FieldBodyETT(songTitle.trim())); |
538 | setField(field); |
539 | } else { |
540 | ((FieldBodyETT) field.getBody()).setTitle(songTitle.trim()); |
541 | } |
542 | } |
543 | |
544 | public void setLeadArtist(String leadArtist) { |
545 | Lyrics3v2Field field = getField("EAR"); |
546 | if (field == null) { |
547 | field = new Lyrics3v2Field(new FieldBodyEAR(leadArtist.trim())); |
548 | setField(field); |
549 | } else { |
550 | ((FieldBodyEAR) field.getBody()).setArtist(leadArtist.trim()); |
551 | } |
552 | } |
553 | |
554 | public void setAlbumTitle(String albumTitle) { |
555 | Lyrics3v2Field field = getField("EAL"); |
556 | if (field == null) { |
557 | field = new Lyrics3v2Field(new FieldBodyEAL(albumTitle.trim())); |
558 | setField(field); |
559 | } else { |
560 | ((FieldBodyEAL) field.getBody()).setAlbum(albumTitle.trim()); |
561 | } |
562 | } |
563 | |
564 | public void setYearReleased(String yearReleased) { |
565 | throw new UnsupportedOperationException("This tag does not contain that information"); |
566 | } |
567 | |
568 | public void setSongComment(String songComment) { |
569 | Lyrics3v2Field field = getField("INF"); |
570 | if (field == null) { |
571 | field = new Lyrics3v2Field(new FieldBodyINF(songComment.trim())); |
572 | setField(field); |
573 | } else { |
574 | ((FieldBodyINF) field.getBody()).setAdditionalInformation(songComment.trim()); |
575 | } |
576 | } |
577 | |
578 | public void setSongGenre(String songGenre) { |
579 | throw new UnsupportedOperationException("This tag does not contain that information"); |
580 | } |
581 | |
582 | public void setTrackNumberOnAlbum(String trackNumberOnAlbum) { |
583 | throw new UnsupportedOperationException("This tag does not contain that information"); |
584 | } |
585 | |
586 | public void setSongLyric(String songLyrics) { |
587 | Lyrics3v2Field field = getField("LYR"); |
588 | if (field == null) { |
589 | field = new Lyrics3v2Field(new FieldBodyLYR(songLyrics.trim())); |
590 | setField(field); |
591 | } else { |
592 | ((FieldBodyLYR) field.getBody()).setLyric(songLyrics.trim()); |
593 | } |
594 | } |
595 | |
596 | public void setAuthorComposer(String authorComposer) { |
597 | Lyrics3v2Field field = getField("AUT"); |
598 | if (field == null) { |
599 | field = new Lyrics3v2Field(new FieldBodyAUT(authorComposer.trim())); |
600 | setField(field); |
601 | } else { |
602 | ((FieldBodyAUT) field.getBody()).setAuthor(authorComposer.trim()); |
603 | } |
604 | } |
605 | } |