1 package org.musicontroller.streaming;
2
3 import java.io.BufferedOutputStream;
4 import java.io.File;
5 import java.io.FileInputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.io.OutputStream;
9 import java.io.PrintStream;
10 import java.util.Date;
11 import java.util.HashMap;
12 import java.util.LinkedList;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.zip.Adler32;
16 import java.util.zip.CheckedOutputStream;
17 import java.util.zip.ZipEntry;
18 import java.util.zip.ZipOutputStream;
19
20 import org.apache.log4j.Logger;
21 import org.apache.tapestry.IRequestCycle;
22 import org.apache.tapestry.engine.ExternalService;
23 import org.apache.tapestry.engine.IEngineService;
24 import org.apache.tapestry.engine.ILink;
25 import org.apache.tapestry.services.LinkFactory;
26 import org.apache.tapestry.services.ServiceConstants;
27 import org.apache.tapestry.util.ContentType;
28 import org.apache.tapestry.web.WebResponse;
29 import org.musicontroller.core.Contract_PS;
30 import org.musicontroller.core.Event;
31 import org.musicontroller.core.Playlist;
32 import org.musicontroller.core.Song;
33 import org.musicontroller.dao.Dao;
34 import org.musicontroller.security.IUser;
35 import org.varienaja.util.FileOperations;
36
37 public class Downloader extends ExternalService implements IEngineService {
38 private static final Logger log = Logger.getLogger(Downloader.class);
39
40 public static final String SERVICE_NAME = "download";
41 private LinkFactory _linkFactory;
42 private WebResponse _response;
43 private Dao _dao;
44
45
46
47
48
49
50
51
52
53
54
55 public ILink getLink(boolean post, Object parameter) {
56
57 Object[] params = (Object[]) parameter;
58
59 String kind = params[0]==null ? "P" : params[0].toString();
60 Long id = params[1]==null ? 0 : (Long) params[1];
61 Long userid = params[2]==null ? 0 : (Long) params[2];
62 String passhash = params[3].toString();
63
64 Map<String,String> parameters = new HashMap<String,String>();
65 parameters.put(ServiceConstants.SERVICE, getName());
66 parameters.put("userid",userid.toString());
67 parameters.put("passhash", passhash);
68 parameters.put("id", id.toString());
69 parameters.put("kind", kind);
70
71 return _linkFactory.constructLink(this, false, parameters, true);
72 }
73
74
75
76
77
78 public void service(IRequestCycle cycle) {
79 String kind = cycle.getParameter("kind");
80 long userid = Long.parseLong(cycle.getParameter("userid"));
81 String passhash = cycle.getParameter("passhash");
82 long id = Long.parseLong(cycle.getParameter("id"));
83 if ("P".equals(kind)) {
84 servePlaylist(userid, passhash, id);
85 } else {
86 serveSong(userid, passhash, id);
87 }
88 }
89
90
91
92
93
94
95
96
97
98 private void serveSong(long userid, String passhash, long songid) {
99 log.debug("Transferring song "+songid+" to client.");
100
101 IUser user = _dao.getUserById(userid);
102 if (passhash!=null && passhash.equals(user.getPassword())) {
103 Song song = _dao.getSongById(songid);
104 File songfile = song.getLink().getFile();
105 String songlink = songfile.getAbsolutePath();
106 String namePart = songlink.substring(songlink.lastIndexOf(File.separator)+1);
107 _response.setHeader("Content-Disposition","attachment; filename="+namePart.replaceAll(" ","_"));
108
109 OutputStream out = null;
110 try {
111 out = _response.getOutputStream(new ContentType("audio/mpeg"));
112 InputStream in = null;
113 try {
114 in = new FileInputStream(songfile);
115 FileOperations.copyStream(in,out);
116 StreamMaster.getDJByUser(userid).getMusiController().
117 saveSongEvent(songid,Event.downloaded,userid,new Date());
118 } catch (IOException e) {
119 log.error("Error copying file to downloadstream. "+e);
120 } finally {
121 try {
122 if (in!=null) in.close();
123 } catch(IOException e) {
124 log.error("Error closing inputstream. "+e);
125 }
126 }
127 } catch (Exception e) {
128 log.fatal("Error while transferring song to client.");
129 } finally {
130 try {
131 if (out!=null) out.close();
132 } catch (IOException e) {
133 log.error("Error closing outputstream "+e);
134 }
135 }
136 } else {
137 log.debug("False credentials provided, ignoring download-request.");
138 }
139 }
140
141
142
143
144
145
146
147
148
149 private void servePlaylist(long userid, String passhash, long playlistid) {
150 log.debug("Transferring playlist "+playlistid+" to client.");
151
152 if (StreamMaster.getDJByUser(userid)!=null) {
153 IUser user = _dao.getUserById(userid);
154 if (passhash!=null && passhash.equals(user.getPassword())) {
155
156
157
158 Playlist playlist = _dao.getPlaylistById(playlistid, user);
159 Map<Long,Date> songidset = new HashMap<Long,Date>();
160 List<Long> songidlist = new LinkedList<Long>();
161 for (Contract_PS psc : playlist.getSongs()) {
162 songidset.put(psc.getSong().getId(),null);
163 songidlist.add(psc.getSong().getId());
164 }
165
166 _response.setHeader("Content-Disposition","attachment; filename="+playlist.getName().replaceAll(" ","_")+".zip");
167 OutputStream out = null;
168 try {
169 out = _response.getOutputStream(new ContentType("application/zip"));
170
171 outputPlaylist(playlist.getName(), out,songidlist,songidset);
172
173
174 for (Map.Entry<Long,Date> es : songidset.entrySet()) {
175 StreamMaster.getDJByUser(userid).getMusiController().
176 saveSongEvent(es.getKey(),Event.downloaded,userid,es.getValue());
177 }
178 } catch (Exception e) {
179 log.fatal("Error while transferring playlist to client.");
180 } finally {
181 try {
182 if (out!=null) out.close();
183 } catch (IOException e) {
184 log.error("Error closing outputstream "+e);
185 }
186 }
187 } else {
188 log.debug("False credentials provided, ignoring download-request.");
189 }
190 } else {
191 log.debug("This user has not logged on, ignoring download-request.");
192 }
193 }
194
195 public String getName() {
196 return SERVICE_NAME;
197 }
198
199 public void setLinkFactory(LinkFactory linkFactory) {
200 _linkFactory = linkFactory;
201 }
202
203 public void setResponse(WebResponse response) {
204 _response = response;
205 }
206
207 public void setDao(Dao dao) {
208 _dao = dao;
209 }
210
211
212
213
214
215
216
217
218
219
220
221
222
223 private void outputPlaylist(String playlistname, OutputStream out, List<Long> songidlist, Map<Long,Date> songidset) {
224
225 List<String> m3uContents = new LinkedList<String>();
226 m3uContents.add("#EXTM3U");
227 ZipOutputStream zout = null;
228
229 try {
230 CheckedOutputStream checksum = new CheckedOutputStream(out, new Adler32());
231 zout = new ZipOutputStream(new BufferedOutputStream(checksum));
232
233 for (Long songId : songidlist) {
234 Song song = _dao.getSongById(songId);
235 File songfile = song.getLink().getFile();
236 String songlink = songfile.getAbsolutePath();
237 String ext = songlink.substring(songlink.lastIndexOf("."));
238 String name = FileOperations.translateIllegalFileChars(song.getBand().getName() +" - " +song.getName());
239 String filename = name+ext;
240
241 m3uContents.add("#EXTINF:" + song.getLength()/1000 + "," + name);
242 m3uContents.add(filename);
243
244 if (songidset.get(songId)==null) {
245 ZipEntry ze = new ZipEntry(filename);
246 zout.putNextEntry(ze);
247 InputStream in = null;
248 try {
249 in = new FileInputStream(songfile);
250 FileOperations.copyStream(in,zout);
251 songidset.put(songId ,new Date());
252 } catch (IOException e) {
253 log.error("Error copying file to downloadstream. "+e);
254 } finally {
255 try {
256 if (in!=null) in.close();
257 } catch(IOException e) {
258 log.error("Error closing inputstream. "+e);
259 }
260 }
261 }
262 _dao.evict(song);
263 }
264 ZipEntry ze = new ZipEntry(playlistname+".m3u");
265 zout.putNextEntry(ze);
266 PrintStream ps = new PrintStream(zout);
267 for (String m3uLine : m3uContents) {
268 ps.println(m3uLine);
269 }
270 ps.flush();
271
272 zout.close();
273 } catch (IOException e) {
274 log.error("Error streaming playlist: "+e);
275 songidset.clear();
276 }
277 }
278
279 }