/* * Entagged Audio Tag library * Copyright (c) 2003-2005 Raphael Slinckx <raphael@slinckx.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jaudiotagger.tag.mp4; import org.jaudiotagger.audio.generic.AbstractTag; import org.jaudiotagger.audio.mp4.atom.Mp4BoxHeader; import org.jaudiotagger.logging.ErrorMessage; import org.jaudiotagger.tag.*; import org.jaudiotagger.tag.images.Artwork; import org.jaudiotagger.tag.images.ArtworkFactory; import org.jaudiotagger.tag.mp4.field.*; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import static org.jaudiotagger.tag.mp4.Mp4FieldKey.*; /** * A Logical representation of Mp4Tag, i.e the meta information stored in an Mp4 file underneath the * moov.udt.meta.ilst atom. */ public class Mp4Tag extends AbstractTag { private static final EnumMap<FieldKey, Mp4FieldKey> tagFieldToMp4Field = new EnumMap<FieldKey, Mp4FieldKey>(FieldKey.class); //Mapping from generic key to mp4 key static { tagFieldToMp4Field.put(FieldKey.ALBUM, Mp4FieldKey.ALBUM); tagFieldToMp4Field.put(FieldKey.ALBUM_ARTIST, Mp4FieldKey.ALBUM_ARTIST); tagFieldToMp4Field.put(FieldKey.ALBUM_ARTIST_SORT, Mp4FieldKey.ALBUM_ARTIST_SORT); tagFieldToMp4Field.put(FieldKey.ALBUM_SORT, Mp4FieldKey.ALBUM_SORT); tagFieldToMp4Field.put(FieldKey.AMAZON_ID, Mp4FieldKey.ASIN); tagFieldToMp4Field.put(FieldKey.ARTIST, Mp4FieldKey.ARTIST); tagFieldToMp4Field.put(FieldKey.ARTIST_SORT, Mp4FieldKey.ARTIST_SORT); tagFieldToMp4Field.put(FieldKey.ARTISTS, Mp4FieldKey.ARTISTS); tagFieldToMp4Field.put(FieldKey.BARCODE, Mp4FieldKey.BARCODE); tagFieldToMp4Field.put(FieldKey.BPM, Mp4FieldKey.BPM); tagFieldToMp4Field.put(FieldKey.CATALOG_NO, Mp4FieldKey.CATALOGNO); tagFieldToMp4Field.put(FieldKey.COMMENT, Mp4FieldKey.COMMENT); tagFieldToMp4Field.put(FieldKey.COMPOSER, Mp4FieldKey.COMPOSER); tagFieldToMp4Field.put(FieldKey.COMPOSER_SORT, Mp4FieldKey.COMPOSER_SORT); tagFieldToMp4Field.put(FieldKey.CONDUCTOR, Mp4FieldKey.CONDUCTOR); tagFieldToMp4Field.put(FieldKey.COVER_ART, Mp4FieldKey.ARTWORK); tagFieldToMp4Field.put(FieldKey.CUSTOM1, Mp4FieldKey.MM_CUSTOM_1); tagFieldToMp4Field.put(FieldKey.CUSTOM2, Mp4FieldKey.MM_CUSTOM_2); tagFieldToMp4Field.put(FieldKey.CUSTOM3, Mp4FieldKey.MM_CUSTOM_3); tagFieldToMp4Field.put(FieldKey.CUSTOM4, Mp4FieldKey.MM_CUSTOM_4); tagFieldToMp4Field.put(FieldKey.CUSTOM5, Mp4FieldKey.MM_CUSTOM_5); tagFieldToMp4Field.put(FieldKey.DISC_NO, Mp4FieldKey.DISCNUMBER); tagFieldToMp4Field.put(FieldKey.DISC_SUBTITLE, Mp4FieldKey.DISC_SUBTITLE); tagFieldToMp4Field.put(FieldKey.DISC_TOTAL, Mp4FieldKey.DISCNUMBER); tagFieldToMp4Field.put(FieldKey.ENCODER, Mp4FieldKey.ENCODER); tagFieldToMp4Field.put(FieldKey.FBPM, Mp4FieldKey.FBPM); tagFieldToMp4Field.put(FieldKey.GENRE, Mp4FieldKey.GENRE); tagFieldToMp4Field.put(FieldKey.GROUPING, Mp4FieldKey.GROUPING); tagFieldToMp4Field.put(FieldKey.ISRC, Mp4FieldKey.ISRC); tagFieldToMp4Field.put(FieldKey.IS_COMPILATION, Mp4FieldKey.COMPILATION); tagFieldToMp4Field.put(FieldKey.KEY, Mp4FieldKey.KEY); tagFieldToMp4Field.put(FieldKey.LANGUAGE, Mp4FieldKey.LANGUAGE); tagFieldToMp4Field.put(FieldKey.LYRICIST, Mp4FieldKey.LYRICIST); tagFieldToMp4Field.put(FieldKey.LYRICS, Mp4FieldKey.LYRICS); tagFieldToMp4Field.put(FieldKey.MEDIA, Mp4FieldKey.MEDIA); tagFieldToMp4Field.put(FieldKey.MOOD, Mp4FieldKey.MOOD); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_ARTISTID, Mp4FieldKey.MUSICBRAINZ_ARTISTID); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_DISC_ID, Mp4FieldKey.MUSICBRAINZ_DISCID); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_ORIGINAL_RELEASE_ID, Mp4FieldKey.MUSICBRAINZ_ORIGINALALBUMID); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_RELEASEARTISTID, Mp4FieldKey.MUSICBRAINZ_ALBUMARTISTID); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_RELEASEID, Mp4FieldKey.MUSICBRAINZ_ALBUMID); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_RELEASE_COUNTRY, Mp4FieldKey.RELEASECOUNTRY); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_RELEASE_GROUP_ID, Mp4FieldKey.MUSICBRAINZ_RELEASE_GROUPID); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_RELEASE_TRACK_ID, Mp4FieldKey.MUSICBRAINZ_RELEASE_TRACKID); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_RELEASE_STATUS, Mp4FieldKey.MUSICBRAINZ_ALBUM_STATUS); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_RELEASE_TYPE, Mp4FieldKey.MUSICBRAINZ_ALBUM_TYPE); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_TRACK_ID, Mp4FieldKey.MUSICBRAINZ_TRACKID); tagFieldToMp4Field.put(FieldKey.MUSICBRAINZ_WORK_ID, Mp4FieldKey.MUSICBRAINZ_WORKID); tagFieldToMp4Field.put(FieldKey.MUSICIP_ID, Mp4FieldKey.MUSICIP_PUID); tagFieldToMp4Field.put(FieldKey.OCCASION, Mp4FieldKey.MM_OCCASION); tagFieldToMp4Field.put(FieldKey.ORIGINAL_ALBUM, Mp4FieldKey.MM_ORIGINAL_ALBUM_TITLE); tagFieldToMp4Field.put(FieldKey.ORIGINAL_ARTIST, Mp4FieldKey.MM_ORIGINAL_ARTIST); tagFieldToMp4Field.put(FieldKey.ORIGINAL_LYRICIST, Mp4FieldKey.MM_ORIGINAL_LYRICIST); tagFieldToMp4Field.put(FieldKey.ORIGINAL_YEAR, Mp4FieldKey.MM_ORIGINAL_YEAR); tagFieldToMp4Field.put(FieldKey.QUALITY, Mp4FieldKey.MM_QUALITY); tagFieldToMp4Field.put(FieldKey.RATING, Mp4FieldKey.SCORE); tagFieldToMp4Field.put(FieldKey.RECORD_LABEL, Mp4FieldKey.LABEL); tagFieldToMp4Field.put(FieldKey.REMIXER, Mp4FieldKey.REMIXER); tagFieldToMp4Field.put(FieldKey.SCRIPT, Mp4FieldKey.SCRIPT); tagFieldToMp4Field.put(FieldKey.SUBTITLE, Mp4FieldKey.SUBTITLE); tagFieldToMp4Field.put(FieldKey.TAGS, Mp4FieldKey.TAGS); tagFieldToMp4Field.put(FieldKey.TEMPO, Mp4FieldKey.TEMPO); tagFieldToMp4Field.put(FieldKey.TITLE, Mp4FieldKey.TITLE); tagFieldToMp4Field.put(FieldKey.TITLE_SORT, Mp4FieldKey.TITLE_SORT); tagFieldToMp4Field.put(FieldKey.TRACK, Mp4FieldKey.TRACK); tagFieldToMp4Field.put(FieldKey.TRACK_TOTAL, Mp4FieldKey.TRACK); tagFieldToMp4Field.put(FieldKey.URL_DISCOGS_ARTIST_SITE, Mp4FieldKey.URL_DISCOGS_ARTIST_SITE); tagFieldToMp4Field.put(FieldKey.URL_DISCOGS_RELEASE_SITE, Mp4FieldKey.URL_DISCOGS_RELEASE_SITE); tagFieldToMp4Field.put(FieldKey.URL_LYRICS_SITE, Mp4FieldKey.URL_LYRICS_SITE); tagFieldToMp4Field.put(FieldKey.URL_OFFICIAL_ARTIST_SITE, Mp4FieldKey.URL_OFFICIAL_ARTIST_SITE); tagFieldToMp4Field.put(FieldKey.URL_OFFICIAL_RELEASE_SITE, Mp4FieldKey.URL_OFFICIAL_RELEASE_SITE); tagFieldToMp4Field.put(FieldKey.URL_WIKIPEDIA_ARTIST_SITE, Mp4FieldKey.URL_WIKIPEDIA_ARTIST_SITE); tagFieldToMp4Field.put(FieldKey.URL_WIKIPEDIA_RELEASE_SITE, Mp4FieldKey.URL_WIKIPEDIA_RELEASE_SITE); tagFieldToMp4Field.put(FieldKey.YEAR, Mp4FieldKey.DAY); tagFieldToMp4Field.put(FieldKey.ENGINEER, Mp4FieldKey.ENGINEER); tagFieldToMp4Field.put(FieldKey.PRODUCER, Mp4FieldKey.PRODUCER); tagFieldToMp4Field.put(FieldKey.DJMIXER, Mp4FieldKey.DJMIXER); tagFieldToMp4Field.put(FieldKey.MIXER, Mp4FieldKey.MIXER); tagFieldToMp4Field.put(FieldKey.ARRANGER, Mp4FieldKey.ARRANGER); tagFieldToMp4Field.put(FieldKey.ACOUSTID_FINGERPRINT, Mp4FieldKey.ACOUSTID_FINGERPRINT); tagFieldToMp4Field.put(FieldKey.ACOUSTID_ID, Mp4FieldKey.ACOUSTID_ID); tagFieldToMp4Field.put(FieldKey.COUNTRY, Mp4FieldKey.COUNTRY); } /** * Create genre field * * <p>If the content can be parsed to one of the known values use the genre field otherwise * use the custom field. * * @param content * @return */ @SuppressWarnings({"JavaDoc"}) private TagField createGenreField(String content) { if (content == null) { throw new IllegalArgumentException(ErrorMessage.GENERAL_INVALID_NULL_ARGUMENT.getMsg()); } //Always write as text if(TagOptionSingleton.getInstance().isWriteMp4GenresAsText()) { return new Mp4TagTextField(GENRE_CUSTOM.getFieldName(), content); } if (Mp4GenreField.isValidGenre(content)) { return new Mp4GenreField(content); } else { return new Mp4TagTextField(GENRE_CUSTOM.getFieldName(), content); } } protected boolean isAllowedEncoding(String enc) { return enc.equals(Mp4BoxHeader.CHARSET_UTF_8); } public String toString() { return "Mpeg4 " + super.toString(); } /** * * @param genericKey * @return */ public boolean hasField(FieldKey genericKey) { return getFields(genericKey).size() != 0; } /** * * @param mp4FieldKey * @return */ public boolean hasField(Mp4FieldKey mp4FieldKey) { return getFields(mp4FieldKey.getFieldName()).size() != 0; } /** * Maps the generic key to the mp4 key and return the list of values for this field * * @param genericKey */ @SuppressWarnings({"JavaDoc"}) public List<TagField> getFields(FieldKey genericKey) throws KeyNotFoundException { if (genericKey == null) { throw new KeyNotFoundException(); } Mp4FieldKey mp4FieldKey = tagFieldToMp4Field.get(genericKey); List<TagField> list = getFields(mp4FieldKey.getFieldName()); List<TagField> filteredList = new ArrayList<TagField>(); if (genericKey==FieldKey.KEY) { if (list.size() == 0) { list = getFields(KEY_OLD.getFieldName()); } return list; } else if(genericKey==FieldKey.GENRE) { if (list.size() == 0) { list = getFields(GENRE_CUSTOM.getFieldName()); } return list; } else if(genericKey==FieldKey.TRACK) { for(TagField next:list) { Mp4TrackField trackField = (Mp4TrackField)next; if(trackField.getTrackNo()>0) { filteredList.add(next); } } return filteredList; } else if(genericKey==FieldKey.TRACK_TOTAL) { for(TagField next:list) { Mp4TrackField trackField = (Mp4TrackField)next; if(trackField.getTrackTotal()>0) { filteredList.add(next); } } return filteredList; } else if(genericKey==FieldKey.DISC_NO) { for(TagField next:list) { Mp4DiscNoField discNoField = (Mp4DiscNoField)next; if(discNoField.getDiscNo()>0) { filteredList.add(next); } } return filteredList; } else if(genericKey==FieldKey.DISC_TOTAL) { for(TagField next:list) { Mp4DiscNoField discNoField = (Mp4DiscNoField)next; if(discNoField.getDiscTotal()>0) { filteredList.add(next); } } return filteredList; } else { return list; } } /** * Maps the generic key to the specific key and return the list of values for this field as strings * * @param genericKey * @return * @throws KeyNotFoundException */ public List<String> getAll(FieldKey genericKey) throws KeyNotFoundException { List<String> values = new ArrayList<String>(); List<TagField> fields = getFields(genericKey); for(TagField tagfield:fields) { if(genericKey==FieldKey.TRACK) { values.add(((Mp4TrackField)tagfield).getTrackNo().toString()); } else if(genericKey==FieldKey.TRACK_TOTAL) { values.add(((Mp4TrackField)tagfield).getTrackTotal().toString()); } else if(genericKey==FieldKey.DISC_NO) { values.add(((Mp4DiscNoField)tagfield).getDiscNo().toString()); } else if(genericKey==FieldKey.DISC_TOTAL) { values.add(((Mp4DiscNoField)tagfield).getDiscTotal().toString()); } else { values.add(tagfield.toString()); } } return values; } /** * Retrieve the values that exists for this mp4keyId (this is the internalid actually used) * * * @param mp4FieldKey * @throws org.jaudiotagger.tag.KeyNotFoundException * @return */ public List<TagField> get(Mp4FieldKey mp4FieldKey) throws KeyNotFoundException { if (mp4FieldKey == null) { throw new KeyNotFoundException(); } return super.getFields(mp4FieldKey.getFieldName()); } /** * Retrieve the indexed value that exists for this generic key * * @param genericKey * @return */ public String getValue(FieldKey genericKey, int index) throws KeyNotFoundException { List<TagField> fields = getFields(genericKey); if(fields.size()>index) { TagField field = fields.get(index); if(genericKey==FieldKey.TRACK) { return ((Mp4TrackField)field).getTrackNo().toString(); } else if(genericKey==FieldKey.DISC_NO) { return ((Mp4DiscNoField)field).getDiscNo().toString(); } else if(genericKey==FieldKey.TRACK_TOTAL) { return ((Mp4TrackField)field).getTrackTotal().toString(); } else if(genericKey==FieldKey.DISC_TOTAL) { return ((Mp4DiscNoField)field).getDiscTotal().toString(); } else { return field.toString(); } } return ""; } /** * Retrieve the first value that exists for this mp4key * * @param mp4Key * @return * @throws org.jaudiotagger.tag.KeyNotFoundException */ public String getFirst(Mp4FieldKey mp4Key) throws KeyNotFoundException { if (mp4Key == null) { throw new KeyNotFoundException(); } return super.getFirst(mp4Key.getFieldName()); } public Mp4TagField getFirstField(FieldKey genericKey) throws KeyNotFoundException { List<TagField> fields = getFields(genericKey); if(fields.size() == 0) { return null; } return (Mp4TagField)fields.get(0); } public Mp4TagField getFirstField(Mp4FieldKey mp4Key) throws KeyNotFoundException { if (mp4Key == null) { throw new KeyNotFoundException(); } return (Mp4TagField) super.getFirstField(mp4Key.getFieldName()); } /** * Delete fields with this generic key * * @param genericKey */ public void deleteField(FieldKey genericKey) throws KeyNotFoundException { if (genericKey == null) { throw new KeyNotFoundException(); } String mp4FieldName = tagFieldToMp4Field.get(genericKey).getFieldName(); if (genericKey == FieldKey.KEY) { deleteField(Mp4FieldKey.KEY_OLD); deleteField(mp4FieldName); } else if (genericKey == FieldKey.TRACK) { String trackTotal = this.getFirst(FieldKey.TRACK_TOTAL); if(trackTotal.length()==0) { super.deleteField(mp4FieldName); return; } else { Mp4TrackField field = (Mp4TrackField)this.getFirstField(FieldKey.TRACK_TOTAL); field.setTrackNo(0); return; } } else if (genericKey == FieldKey.TRACK_TOTAL) { String track = this.getFirst(FieldKey.TRACK); if(track.length()==0) { super.deleteField(mp4FieldName); return; } else { Mp4TrackField field = (Mp4TrackField)this.getFirstField(FieldKey.TRACK); field.setTrackTotal(0); return; } } else if (genericKey == FieldKey.DISC_NO) { String discTotal = this.getFirst(FieldKey.DISC_TOTAL); if(discTotal.length()==0) { super.deleteField(mp4FieldName); return; } else { Mp4DiscNoField field = (Mp4DiscNoField)this.getFirstField(FieldKey.DISC_TOTAL); field.setDiscNo(0); return; } } else if (genericKey == FieldKey.DISC_TOTAL) { String discno = this.getFirst(FieldKey.DISC_NO); if(discno.length()==0) { super.deleteField(mp4FieldName); return; } else { Mp4DiscNoField field = (Mp4DiscNoField)this.getFirstField(FieldKey.DISC_NO); field.setDiscTotal(0); return; } } else if(genericKey == FieldKey.GENRE) { super.deleteField(Mp4FieldKey.GENRE.getFieldName()); super.deleteField(Mp4FieldKey.GENRE_CUSTOM.getFieldName()); } else { super.deleteField(mp4FieldName); } } /** * Delete fields with this mp4key * * @param mp4Key * @throws org.jaudiotagger.tag.KeyNotFoundException */ public void deleteField(Mp4FieldKey mp4Key) throws KeyNotFoundException { if (mp4Key == null) { throw new KeyNotFoundException(); } super.deleteField(mp4Key.getFieldName()); } /** * Create artwork field * * @param data raw image data * @return * @throws org.jaudiotagger.tag.FieldDataInvalidException */ public TagField createArtworkField(byte[] data) { return new Mp4TagCoverField(data); } /** * Create artwork field * * @return */ public TagField createField(Artwork artwork) throws FieldDataInvalidException { return new Mp4TagCoverField(artwork.getBinaryData()); } /** * Create new field and add it to the tag, with special handling for trackNo, discNo fields as we dont want multiple * fields to be created for these fields * * @param genericKey * @param value * @throws KeyNotFoundException * @throws FieldDataInvalidException */ @Override public void addField(FieldKey genericKey, String value) throws KeyNotFoundException, FieldDataInvalidException { if( (genericKey==FieldKey.TRACK)|| (genericKey==FieldKey.TRACK_TOTAL)|| (genericKey==FieldKey.DISC_NO)|| (genericKey==FieldKey.DISC_TOTAL) ) { setField(genericKey, value); } else { TagField tagfield = createField(genericKey,value); addField(tagfield); } } /** * Create Tag Field using generic key * * This should use the correct subclass for the key * * @param genericKey * @param value * @return * @throws KeyNotFoundException * @throws FieldDataInvalidException */ @Override public TagField createField(FieldKey genericKey, String value) throws KeyNotFoundException, FieldDataInvalidException { if (value == null) { throw new IllegalArgumentException(ErrorMessage.GENERAL_INVALID_NULL_ARGUMENT.getMsg()); } if (genericKey == null) { throw new KeyNotFoundException(); } //Special handling for these number fields because multiple generic keys map to a single mp4 field if( (genericKey == FieldKey.TRACK)|| (genericKey == FieldKey.TRACK_TOTAL)|| (genericKey == FieldKey.DISC_NO)|| (genericKey == FieldKey.DISC_TOTAL) ) { try { int number = Integer.parseInt(value); if(genericKey== FieldKey.TRACK) { return new Mp4TrackField(number); } else if(genericKey== FieldKey.TRACK_TOTAL) { return new Mp4TrackField(0,number); } else if(genericKey== FieldKey.DISC_NO) { return new Mp4DiscNoField(number); } else if(genericKey== FieldKey.DISC_TOTAL) { return new Mp4DiscNoField(0,number); } } catch(NumberFormatException nfe) { //If not number we want to convert to an expected exception (which is not a RuntimeException) //so can be handled properly by calling program throw new FieldDataInvalidException("Value "+value + " is not a number as required",nfe); } } else if (genericKey == FieldKey.GENRE) { //Always write as text if(TagOptionSingleton.getInstance().isWriteMp4GenresAsText()) { return new Mp4TagTextField(GENRE_CUSTOM.getFieldName(), value); } if (Mp4GenreField.isValidGenre(value)) { return new Mp4GenreField(value); } else { return new Mp4TagTextField(GENRE_CUSTOM.getFieldName(), value); } } //Default for all other fields return createField(tagFieldToMp4Field.get(genericKey), value); } /** * Overidden to ensure cannot have both a genre field and a custom genre field * * @param genericKey * @param value * @throws KeyNotFoundException * @throws FieldDataInvalidException */ @Override public void setField(FieldKey genericKey, String value) throws KeyNotFoundException, FieldDataInvalidException { TagField tagfield = createField(genericKey,value); if(genericKey==FieldKey.GENRE) { if(tagfield.getId().equals(GENRE.getFieldName())) { this.deleteField(Mp4FieldKey.GENRE_CUSTOM); } else if(tagfield.getId().equals(GENRE_CUSTOM.getFieldName())) { this.deleteField(Mp4FieldKey.GENRE); } } setField(tagfield); } /** * Set mp4 field * @param fieldKey * @param value * @throws KeyNotFoundException * @throws FieldDataInvalidException */ public void setField(Mp4FieldKey fieldKey, String value) throws KeyNotFoundException, FieldDataInvalidException { TagField tagfield = createField(fieldKey,value); setField(tagfield); } /** * Set field, special handling for track and disc because they hold two fields * * @param field */ @Override public void setField(TagField field) { if (field == null) { return; } if(field.getId().equals(TRACK.getFieldName())) { List<TagField> list = fields.get(field.getId()); if(list==null||list.size()==0) { super.setField(field); } else { Mp4TrackField existingTrackField = (Mp4TrackField)list.get(0); Mp4TrackField newTrackField = (Mp4TrackField)field; Short trackNo = existingTrackField.getTrackNo(); Short trackTotal = existingTrackField.getTrackTotal(); if(newTrackField.getTrackNo()>0 ) { trackNo = newTrackField.getTrackNo(); } if(newTrackField.getTrackTotal()>0 ) { trackTotal = newTrackField.getTrackTotal(); } Mp4TrackField mergedTrackField = new Mp4TrackField(trackNo,trackTotal); super.setField(mergedTrackField); } } else if(field.getId().equals(DISCNUMBER.getFieldName())) { List<TagField> list = fields.get(field.getId()); if(list==null||list.size()==0) { super.setField(field); } else { Mp4DiscNoField existingDiscNoField = (Mp4DiscNoField)list.get(0); Mp4DiscNoField newDiscNoField = (Mp4DiscNoField)field; Short discNo = existingDiscNoField.getDiscNo(); Short discTotal = existingDiscNoField.getDiscTotal(); if(newDiscNoField.getDiscNo()>0 ) { discNo = newDiscNoField.getDiscNo(); } if(newDiscNoField.getDiscTotal()>0 ) { discTotal = newDiscNoField.getDiscTotal(); } Mp4DiscNoField mergedDiscNoField = new Mp4DiscNoField(discNo,discTotal); super.setField(mergedDiscNoField); } } else { super.setField(field); } } /** * Create Tag Field using mp4 key * * Uses the correct subclass for the key * * @param mp4FieldKey * @param value * @return * @throws KeyNotFoundException * @throws FieldDataInvalidException */ public TagField createField(Mp4FieldKey mp4FieldKey, String value) throws KeyNotFoundException, FieldDataInvalidException { if (value == null) { throw new IllegalArgumentException(ErrorMessage.GENERAL_INVALID_NULL_ARGUMENT.getMsg()); } if (mp4FieldKey == null) { throw new KeyNotFoundException(); } //This is boolean stored as 1, but calling program might setField as 'true' so we handle this //case internally , any other values it is set to we treat as false if(mp4FieldKey==Mp4FieldKey.COMPILATION) { if(value.equalsIgnoreCase("true") || value.equals("1")) { return createCompilationField(true); } else { return createCompilationField(false); } } else if(mp4FieldKey==Mp4FieldKey.GENRE) { if (Mp4GenreField.isValidGenre(value)) { return new Mp4GenreField(value); } else { throw new IllegalArgumentException(ErrorMessage.NOT_STANDARD_MP$_GENRE.getMsg()); } } else if(mp4FieldKey==Mp4FieldKey.GENRE_CUSTOM) { return new Mp4TagTextField(GENRE_CUSTOM.getFieldName(), value); } else if(mp4FieldKey.getSubClassFieldType()==Mp4TagFieldSubType.DISC_NO) { return new Mp4DiscNoField(value); } else if(mp4FieldKey.getSubClassFieldType()==Mp4TagFieldSubType.TRACK_NO) { return new Mp4TrackField(value); } else if(mp4FieldKey.getSubClassFieldType()==Mp4TagFieldSubType.BYTE) { return new Mp4TagByteField(mp4FieldKey, value, mp4FieldKey.getFieldLength()); } else if(mp4FieldKey.getSubClassFieldType()==Mp4TagFieldSubType.NUMBER) { return new Mp4TagTextNumberField(mp4FieldKey.getFieldName(), value); } else if(mp4FieldKey.getSubClassFieldType()==Mp4TagFieldSubType.REVERSE_DNS) { return new Mp4TagReverseDnsField(mp4FieldKey, value); } else if(mp4FieldKey.getSubClassFieldType()==Mp4TagFieldSubType.ARTWORK) { throw new UnsupportedOperationException(ErrorMessage.ARTWORK_CANNOT_BE_CREATED_WITH_THIS_METHOD.getMsg()); } else if(mp4FieldKey.getSubClassFieldType()==Mp4TagFieldSubType.TEXT) { return new Mp4TagTextField(mp4FieldKey.getFieldName(), value); } else if(mp4FieldKey.getSubClassFieldType()==Mp4TagFieldSubType.UNKNOWN) { throw new UnsupportedOperationException(ErrorMessage.DO_NOT_KNOW_HOW_TO_CREATE_THIS_ATOM_TYPE.getMsg(mp4FieldKey.getFieldName())); } else { throw new UnsupportedOperationException(ErrorMessage.DO_NOT_KNOW_HOW_TO_CREATE_THIS_ATOM_TYPE.getMsg(mp4FieldKey.getFieldName())); } } public List<Artwork> getArtworkList() { List<TagField> coverartList = get(Mp4FieldKey.ARTWORK); List<Artwork> artworkList = new ArrayList<Artwork>(coverartList.size()); for(TagField next:coverartList) { Mp4TagCoverField mp4CoverArt = (Mp4TagCoverField)next; Artwork artwork = ArtworkFactory.getNew(); artwork.setBinaryData(mp4CoverArt.getData()); artwork.setMimeType(Mp4TagCoverField.getMimeTypeForImageType(mp4CoverArt.getFieldType())); artworkList.add(artwork); } return artworkList; } public TagField createCompilationField(boolean origValue) throws KeyNotFoundException, FieldDataInvalidException { String value = ""; if(origValue) { value=Mp4TagByteField.TRUE_VALUE; return new Mp4TagByteField(Mp4FieldKey.COMPILATION, value, Mp4FieldKey.COMPILATION.getFieldLength()); } else { value=Mp4TagByteField.FALSE_VALUE; return new Mp4TagByteField(Mp4FieldKey.COMPILATION, value, Mp4FieldKey.COMPILATION.getFieldLength()); } } }