package com.limegroup.gnutella.metadata.audio.reader;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.mp3.MP3File;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.id3.AbstractID3v2Frame;
import org.jaudiotagger.tag.id3.AbstractID3v2Tag;
import org.jaudiotagger.tag.id3.ID3v1Tag;
import org.jaudiotagger.tag.id3.ID3v24Frames;
import com.limegroup.gnutella.metadata.audio.AudioMetaData;
/**
* Reads MetaData from MP3 files. This extends AudioDataReader which also
* handles this format. However, store files need to get checked and parsed
* correctly so we do that here.
*/
public class MP3Reader extends AudioDataReader {
@Override
protected void readTag(AudioMetaData audioData, AudioFile audioFile, Tag tag) {
MP3File mp3File = ((MP3File)audioFile);
mp3File.getID3v1Tag();
AbstractID3v2Tag v2Tag = mp3File.getID3v2Tag();
ID3v1Tag v1Tag = mp3File.getID3v1Tag();
// check v2 tags first if they exist
if( v2Tag != null )
readV2Tag(audioData, v2Tag);
// check v1 tags next
if( v1Tag != null )
readV1Tag(audioData, v1Tag);
}
/**
* Reads v1 tags from the mp3. Only writes the field to the AudioData if
* it has not been filled in by v2 tags
*/
private void readV1Tag(AudioMetaData audioData, ID3v1Tag tag){
if( audioData.getTitle() == null || audioData.getTitle().length() == 0)
audioData.setTitle(tag.getFirstTitle());
if( audioData.getArtist() == null || audioData.getArtist().length() == 0)
audioData.setArtist(tag.getFirstArtist());
if( audioData.getAlbum() == null || audioData.getAlbum().length() == 0)
audioData.setAlbum(tag.getFirstAlbum());
if( audioData.getYear() == null || audioData.getYear().length() == 0)
audioData.setYear(tag.getFirstYear());
if( audioData.getComment() == null || audioData.getComment().length() == 0)
audioData.setComment(tag.getFirstComment());
if( audioData.getGenre() == null || audioData.getGenre().length() == 0)
audioData.setGenre(tag.getFirstGenre());
if( audioData.getTrack() == null || audioData.getTrack().length() == 0) {
try {
audioData.setTrack(tag.getFirstTrack());
}
catch(UnsupportedOperationException e) {
// id3v1.0 tags dont have tracks
}
}
}
/**
* Reads v2 tags from the mp3.
*/
private void readV2Tag(AudioMetaData audioData, AbstractID3v2Tag tag) {
audioData.setTitle(tag.getFirstTitle());
audioData.setArtist(tag.getFirstArtist());
audioData.setAlbum(tag.getFirstAlbum());
audioData.setYear(tag.getFirstYear());
audioData.setComment(tag.getFirstComment());
audioData.setGenre(parseGenre(tag.getFirstGenre()));
audioData.setTrack(tag.getFirstTrack());
audioData.setLicense(tag.getFirst(ID3v24Frames.FRAME_ID_COPYRIGHTINFO));
Iterator iter = tag.iterator();
while(iter.hasNext()) {
if( audioData.getLicenseType() != null && audioData.getLicenseType().equals(MAGIC_KEY) )
return;
Object nextFrame = iter.next();
if( !(nextFrame instanceof AbstractID3v2Frame))
continue;
AbstractID3v2Frame o = (AbstractID3v2Frame)nextFrame;
if( !(o.getId().equals("TIT2") || o.getId().equals("TALB") || o.getId().equals("TOAL") ||
o.getId().equals("TOPE") || o.getId().equals("TPE1") || o.getId().equals("TPE2")
|| o.getId().equals("TPE3") || o.getId().equals("TPE4")) )
if( o.getBody().getObject("Text") != null )
checkLWS(audioData, o.getBody().getObject("Text").toString());
else
isRawCheck(audioData, o.getRawContent());
}
}
/**
* Checks a raw content field for the magic String. This is always in UTF-8 encoding so use the
* content byte array instead.
*/
private void isRawCheck(AudioMetaData audioDatas, byte[] contentBytes) {
try {
String content = new String(contentBytes,"UTF-8");
checkLWS(audioDatas, content);
} catch (UnsupportedEncodingException e) {
}
}
/**
* If the song is not a LWS already, do a substring search to see if
* it is a LWS song
* @param content - ID3 tag to scan for a substring
*/
private void checkLWS(AudioMetaData audioData, String content) {
if( audioData.getLicenseType() == null || !audioData.getLicenseType().equals(MAGIC_KEY))
if( content.indexOf(MAGIC_KEY) != -1) {
audioData.setLicenseType(MAGIC_KEY);
}
}
/**
* Some genres in ID3v2 tags are displaying (XXX) numbers along side the genre.
* If this exists it hides the number from the user
*/
private String parseGenre(String genre){
if( genre == null || genre.length() <= 0)
return genre;
String cleanGenre = genre;
if( genre.charAt(0) == '(') {
int startIndex = 0;
for(int i = 0; i < genre.length(); i++) {
if( genre.charAt(i) == ')') {
startIndex = i + 1;
}
}
cleanGenre = genre.substring(startIndex);
}
return cleanGenre;
}
@Override
public String[] getSupportedExtensions() {
return new String[] { "mp3" };
}
}