1 package com.melloware.jukes.file;
2
3 import java.io.File;
4 import java.text.MessageFormat;
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.Collections;
8 import java.util.Date;
9 import java.util.Iterator;
10
11 import org.apache.commons.io.FileUtils;
12 import org.apache.commons.lang.StringEscapeUtils;
13 import org.apache.commons.logging.Log;
14 import org.apache.commons.logging.LogFactory;
15
16 import com.jgoodies.uif.application.Application;
17 import com.jgoodies.uif.util.ResourceUtils;
18 import com.jgoodies.validation.Severity;
19 import com.melloware.jukes.db.HibernateDao;
20 import com.melloware.jukes.db.HibernateUtil;
21 import com.melloware.jukes.db.orm.Artist;
22 import com.melloware.jukes.db.orm.Disc;
23 import com.melloware.jukes.db.orm.Track;
24 import com.melloware.jukes.exception.InfrastructureException;
25 import com.melloware.jukes.exception.MusicTagException;
26 import com.melloware.jukes.file.filter.FilterFactory;
27 import com.melloware.jukes.file.tag.MusicTag;
28 import com.melloware.jukes.file.tag.TagFactory;
29 import com.melloware.jukes.file.image.ImageFactory;
30 import com.melloware.jukes.gui.tool.Resources;
31 import com.melloware.jukes.gui.view.MainFrame;
32 import com.melloware.jukes.util.JukesValidationMessage;
33 import com.melloware.jukes.util.MessageUtil;
34 import com.melloware.jukes.util.TimeSpan;
35
36
37
38
39
40
41
42
43
44
45
46 public final class MusicDirectory {
47
48 private static final Log LOG = LogFactory.getLog(MusicDirectory.class);
49
50
51
52
53 private MusicDirectory() {
54 super();
55 }
56
57
58
59
60
61
62
63
64
65
66 @SuppressWarnings("unused")
67 public static JukesValidationMessage createNewDisc(final Object[] aTags, final File aCoverImage,
68 final File aDirectory, final JukesValidationMessage aMessage, final boolean aUpdateTags) {
69 JukesValidationMessage result = null;
70 final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
71 boolean setDiscComment = true;
72 String previousComment = "";
73 if (aMessage == null) {
74 result = new JukesValidationMessage("Disc created successfully", Severity.OK);
75 } else {
76 result = aMessage;
77 }
78 String message = null;
79 try {
80 HibernateUtil.beginTransaction();
81 Artist artist = null;
82 Disc disc = null;
83 String artistName, discName, sGenre, sYear, sTitle, sComment;
84 long totalDuration = 0;
85 final int padding = ((aTags.length >= 100) ? 3 : 2);
86
87 final MusicTag firstMusicFile = (MusicTag) aTags[0];
88
89
90 artistName = firstMusicFile.getArtist();
91 if (artistName.length()>100) {
92 artistName = artistName.substring(0, 100);
93 message = "Artist Name was too long: " + artistName;
94 result.setMessage(message);
95 result.setSeverity(Severity.WARNING);
96 result.setToolTip(message);
97 LOG.warn(message);
98 }
99
100 String resource = ResourceUtils.getString("hql.artist.find");
101 String hql = MessageFormat.format(resource,
102 new Object[] { StringEscapeUtils.escapeSql(artistName) });
103 artist = (Artist) HibernateDao.findUniqueByQuery(hql);
104 if (artist == null) {
105 artist = new Artist();
106 artist.setName(artistName);
107 }
108
109 discName = firstMusicFile.getDisc();
110 if (discName.length()>100) {
111 discName = discName.substring(0, 100);
112 message = "Disc Name was too long: " + discName;
113 result.setMessage(message);
114 result.setSeverity(Severity.WARNING);
115 result.setToolTip(message);
116 LOG.warn(message);
117 }
118 resource = ResourceUtils.getString("hql.disc.find");
119 hql = MessageFormat.format(resource, new Object[] {
120 StringEscapeUtils.escapeSql(artist.getName()), StringEscapeUtils.escapeSql(discName) });
121 disc = (Disc) HibernateDao.findUniqueByQuery(hql);
122 if (disc == null) {
123 disc = new Disc();
124 disc.setArtist(artist);
125 disc.setName(discName);
126 artist.addDisc(disc);
127 sGenre = firstMusicFile.getGenre();
128 if (sGenre.length()>100) {
129 sGenre = sGenre.substring(0, 100);
130 }
131 disc.setGenre(sGenre);
132 sYear = firstMusicFile.getYear();
133 if (sYear.length()>4) {
134 sYear = sYear.substring(0, 4);
135 }
136 disc.setYear(sYear);
137 disc.setBitrate(firstMusicFile.getBitRate());
138
139 if (aCoverImage != null) {
140 disc.setCoverUrl(aCoverImage.getAbsolutePath());
141 disc.setCoverSize(aCoverImage.length());
142
143
144 String imageLocation = ImageFactory.saveImageToUserDefinedDirectory(aCoverImage,
145 disc.getArtist().getName(),
146 disc.getName(),
147 disc.getYear());
148 } else {
149 message = "No images found in " + aDirectory;
150 result.setMessage(message);
151 result.setSeverity(Severity.WARNING);
152 result.setToolTip(message);
153 LOG.warn(message);
154 }
155 disc.setCreatedDate(new Date(aDirectory.lastModified()));
156 disc.setLocation(aDirectory.getAbsolutePath());
157
158 MUSIC_LOOP: for (int i = 0; i < aTags.length; i++) {
159 final MusicTag musicFile = (MusicTag) aTags[i];
160 musicFile.setArtist(artist.getName());
161 musicFile.setDisc(discName);
162
163
164
165
166
167 final Track track = new Track();
168 disc.addTrack(track);
169 track.setBitrate(musicFile.getBitRate());
170 track.setDuration(musicFile.getTrackLength());
171 track.setDurationTime(musicFile.getTrackLengthAsString());
172 sTitle = musicFile.getTitle();
173 if (sTitle.length()>100) {
174 sTitle = sTitle.substring(0, 100);
175 }
176 track.setName(sTitle);
177 track.setComment(musicFile.getComment());
178 track.setTrackUrl(musicFile.getAbsolutePath());
179 track.setTrackSize(musicFile.getFile().length());
180 musicFile.setTrack(Integer.toString(i + 1), padding);
181 track.setTrackNumber(musicFile.getTrack());
182 track.setCreatedDate(new Date(musicFile.getFile().lastModified()));
183 totalDuration = totalDuration + musicFile.getTrackLength();
184
185
186 if (aUpdateTags) {
187 musicFile.save();
188 }
189 }
190
191
192 final MusicTag musicFileFirst = (MusicTag) aTags[0];
193 previousComment = musicFileFirst.getComment();
194 for (int i = 1; i < aTags.length; i++) {
195 final MusicTag musicFile = (MusicTag) aTags[i];
196 if (!(previousComment.equals(musicFile.getComment()))) {
197 setDiscComment = false;
198 }
199 previousComment = musicFile.getComment();
200 }
201
202 if (setDiscComment) {
203 if (previousComment.length()>500) {
204 previousComment = previousComment.substring(0, 500);
205 }
206 disc.setNotes(previousComment);
207 final Collection tracks = disc.getTracks();
208 Track track;
209 for (final Iterator iter = tracks.iterator(); iter.hasNext();) {
210 track = (Track) iter.next();
211 track.setComment("");
212 }
213 } else {
214 final Collection tracks = disc.getTracks();
215 Track track;
216 for (final Iterator iter = tracks.iterator(); iter.hasNext();) {
217 track = (Track) iter.next();
218 sComment = track.getComment();
219 if (sComment.length()>254) {
220 sComment = sComment.substring(0, 254);
221 track.setComment(sComment);
222 }
223 }
224 }
225
226
227 disc.setDuration(totalDuration);
228 final TimeSpan timespan = new TimeSpan(totalDuration * 1000);
229 disc.setDurationTime(timespan.getMusicDuration());
230
231
232 HibernateDao.saveOrUpdate(artist);
233 HibernateUtil.commitTransaction();
234 }
235
236 else {
237 message = Resources.getString("messages.DiscAlreadyExist") + disc.getArtist().getName() + " [" + disc.getYear() + "] " + disc.getName() + " at " + disc.getLocation();
238 result.setSeverity(Severity.ERROR);
239 result.setMessage(message);
240 result.setToolTip(message);
241 LOG.warn(message);
242 }
243 } catch (MusicTagException ex) {
244 message = Resources.getString("messages.ErrorWritingMusicTag") + ex.getMessage();
245 result.setMessage(message);
246 result.setSeverity(Severity.ERROR);
247 result.setToolTip(message);
248 LOG.error(message, ex);
249 HibernateUtil.rollbackTransaction();
250 } catch (InfrastructureException ex) {
251 message = Resources.getString("messages.ErrorDB") + ex.getMessage();
252 result.setMessage(message);
253 result.setSeverity(Severity.ERROR);
254 result.setToolTip(message);
255 LOG.error(message, ex);
256 HibernateUtil.rollbackTransaction();
257 } catch (Throwable ex) {
258 message = Resources.getString("messages.UnexpectedError") + ex.getMessage();
259 result.setMessage(message);
260 result.setSeverity(Severity.ERROR);
261 result.setToolTip(message);
262 LOG.error(message, ex);
263 HibernateUtil.rollbackTransaction();
264 }
265
266 return result;
267 }
268
269
270
271
272
273
274
275 public static File findLargestImageFile(final File aDirectory) {
276 return findLargestImageFile(aDirectory, null);
277 }
278
279
280
281
282
283
284
285
286 public static Collection findMusicFiles(final File aDirectory) {
287 if (LOG.isDebugEnabled()) {
288 LOG.debug("Finding music files in directory:" + aDirectory);
289 }
290 return findMusicFiles(aDirectory, null);
291 }
292
293
294
295
296
297
298
299
300
301
302 @SuppressWarnings( { "unused", "unchecked" })
303 public static JukesValidationMessage loadDiscFromDirectory(final File aDirectory, final boolean aUpdateTags) {
304
305 if (aDirectory == null) {
306 throw new IllegalArgumentException("A directory must be selected");
307 }
308
309 JukesValidationMessage result = new JukesValidationMessage(aDirectory.getAbsolutePath(), Severity.OK);
310 String message = null;
311 final File directory = aDirectory;
312
313 if ((!directory.isDirectory()) || (!directory.exists())) {
314 throw new IllegalArgumentException("A directory must be selected");
315 }
316
317 try {
318 if (LOG.isDebugEnabled()) {
319 LOG.debug("Loading music directory: " + directory);
320 }
321
322
323 final Collection musicFiles = findMusicFiles(directory, result);
324 if ((musicFiles == null) || (musicFiles.isEmpty())) {
325 return result;
326 }
327
328
329 final File imageFile = findLargestImageFile(aDirectory, result);
330 if (LOG.isDebugEnabled()) {
331 LOG.debug("Files found = " + musicFiles.size());
332 LOG.debug("Image selected = " + imageFile);
333 }
334
335 final ArrayList tagList = new ArrayList();
336 MUSIC_LOOP: for (final Iterator iter = musicFiles.iterator(); iter.hasNext();) {
337 final File musicFile = (File) iter.next();
338 if (LOG.isDebugEnabled()) {
339 LOG.debug("Music File: " + musicFile);
340 }
341
342 final MusicTag tag = TagFactory.getTag(musicFile);
343 tagList.add(tag);
344 }
345
346
347 Collections.sort(tagList);
348
349 result = createNewDisc(tagList.toArray(), imageFile, aDirectory, result, aUpdateTags);
350
351 } catch (MusicTagException ex) {
352 message = "Error opening music file: " + ex.getMessage();
353 result.setSeverity(Severity.ERROR);
354 result.setToolTip(message);
355 LOG.error(message, ex);
356 } catch (Throwable ex) {
357 message = "Unexpected Error: " + ex.getMessage();
358 result.setSeverity(Severity.ERROR);
359 result.setToolTip(message);
360 LOG.error(message, ex);
361 }
362
363 return result;
364 }
365
366
367
368
369
370
371
372
373
374
375 public static boolean removeDiscIfNoLongerExists(final Disc aDisc) {
376 boolean result = false;
377 final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
378
379 if (aDisc == null) {
380 throw new IllegalArgumentException("Disc may not be null");
381 }
382
383 if (LOG.isDebugEnabled()) {
384 LOG.debug("Checking disc for validity: " + aDisc.getName());
385 }
386
387
388 final File discDirectory = new File(aDisc.getLocation());
389
390
391 result = discDirectory.exists();
392
393
394 try {
395 if (!result) {
396
397 HibernateUtil.beginTransaction();
398 final Artist artist = aDisc.getArtist();
399 artist.getDiscs().remove(aDisc);
400 HibernateDao.delete(aDisc);
401 HibernateUtil.commitTransaction();
402 }
403 } catch (InfrastructureException ex) {
404 final String errorMessage = ResourceUtils.getString("messages.ErrorDeletingDisc");
405 LOG.error(errorMessage, ex);
406 MessageUtil.showError(mainFrame, errorMessage);
407 HibernateUtil.rollbackTransaction();
408 result = false;
409 }
410
411 return result;
412 }
413
414
415
416
417
418
419
420
421
422 private static File findLargestImageFile(final File aDirectory, final JukesValidationMessage aMessage) {
423 if (LOG.isDebugEnabled()) {
424 LOG.debug("Finding largest image in directory: " + aDirectory);
425 }
426
427 File result = null;
428 final Collection imageFiles = FileUtils.listFiles(aDirectory, FilterFactory.IMAGE_FILTER, null);
429
430
431 if ((imageFiles != null) && (!imageFiles.isEmpty())) {
432 LOG.debug("Images found in directory");
433 final Object[] files = (Object[]) imageFiles.toArray();
434
435
436 if (imageFiles.size() == 1) {
437 result = (File) files[0];
438 } else {
439
440 long filesize = 0;
441
442
443 File coverFile = null;
444 for (int i = 0; i < files.length; i++) {
445 final File file = (File) files[i];
446 final long size = file.length();
447 if (size > filesize) {
448 filesize = size;
449 result = file;
450 }
451 final String shortName;
452 if (file.getName().lastIndexOf(".") != -1) {
453 shortName = file.getName().substring(0,file.getName().lastIndexOf(".")).toUpperCase();
454 } else {
455 shortName = file.getName().toUpperCase();
456 }
457 if ((shortName.equals("COVER") ||
458 shortName.equals("FOLDER") ||
459 shortName.equals("FRONT") ) &
460 (coverFile == null ) ) {
461 coverFile = file;
462 }
463 }
464 if (coverFile != null) {
465 result = coverFile;
466 }
467 else {
468 if (aMessage != null) {
469 LOG.info("More than one image found.");
470 aMessage.setSeverity(Severity.WARNING);
471 aMessage.setToolTip("Directory contained more than one image so largest was selected. ");
472 }
473 }
474 }
475 }
476 return result;
477 }
478
479
480
481
482
483
484
485
486
487
488 private static Collection findMusicFiles(final File aDirectory, final JukesValidationMessage aMessage) {
489
490 final Collection results = FileUtils.listFiles(aDirectory, FilterFactory.MUSIC_FILTER, null);
491 final Collection resultsDir = FileUtils.listFiles(aDirectory, FilterFactory.dirIOFilter(), null);
492
493
494 if (((results == null) || (results.isEmpty())) && ((resultsDir==null) || (resultsDir.isEmpty()) ) && (aMessage != null)) {
495 final String message = "Directory contained no music files. " + aDirectory;
496 LOG.warn(message);
497 aMessage.setSeverity(Severity.WARNING);
498 aMessage.setToolTip(message);
499 }
500
501 return results;
502 }
503 }