1 /* 2 * aTunes 3 * Copyright (C) Alex Aranda, Sylvain Gaudard and contributors 4 * 5 * See http://www.atunes.org/wiki/index.php?title=Contributing for information about contributors 6 * 7 * http://www.atunes.org 8 * http://sourceforge.net/projects/atunes 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License 12 * as published by the Free Software Foundation; either version 2 13 * of the License, or (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 */ 20 21 package net.sourceforge.atunes.kernel.modules.tags; 22 23 import net.sourceforge.atunes.model.ILocalAudioObject; 24 import net.sourceforge.atunes.model.ITag; 25 import net.sourceforge.atunes.utils.DateUtils; 26 import net.sourceforge.atunes.utils.Logger; 27 import net.sourceforge.atunes.utils.StringUtils; 28 29 import org.jaudiotagger.audio.AudioFile; 30 import org.jaudiotagger.tag.FieldKey; 31 import org.jaudiotagger.tag.Tag; 32 import org.joda.time.DateMidnight; 33 import org.joda.time.DateTime; 34 35 /** 36 * Creates tags by reading with JAudiotagger 37 * 38 * @author alex 39 * 40 */ 41 public class JAudiotaggerTagCreator { 42 43 private Genres genresHelper; 44 45 private RatingsToStars ratingsToStars; 46 47 private TagFactory tagFactory; 48 49 /** 50 * @param tagFactory 51 */ setTagFactory(final TagFactory tagFactory)52 public void setTagFactory(final TagFactory tagFactory) { 53 this.tagFactory = tagFactory; 54 } 55 56 /** 57 * @param ratingsToStars 58 */ setRatingsToStars(final RatingsToStars ratingsToStars)59 public void setRatingsToStars(final RatingsToStars ratingsToStars) { 60 this.ratingsToStars = ratingsToStars; 61 } 62 63 /** 64 * @param genresHelper 65 */ setGenresHelper(final Genres genresHelper)66 public void setGenresHelper(final Genres genresHelper) { 67 this.genresHelper = genresHelper; 68 } 69 70 /** 71 * Creates tag for given audio file 72 * 73 * @param ao 74 * @param file 75 * @param readRating 76 * @return 77 */ createTag(final ILocalAudioObject ao, final AudioFile file, boolean readRating)78 ITag createTag(final ILocalAudioObject ao, final AudioFile file, 79 boolean readRating) { 80 ITag iTag = this.tagFactory.getNewTag(); 81 if (file != null) { 82 Tag tag = file.getTag(); 83 iTag.setAlbum(getFirstTagValue(tag, FieldKey.ALBUM)); 84 iTag.setArtist(getFirstTagValue(tag, FieldKey.ARTIST)); 85 iTag.setComment(getFirstTagValue(tag, FieldKey.COMMENT)); 86 setGenreFromTag(iTag, getFirstTagValue(tag, FieldKey.GENRE), 87 this.genresHelper); 88 iTag.setTitle(getFirstTagValue(tag, FieldKey.TITLE)); 89 setTrackNumberFromTag(iTag, getFirstTagValue(tag, FieldKey.TRACK)); 90 setYearFromTag(iTag, getFirstTagValue(tag, FieldKey.YEAR)); 91 iTag.setLyrics(getFirstTagValue(tag, FieldKey.LYRICS)); 92 iTag.setComposer(getFirstTagValue(tag, FieldKey.COMPOSER)); 93 iTag.setAlbumArtist(getFirstTagValue(tag, FieldKey.ALBUM_ARTIST)); 94 setInternalImage(iTag, tag); 95 setDate(iTag, tag); 96 setDiscNumber(iTag, tag); 97 if (readRating) { 98 iTag.setStars(this.ratingsToStars 99 .ratingToStars(getFirstTagValue(tag, FieldKey.RATING))); 100 } 101 } 102 return iTag; 103 } 104 105 /** 106 * Sets tag in given audio object 107 * 108 * @param ao 109 * @param file 110 * @return 111 */ setRatingInTag(ILocalAudioObject ao, AudioFile file)112 public void setRatingInTag(ILocalAudioObject ao, AudioFile file) { 113 if (ao != null && ao.getTag() != null && file != null) { 114 Tag tag = file.getTag(); 115 ao.getTag().setStars( 116 this.ratingsToStars.ratingToStars(getFirstTagValue(tag, 117 FieldKey.RATING))); 118 } 119 } 120 121 /** 122 * Sets date from tag 123 * 124 * @param iTag 125 * @param tag 126 */ setDate(final ITag iTag, final org.jaudiotagger.tag.Tag tag)127 private void setDate(final ITag iTag, final org.jaudiotagger.tag.Tag tag) { 128 if (tag instanceof org.jaudiotagger.tag.vorbiscomment.VorbisCommentTag 129 || tag instanceof org.jaudiotagger.tag.flac.FlacTag) { 130 iTag.setDate(DateUtils.parseRFC3339Date(getFirstTagValue(tag, 131 "DATE"))); 132 } else if (tag instanceof org.jaudiotagger.tag.id3.ID3v24Tag) { 133 iTag.setDate(DateUtils.parseRFC3339Date(getFirstTagValue(tag, 134 "TDRC"))); 135 } else if (tag instanceof org.jaudiotagger.tag.id3.ID3v23Tag) { 136 setDateFromID3v23Tag(iTag, tag); 137 } else { 138 iTag.setDate((DateTime) null); 139 } 140 } 141 142 /** 143 * Sets internal image 144 * 145 * @param iTag 146 * @param tag 147 */ setInternalImage(final ITag iTag, final org.jaudiotagger.tag.Tag tag)148 private void setInternalImage(final ITag iTag, 149 final org.jaudiotagger.tag.Tag tag) { 150 boolean hasImage = false; 151 if (tag != null) { 152 try { 153 hasImage = tag.hasField(FieldKey.COVER_ART.name()); 154 } catch (UnsupportedOperationException e) { 155 Logger.info(e.getMessage()); 156 // Sometimes image is not supported (ID3v1). It's not a problem 157 } 158 } 159 iTag.setInternalImage(hasImage); 160 } 161 162 /** 163 * Sets year from tag 164 * 165 * @param tag 166 */ setYearFromTag(final ITag iTag, final String year)167 private void setYearFromTag(final ITag iTag, final String year) { 168 if (StringUtils.isEmpty(year)) { 169 iTag.setYear(-1); 170 } else { 171 try { 172 iTag.setYear(Integer.parseInt(year)); 173 } catch (NumberFormatException e) { 174 iTag.setYear(-1); 175 } 176 } 177 } 178 179 /** 180 * Sets track number from tag 181 * 182 * @param iTag 183 * @param track 184 */ setTrackNumberFromTag(final ITag iTag, final String track)185 private void setTrackNumberFromTag(final ITag iTag, final String track) { 186 String result = track; 187 try { 188 if (StringUtils.isEmpty(result)) { 189 result = "-1"; 190 } 191 // Certain tags are in the form of track number/total number of 192 // tracks so check for this: 193 if (result.contains("/")) { 194 int separatorPosition; 195 separatorPosition = result.indexOf('/'); 196 iTag.setTrackNumber(Integer.parseInt(result.substring(0, 197 separatorPosition))); 198 } else { 199 iTag.setTrackNumber(Integer.parseInt(result)); 200 } 201 } catch (NumberFormatException e) { 202 iTag.setTrackNumber(-1); 203 } 204 } 205 206 /** 207 * Code taken from Jajuk http://jajuk.info - (Copyright (C) 2008) The Jajuk 208 * team Detects if Genre is a number and try to map the corresponding genre 209 * This should only happen with ID3 tags Sometimes, the style has this form 210 * : (nb) 211 * 212 * @param iTag 213 * @param genre 214 * @param genres 215 */ setGenreFromTag(final ITag iTag, final String genre, final Genres genres)216 private void setGenreFromTag(final ITag iTag, final String genre, 217 final Genres genres) { 218 String result = genre; 219 if (!StringUtils.isEmpty(result) && result.matches("\\(.*\\).*")) { 220 result = result.substring(1, result.indexOf(')')); 221 try { 222 result = genres.getGenreForCode(Integer.parseInt(result)); 223 } catch (Exception e) { 224 iTag.setGenre(""); // error, return unknown 225 } 226 } 227 // If genre is a number mapping a known style, use this style 228 try { 229 int number = Integer.parseInt(result); 230 if (number >= 0 231 && !StringUtils.isEmpty(genres.getGenreForCode(number))) { 232 result = genres.getGenreForCode(Integer.parseInt(result)); 233 } 234 } catch (NumberFormatException e) { 235 // nothing wrong here 236 } 237 iTag.setGenre(result); 238 } 239 240 /** 241 * @param iTag 242 * @param tag 243 */ setDateFromID3v23Tag(final ITag iTag, final org.jaudiotagger.tag.Tag tag)244 private void setDateFromID3v23Tag(final ITag iTag, 245 final org.jaudiotagger.tag.Tag tag) { 246 // Set date from fields tag TYER and date/month tag TDAT 247 DateMidnight c = null; 248 String yearPart = getFirstTagValue(tag, "TYER"); 249 if (!StringUtils.isEmpty(yearPart)) { 250 try { 251 c = new DateMidnight().withYear(Integer.parseInt(yearPart)) 252 .withMonthOfYear(1).withDayOfMonth(1); 253 String dateMonthPart = getFirstTagValue(tag, "TDAT"); 254 if (!StringUtils.isEmpty(dateMonthPart) 255 && dateMonthPart.length() >= 4) { 256 c = c.withMonthOfYear( 257 Integer.parseInt(dateMonthPart.substring(2, 4))) 258 .withDayOfMonth( 259 Integer.parseInt(dateMonthPart.substring(0, 260 2))); 261 } 262 } catch (NumberFormatException e) { 263 // Skip this date 264 } catch (IllegalArgumentException e) { 265 // Skip this date 266 } 267 } 268 269 if (c != null) { 270 iTag.setDate(c); 271 } 272 } 273 274 /** 275 * Sets disc number 276 * 277 * @param iTag 278 * @param tag 279 */ setDiscNumber(final ITag iTag, final org.jaudiotagger.tag.Tag tag)280 private void setDiscNumber(final ITag iTag, 281 final org.jaudiotagger.tag.Tag tag) { 282 // Disc Number 283 String discNumberStr = getFirstTagValue(tag, FieldKey.DISC_NO); 284 if (discNumberStr != null && !discNumberStr.trim().equals("")) { 285 // try to get disc number parsing string 286 try { 287 iTag.setDiscNumber(Integer.parseInt(discNumberStr)); 288 } catch (NumberFormatException e) { 289 // Sometimes disc number appears as relative to overall disc 290 // count: "1/2" 291 if (discNumberStr.contains("/")) { 292 int separatorPosition = discNumberStr.indexOf('/'); 293 try { 294 iTag.setDiscNumber(Integer.parseInt(discNumberStr 295 .substring(0, separatorPosition))); 296 } catch (NumberFormatException e2) { 297 // Disc number seems not valid 298 } 299 } 300 } 301 } 302 } 303 getFirstTagValue(final Tag tag, final String field)304 private String getFirstTagValue(final Tag tag, final String field) { 305 if (tag != null && field != null) { 306 try { 307 return tag.getFirst(field); 308 } catch (UnsupportedOperationException e) { 309 Logger.info(e.getMessage()); 310 } catch (Exception e) { 311 // Avoid any error caused by underlying tag reader 312 Logger.error(e.getMessage()); 313 } 314 } 315 return null; 316 } 317 getFirstTagValue(final Tag tag, final FieldKey field)318 private String getFirstTagValue(final Tag tag, final FieldKey field) { 319 if (tag != null && field != null) { 320 try { 321 return tag.getFirst(field); 322 } catch (UnsupportedOperationException e) { 323 Logger.info(e.getMessage()); 324 } catch (Exception e) { 325 // Avoid any error caused by underlying tag reader 326 Logger.error(e.getMessage()); 327 } 328 } 329 return null; 330 } 331 } 332