View Javadoc

1   // Copyright (C) 2005  Michael Wever ( mick@wever.org )
2   // 
3   // This program is free software; you can redistribute it and/or
4   // modify it under the terms of the GNU General Public License
5   // as published by the Free Software Foundation; either version 2
6   // of the License, or (at your option) any later version.
7   // 
8   // This program is distributed in the hope that it will be useful,
9   // but WITHOUT ANY WARRANTY; without even the implied warranty of
10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  // GNU General Public License for more details.
12  // 
13  // You should have received a copy of the GNU General Public License
14  // along with this program; if not, write to the Free Software
15  // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16  /*
17   * CollectionManagerImpl.java
18   *
19   * Created on 16. juli 2005, 20:06
20   *
21   * To change this template, choose Tools | Options and locate the template under
22   * the Source Creation and Management node. Right-click the template and choose
23   * Open. You can then make changes to the template in the Source Editor.
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              // get property listing where music is kept
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          // Crawl down into children directories first
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         // Now search for *.mp3 and *.ogg at this level
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                     //addGenre( tag.getGenre(), song);
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                 //addGenre( tag.getGenre(), file);
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     // Progress indicator //
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 }