| 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 | } |