package com.mpatric.mp3agic;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public abstract class AbstractID3v2Tag implements ID3v2 {
public static final String ID_IMAGE = "APIC";
public static final String ID_ENCODER = "TENC";
public static final String ID_URL = "WXXX";
public static final String ID_ARTIST_URL = "WOAR";
public static final String ID_COMMERCIAL_URL = "WCOM";
public static final String ID_COPYRIGHT_URL = "WCOP";
public static final String ID_AUDIOFILE_URL = "WOAF";
public static final String ID_AUDIOSOURCE_URL = "WOAS";
public static final String ID_RADIOSTATION_URL = "WORS";
public static final String ID_PAYMENT_URL = "WPAY";
public static final String ID_PUBLISHER_URL = "WPUB";
public static final String ID_COPYRIGHT = "TCOP";
public static final String ID_ORIGINAL_ARTIST = "TOPE";
public static final String ID_BPM = "TBPM";
public static final String ID_COMPOSER = "TCOM";
public static final String ID_PUBLISHER = "TPUB";
public static final String ID_COMMENT = "COMM";
public static final String ID_GENRE = "TCON";
public static final String ID_YEAR = "TYER";
public static final String ID_DATE = "TDAT";
public static final String ID_ALBUM = "TALB";
public static final String ID_TITLE = "TIT2";
public static final String ID_KEY = "TKEY";
public static final String ID_ARTIST = "TPE1";
public static final String ID_ALBUM_ARTIST = "TPE2";
public static final String ID_TRACK = "TRCK";
public static final String ID_PART_OF_SET = "TPOS";
public static final String ID_COMPILATION = "TCMP";
public static final String ID_CHAPTER_TOC = "CTOC";
public static final String ID_CHAPTER = "CHAP";
public static final String ID_GROUPING = "TIT1";
public static final String ID_IMAGE_OBSELETE = "PIC";
public static final String ID_ENCODER_OBSELETE = "TEN";
public static final String ID_URL_OBSELETE = "WXX";
public static final String ID_COPYRIGHT_OBSELETE = "TCR";
public static final String ID_ORIGINAL_ARTIST_OBSELETE = "TOA";
public static final String ID_BPM_OBSELETE = "TBP";
public static final String ID_COMPOSER_OBSELETE = "TCM";
public static final String ID_PUBLISHER_OBSELETE = "TBP";
public static final String ID_COMMENT_OBSELETE = "COM";
public static final String ID_GENRE_OBSELETE = "TCO";
public static final String ID_YEAR_OBSELETE = "TYE";
public static final String ID_DATE_OBSELETE = "TDA";
public static final String ID_ALBUM_OBSELETE = "TAL";
public static final String ID_TITLE_OBSELETE = "TT2";
public static final String ID_KEY_OBSELETE = "TKE";
public static final String ID_ARTIST_OBSELETE = "TP1";
public static final String ID_ALBUM_ARTIST_OBSELETE = "TP2";
public static final String ID_TRACK_OBSELETE = "TRK";
public static final String ID_PART_OF_SET_OBSELETE = "TPA";
public static final String ID_COMPILATION_OBSELETE = "TCP";
public static final String ID_GROUPING_OBSELETE = "TT1";
protected static final String TAG = "ID3";
protected static final String FOOTER_TAG = "3DI";
protected static final int HEADER_LENGTH = 10;
protected static final int FOOTER_LENGTH = 10;
protected static final int MAJOR_VERSION_OFFSET = 3;
protected static final int MINOR_VERSION_OFFSET = 4;
protected static final int FLAGS_OFFSET = 5;
protected static final int DATA_LENGTH_OFFSET = 6;
protected static final int FOOTER_BIT = 4;
protected static final int EXPERIMENTAL_BIT = 5;
protected static final int EXTENDED_HEADER_BIT = 6;
protected static final int COMPRESSION_BIT = 6;
protected static final int UNSYNCHRONISATION_BIT = 7;
protected static final int PADDING_LENGTH = 256;
private static final String ITUNES_COMMENT_DESCRIPTION = "iTunNORM";
protected boolean unsynchronisation = false;
protected boolean extendedHeader = false;
protected boolean experimental = false;
protected boolean footer = false;
protected boolean compression = false;
protected boolean padding = false;
protected String version = null;
private int dataLength = 0;
private int extendedHeaderLength;
private byte[] extendedHeaderData;
private boolean obseleteFormat = false;
private final Map<String, ID3v2FrameSet> frameSets;
public AbstractID3v2Tag() {
frameSets = new TreeMap<String, ID3v2FrameSet>();
}
public AbstractID3v2Tag(byte[] bytes) throws NoSuchTagException, UnsupportedTagException, InvalidDataException {
this(bytes, false);
}
public AbstractID3v2Tag(byte[] bytes, boolean obseleteFormat) throws NoSuchTagException, UnsupportedTagException, InvalidDataException {
frameSets = new TreeMap<String, ID3v2FrameSet>();
this.obseleteFormat = obseleteFormat;
unpackTag(bytes);
}
private void unpackTag(byte[] bytes) throws NoSuchTagException, UnsupportedTagException, InvalidDataException {
ID3v2TagFactory.sanityCheckTag(bytes);
int offset = unpackHeader(bytes);
try {
if (extendedHeader) {
offset = unpackExtendedHeader(bytes, offset);
}
int framesLength = dataLength;
if (footer) framesLength -= 10;
offset = unpackFrames(bytes, offset, framesLength);
if (footer) {
offset = unpackFooter(bytes, dataLength);
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new InvalidDataException("Premature end of tag", e);
}
}
private int unpackHeader(byte[] bytes) throws UnsupportedTagException, InvalidDataException {
int majorVersion = bytes[MAJOR_VERSION_OFFSET];
int minorVersion = bytes[MINOR_VERSION_OFFSET];
version = majorVersion + "." + minorVersion;
if (majorVersion != 2 && majorVersion != 3 && majorVersion != 4) {
throw new UnsupportedTagException("Unsupported version " + version);
}
unpackFlags(bytes);
if ((bytes[FLAGS_OFFSET] & 0x0F) != 0) throw new UnsupportedTagException("Unrecognised bits in header");
dataLength = BufferTools.unpackSynchsafeInteger(bytes[DATA_LENGTH_OFFSET], bytes[DATA_LENGTH_OFFSET + 1], bytes[DATA_LENGTH_OFFSET + 2], bytes[DATA_LENGTH_OFFSET + 3]);
if (dataLength < 1) throw new InvalidDataException("Zero size tag");
return HEADER_LENGTH;
}
protected abstract void unpackFlags(byte[] bytes);
private int unpackExtendedHeader(byte[] bytes, int offset) {
extendedHeaderLength = BufferTools.unpackSynchsafeInteger(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]) + 4;
extendedHeaderData = BufferTools.copyBuffer(bytes, offset + 4, extendedHeaderLength);
return extendedHeaderLength;
}
protected int unpackFrames(byte[] bytes, int offset, int framesLength) {
int currentOffset = offset;
while (currentOffset <= framesLength) {
ID3v2Frame frame;
try {
frame = createFrame(bytes, currentOffset);
addFrame(frame, false);
currentOffset += frame.getLength();
} catch (InvalidDataException e) {
break;
}
}
return currentOffset;
}
protected void addFrame(ID3v2Frame frame, boolean replace) {
ID3v2FrameSet frameSet = frameSets.get(frame.getId());
if (frameSet == null) {
frameSet = new ID3v2FrameSet(frame.getId());
frameSet.addFrame(frame);
frameSets.put(frame.getId(), frameSet);
} else if (replace) {
frameSet.clear();
frameSet.addFrame(frame);
} else {
frameSet.addFrame(frame);
}
}
protected ID3v2Frame createFrame(byte[] bytes, int currentOffset) throws InvalidDataException {
if (obseleteFormat) return new ID3v2ObseleteFrame(bytes, currentOffset);
return new ID3v2Frame(bytes, currentOffset);
}
protected ID3v2Frame createFrame(String id, byte[] data) {
if (obseleteFormat) return new ID3v2ObseleteFrame(id, data);
else return new ID3v2Frame(id, data);
}
private int unpackFooter(byte[] bytes, int offset) throws InvalidDataException {
if (! FOOTER_TAG.equals(BufferTools.byteBufferToStringIgnoringEncodingIssues(bytes, offset, FOOTER_TAG.length()))) {
throw new InvalidDataException("Invalid footer");
}
return FOOTER_LENGTH;
}
public byte[] toBytes() throws NotSupportedException {
byte[] bytes = new byte[getLength()];
packTag(bytes);
return bytes;
}
public void packTag(byte[] bytes) throws NotSupportedException {
int offset = packHeader(bytes, 0);
if (extendedHeader) {
offset = packExtendedHeader(bytes, offset);
}
offset = packFrames(bytes, offset);
if (footer) {
offset = packFooter(bytes, dataLength);
}
}
private int packHeader(byte[] bytes, int offset) {
try {
BufferTools.stringIntoByteBuffer(TAG, 0, TAG.length(), bytes, offset);
} catch (UnsupportedEncodingException e) {
}
String s[] = version.split("\\.");
if (s.length > 0) {
byte majorVersion = Byte.parseByte(s[0]);
bytes[offset + MAJOR_VERSION_OFFSET] = majorVersion;
}
if (s.length > 1) {
byte minorVersion = Byte.parseByte(s[1]);
bytes[offset + MINOR_VERSION_OFFSET] = minorVersion;
}
packFlags(bytes, offset);
BufferTools.packSynchsafeInteger(getDataLength(), bytes, offset + DATA_LENGTH_OFFSET);
return offset + HEADER_LENGTH;
}
protected abstract void packFlags(byte[] bytes, int i);
private int packExtendedHeader(byte[] bytes, int offset) {
BufferTools.packSynchsafeInteger(extendedHeaderLength, bytes, offset);
BufferTools.copyIntoByteBuffer(extendedHeaderData, 0, extendedHeaderData.length, bytes, offset + 4);
return offset + 4 + extendedHeaderData.length;
}
public int packFrames(byte[] bytes, int offset) throws NotSupportedException {
int newOffset = packSpecifiedFrames(bytes, offset, null, "APIC");
newOffset = packSpecifiedFrames(bytes, newOffset, "APIC", null);
return newOffset;
}
private int packSpecifiedFrames(byte[] bytes, int offset, String onlyId, String notId) throws NotSupportedException {
Iterator<ID3v2FrameSet> setIterator = frameSets.values().iterator();
while (setIterator.hasNext()) {
ID3v2FrameSet frameSet = setIterator.next();
if ((onlyId == null || onlyId.equals(frameSet.getId())) && (notId == null || !notId.equals(frameSet.getId()))) {
Iterator<ID3v2Frame> frameIterator = frameSet.getFrames().iterator();
while (frameIterator.hasNext()) {
ID3v2Frame frame = (ID3v2Frame) frameIterator.next();
if (frame.getDataLength() > 0) {
byte[] frameData = frame.toBytes();
BufferTools.copyIntoByteBuffer(frameData, 0, frameData.length, bytes, offset);
offset += frameData.length;
}
}
}
}
return offset;
}
private int packFooter(byte[] bytes, int offset) {
try {
BufferTools.stringIntoByteBuffer(FOOTER_TAG, 0, FOOTER_TAG.length(), bytes, offset);
} catch (UnsupportedEncodingException e) {
}
String s[] = version.split("\\.");
if (s.length > 0) {
byte majorVersion = Byte.parseByte(s[0]);
bytes[offset + MAJOR_VERSION_OFFSET] = majorVersion;
}
if (s.length > 1) {
byte minorVersion = Byte.parseByte(s[1]);
bytes[offset + MINOR_VERSION_OFFSET] = minorVersion;
}
packFlags(bytes, offset);
BufferTools.packSynchsafeInteger(getDataLength(), bytes, offset + DATA_LENGTH_OFFSET);
return offset + FOOTER_LENGTH;
}
private int calculateDataLength() {
int length = 0;
if (extendedHeader) length += extendedHeaderLength;
if (footer) length += FOOTER_LENGTH;
else if (padding) length += PADDING_LENGTH;
Iterator<ID3v2FrameSet> setIterator = frameSets.values().iterator();
while (setIterator.hasNext()) {
ID3v2FrameSet frameSet = setIterator.next();
Iterator<ID3v2Frame> frameIterator = frameSet.getFrames().iterator();
while (frameIterator.hasNext()) {
ID3v2Frame frame = (ID3v2Frame) frameIterator.next();
length += frame.getLength();
}
}
return length;
}
protected boolean useFrameUnsynchronisation() {
return false;
}
public String getVersion() {
return version;
}
protected void invalidateDataLength() {
dataLength = 0;
}
public int getDataLength() {
if (dataLength == 0) {
dataLength = calculateDataLength();
}
return dataLength;
}
public int getLength() {
return getDataLength() + HEADER_LENGTH;
}
public Map<String, ID3v2FrameSet> getFrameSets() {
return frameSets;
}
public boolean getPadding() {
return padding;
}
public void setPadding(boolean padding) {
if (this.padding != padding) {
invalidateDataLength();
this.padding = padding;
}
}
public boolean hasFooter() {
return footer;
}
public void setFooter(boolean footer) {
if (this.footer != footer) {
invalidateDataLength();
this.footer = footer;
}
}
public boolean hasUnsynchronisation() {
return unsynchronisation;
}
public void setUnsynchronisation(boolean unsynchronisation) {
if (this.unsynchronisation != unsynchronisation) {
invalidateDataLength();
this.unsynchronisation = unsynchronisation;
}
}
public boolean getObseleteFormat() {
return obseleteFormat;
}
public String getTrack() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_TRACK_OBSELETE : ID_TRACK);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setTrack(String track) {
if (track != null && track.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(track));
addFrame(createFrame(ID_TRACK, frameData.toBytes()), true);
}
}
public String getPartOfSet() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_PART_OF_SET_OBSELETE : ID_PART_OF_SET);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setPartOfSet(String partOfSet) {
if (partOfSet != null && partOfSet.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(partOfSet));
addFrame(createFrame(ID_PART_OF_SET, frameData.toBytes()), true);
}
}
public boolean isCompilation() {
// unofficial frame used by iTunes
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_COMPILATION_OBSELETE : ID_COMPILATION);
if (frameData != null && frameData.getText() != null) return "1".equals(frameData.getText().toString());
return false;
}
public void setCompilation(boolean compilation) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(compilation ? "1" : "0"));
addFrame(createFrame(ID_COMPILATION, frameData.toBytes()), true);
}
public String getGrouping() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_GROUPING_OBSELETE : ID_GROUPING);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setGrouping(String grouping) {
if (grouping != null && grouping.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(grouping));
addFrame(createFrame(ID_GROUPING, frameData.toBytes()), true);
}
}
public String getArtist() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_ARTIST_OBSELETE : ID_ARTIST);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setArtist(String artist) {
if (artist != null && artist.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(artist));
addFrame(createFrame(ID_ARTIST, frameData.toBytes()), true);
}
}
public String getAlbumArtist() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_ALBUM_ARTIST_OBSELETE : ID_ALBUM_ARTIST);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setAlbumArtist(String albumArtist) {
if (albumArtist != null && albumArtist.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(albumArtist));
addFrame(createFrame(ID_ALBUM_ARTIST, frameData.toBytes()), true);
}
}
public String getTitle() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_TITLE_OBSELETE : ID_TITLE);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setTitle(String title) {
if (title != null && title.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(title));
addFrame(createFrame(ID_TITLE, frameData.toBytes()), true);
}
}
public String getAlbum() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_ALBUM_OBSELETE : ID_ALBUM);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setAlbum(String album) {
if (album != null && album.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(album));
addFrame(createFrame(ID_ALBUM, frameData.toBytes()), true);
}
}
public String getYear() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_YEAR_OBSELETE : ID_YEAR);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setYear(String year) {
if (year != null && year.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(year));
addFrame(createFrame(ID_YEAR, frameData.toBytes()), true);
}
}
public String getDate() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_DATE_OBSELETE : ID_DATE);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setDate(String date) {
if (date != null && date.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(date));
addFrame(createFrame(ID_DATE, frameData.toBytes()), true);
}
}
private int getGenre(String text) {
if (text != null && text.length() > 0) {
try {
return extractGenreNumber(text);
} catch (NumberFormatException e) { // match genre description
String description = extractGenreDescription(text);
return ID3v1Genres.matchGenreDescription(description);
}
}
return -1;
}
public int getGenre() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_GENRE_OBSELETE : ID_GENRE);
if (frameData == null || frameData.getText() == null) {
return -1;
}
return getGenre(frameData.getText().toString());
}
public void setGenre(int genre) {
if (genre >= 0) {
invalidateDataLength();
String genreDescription = genre < ID3v1Genres.GENRES.length ? ID3v1Genres.GENRES[genre] : "";
String combinedGenre = "(" + Integer.toString(genre) + ")" + genreDescription;
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(combinedGenre));
addFrame(createFrame(ID_GENRE, frameData.toBytes()), true);
} else {
// TODO remove frame?
}
}
public int getBPM() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_BPM_OBSELETE : ID_BPM);
if (frameData == null || frameData.getText() == null) {
return -1;
}
return Integer.parseInt(frameData.getText().toString());
}
public void setBPM(int bpm) {
if (bpm >= 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(Integer.toString(bpm)));
addFrame(createFrame(ID_BPM, frameData.toBytes()), true);
}
}
public String getKey() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_KEY_OBSELETE : ID_KEY);
if (frameData == null || frameData.getText() == null) {
return null;
}
return frameData.getText().toString();
}
public void setKey(String key) {
if (key != null && key.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(key));
addFrame(createFrame(ID_KEY, frameData.toBytes()), true);
}
}
public String getGenreDescription() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_GENRE_OBSELETE : ID_GENRE);
if (frameData == null || frameData.getText() == null) {
return null;
}
String text = frameData.getText().toString();
if (text != null) {
int genreNum = getGenre(text);
if (genreNum >= 0 && genreNum < ID3v1Genres.GENRES.length) {
return ID3v1Genres.GENRES[genreNum];
} else {
String description = extractGenreDescription(text);
if (description != null && description.length() > 0) {
return description;
}
}
}
return null;
}
public void setGenreDescription(String text) throws IllegalArgumentException {
int genreNum = ID3v1Genres.matchGenreDescription(text);
if (genreNum < 0) {
throw new IllegalArgumentException("Unknown genre: " + text);
}
setGenre(genreNum);
}
protected int extractGenreNumber(String genreValue) throws NumberFormatException {
String value = genreValue.trim();
if (value.length() > 0) {
if (value.charAt(0) == '(') {
int pos = value.indexOf(')');
if (pos > 0) {
return Integer.parseInt(value.substring(1, pos));
}
}
}
return Integer.parseInt(value);
}
protected String extractGenreDescription(String genreValue) throws NumberFormatException {
String value = genreValue.trim();
if (value.length() > 0) {
if (value.charAt(0) == '(') {
int pos = value.indexOf(')');
if (pos > 0) {
return value.substring(pos + 1);
}
}
return value;
}
return null;
}
public String getComment() {
ID3v2CommentFrameData frameData = extractCommentFrameData(obseleteFormat ? ID_COMMENT_OBSELETE : ID_COMMENT, false);
if (frameData != null && frameData.getComment() != null) return frameData.getComment().toString();
return null;
}
public void setComment(String comment) {
if (comment != null && comment.length() > 0) {
invalidateDataLength();
ID3v2CommentFrameData frameData = new ID3v2CommentFrameData(useFrameUnsynchronisation(), "eng", null, new EncodedText(comment));
addFrame(createFrame(ID_COMMENT, frameData.toBytes()), true);
}
}
public String getItunesComment() {
ID3v2CommentFrameData frameData = extractCommentFrameData(obseleteFormat ? ID_COMMENT_OBSELETE : ID_COMMENT, true);
if (frameData != null && frameData.getComment() != null) return frameData.getComment().toString();
return null;
}
public void setItunesComment(String itunesComment) {
if (itunesComment != null && itunesComment.length() > 0) {
invalidateDataLength();
ID3v2CommentFrameData frameData = new ID3v2CommentFrameData(useFrameUnsynchronisation(), "eng", new EncodedText(ITUNES_COMMENT_DESCRIPTION), new EncodedText(itunesComment));
addFrame(createFrame(ID_COMMENT, frameData.toBytes()), true);
}
}
public String getComposer() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_COMPOSER_OBSELETE : ID_COMPOSER);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setComposer(String composer) {
if (composer != null && composer.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(composer));
addFrame(createFrame(ID_COMPOSER, frameData.toBytes()), true);
}
}
public String getPublisher() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_PUBLISHER_OBSELETE : ID_PUBLISHER);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setPublisher(String publisher) {
if (publisher != null && publisher.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(publisher));
addFrame(createFrame(ID_PUBLISHER, frameData.toBytes()), true);
}
}
public String getOriginalArtist() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_ORIGINAL_ARTIST_OBSELETE : ID_ORIGINAL_ARTIST);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setOriginalArtist(String originalArtist) {
if (originalArtist != null && originalArtist.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(originalArtist));
addFrame(createFrame(ID_ORIGINAL_ARTIST, frameData.toBytes()), true);
}
}
public String getCopyright() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_COPYRIGHT_OBSELETE : ID_COPYRIGHT);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setCopyright(String copyright) {
if (copyright != null && copyright.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(copyright));
addFrame(createFrame(ID_COPYRIGHT, frameData.toBytes()), true);
}
}
public String getArtistUrl() {
ID3v2WWWFrameData frameData = extractWWWFrameData(ID_ARTIST_URL);
if (frameData != null) return frameData.getUrl();
return null;
}
public void setArtistUrl(String url) {
if (url != null && url.length() > 0) {
invalidateDataLength();
ID3v2WWWFrameData frameData = new ID3v2WWWFrameData(useFrameUnsynchronisation(), url);
addFrame(createFrame(ID_ARTIST_URL, frameData.toBytes()), true);
}
}
public String getCommercialUrl() {
ID3v2WWWFrameData frameData = extractWWWFrameData(ID_COMMERCIAL_URL);
if (frameData != null) return frameData.getUrl();
return null;
}
public void setCommercialUrl(String url) {
if (url != null && url.length() > 0) {
invalidateDataLength();
ID3v2WWWFrameData frameData = new ID3v2WWWFrameData(useFrameUnsynchronisation(), url);
addFrame(createFrame(ID_COMMERCIAL_URL, frameData.toBytes()), true);
}
}
public String getCopyrightUrl() {
ID3v2WWWFrameData frameData = extractWWWFrameData(ID_COPYRIGHT_URL);
if (frameData != null) return frameData.getUrl();
return null;
}
public void setCopyrightUrl(String url) {
if (url != null && url.length() > 0) {
invalidateDataLength();
ID3v2WWWFrameData frameData = new ID3v2WWWFrameData(useFrameUnsynchronisation(), url);
addFrame(createFrame(ID_COPYRIGHT_URL, frameData.toBytes()), true);
}
}
public String getAudiofileUrl() {
ID3v2WWWFrameData frameData = extractWWWFrameData(ID_AUDIOFILE_URL);
if (frameData != null) return frameData.getUrl();
return null;
}
public void setAudiofileUrl(String url) {
if (url != null && url.length() > 0) {
invalidateDataLength();
ID3v2WWWFrameData frameData = new ID3v2WWWFrameData(useFrameUnsynchronisation(), url);
addFrame(createFrame(ID_AUDIOFILE_URL, frameData.toBytes()), true);
}
}
public String getAudioSourceUrl() {
ID3v2WWWFrameData frameData = extractWWWFrameData(ID_AUDIOSOURCE_URL);
if (frameData != null) return frameData.getUrl();
return null;
}
public void setAudioSourceUrl(String url) {
if (url != null && url.length() > 0) {
invalidateDataLength();
ID3v2WWWFrameData frameData = new ID3v2WWWFrameData(useFrameUnsynchronisation(), url);
addFrame(createFrame(ID_AUDIOSOURCE_URL, frameData.toBytes()), true);
}
}
public String getRadiostationUrl() {
ID3v2WWWFrameData frameData = extractWWWFrameData(ID_RADIOSTATION_URL);
if (frameData != null) return frameData.getUrl();
return null;
}
public void setRadiostationUrl(String url) {
if (url != null && url.length() > 0) {
invalidateDataLength();
ID3v2WWWFrameData frameData = new ID3v2WWWFrameData(useFrameUnsynchronisation(), url);
addFrame(createFrame(ID_RADIOSTATION_URL, frameData.toBytes()), true);
}
}
public String getPaymentUrl() {
ID3v2WWWFrameData frameData = extractWWWFrameData(ID_PAYMENT_URL);
if (frameData != null) return frameData.getUrl();
return null;
}
public void setPaymentUrl(String url) {
if (url != null && url.length() > 0) {
invalidateDataLength();
ID3v2WWWFrameData frameData = new ID3v2WWWFrameData(useFrameUnsynchronisation(), url);
addFrame(createFrame(ID_PAYMENT_URL, frameData.toBytes()), true);
}
}
public String getPublisherUrl() {
ID3v2WWWFrameData frameData = extractWWWFrameData(ID_PUBLISHER_URL);
if (frameData != null) return frameData.getUrl();
return null;
}
public void setPublisherUrl(String url) {
if (url != null && url.length() > 0) {
invalidateDataLength();
ID3v2WWWFrameData frameData = new ID3v2WWWFrameData(useFrameUnsynchronisation(), url);
addFrame(createFrame(ID_PUBLISHER_URL, frameData.toBytes()), true);
}
}
public String getUrl() {
ID3v2UrlFrameData frameData = extractUrlFrameData(obseleteFormat ? ID_URL_OBSELETE : ID_URL);
if (frameData != null) return frameData.getUrl();
return null;
}
public void setUrl(String url) {
if (url != null && url.length() > 0) {
invalidateDataLength();
ID3v2UrlFrameData frameData = new ID3v2UrlFrameData(useFrameUnsynchronisation(), null, url);
addFrame(createFrame(ID_URL, frameData.toBytes()), true);
}
}
public ArrayList<ID3v2ChapterFrameData> getChapters() {
if (obseleteFormat) {
return null;
}
return extractChapterFrameData(ID_CHAPTER);
}
public void setChapters(ArrayList<ID3v2ChapterFrameData> chapters) {
if(chapters != null) {
invalidateDataLength();
boolean first = true;
for(ID3v2ChapterFrameData chapter: chapters) {
if(first) {
first = false;
addFrame(createFrame(ID_CHAPTER, chapter.toBytes()), true);
} else {
addFrame(createFrame(ID_CHAPTER, chapter.toBytes()), false);
}
}
}
}
public ArrayList<ID3v2ChapterTOCFrameData> getChapterTOC() {
if (obseleteFormat) {
return null;
}
return extractChapterTOCFrameData(ID_CHAPTER_TOC);
}
public void setChapterTOC(ArrayList<ID3v2ChapterTOCFrameData> toc) {
if(toc != null) {
invalidateDataLength();
boolean first = true;
for(ID3v2ChapterTOCFrameData ct: toc) {
if(first) {
first = false;
addFrame(createFrame(ID_CHAPTER_TOC, ct.toBytes()), true);
} else {
addFrame(createFrame(ID_CHAPTER_TOC, ct.toBytes()), false);
}
}
}
}
public String getEncoder() {
ID3v2TextFrameData frameData = extractTextFrameData(obseleteFormat ? ID_ENCODER_OBSELETE: ID_ENCODER);
if (frameData != null && frameData.getText() != null) return frameData.getText().toString();
return null;
}
public void setEncoder(String encoder) {
if (encoder != null && encoder.length() > 0) {
invalidateDataLength();
ID3v2TextFrameData frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), new EncodedText(encoder));
addFrame(createFrame(ID_ENCODER, frameData.toBytes()), true);
}
}
public byte[] getAlbumImage() {
ID3v2PictureFrameData frameData = createPictureFrameData(obseleteFormat ? ID_IMAGE_OBSELETE : ID_IMAGE);
if (frameData != null) return frameData.getImageData();
return null;
}
public void setAlbumImage(byte[] albumImage, String mimeType) {
if (albumImage != null && albumImage.length > 0 && mimeType != null && mimeType.length() > 0) {
invalidateDataLength();
ID3v2PictureFrameData frameData = new ID3v2PictureFrameData(useFrameUnsynchronisation(), mimeType, (byte)0, null, albumImage);
addFrame(createFrame(ID_IMAGE, frameData.toBytes()), true);
}
}
public void clearAlbumImage() {
clearFrameSet(obseleteFormat ? ID_IMAGE_OBSELETE : ID_IMAGE);
}
public String getAlbumImageMimeType() {
ID3v2PictureFrameData frameData = createPictureFrameData(obseleteFormat ? ID_IMAGE_OBSELETE : ID_IMAGE);
if (frameData != null && frameData.getMimeType() != null) return frameData.getMimeType();
return null;
}
public void clearFrameSet(String id) {
if (frameSets.remove(id) != null) {
invalidateDataLength();
}
}
private ArrayList<ID3v2ChapterFrameData> extractChapterFrameData(String id) {
ID3v2FrameSet frameSet = frameSets.get(id);
if (frameSet != null) {
ArrayList<ID3v2ChapterFrameData> chapterData = new ArrayList<ID3v2ChapterFrameData>();
List<ID3v2Frame> frames = frameSet.getFrames();
for (ID3v2Frame frame : frames) {
ID3v2ChapterFrameData frameData;
try {
frameData = new ID3v2ChapterFrameData(useFrameUnsynchronisation(),
frame.getData());
chapterData.add(frameData);
} catch (InvalidDataException e) {
// do nothing
}
}
return chapterData;
}
return null;
}
private ArrayList<ID3v2ChapterTOCFrameData> extractChapterTOCFrameData(String id) {
ID3v2FrameSet frameSet = frameSets.get(id);
if (frameSet != null) {
ArrayList<ID3v2ChapterTOCFrameData> chapterData = new ArrayList<ID3v2ChapterTOCFrameData>();
List<ID3v2Frame> frames = frameSet.getFrames();
for (ID3v2Frame frame : frames) {
ID3v2ChapterTOCFrameData frameData;
try {
frameData = new ID3v2ChapterTOCFrameData(useFrameUnsynchronisation(),
frame.getData());
chapterData.add(frameData);
} catch (InvalidDataException e) {
// do nothing
}
}
return chapterData;
}
return null;
}
protected ID3v2TextFrameData extractTextFrameData(String id) {
ID3v2FrameSet frameSet = frameSets.get(id);
if (frameSet != null) {
ID3v2Frame frame = (ID3v2Frame) frameSet.getFrames().get(0);
ID3v2TextFrameData frameData;
try {
frameData = new ID3v2TextFrameData(useFrameUnsynchronisation(), frame.getData());
return frameData;
} catch (InvalidDataException e) {
// do nothing
}
}
return null;
}
private ID3v2WWWFrameData extractWWWFrameData(String id) {
ID3v2FrameSet frameSet = frameSets.get(id);
if (frameSet != null) {
ID3v2Frame frame = (ID3v2Frame) frameSet.getFrames().get(0);
ID3v2WWWFrameData frameData;
try {
frameData = new ID3v2WWWFrameData(useFrameUnsynchronisation(), frame.getData());
return frameData;
} catch (InvalidDataException e) {
// do nothing
}
}
return null;
}
private ID3v2UrlFrameData extractUrlFrameData(String id) {
ID3v2FrameSet frameSet = frameSets.get(id);
if (frameSet != null) {
ID3v2Frame frame = (ID3v2Frame) frameSet.getFrames().get(0);
ID3v2UrlFrameData frameData;
try {
frameData = new ID3v2UrlFrameData(useFrameUnsynchronisation(), frame.getData());
return frameData;
} catch (InvalidDataException e) {
// do nothing
}
}
return null;
}
private ID3v2CommentFrameData extractCommentFrameData(String id, boolean itunes) {
ID3v2FrameSet frameSet = frameSets.get(id);
if (frameSet != null) {
Iterator<ID3v2Frame> iterator = frameSet.getFrames().iterator();
while (iterator.hasNext()) {
ID3v2Frame frame = (ID3v2Frame) iterator.next();
ID3v2CommentFrameData frameData;
try {
frameData = new ID3v2CommentFrameData(useFrameUnsynchronisation(), frame.getData());
if (itunes && ITUNES_COMMENT_DESCRIPTION.equals(frameData.getDescription().toString())) {
return frameData;
} else if (! itunes) {
return frameData;
}
} catch (InvalidDataException e) {
// Do nothing
}
}
}
return null;
}
private ID3v2PictureFrameData createPictureFrameData(String id) {
ID3v2FrameSet frameSet = frameSets.get(id);
if (frameSet != null) {
ID3v2Frame frame = (ID3v2Frame) frameSet.getFrames().get(0);
ID3v2PictureFrameData frameData;
try {
if (obseleteFormat) frameData = new ID3v2ObseletePictureFrameData(useFrameUnsynchronisation(), frame.getData());
else frameData = new ID3v2PictureFrameData(useFrameUnsynchronisation(), frame.getData());
return frameData;
} catch (InvalidDataException e) {
// do nothing
}
}
return null;
}
public boolean equals(Object obj) {
if (! (obj instanceof AbstractID3v2Tag)) return false;
if (super.equals(obj)) return true;
AbstractID3v2Tag other = (AbstractID3v2Tag) obj;
if (unsynchronisation != other.unsynchronisation) return false;
if (extendedHeader != other.extendedHeader) return false;
if (experimental != other.experimental) return false;
if (footer != other.footer) return false;
if (compression != other.compression) return false;
if (dataLength != other.dataLength) return false;
if (extendedHeaderLength != other.extendedHeaderLength) return false;
if (version == null) {
if (other.version != null) return false;
} else if (other.version == null) return false;
else if (! version.equals(other.version)) return false;
if (frameSets == null) {
if (other.frameSets != null) return false;
} else if (other.frameSets == null) return false;
else if (! frameSets.equals(other.frameSets)) return false;
return true;
}
}