Constructors Summary |
---|
public MP3File()Creates a new empty MP3File datatype that is not associated with a
specific file.
|
public MP3File(File file)Creates a new MP3File datatype and parse the tag from the given file
Object.
this(file, LOAD_ALL);
|
public MP3File(String filename)Creates a new MP3File datatype and parse the tag from the given filename.
this(new File(filename));
|
public MP3File(File file, int loadOptions)Creates a new MP3File datatype and parse the tag from the given file
Object, files must be writable to use this constructor.
this(file, loadOptions, false);
|
public MP3File(File file, int loadOptions, boolean readOnly)Creates a new MP3File dataType and parse the tag from the given file
Object, files can be opened read only if required.
RandomAccessFile newFile = null;
try
{
this.file = file;
//Check File accessibility
newFile = checkFilePermissions(file, readOnly);
//Read ID3v2 tag size (if tag exists) to allow audioHeader parsing to skip over tag
long startByte = AbstractID3v2Tag.getV2TagSizeIfExists(file);
//If exception reading Mpeg then we should give up no point continuing
audioHeader = new MP3AudioHeader(file, startByte);
if (startByte != ((MP3AudioHeader) audioHeader).getMp3StartByte())
{
logger.config("First header found after tag:" + audioHeader);
audioHeader = checkAudioStart(startByte, (MP3AudioHeader) audioHeader);
}
//Read v1 tags (if any)
readV1Tag(file, newFile, loadOptions);
//Read v2 tags (if any)
readV2Tag(file, loadOptions);
//If we have a v2 tag use that, if we dont but have v1 tag use that
//otherwise use nothing
//TODO:if have both should we merge
//rather than just returning specific ID3v22 tag, would it be better to return v24 version ?
if (this.getID3v2Tag() != null)
{
tag = this.getID3v2Tag();
}
else if (id3v1tag != null)
{
tag = id3v1tag;
}
//Read Lyrics 3
//readLyrics3Tag(File file,RandomAccessFile newFile,int loadOptions)
}
finally
{
if (newFile != null)
{
newFile.close();
}
}
|
Methods Summary |
---|
private MP3AudioHeader | checkAudioStart(long startByte, MP3AudioHeader currentHeader)Regets the audio header starting from start of file, and write appropriate logging to indicate
potential problem to user.
MP3AudioHeader newAudioHeader;
MP3AudioHeader nextAudioHeader;
logger.warning(ErrorMessage.MP3_ID3TAG_LENGTH_INCORRECT.getMsg(file.getPath(), Hex.asHex(startByte), Hex.asHex(currentHeader.getMp3StartByte())));
//because we cant agree on start location we reread the audioheader from the start of the file, at least
//this way we cant overwrite the audio although we might overwrite part of the tag if we write this file
//back later
newAudioHeader = new MP3AudioHeader(file, 0);
logger.config("Checking from start:" + newAudioHeader);
if (currentHeader.getMp3StartByte() == newAudioHeader.getMp3StartByte())
{
//Although the tag size appears to be incorrect at least we have found the same location for the start
//of audio whether we start searching from start of file or at the end of the alleged of file so no real
//problem
logger.config(ErrorMessage.MP3_START_OF_AUDIO_CONFIRMED.getMsg(file.getPath(), Hex.asHex(newAudioHeader.getMp3StartByte())));
return currentHeader;
}
else
{
//We get a different value if read from start, can't guarantee 100% correct lets do some more checks
logger.config((ErrorMessage.MP3_RECALCULATED_POSSIBLE_START_OF_MP3_AUDIO.getMsg(file.getPath(), Hex.asHex(newAudioHeader.getMp3StartByte()))));
//Frame counts dont match so eiither currentHeader or newAudioHeader isnt really audio header
if (currentHeader.getNumberOfFrames() != newAudioHeader.getNumberOfFrames())
{
//Skip to the next header (header 2, counting from start of file)
nextAudioHeader = new MP3AudioHeader(file, newAudioHeader.getMp3StartByte() + newAudioHeader.mp3FrameHeader.getFrameLength());
logger.config("Checking next:" + nextAudioHeader);
//It matches the header we found when doing the original search from after the ID3Tag therefore it
//seems that newAudioHeader was a false match and the original header was correct
if (nextAudioHeader.getMp3StartByte() == currentHeader.getMp3StartByte())
{
logger.warning((ErrorMessage.MP3_START_OF_AUDIO_CONFIRMED.getMsg(file.getPath(), Hex.asHex(currentHeader.getMp3StartByte()))));
return currentHeader;
}
//it matches the header we just found so lends weight to the fact that the audio does indeed start at new header
else if (nextAudioHeader.getNumberOfFrames() == newAudioHeader.getNumberOfFrames())
{
logger.warning((ErrorMessage.MP3_RECALCULATED_START_OF_MP3_AUDIO.getMsg(file.getPath(), Hex.asHex(newAudioHeader.getMp3StartByte()))));
return newAudioHeader;
}
///Not sure but safer to return earlier audio beause stops jaudiotagger overwriting when writing tag
else
{
logger.warning((ErrorMessage.MP3_RECALCULATED_START_OF_MP3_AUDIO.getMsg(file.getPath(), Hex.asHex(newAudioHeader.getMp3StartByte()))));
return newAudioHeader;
}
}
//Same frame count so probably both audio headers with newAudioHeader being the firt one
else
{
logger.warning((ErrorMessage.MP3_RECALCULATED_START_OF_MP3_AUDIO.getMsg(file.getPath(), Hex.asHex(newAudioHeader.getMp3StartByte()))));
return newAudioHeader;
}
}
|
public void | commit()Overridden for compatibility with merged code
try
{
save();
}
catch (IOException ioe)
{
throw new CannotWriteException(ioe);
}
catch (TagException te)
{
throw new CannotWriteException(te);
}
|
public org.jaudiotagger.tag.Tag | createDefaultTag()Create Default Tag
return new ID3v23Tag();
|
private static void | createPlainTextStructureFormatter()
tagFormatter = new PlainTextTagDisplayFormatter();
|
private static void | createXMLStructureFormatter()
tagFormatter = new XMLTagDisplayFormatter();
|
public void | delete(AbstractTag mp3tag)Remove tag from file
RandomAccessFile raf = new RandomAccessFile(this.file, "rws");
mp3tag.delete(raf);
raf.close();
if(mp3tag instanceof ID3v1Tag)
{
id3v1tag=null;
}
if(mp3tag instanceof AbstractID3v2Tag)
{
id3v2tag=null;
}
|
public java.lang.String | displayStructureAsPlainText()Displays MP3File Structure
createPlainTextStructureFormatter();
tagFormatter.openHeadingElement("file", this.getFile().getAbsolutePath());
if (this.getID3v1Tag() != null)
{
this.getID3v1Tag().createStructure();
}
if (this.getID3v2Tag() != null)
{
this.getID3v2Tag().createStructure();
}
tagFormatter.closeHeadingElement("file");
return tagFormatter.toString();
|
public java.lang.String | displayStructureAsXML()Displays MP3File Structure
createXMLStructureFormatter();
tagFormatter.openHeadingElement("file", this.getFile().getAbsolutePath());
if (this.getID3v1Tag() != null)
{
this.getID3v1Tag().createStructure();
}
if (this.getID3v2Tag() != null)
{
this.getID3v2Tag().createStructure();
}
tagFormatter.closeHeadingElement("file");
return tagFormatter.toString();
|
public java.io.File | extractID3v2TagDataIntoFile(java.io.File outputFile)Extracts the raw ID3v2 tag data into a file.
This provides access to the raw data before manipulation, the data is written from the start of the file
to the start of the Audio Data. This is primarily useful for manipulating corrupted tags that are not
(fully) loaded using the standard methods.
int startByte = (int) ((MP3AudioHeader) audioHeader).getMp3StartByte();
if (startByte >= 0)
{
//Read byte into buffer
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteBuffer bb = ByteBuffer.allocate(startByte);
fc.read(bb);
//Write bytes to outputFile
FileOutputStream out = new FileOutputStream(outputFile);
out.write(bb.array());
out.close();
fc.close();
fis.close();
return outputFile;
}
throw new TagNotFoundException("There is no ID3v2Tag data in this file");
|
public ID3v1Tag | getID3v1Tag()Returns the ID3v1 tag for this dataType.
return id3v1tag;
|
public AbstractID3v2Tag | getID3v2Tag()Returns the ID3v2 tag for this datatype.
return id3v2tag;
|
public ID3v24Tag | getID3v2TagAsv24()
return id3v2Asv24tag;
|
public MP3AudioHeader | getMP3AudioHeader()Return audio header
return (MP3AudioHeader) getAudioHeader();
|
public long | getMP3StartByte(java.io.File file)Used by tags when writing to calculate the location of the music file
try
{
//Read ID3v2 tag size (if tag exists) to allow audio header parsing to skip over tag
long startByte = AbstractID3v2Tag.getV2TagSizeIfExists(file);
MP3AudioHeader audioHeader = new MP3AudioHeader(file, startByte);
if (startByte != audioHeader.getMp3StartByte())
{
logger.config("First header found after tag:" + audioHeader);
audioHeader = checkAudioStart(startByte, audioHeader);
}
return audioHeader.getMp3StartByte();
}
catch (InvalidAudioFrameException iafe)
{
throw iafe;
}
catch (IOException ioe)
{
throw ioe;
}
|
public static AbstractTagDisplayFormatter | getStructureFormatter()
return tagFormatter;
|
public boolean | hasID3v1Tag()Returns true if this datatype contains an Id3v1 tag
return (id3v1tag != null);
|
public boolean | hasID3v2Tag()Returns true if this datatype contains an Id3v2 tag
return (id3v2tag != null);
|
public void | precheck(java.io.File file)Check can write to file
if (!file.exists())
{
logger.severe(ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_NOT_FOUND.getMsg(file.getName()));
throw new IOException(ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_NOT_FOUND.getMsg(file.getName()));
}
if (!file.canWrite())
{
logger.severe(ErrorMessage.GENERAL_WRITE_FAILED.getMsg(file.getName()));
throw new IOException(ErrorMessage.GENERAL_WRITE_FAILED.getMsg(file.getName()));
}
if (file.length() <= MINIMUM_FILESIZE)
{
logger.severe(ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_IS_TOO_SMALL.getMsg(file.getName()));
throw new IOException(ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_IS_TOO_SMALL.getMsg(file.getName()));
}
|
private void | readLyrics3Tag(java.io.File file, java.io.RandomAccessFile newFile, int loadOptions)Read lyrics3 Tag
TODO:not working
/*if ((loadOptions & LOAD_LYRICS3) != 0)
{
try
{
lyrics3tag = new Lyrics3v2(newFile);
}
catch (TagNotFoundException ex)
{
}
try
{
if (lyrics3tag == null)
{
lyrics3tag = new Lyrics3v1(newFile);
}
}
catch (TagNotFoundException ex)
{
}
}
*/
|
private void | readV1Tag(java.io.File file, java.io.RandomAccessFile newFile, int loadOptions)Read v1 tag
if ((loadOptions & LOAD_IDV1TAG) != 0)
{
logger.finer("Attempting to read id3v1tags");
try
{
id3v1tag = new ID3v11Tag(newFile, file.getName());
}
catch (TagNotFoundException ex)
{
logger.config("No ids3v11 tag found");
}
try
{
if (id3v1tag == null)
{
id3v1tag = new ID3v1Tag(newFile, file.getName());
}
}
catch (TagNotFoundException ex)
{
logger.config("No id3v1 tag found");
}
}
|
private void | readV2Tag(java.io.File file, int loadOptions)Read V2tag if exists
TODO:shouldnt we be handing TagExceptions:when will they be thrown
//We know where the Actual Audio starts so load all the file from start to that point into
//a buffer then we can read the IDv2 information without needing any more file I/O
int startByte = (int) ((MP3AudioHeader) audioHeader).getMp3StartByte();
if (startByte >= AbstractID3v2Tag.TAG_HEADER_LENGTH)
{
logger.finer("Attempting to read id3v2tags");
FileInputStream fis = null;
FileChannel fc = null;
ByteBuffer bb = null;
try
{
fis = new FileInputStream(file);
fc = fis.getChannel();
//Read into Byte Buffer
bb = ByteBuffer.allocate(startByte);
fc.read(bb);
}
finally
{
if (fc != null)
{
fc.close();
}
if (fis != null)
{
fis.close();
}
}
bb.rewind();
if ((loadOptions & LOAD_IDV2TAG) != 0)
{
logger.config("Attempting to read id3v2tags");
try
{
this.setID3v2Tag(new ID3v24Tag(bb, file.getName()));
}
catch (TagNotFoundException ex)
{
logger.config("No id3v24 tag found");
}
try
{
if (id3v2tag == null)
{
this.setID3v2Tag(new ID3v23Tag(bb, file.getName()));
}
}
catch (TagNotFoundException ex)
{
logger.config("No id3v23 tag found");
}
try
{
if (id3v2tag == null)
{
this.setID3v2Tag(new ID3v22Tag(bb, file.getName()));
}
}
catch (TagNotFoundException ex)
{
logger.config("No id3v22 tag found");
}
}
}
else
{
logger.config("Not enough room for valid id3v2 tag:" + startByte);
}
|
public void | save()Saves the tags in this dataType to the file referred to by this dataType.
save(this.file);
|
public void | save(java.io.File fileToSave)Saves the tags in this dataType to the file argument. It will be saved as
TagConstants.MP3_FILE_SAVE_WRITE
//Ensure we are dealing with absolute filepqaths not relative ones
File file = fileToSave.getAbsoluteFile();
logger.config("Saving : " + file.getPath());
//Checks before starting write
precheck(file);
RandomAccessFile rfile = null;
try
{
//ID3v2 Tag
if (TagOptionSingleton.getInstance().isId3v2Save())
{
if (id3v2tag == null)
{
rfile = new RandomAccessFile(file, "rws");
(new ID3v24Tag()).delete(rfile);
(new ID3v23Tag()).delete(rfile);
(new ID3v22Tag()).delete(rfile);
logger.config("Deleting ID3v2 tag:"+file.getName());
rfile.close();
}
else
{
logger.config("Writing ID3v2 tag:"+file.getName());
id3v2tag.write(file, ((MP3AudioHeader) this.getAudioHeader()).getMp3StartByte());
}
}
rfile = new RandomAccessFile(file, "rws");
//Lyrics 3 Tag
if (TagOptionSingleton.getInstance().isLyrics3Save())
{
if (lyrics3tag != null)
{
lyrics3tag.write(rfile);
}
}
//ID3v1 tag
if (TagOptionSingleton.getInstance().isId3v1Save())
{
logger.config("Processing ID3v1");
if (id3v1tag == null)
{
logger.config("Deleting ID3v1");
(new ID3v1Tag()).delete(rfile);
}
else
{
logger.config("Saving ID3v1");
id3v1tag.write(rfile);
}
}
}
catch (FileNotFoundException ex)
{
logger.log(Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_NOT_FOUND.getMsg(file.getName()), ex);
throw ex;
}
catch (IOException iex)
{
logger.log(Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE.getMsg(file.getName(), iex.getMessage()), iex);
throw iex;
}
catch (RuntimeException re)
{
logger.log(Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE.getMsg(file.getName(), re.getMessage()), re);
throw re;
}
finally
{
if (rfile != null)
{
rfile.close();
}
}
|
public void | setID3v1Tag(ID3v1Tag id3v1tag)Sets the ID3v1(_1)tag to the tag provided as an argument.
logger.config("setting tagv1:v1 tag");
this.id3v1tag = id3v1tag;
|
public void | setID3v1Tag(org.jaudiotagger.tag.Tag id3v1tag)
logger.config("setting tagv1:v1 tag");
this.id3v1tag = (ID3v1Tag) id3v1tag;
|
public void | setID3v1Tag(AbstractTag mp3tag)Sets the ID3v1 tag for this dataType. A new
ID3v1_1 dataType is created from the argument and then used
here.
logger.config("setting tagv1:abstract");
id3v1tag = new ID3v11Tag(mp3tag);
|
public void | setID3v2Tag(AbstractTag mp3tag)Sets the ID3v2 tag for this dataType. A new
ID3v2_4 dataType is created from the argument and then used
here.
id3v2tag = new ID3v24Tag(mp3tag);
|
public void | setID3v2Tag(AbstractID3v2Tag id3v2tag)Sets the v2 tag to the v2 tag provided as an argument.
Also store a v24 version of tag as v24 is the interface to be used
when talking with client applications.
this.id3v2tag = id3v2tag;
if (id3v2tag instanceof ID3v24Tag)
{
this.id3v2Asv24tag = (ID3v24Tag) this.id3v2tag;
}
else
{
this.id3v2Asv24tag = new ID3v24Tag(id3v2tag);
}
|
public void | setID3v2TagOnly(AbstractID3v2Tag id3v2tag)Set v2 tag ,don't need to set v24 tag because saving
this.id3v2tag = id3v2tag;
this.id3v2Asv24tag = null;
|
public void | setTag(org.jaudiotagger.tag.Tag tag)Set the Tag
If the parameter tag is a v1tag then the v1 tag is set if v2tag then the v2tag.
this.tag = tag;
if (tag instanceof ID3v1Tag)
{
setID3v1Tag((ID3v1Tag) tag);
}
else
{
setID3v2Tag((AbstractID3v2Tag) tag);
}
|