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.util.ResourceUtils;
17 import com.jgoodies.validation.Severity;
18 import com.melloware.jukes.db.HibernateDao;
19 import com.melloware.jukes.db.HibernateUtil;
20 import com.melloware.jukes.db.orm.Artist;
21 import com.melloware.jukes.db.orm.Disc;
22 import com.melloware.jukes.db.orm.Track;
23 import com.melloware.jukes.exception.InfrastructureException;
24 import com.melloware.jukes.exception.MusicTagException;
25 import com.melloware.jukes.file.filter.FilterFactory;
26 import com.melloware.jukes.file.tag.MusicTag;
27 import com.melloware.jukes.file.tag.TagFactory;
28 import com.melloware.jukes.util.JukesValidationMessage;
29 import com.melloware.jukes.util.TimeSpan;
30
31
32
33
34
35
36
37
38
39
40 public final class MusicDirectory {
41
42 private static final Log LOG = LogFactory.getLog(MusicDirectory.class);
43
44
45
46
47 private MusicDirectory() {
48 super();
49 }
50
51
52
53
54
55
56
57
58
59
60 @SuppressWarnings("unused")
61 public static JukesValidationMessage createNewDisc(final Object[] aTags, final File aCoverImage,
62 final File aDirectory, final JukesValidationMessage aMessage, final boolean aUpdateTags) {
63 JukesValidationMessage result = null;
64 if (aMessage == null) {
65 result = new JukesValidationMessage("Disc created successfully", Severity.OK);
66 } else {
67 result = aMessage;
68 }
69 String message = null;
70 try {
71 HibernateUtil.beginTransaction();
72 Artist artist = null;
73 Disc disc = null;
74 long totalDuration = 0;
75 final int padding = ((aTags.length >= 100) ? 3 : 2);
76 MUSIC_LOOP: for (int i = 0; i < aTags.length; i++) {
77 final MusicTag musicFile = (MusicTag) aTags[i];
78
79
80 if (artist == null) {
81 final String artistName = musicFile.getArtist();
82 final String resource = ResourceUtils.getString("hql.artist.find");
83 final String hql = MessageFormat.format(resource,
84 new Object[] { StringEscapeUtils.escapeSql(artistName) });
85 artist = (Artist) HibernateDao.findUniqueByQuery(hql);
86 if (artist == null) {
87 artist = new Artist();
88 artist.setName(artistName);
89 }
90 musicFile.setArtist(artistName);
91 } else {
92 musicFile.setArtist(artist.getName());
93 }
94
95
96 if (disc == null) {
97 final String discName = musicFile.getDisc();
98 final String resource = ResourceUtils.getString("hql.disc.find");
99 final String hql = MessageFormat.format(resource, new Object[] {
100 StringEscapeUtils.escapeSql(artist.getName()), StringEscapeUtils.escapeSql(discName) });
101 disc = (Disc) HibernateDao.findUniqueByQuery(hql);
102 if (disc == null) {
103 disc = new Disc();
104 disc.setArtist(artist);
105 disc.setName(discName);
106 artist.addDisc(disc);
107 }
108 musicFile.setDisc(discName);
109 disc.setGenre(musicFile.getGenre());
110 disc.setYear(musicFile.getYear());
111 disc.setBitrate(musicFile.getBitRate());
112 if (aCoverImage != null) {
113 disc.setCoverUrl(aCoverImage.getAbsolutePath());
114 disc.setCoverSize(aCoverImage.length());
115 }
116 disc.setCreatedDate(new Date(aDirectory.lastModified()));
117 disc.setLocation(aDirectory.getAbsolutePath());
118
119 HibernateDao.deleteAll(disc.getTracks());
120 } else {
121 musicFile.setDisc(disc.getName());
122 }
123
124
125 final Track track = new Track();
126 disc.addTrack(track);
127 track.setBitrate(musicFile.getBitRate());
128 track.setDuration(musicFile.getTrackLength());
129 track.setDurationTime(musicFile.getTrackLengthAsString());
130 track.setName(musicFile.getTitle());
131 track.setComment(musicFile.getComment());
132 track.setTrackUrl(musicFile.getAbsolutePath());
133 track.setTrackSize(musicFile.getFile().length());
134 musicFile.setTrack(Integer.toString(i + 1), padding);
135 track.setTrackNumber(musicFile.getTrack());
136 track.setCreatedDate(new Date(musicFile.getFile().lastModified()));
137 totalDuration = totalDuration + musicFile.getTrackLength();
138
139
140 if (aUpdateTags) {
141 musicFile.save();
142 }
143 }
144
145
146 disc.setDuration(totalDuration);
147 final TimeSpan timespan = new TimeSpan(totalDuration * 1000);
148 disc.setDurationTime(timespan.getMusicDuration());
149
150
151 HibernateDao.saveOrUpdate(artist);
152 HibernateUtil.commitTransaction();
153 } catch (MusicTagException ex) {
154 message = "Error writing music tag: " + ex.getMessage();
155 result.setMessage(message);
156 result.setSeverity(Severity.ERROR);
157 result.setToolTip(message);
158 LOG.error(message, ex);
159 HibernateUtil.rollbackTransaction();
160 } catch (InfrastructureException ex) {
161 message = "Error persisting to the database: " + ex.getMessage();
162 result.setMessage(message);
163 result.setSeverity(Severity.ERROR);
164 result.setToolTip(message);
165 LOG.error(message, ex);
166 HibernateUtil.rollbackTransaction();
167 } catch (Throwable ex) {
168 message = "Unexpected Error: " + ex.getMessage();
169 result.setMessage(message);
170 result.setSeverity(Severity.ERROR);
171 result.setToolTip(message);
172 LOG.error(message, ex);
173 HibernateUtil.rollbackTransaction();
174 }
175
176 return result;
177 }
178
179
180
181
182
183
184
185 public static File findLargestImageFile(final File aDirectory) {
186 return findLargestImageFile(aDirectory, null);
187 }
188
189
190
191
192
193
194
195
196 public static Collection findMusicFiles(final File aDirectory) {
197 if (LOG.isDebugEnabled()) {
198 LOG.debug("Finding music files in directory:" + aDirectory);
199 }
200 return findMusicFiles(aDirectory, null);
201 }
202
203
204
205
206
207
208
209
210
211
212 @SuppressWarnings( { "unused", "unchecked" })
213 public static JukesValidationMessage loadDiscFromDirectory(final File aDirectory, final boolean aUpdateTags) {
214 if (aDirectory == null) {
215 throw new IllegalArgumentException("A directory must be selected");
216 }
217
218 JukesValidationMessage result = new JukesValidationMessage(aDirectory.getAbsolutePath(), Severity.OK);
219 String message = null;
220 final File directory = aDirectory;
221
222
223 if ((!directory.isDirectory()) || (!directory.exists())) {
224 throw new IllegalArgumentException("A directory must be selected");
225 }
226
227 try {
228 if (LOG.isDebugEnabled()) {
229 LOG.debug("Loading music directory: " + directory);
230 }
231
232
233 final Collection musicFiles = findMusicFiles(directory, result);
234 if ((musicFiles == null) || (musicFiles.isEmpty())) {
235 return result;
236 }
237
238
239 final File imageFile = findLargestImageFile(aDirectory, result);
240
241 if (LOG.isDebugEnabled()) {
242 LOG.debug("Files found = " + musicFiles.size());
243 LOG.debug("Image selected = " + imageFile);
244 }
245
246 final ArrayList tagList = new ArrayList();
247 MUSIC_LOOP: for (final Iterator iter = musicFiles.iterator(); iter.hasNext();) {
248 final File musicFile = (File) iter.next();
249 if (LOG.isDebugEnabled()) {
250 LOG.debug("Music File: " + musicFile);
251 }
252
253 final MusicTag tag = TagFactory.getTag(musicFile);
254 tagList.add(tag);
255 }
256
257
258 Collections.sort(tagList);
259
260 result = createNewDisc(tagList.toArray(), imageFile, aDirectory, result, aUpdateTags);
261
262 } catch (MusicTagException ex) {
263 message = "Error opening music file: " + ex.getMessage();
264 result.setSeverity(Severity.ERROR);
265 result.setToolTip(message);
266 LOG.error(message, ex);
267 } catch (Throwable ex) {
268 message = "Unexpected Error: " + ex.getMessage();
269 result.setSeverity(Severity.ERROR);
270 result.setToolTip(message);
271 LOG.error(message, ex);
272 }
273
274 return result;
275 }
276
277
278
279
280
281
282
283
284
285
286 public static boolean removeDiscIfNoLongerExists(final Disc aDisc) {
287 boolean result = false;
288
289 if (aDisc == null) {
290 throw new IllegalArgumentException("Disc may not be null");
291 }
292
293 if (LOG.isDebugEnabled()) {
294 LOG.debug("Checking disc for validity: " + aDisc.getName());
295 }
296
297
298 final File discDirectory = new File(aDisc.getLocation());
299
300
301 result = discDirectory.exists();
302
303
304 try {
305 if (!result) {
306
307 HibernateUtil.beginTransaction();
308 final Artist artist = aDisc.getArtist();
309 artist.getDiscs().remove(aDisc);
310 HibernateDao.delete(aDisc);
311 HibernateUtil.commitTransaction();
312 }
313 } catch (InfrastructureException ex) {
314 LOG.error("Error deleting disc from database.", ex);
315 HibernateUtil.rollbackTransaction();
316 result = false;
317 }
318
319 return result;
320 }
321
322
323
324
325
326
327
328
329
330 private static File findLargestImageFile(final File aDirectory, final JukesValidationMessage aMessage) {
331 if (LOG.isDebugEnabled()) {
332 LOG.debug("Finding largest image in directory: " + aDirectory);
333 }
334
335 File result = null;
336 final Collection imageFiles = FileUtils.listFiles(aDirectory, FilterFactory.IMAGE_FILTER, null);
337
338
339 if ((imageFiles != null) && (!imageFiles.isEmpty())) {
340 LOG.debug("Images found in directory");
341 final Object[] files = (Object[]) imageFiles.toArray();
342
343
344 if (imageFiles.size() == 1) {
345 result = (File) files[0];
346 } else {
347 if (aMessage != null) {
348 LOG.info("More than one image found.");
349 aMessage.setSeverity(Severity.WARNING);
350 aMessage.setToolTip("Directory contained more than one image so largest was selected. ");
351 }
352
353
354 long filesize = 0;
355 for (int i = 0; i < files.length; i++) {
356 final File file = (File) files[i];
357 final long size = file.length();
358 if (size > filesize) {
359 filesize = size;
360 result = file;
361 }
362 }
363 }
364 }
365 return result;
366 }
367
368
369
370
371
372
373
374
375
376 private static Collection findMusicFiles(final File aDirectory, final JukesValidationMessage aMessage) {
377
378 final Collection results = FileUtils.listFiles(aDirectory, FilterFactory.MUSIC_FILTER, null);
379
380
381 if (((results == null) || (results.isEmpty())) && (aMessage != null)) {
382 final String message = "Directory contained no music files. " + aDirectory;
383 LOG.warn(message);
384 aMessage.setSeverity(Severity.WARNING);
385 aMessage.setToolTip(message);
386 }
387
388 return results;
389 }
390 }