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 if (aMessage == null) {
72 result = new JukesValidationMessage("Disc created successfully", Severity.OK);
73 } else {
74 result = aMessage;
75 }
76 String message = null;
77 try {
78 HibernateUtil.beginTransaction();
79 Artist artist = null;
80 Disc disc = null;
81 long totalDuration = 0;
82 final int padding = ((aTags.length >= 100) ? 3 : 2);
83
84 final MusicTag firstMusicFile = (MusicTag) aTags[0];
85
86
87 final String artistName = firstMusicFile.getArtist();
88 String resource = ResourceUtils.getString("hql.artist.find");
89 String hql = MessageFormat.format(resource,
90 new Object[] { StringEscapeUtils.escapeSql(artistName) });
91 artist = (Artist) HibernateDao.findUniqueByQuery(hql);
92 if (artist == null) {
93 artist = new Artist();
94 artist.setName(artistName);
95 }
96
97
98 final String discName = firstMusicFile.getDisc();
99 resource = ResourceUtils.getString("hql.disc.find");
100 hql = MessageFormat.format(resource, new Object[] {
101 StringEscapeUtils.escapeSql(artist.getName()), StringEscapeUtils.escapeSql(discName) });
102 disc = (Disc) HibernateDao.findUniqueByQuery(hql);
103 if (disc == null) {
104 disc = new Disc();
105 disc.setArtist(artist);
106 disc.setName(discName);
107 artist.addDisc(disc);
108 disc.setGenre(firstMusicFile.getGenre());
109 disc.setYear(firstMusicFile.getYear());
110 disc.setBitrate(firstMusicFile.getBitRate());
111 if (aCoverImage != null) {
112 disc.setCoverUrl(aCoverImage.getAbsolutePath());
113 disc.setCoverSize(aCoverImage.length());
114 }
115 disc.setCreatedDate(new Date(aDirectory.lastModified()));
116 disc.setLocation(aDirectory.getAbsolutePath());
117
118 MUSIC_LOOP: for (int i = 0; i < aTags.length; i++) {
119 final MusicTag musicFile = (MusicTag) aTags[i];
120 musicFile.setArtist(artist.getName());
121 musicFile.setDisc(discName);
122
123
124
125
126
127 final Track track = new Track();
128 disc.addTrack(track);
129 track.setBitrate(musicFile.getBitRate());
130 track.setDuration(musicFile.getTrackLength());
131 track.setDurationTime(musicFile.getTrackLengthAsString());
132 track.setName(musicFile.getTitle());
133 track.setComment(musicFile.getComment());
134 track.setTrackUrl(musicFile.getAbsolutePath());
135 track.setTrackSize(musicFile.getFile().length());
136 musicFile.setTrack(Integer.toString(i + 1), padding);
137 track.setTrackNumber(musicFile.getTrack());
138 track.setCreatedDate(new Date(musicFile.getFile().lastModified()));
139 totalDuration = totalDuration + musicFile.getTrackLength();
140
141
142 if (aUpdateTags) {
143 musicFile.save();
144 }
145 }
146
147
148 disc.setDuration(totalDuration);
149 final TimeSpan timespan = new TimeSpan(totalDuration * 1000);
150 disc.setDurationTime(timespan.getMusicDuration());
151
152
153 HibernateDao.saveOrUpdate(artist);
154 HibernateUtil.commitTransaction();
155 }
156
157 else {
158 message = Resources.getString("messages.DiscAlreadyExist") + disc.getArtist().getName() + " [" + disc.getYear() + "] " + disc.getName() + " at " + disc.getLocation();
159 result.setSeverity(Severity.ERROR);
160 result.setMessage(message);
161 result.setToolTip(message);
162 LOG.warn(message);
163 }
164 } catch (MusicTagException ex) {
165 message = Resources.getString("messages.ErrorWritingMusicTag") + ex.getMessage();
166 result.setMessage(message);
167 result.setSeverity(Severity.ERROR);
168 result.setToolTip(message);
169 LOG.error(message, ex);
170 HibernateUtil.rollbackTransaction();
171 } catch (InfrastructureException ex) {
172 message = Resources.getString("messages.ErrorDB") + ex.getMessage();
173 result.setMessage(message);
174 result.setSeverity(Severity.ERROR);
175 result.setToolTip(message);
176 LOG.error(message, ex);
177 HibernateUtil.rollbackTransaction();
178 } catch (Throwable ex) {
179 message = Resources.getString("messages.UnexpectedError") + ex.getMessage();
180 result.setMessage(message);
181 result.setSeverity(Severity.ERROR);
182 result.setToolTip(message);
183 LOG.error(message, ex);
184 HibernateUtil.rollbackTransaction();
185 }
186
187 return result;
188 }
189
190
191
192
193
194
195
196 public static File findLargestImageFile(final File aDirectory) {
197 return findLargestImageFile(aDirectory, null);
198 }
199
200
201
202
203
204
205
206
207 public static Collection findMusicFiles(final File aDirectory) {
208 if (LOG.isDebugEnabled()) {
209 LOG.debug("Finding music files in directory:" + aDirectory);
210 }
211 return findMusicFiles(aDirectory, null);
212 }
213
214
215
216
217
218
219
220
221
222
223 @SuppressWarnings( { "unused", "unchecked" })
224 public static JukesValidationMessage loadDiscFromDirectory(final File aDirectory, final boolean aUpdateTags) {
225 String sArtist = "";
226 String sDisc = "";
227 String sYear = "";
228 File new_imageFile;
229
230 if (aDirectory == null) {
231 throw new IllegalArgumentException("A directory must be selected");
232 }
233
234 JukesValidationMessage result = new JukesValidationMessage(aDirectory.getAbsolutePath(), Severity.OK);
235 String message = null;
236 final File directory = aDirectory;
237
238
239 if ((!directory.isDirectory()) || (!directory.exists())) {
240 throw new IllegalArgumentException("A directory must be selected");
241 }
242
243 try {
244 if (LOG.isDebugEnabled()) {
245 LOG.debug("Loading music directory: " + directory);
246 }
247
248
249 final Collection musicFiles = findMusicFiles(directory, result);
250 if ((musicFiles == null) || (musicFiles.isEmpty())) {
251 return result;
252 }
253
254
255 final File imageFile = findLargestImageFile(aDirectory, result);
256 if (LOG.isDebugEnabled()) {
257 LOG.debug("Files found = " + musicFiles.size());
258 LOG.debug("Image selected = " + imageFile);
259 }
260
261 final ArrayList tagList = new ArrayList();
262 MUSIC_LOOP: for (final Iterator iter = musicFiles.iterator(); iter.hasNext();) {
263 final File musicFile = (File) iter.next();
264 if (LOG.isDebugEnabled()) {
265 LOG.debug("Music File: " + musicFile);
266 }
267
268 final MusicTag tag = TagFactory.getTag(musicFile);
269 sArtist = tag.getArtist();
270 sDisc = tag.getDisc();
271 sYear = tag.getYear();
272 tagList.add(tag);
273 }
274
275
276
277 if (imageFile != null) {
278 String imageLocation = ImageFactory.saveImageToUserDefinedDirectory(imageFile,
279 sArtist,
280 sDisc,
281 sYear);
282
283 new_imageFile = new File(imageLocation);
284 }
285 else {
286 message = "No images found in " + aDirectory;
287 result.setMessage(message);
288 result.setSeverity(Severity.WARNING);
289 result.setToolTip(message);
290 LOG.warn(message);
291 new_imageFile = imageFile;
292 }
293
294
295 Collections.sort(tagList);
296
297 result = createNewDisc(tagList.toArray(), new_imageFile, aDirectory, result, aUpdateTags);
298
299 } catch (MusicTagException ex) {
300 message = "Error opening music file: " + ex.getMessage();
301 result.setSeverity(Severity.ERROR);
302 result.setToolTip(message);
303 LOG.error(message, ex);
304 } catch (Throwable ex) {
305 message = "Unexpected Error: " + ex.getMessage();
306 result.setSeverity(Severity.ERROR);
307 result.setToolTip(message);
308 LOG.error(message, ex);
309 }
310
311 return result;
312 }
313
314
315
316
317
318
319
320
321
322
323 public static boolean removeDiscIfNoLongerExists(final Disc aDisc) {
324 boolean result = false;
325 final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
326
327 if (aDisc == null) {
328 throw new IllegalArgumentException("Disc may not be null");
329 }
330
331 if (LOG.isDebugEnabled()) {
332 LOG.debug("Checking disc for validity: " + aDisc.getName());
333 }
334
335
336 final File discDirectory = new File(aDisc.getLocation());
337
338
339 result = discDirectory.exists();
340
341
342 try {
343 if (!result) {
344
345 HibernateUtil.beginTransaction();
346 final Artist artist = aDisc.getArtist();
347 artist.getDiscs().remove(aDisc);
348 HibernateDao.delete(aDisc);
349 HibernateUtil.commitTransaction();
350 }
351 } catch (InfrastructureException ex) {
352 final String errorMessage = ResourceUtils.getString("messages.ErrorDeletingDisc");
353 LOG.error(errorMessage, ex);
354 MessageUtil.showError(mainFrame, errorMessage);
355 HibernateUtil.rollbackTransaction();
356 result = false;
357 }
358
359 return result;
360 }
361
362
363
364
365
366
367
368
369
370 private static File findLargestImageFile(final File aDirectory, final JukesValidationMessage aMessage) {
371 if (LOG.isDebugEnabled()) {
372 LOG.debug("Finding largest image in directory: " + aDirectory);
373 }
374
375 File result = null;
376 final Collection imageFiles = FileUtils.listFiles(aDirectory, FilterFactory.IMAGE_FILTER, null);
377
378
379 if ((imageFiles != null) && (!imageFiles.isEmpty())) {
380 LOG.debug("Images found in directory");
381 final Object[] files = (Object[]) imageFiles.toArray();
382
383
384 if (imageFiles.size() == 1) {
385 result = (File) files[0];
386 } else {
387
388 long filesize = 0;
389
390
391 File coverFile = null;
392 for (int i = 0; i < files.length; i++) {
393 final File file = (File) files[i];
394 final long size = file.length();
395 if (size > filesize) {
396 filesize = size;
397 result = file;
398 }
399 final String shortName;
400 if (file.getName().lastIndexOf(".") != -1) {
401 shortName = file.getName().substring(0,file.getName().lastIndexOf(".")).toUpperCase();
402 } else {
403 shortName = file.getName().toUpperCase();
404 }
405 if ((shortName.equals("COVER") ||
406 shortName.equals("FOLDER") ||
407 shortName.equals("FRONT") ) &
408 (coverFile == null ) ) {
409 coverFile = file;
410 }
411 }
412 if (coverFile != null) {
413 result = coverFile;
414 }
415 else {
416 if (aMessage != null) {
417 LOG.info("More than one image found.");
418 aMessage.setSeverity(Severity.WARNING);
419 aMessage.setToolTip("Directory contained more than one image so largest was selected. ");
420 }
421 }
422 }
423 }
424 return result;
425 }
426
427
428
429
430
431
432
433
434
435 private static Collection findMusicFiles(final File aDirectory, final JukesValidationMessage aMessage) {
436
437 final Collection results = FileUtils.listFiles(aDirectory, FilterFactory.MUSIC_FILTER, null);
438
439
440 if (((results == null) || (results.isEmpty())) && (aMessage != null)) {
441 final String message = "Directory contained no music files. " + aDirectory;
442 LOG.warn(message);
443 aMessage.setSeverity(Severity.WARNING);
444 aMessage.setToolTip(message);
445 }
446
447 return results;
448 }
449 }