1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.webstersmalley.iplayer.mp3;
18
19 import java.io.BufferedReader;
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.util.Set;
25
26 import org.apache.log4j.Logger;
27 import org.blinkenlights.jid3.ID3Exception;
28 import org.blinkenlights.jid3.ID3Tag;
29 import org.blinkenlights.jid3.MP3File;
30 import org.blinkenlights.jid3.MediaFile;
31 import org.blinkenlights.jid3.v2.ID3V2Tag;
32 import org.blinkenlights.jid3.v2.ID3V2_3_0Tag;
33
34 import com.webstersmalley.iplayer.informer.ProgramInformation;
35 import com.webstersmalley.iplayer.organiser.RadioProgramBean;
36 import com.webstersmalley.musiclibrary.model.FileSystemMusicFileFactory;
37 import com.webstersmalley.musiclibrary.model.MusicFile;
38
39
40
41
42
43 public class MP3Utils {
44 private static final String COMMAND_LINE_TEMPLATE = "%1$s --nohist --preset \"%2$s\" \"%3$s\" \"%4$s\"";
45 private String mp3Encoder;
46 private FileSystemMusicFileFactory musicFileFactory = new FileSystemMusicFileFactory();
47 private FileSystemUtils fileSystemUtils = new FileSystemUtils();
48
49 private Logger logger = Logger.getLogger(getClass());
50
51 public void setMp3Encoder(String mp3Encoder) {
52 this.mp3Encoder = mp3Encoder;
53 }
54
55 public ID3V2Tag getID3V2Tags(String filename) {
56 try {
57 MediaFile mediaFile = new MP3File(new File(filename));
58 ID3Tag[] tags = mediaFile.getTags();
59 for (ID3Tag tag : tags) {
60 if (tag instanceof ID3V2Tag) {
61 return (ID3V2Tag) tag;
62 }
63 }
64 } catch (ID3Exception e) {
65 throw new RuntimeException("Error reading tags", e);
66 }
67 return null;
68 }
69
70 public String dumpAllTags(String filename) {
71 StringBuffer sb = new StringBuffer();
72 try {
73 MediaFile mediaFile = new MP3File(new File(filename));
74
75 ID3Tag[] tags = mediaFile.getTags();
76
77 for (ID3Tag tag : tags) {
78 sb.append(tag.getClass());
79 sb.append("\n");
80 sb.append(tag);
81
82 sb.append("\n");
83 }
84
85 } catch (ID3Exception e) {
86 throw new RuntimeException("Error reading tags", e);
87 }
88
89 return sb.toString();
90 }
91
92 public void reencodeFolder(String inputFolder, String outputFolder, String preset) {
93 logger.info("Re-encoding folder: " + inputFolder + " into " + outputFolder + ", using preset " + preset);
94 if (inputFolder == null || outputFolder == null || preset == null) {
95 throw new RuntimeException("Null parameters not allowed");
96 }
97 if (!inputFolder.endsWith(File.separator)) {
98 inputFolder += File.separator;
99 }
100 if (!outputFolder.endsWith(File.separator)) {
101 outputFolder += File.separator;
102 }
103
104 Set<MusicFile> inputFiles = musicFileFactory.getMusicFiles(inputFolder);
105 for (MusicFile musicFile : inputFiles) {
106 String inputFilename = inputFolder + musicFile.getFilename();
107 String outputFilename = outputFolder + musicFile.getFilename();
108 logger.info("Processing file: " + inputFilename);
109 fileSystemUtils.checkFolder(new File(outputFilename).getParentFile().getAbsolutePath());
110 if (fileSystemUtils.destinationFileNewer(inputFilename, outputFilename)) {
111 logger.info("Destination file exists + is newer, skipping");
112 } else {
113 logger.info("Running: " + inputFilename + ", " + outputFilename);
114 reencodeMP3(inputFilename, outputFilename, preset);
115 }
116 }
117 }
118
119 public void reencodeMP3(String inputFilename, String outputFilename, String preset) {
120 reencodeMP3(inputFilename, outputFilename, preset, true);
121 }
122
123 public void reencodeMP3(String inputFilename, String outputFilename, String preset, boolean retag) {
124
125 try {
126 File outputFolder = new File(outputFilename).getParentFile();
127 if (!outputFolder.exists()) {
128 if (!outputFolder.mkdirs()) {
129 throw new RuntimeException("Error creating output folder");
130 }
131 }
132 String commandLine = String.format(COMMAND_LINE_TEMPLATE, mp3Encoder, preset, inputFilename, outputFilename).replaceAll("\\\\", "/");
133
134 logger.debug("Running command: " + commandLine);
135
136 Process p = Runtime.getRuntime().exec(commandLine);
137
138 StreamConsumer stdout = new StreamConsumer(p.getInputStream(), true);
139 StreamConsumer error = new StreamConsumer(p.getErrorStream(), false);
140
141 new Thread(stdout, "stdout reader").start();
142 new Thread(error, "error reader").start();
143
144 p.waitFor();
145 int exitValue = p.exitValue();
146 if (exitValue != 0) {
147 throw new RuntimeException("MP3 Encoder exited with code: " + exitValue);
148 }
149 if (retag) {
150 copyTags(inputFilename, outputFilename);
151 }
152 } catch (Exception e) {
153 logger.error("Error running mp3 encoder", e);
154 throw new RuntimeException("Error running mp3 encoder", e);
155 }
156 }
157
158 private void copyTags(String inputFilename, String outputFilename) {
159 ID3V2Tag inputTags = getID3V2Tags(inputFilename);
160 MediaFile mediaFile = new MP3File(new File(outputFilename));
161 mediaFile.setID3Tag(inputTags);
162 try {
163 mediaFile.sync();
164 } catch (ID3Exception e) {
165 throw new RuntimeException("Error writing tags to: " + outputFilename, e);
166 }
167 }
168
169 private class StreamConsumer implements Runnable {
170 private Logger logger = Logger.getLogger(getClass());
171 private InputStream stream;
172 private boolean log;
173
174 public StreamConsumer(InputStream stream, boolean log) {
175 this.stream = stream;
176 this.log = log;
177 }
178
179 public void run() {
180 BufferedReader br = new BufferedReader(new InputStreamReader(stream));
181 try {
182 for (String line; (line = br.readLine()) != null;) {
183 if (log) {
184 logger.info(line);
185 }
186 }
187 } catch (IOException e) {
188 logger.error("Error reading stream", e);
189 }
190 }
191 }
192
193 public void retagMP3(String filename, RadioProgramBean program) {
194 try {
195 ID3V2_3_0Tag tag = new ID3V2_3_0Tag();
196 tag.setTitle(program.getEpisodeTitle());
197 tag.setAlbum(program.getSeriesTitle());
198 tag.setArtist(program.getArtist());
199
200 MediaFile mediaFile = new MP3File(new File(filename));
201 mediaFile.setID3Tag(tag);
202 mediaFile.sync();
203 } catch (Exception e) {
204 logger.error("Error writing mp3 tags", e);
205 throw new RuntimeException("Error writing mp3 tags", e);
206 }
207 }
208
209 public void retagMP3(String filename, ProgramInformation prog) {
210 try {
211 ID3V2_3_0Tag tag = new ID3V2_3_0Tag();
212 tag.setTitle(prog.getProgramName());
213 tag.setAlbum(prog.getProgramSetName());
214 tag.setArtist(prog.getAuthor());
215
216 MediaFile mediaFile = new MP3File(new File(filename));
217 mediaFile.setID3Tag(tag);
218 mediaFile.sync();
219 } catch (Exception e) {
220 logger.error("Error writing mp3 tags", e);
221 throw new RuntimeException("Error writing mp3 tags", e);
222 }
223 }
224
225 }