1 package com.melloware.jukes.file.tag;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.Calendar;
6 import java.util.GregorianCalendar;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.Locale;
10 import java.util.Map;
11
12 import javax.sound.sampled.AudioFileFormat;
13 import javax.sound.sampled.AudioSystem;
14 import javax.sound.sampled.UnsupportedAudioFileException;
15
16 import org.apache.commons.io.FilenameUtils;
17 import org.apache.commons.lang.StringUtils;
18 import org.apache.commons.lang.builder.CompareToBuilder;
19 import org.apache.commons.lang.builder.EqualsBuilder;
20 import org.apache.commons.lang.builder.HashCodeBuilder;
21 import org.apache.commons.lang.builder.ToStringBuilder;
22 import org.apache.commons.lang.builder.ToStringStyle;
23 import org.apache.commons.lang.math.NumberUtils;
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.jaudiotagger.tag.id3.valuepair.GenreTypes;
27 import org.tritonus.share.sampled.file.TAudioFileFormat;
28
29 import com.melloware.jukes.exception.MusicTagException;
30 import com.melloware.jukes.file.FileUtil;
31 import com.melloware.jukes.util.TimeSpan;
32
33
34
35
36
37
38
39
40 @SuppressWarnings("PMD")
41 abstract public class MusicTag implements ITag, Comparable {
42
43 private static final Log LOG = LogFactory.getLog(MusicTag.class);
44 public static final String NO_TAG = "[notag]";
45 public static final String CURRENT_YEAR = currentYear();
46 public static final String DEFAULT_FILE_FORMAT = "%n - %t";
47
48 protected File file;
49 protected Long bitRate = null;
50 protected long trackLength = 1;
51 protected Map header;
52 protected String artist = null;
53 protected String comment = null;
54 protected String disc = null;
55 protected String encodedBy = null;
56 protected String genre = null;
57 protected String title = null;
58 protected String track = null;
59 protected String year = null;
60
61
62
63
64
65
66
67 public MusicTag(File aFile) throws MusicTagException {
68 super();
69 if (aFile == null) {
70 throw new MusicTagException("File is not valid.");
71 }
72 this.file = aFile;
73
74 try {
75
76 if (!aFile.canWrite()) {
77 aFile.setReadable(true, false);
78 }
79
80
81 final AudioFileFormat format = AudioSystem.getAudioFileFormat(aFile);
82 if (format instanceof TAudioFileFormat) {
83
84 final Map props = ((TAudioFileFormat) format).properties();
85
86 this.header = FileUtil.deepCopy(props);
87 } else {
88 this.header = null;
89 }
90 } catch (UnsupportedAudioFileException ex) {
91 LOG.error(ex.getMessage(), ex);
92 throw new MusicTagException("UnsupportedAudioFileException opening Music File Tag. "
93 + aFile.getAbsolutePath() + "\n\n" + ex.getMessage());
94 } catch (IOException ex) {
95 LOG.error(ex.getMessage(), ex);
96 throw new MusicTagException("IOException opening Music File Tag. " + aFile.getAbsolutePath() + "\n\n"
97 + ex.getMessage());
98 } catch (Exception ex) {
99 LOG.error(ex.getMessage(), ex);
100 throw new MusicTagException("Error opening Music File Tag. " + aFile.getAbsolutePath() + "\n\n"
101 + ex.getMessage());
102 }
103 }
104
105
106
107
108
109
110 abstract public String getArtist();
111
112
113
114
115
116
117 abstract public Long getBitRate();
118
119
120
121
122
123
124 abstract public String getComment();
125
126
127
128
129
130 abstract public String getCopyrighted();
131
132
133
134
135
136
137 abstract public String getDisc();
138
139
140
141
142
143 abstract public String getEmphasis();
144
145
146
147
148
149
150 abstract public String getEncodedBy();
151
152
153
154
155
156 abstract public String getFrequency();
157
158
159
160
161
162
163 abstract public String getGenre();
164
165
166
167
168
169
170 abstract public Map getHeader();
171
172
173
174
175
176 abstract public String getLayer();
177
178
179
180
181
182 abstract public String getMode();
183
184
185
186
187
188
189 abstract public String getTitle();
190
191
192
193
194
195
196 abstract public String getTrack();
197
198
199
200
201
202
203 abstract public long getTrackLength();
204
205
206
207
208
209 abstract public String getVersion();
210
211
212
213
214
215
216 abstract public String getYear();
217
218
219
220
221
222
223 abstract public void setArtist(String aArtist);
224
225
226
227
228
229
230 abstract public void setComment(String aComment);
231
232
233
234
235
236
237 abstract public void setDisc(String aDisc);
238
239
240
241
242
243
244 abstract public void setEncodedBy(String aEncodedBy);
245
246
247
248
249
250
251 abstract public void setGenre(String aGenre);
252
253
254
255
256
257
258 abstract public void setTitle(String aTitle);
259
260
261
262
263
264
265 abstract public void setTrack(String aTrack);
266
267
268
269
270
271
272
273 abstract public void setTrack(String track, int padding);
274
275
276
277
278
279
280 abstract public void setTrackLength(long aTrackLength);
281
282
283
284
285
286
287 abstract public void setYear(String aYear);
288
289
290
291
292
293 abstract public boolean isVBR();
294
295
296
297
298
299
300 abstract public void removeTags() throws MusicTagException;
301
302
303
304
305
306
307
308
309
310
311
312
313 abstract public boolean renameFile(String aFormat);
314
315
316
317
318
319
320 abstract public void save() throws MusicTagException;
321
322
323
324
325
326
327 public static List getGenreTypes() {
328 return GenreTypes.getInstanceOf().getAlphabeticalValueList();
329 }
330
331
332
333
334
335
336 public String getAbsolutePath() {
337 return this.getFile().getAbsolutePath();
338 }
339
340
341
342
343
344 public String getBitRateAsString() {
345 String rate = "0 ";
346 if (isVBR()) {
347 rate = "VBR ~" + getBitRate().toString();
348 } else {
349 rate = getBitRate().toString();
350 }
351
352 return rate + " kbps";
353 }
354
355
356
357
358
359
360 public File getFile() {
361 return this.file;
362 }
363
364
365
366
367
368
369 public String getHeaderInfo() {
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388 final StringBuffer buffer = new StringBuffer();
389 if (header != null) {
390 final Iterator it = header.keySet().iterator();
391 while (it.hasNext()) {
392 final Object key = it.next();
393 final Object value = header.get(key);
394 buffer.append(key);
395 buffer.append(" = ");
396 buffer.append(value);
397 }
398 }
399 return buffer.toString();
400 }
401
402
403
404
405
406
407 public String getHeaderValue(final String aKey) {
408 return ((String) header.get(aKey));
409 }
410
411
412
413
414
415
416 public String getTrackLengthAsString() {
417 return new TimeSpan(getTrackLength() * 1000).getMusicDuration();
418 }
419
420
421
422
423 public boolean equals(Object obj) {
424 if (!(obj instanceof MusicTag)) {
425 return false;
426 }
427 if (this == obj) {
428 return true;
429 }
430 final MusicTag rhs = (MusicTag) obj;
431 final EqualsBuilder builder = new EqualsBuilder();
432 builder.append(artist, rhs.artist);
433 builder.append(disc, rhs.disc);
434 builder.append(title, rhs.title);
435 builder.append(year, rhs.year);
436 builder.append(genre, rhs.genre);
437
438 return builder.isEquals();
439 }
440
441
442
443
444
445
446
447
448
449
450 public String extractTitleFromFilename() {
451 String title = "";
452
453
454 final String filename = FilenameUtils.getBaseName(file.getAbsolutePath());
455
456
457 final String[] tokens = StringUtils.split(filename);
458
459
460 for (int i = tokens.length - 1; i >= 0; i--) {
461 final String token = tokens[i];
462
463
464 if (StringUtils.equalsIgnoreCase(token, "-")) {
465 continue;
466 }
467
468
469 if ((StringUtils.isNumericSpace(token)) && (NumberUtils.toInt(token) == NumberUtils.toInt(this.getTrack()))) {
470 break;
471 }
472
473
474 title = token + " " + title;
475 }
476
477
478 if (StringUtils.isBlank(title)) {
479 title = filename;
480 }
481
482 return title;
483 }
484
485
486
487
488
489
490 public boolean fileExists() {
491 return this.file.exists();
492 }
493
494
495
496
497 public int hashCode() {
498 return HashCodeBuilder.reflectionHashCode(this);
499 }
500
501
502
503
504
505
506
507 public boolean renameFile() {
508 return renameFile(DEFAULT_FILE_FORMAT);
509 }
510
511
512
513
514 public void synchronize() {
515 this.setDisc(this.getDisc());
516 this.setArtist(this.getArtist());
517 this.setComment(this.getComment());
518 this.setGenre(this.getGenre());
519 this.setTitle(this.getTitle());
520 this.setTrack(this.getTrack());
521 this.setYear(this.getYear());
522 this.setTrackLength(this.getTrackLength());
523 this.setEncodedBy(this.getEncodedBy());
524 }
525
526
527
528
529 @Override
530 public int compareTo(Object o) {
531 MusicTag myClass = (MusicTag) o;
532 return new CompareToBuilder().append(this.track, myClass.track).append(this.title, myClass.title).toComparison();
533 }
534
535
536
537
538 public String toString() {
539 final ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE);
540 builder.append("artist", artist);
541 builder.append("disc", disc);
542 builder.append("title", title);
543 builder.append("track", track);
544 builder.append("genre", genre);
545 builder.append("year", year);
546 builder.append("comment", comment);
547 builder.append("length", trackLength);
548
549 return builder.toString();
550 }
551
552
553
554
555
556
557
558 protected String createFilenameFromFormat(String aFormat) {
559 final String oldFileName = this.file.getAbsolutePath();
560 final String directory = FilenameUtils.getFullPath(oldFileName);
561 final String extension = FilenameUtils.getExtension(oldFileName.toLowerCase(Locale.US));
562 String newFileName = aFormat;
563 newFileName = StringUtils.replace(newFileName, "%n", getTrack());
564 newFileName = StringUtils.replace(newFileName, "%t", getTitle());
565 newFileName = StringUtils.replace(newFileName, "%a", getArtist());
566 newFileName = StringUtils.replace(newFileName, "%d", getDisc());
567 newFileName = StringUtils.replace(newFileName, "%N", getTrack().toUpperCase());
568 newFileName = StringUtils.replace(newFileName, "%T", getTitle().toUpperCase());
569 newFileName = StringUtils.replace(newFileName, "%A", getArtist().toUpperCase());
570 newFileName = StringUtils.replace(newFileName, "%D", getDisc().toUpperCase());
571 newFileName = FileUtil.correctFileName(newFileName);
572 newFileName = directory + newFileName + "." + extension;
573 if (LOG.isDebugEnabled()) {
574 LOG.debug("Renaming: '" + oldFileName + "' to '" + newFileName + "'");
575 }
576 return newFileName;
577 }
578
579
580
581
582
583
584
585 private static String currentYear() {
586 final GregorianCalendar now = new GregorianCalendar();
587 final int year = now.get(Calendar.YEAR);
588 return Integer.toString(year);
589 }
590
591 }