1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package org.wever.jradiodj.services.collection;
27
28 import de.jarnbjo.ogg.FileStream;
29 import de.jarnbjo.ogg.LogicalOggStream;
30 import de.jarnbjo.vorbis.CommentHeader;
31 import de.jarnbjo.vorbis.VorbisStream;
32 import java.io.File;
33 import java.io.FileFilter;
34 import java.io.FilenameFilter;
35 import java.io.IOException;
36 import java.io.RandomAccessFile;
37 import java.util.Collection;
38 import java.util.Collections;
39 import java.util.HashSet;
40 import java.util.Set;
41 import java.util.logging.Level;
42 import java.util.logging.Logger;
43 import javax.sound.sampled.UnsupportedAudioFileException;
44 import org.jaudiotagger.audio.ReadOnlyFileException;
45 import org.jaudiotagger.audio.mp3.MP3File;
46 import org.jaudiotagger.tag.TagException;
47 import org.jaudiotagger.tag.id3.AbstractID3v2Frame;
48 import org.jaudiotagger.tag.id3.AbstractID3v2Tag;
49 import org.jaudiotagger.tag.id3.ID3v1Tag;
50 import org.jaudiotagger.tag.id3.framebody.FrameBodyTALB;
51 import org.jaudiotagger.tag.id3.framebody.FrameBodyTIT2;
52 import org.jaudiotagger.tag.id3.framebody.FrameBodyTPE1;
53 import org.wever.jradiodj.beans.ProcessingState;
54 import org.wever.jradiodj.beans.SongCollection;
55
56 /***
57 *
58 * @author mick
59 * @version $Id$
60 */
61 public final class CollectionManagerImpl implements CollectionManager {
62 private SongCollection collection = null;
63 private static final String MUSIC_LOCATION = "org.wever.jradiodj.musicLocations";
64 private static final Logger LOG = Logger.getLogger( CollectionManager.class.getName() );
65 private final ProcessingState processingState = new ProcessingState();
66 private final Set < File > filesToProcess = Collections.synchronizedSet( new HashSet< File > () );
67
68 static{
69 LOG.setLevel(Level.FINEST);
70 }
71
72 /*** Creates a new instance of CollectionManagerImpl */
73 public CollectionManagerImpl() {
74 }
75
76 public synchronized SongCollection getCollection(){
77 if( this.collection == null ) {
78
79 final String[] locations = System.getProperty(MUSIC_LOCATION).split(",");
80
81 final SongCollection collection = new SongCollection();
82 for( String directory : locations ) {
83 searchDirectory( new File(directory), collection );
84 }
85 this.collection = collection;
86 }
87 return this.collection;
88 }
89
90 public synchronized void reset() {
91 collection = null;
92 processingState.reset();
93 }
94
95 private SongCollection searchDirectory(
96 final File directory,
97 final SongCollection collection) {
98
99
100 final File[] childDirectories = directory.listFiles(new FileFilter() {
101 public boolean accept(File pathname) {
102 return pathname.isDirectory();
103 }
104 });
105 if( childDirectories != null ) {
106 filesToProcess( childDirectories );
107
108 for( File childDirectory : childDirectories) {
109 searchDirectory( childDirectory, collection );
110 fileProcessed( childDirectory );
111 }
112 }
113
114
115 final File[] songs = directory.listFiles(new FilenameFilter() {
116 public boolean accept(File dir, String name) {
117 name = name.toLowerCase();
118 return name.endsWith(".mp3") || name.endsWith(".ogg");
119 }
120 });
121 if( songs != null ){
122 filesToProcess( songs );
123
124 for( File song : songs ) {
125 try {
126 addSong( song, collection );
127 } finally {
128 fileProcessed( song );
129 }
130 }
131 }
132 return collection;
133 }
134
135 private void addSong(
136 final File file,
137 final SongCollection collection ) {
138
139 try {
140 final String name = file.getName().toLowerCase();
141 if( name.endsWith(".mp3") ){
142 addMP3Song( file, collection );
143 }else if( name.endsWith(".ogg") ){
144 addVorbisSong( file, collection );
145 }
146
147 } catch(IOException ioe) {
148 LOG.log(Level.WARNING, "Failed adding "+file.getName()+" to collection", ioe);
149 } catch(ReadOnlyFileException rofe){
150 LOG.log(Level.WARNING, "Failed adding "+file.getName()+" to collection", rofe);
151 } catch(TagException te) {
152 LOG.log(Level.WARNING, "Failed adding "+file.getName()+" to collection", te);
153 } catch(UnsupportedAudioFileException te) {
154 LOG.log(Level.WARNING, "Failed adding "+file.getName()+" to collection", te);
155 }
156 }
157
158 private void addMP3Song(
159 final File file,
160 final SongCollection collection ) throws IOException, ReadOnlyFileException, TagException {
161
162 final MP3File song = new MP3File( file );
163 if( song.hasID3v2Tag() ) {
164 final AbstractID3v2Tag tag = song.getID3v2Tag();
165 if( tag != null ){
166 final AbstractID3v2Frame artist = (AbstractID3v2Frame)tag.getFrame("TPE1");
167 if( artist != null && artist.getBody() != null) {
168 collection.addArtist( ((FrameBodyTPE1)artist.getBody()).getText(), file);
169 }
170 final AbstractID3v2Frame album = (AbstractID3v2Frame)tag.getFrame("TALB");
171 if( album != null && album.getBody() != null) {
172 collection.addAlbum( ((FrameBodyTALB)album.getBody()).getText(), file);
173 }
174 final AbstractID3v2Frame title = (AbstractID3v2Frame)tag.getFrame("TIT2");
175 if( title != null && title.getBody() != null) {
176 collection.addTitle( ((FrameBodyTIT2)title.getBody()).getText(), file);
177 }
178
179 } else {
180 LOG.log(Level.INFO, file.getName() + " has no tag.");
181 LOG.log(Level.INFO, "Will not appear under any artist, albums, or genre.");
182 }
183 } else if ( song.hasID3v2Tag() ) {
184 final ID3v1Tag tag = song.getID3v1Tag();
185 collection.addArtist( tag.getArtist(), file );
186 collection.addAlbum( tag.getAlbum(), file );
187 collection.addTitle( tag.getTitle(), file );
188
189 } else {
190 LOG.log(Level.INFO, file.getName() + " has no tag.");
191 LOG.log(Level.INFO, "Will not appear under any artist, albums, or genre.");
192 }
193 }
194
195 private void addVorbisSong(
196 final File file,
197 final SongCollection collection ) throws IOException, UnsupportedAudioFileException {
198
199 final FileStream stream = new FileStream( new RandomAccessFile(file, "r") );
200 final Collection < LogicalOggStream > streams = (Collection<LogicalOggStream>)stream.getLogicalStreams();
201 final LogicalOggStream los = (LogicalOggStream)streams.iterator().next();
202
203 if( streams.size()!=1 || los.getFormat()!=LogicalOggStream.FORMAT_VORBIS ) {
204 throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported.");
205 }
206
207 VorbisStream vs = null;
208 try {
209 vs = new VorbisStream(los);
210
211 final CommentHeader tag = vs.getCommentHeader();
212 collection.addArtist( tag.getArtist(), file);
213 collection.addAlbum( tag.getAlbum(), file );
214 collection.addTitle( tag.getTitle(), file );
215 } finally {
216 vs.close();
217 }
218 }
219
220
221
222 private void filesToProcess(final File[] files) {
223 for( File f : files ) {
224 filesToProcess.add( f );
225 }
226 processingState.incrementStepsLoaded( files.length );
227 }
228
229 private void fileProcessed(final File file) {
230 filesToProcess.remove(file);
231 processingState.incrementStepsProcessed( 1 );
232 }
233
234 public ProcessingState getProcessingState() {
235 return processingState;
236 }
237 }