diff options
-rw-r--r-- | .gitmodules | 3 | ||||
m--------- | ServerProxy | 0 | ||||
-rw-r--r-- | Subsonic.iml | 1 | ||||
-rw-r--r-- | project.properties | 3 | ||||
-rw-r--r-- | src/github/daneren2005/dsub/service/DownloadFile.java | 37 | ||||
-rw-r--r-- | src/github/daneren2005/dsub/service/DownloadService.java | 16 | ||||
-rw-r--r-- | src/github/daneren2005/dsub/service/StreamProxy.java | 311 |
7 files changed, 50 insertions, 321 deletions
diff --git a/.gitmodules b/.gitmodules index 5a0b6b8c..dcb0d0b7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "DragSortListView"] path = DragSortListView url = https://github.com/bauerca/drag-sort-listview.git +[submodule "ServerProxy"] + path = ServerProxy + url = https://github.com/daneren2005/ServerProxy.git diff --git a/ServerProxy b/ServerProxy new file mode 160000 +Subproject 1fab7a8626616ea7f4df9aa3381a7c0bbf5529d diff --git a/Subsonic.iml b/Subsonic.iml index 31ebc6b4..0e656645 100644 --- a/Subsonic.iml +++ b/Subsonic.iml @@ -21,6 +21,7 @@ <orderEntry type="module" module-name="mediarouter" />
<orderEntry type="library" name="google-player-services" level="project" />
<orderEntry type="module" module-name="google-play-services_lib" />
+ <orderEntry type="module" module-name="ServerProxy" />
</component>
</module>
diff --git a/project.properties b/project.properties index 80f83a52..d03eb683 100644 --- a/project.properties +++ b/project.properties @@ -12,4 +12,5 @@ target=android-19 android.library.reference.1=../../../../Program Files (x86)/Android/android-sdk/extras/android/support/v7/appcompat android.library.reference.2=DragSortListView/library android.library.reference.3=../../../../Program Files (x86)/Android/android-sdk/extras/android/support/v7/mediarouter -android.library.reference.4=../../../../Program Files (x86)/Android/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib
\ No newline at end of file +android.library.reference.4=../../../../Program Files (x86)/Android/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib +android.library.reference.5=ServerProxy
\ No newline at end of file diff --git a/src/github/daneren2005/dsub/service/DownloadFile.java b/src/github/daneren2005/dsub/service/DownloadFile.java index 9598b572..d7c3959c 100644 --- a/src/github/daneren2005/dsub/service/DownloadFile.java +++ b/src/github/daneren2005/dsub/service/DownloadFile.java @@ -36,6 +36,8 @@ import github.daneren2005.dsub.util.CancellableTask; import github.daneren2005.dsub.util.FileUtil; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.util.CacheCleaner; +import github.daneren2005.serverproxy.BufferFile; + import org.apache.http.Header; import org.apache.http.HttpResponse; @@ -45,7 +47,7 @@ import org.apache.http.HttpStatus; * @author Sindre Mehus * @version $Id$ */ -public class DownloadFile { +public class DownloadFile implements BufferFile { private static final String TAG = DownloadFile.class.getSimpleName(); private static final int MAX_FAILURES = 5; private final Context context; @@ -63,7 +65,7 @@ public class DownloadFile { private boolean isPlaying = false; private boolean saveWhenDone = false; private boolean completeWhenDone = false; - private Integer contentLength = null; + private Long contentLength = null; private long currentSpeed = 0; public DownloadFile(Context context, MusicDirectory.Entry song, boolean save) { @@ -108,7 +110,7 @@ public class DownloadFile { return br; } - public Integer getContentLength() { + public Long getContentLength() { return contentLength; } @@ -125,6 +127,7 @@ public class DownloadFile { } } + @Override public long getEstimatedSize() { if(contentLength != null) { return contentLength; @@ -136,7 +139,7 @@ public class DownloadFile { } else if(song.getDuration() == null) { return 0; } else { - int br = (getBitRate() * 1024) / 8; + int br = (getBitRate() * 1000) / 8; int duration = song.getDuration(); return br * duration; } @@ -171,6 +174,17 @@ public class DownloadFile { } } + @Override + public File getFile() { + if (saveFile.exists()) { + return saveFile; + } else if (completeFile.exists()) { + return completeFile; + } else { + return partialFile; + } + } + public File getCompleteFile() { if (saveFile.exists()) { return saveFile; @@ -195,11 +209,22 @@ public class DownloadFile { return saveFile.exists() || completeFile.exists(); } + @Override public synchronized boolean isWorkDone() { return saveFile.exists() || (completeFile.exists() && !save) || saveWhenDone || completeWhenDone; } - public synchronized boolean isDownloading() { + @Override + public void onStart() { + setPlaying(true); + } + + @Override + public void onStop() { + setPlaying(false); + } + + public synchronized boolean isDownloading() { return downloadTask != null && downloadTask.isRunning(); } @@ -353,7 +378,7 @@ public class DownloadFile { String contentLengthString = contentLengthHeader.getValue(); if(contentLengthString != null) { Log.i(TAG, "Content Length: " + contentLengthString); - contentLength = Integer.parseInt(contentLengthString); + contentLength = Long.parseLong(contentLengthString); } } in = response.getEntity().getContent(); diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java index e8829aa6..5e02641a 100644 --- a/src/github/daneren2005/dsub/service/DownloadService.java +++ b/src/github/daneren2005/dsub/service/DownloadService.java @@ -43,6 +43,7 @@ import github.daneren2005.dsub.util.ShufflePlayBuffer; import github.daneren2005.dsub.util.SimpleServiceBinder; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.util.compat.RemoteControlClientHelper; +import github.daneren2005.serverproxy.BufferProxy; import java.io.File; import java.util.ArrayList; @@ -133,7 +134,7 @@ public class DownloadService extends Service { private boolean showVisualization; private RemoteControlState remoteState = RemoteControlState.LOCAL; private PositionCache positionCache; - private StreamProxy proxy; + private BufferProxy proxy; private Timer sleepTimer; private int timerDuration; @@ -274,6 +275,10 @@ public class DownloadService extends Service { remoteController.stop(); remoteController.shutdown(); } + if(proxy != null) { + proxy.stop(); + proxy = null; + } mediaRouter.destroy(); Util.hidePlayingNotification(this, this, handler); Util.hideDownloadingNotification(this); @@ -552,6 +557,10 @@ public class DownloadService extends Service { } updateJukeboxPlaylist(); setNextPlaying(); + if(proxy != null) { + proxy.stop(); + proxy = null; + } } public synchronized void remove(int which) { @@ -1206,10 +1215,11 @@ public class DownloadService extends Service { String dataSource = file.getPath(); if(isPartial) { if (proxy == null) { - proxy = new StreamProxy(this); + proxy = new BufferProxy(this); proxy.start(); } - dataSource = String.format("http://127.0.0.1:%d/%s", proxy.getPort(), URLEncoder.encode(dataSource, Constants.UTF_8)); + proxy.setBufferFile(downloadFile); + dataSource = proxy.getPrivateAddress(dataSource); Log.i(TAG, "Data Source: " + dataSource); } else if(proxy != null) { proxy.stop(); diff --git a/src/github/daneren2005/dsub/service/StreamProxy.java b/src/github/daneren2005/dsub/service/StreamProxy.java deleted file mode 100644 index d660b6d3..00000000 --- a/src/github/daneren2005/dsub/service/StreamProxy.java +++ /dev/null @@ -1,311 +0,0 @@ -package github.daneren2005.dsub.service;
-
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-import java.net.URLDecoder;
-import java.net.UnknownHostException;
-import java.util.StringTokenizer;
-
-import org.apache.http.Header;
-import org.apache.http.HttpRequest;
-import org.apache.http.message.BasicHttpRequest;
-
-import android.os.AsyncTask;
-import android.os.Looper;
-import android.util.Log;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.Constants;
-
-public class StreamProxy implements Runnable {
- private static final String TAG = StreamProxy.class.getSimpleName();
-
- private Thread thread;
- private boolean isRunning;
- private ServerSocket socket;
- private int port;
- private DownloadService downloadService;
-
- public StreamProxy(DownloadService downloadService) {
-
- // Create listening socket
- try {
- socket = new ServerSocket(0, 0, InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }));
- socket.setSoTimeout(5000);
- port = socket.getLocalPort();
- this.downloadService = downloadService;
- } catch (UnknownHostException e) { // impossible
- } catch (IOException e) {
- Log.e(TAG, "IOException initializing server", e);
- }
- }
-
- public int getPort() {
- return port;
- }
-
- public void start() {
- thread = new Thread(this, "StreamProxy");
- thread.start();
- }
-
- public void stop() {
- isRunning = false;
- thread.interrupt();
- }
-
- @Override
- public void run() {
- isRunning = true;
- while (isRunning) {
- try {
- Socket client = socket.accept();
- if (client == null) {
- continue;
- }
- Log.i(TAG, "client connected");
-
- StreamToMediaPlayerTask task = new StreamToMediaPlayerTask(client);
- if (task.processRequest()) {
- new Thread(task).start();
- }
-
- } catch (SocketTimeoutException e) {
- // Do nothing
- } catch (IOException e) {
- Log.e(TAG, "Error connecting to client", e);
- }
- }
- Log.i(TAG, "Proxy interrupted. Shutting down.");
- }
-
- private class StreamToMediaPlayerTask implements Runnable {
- DownloadFile downloadFile;
- File file;
- Socket client;
- int cbSkip = 0;
-
- public StreamToMediaPlayerTask(Socket client) {
- this.client = client;
- }
-
- private HttpRequest readRequest() {
- HttpRequest request = null;
- InputStream is;
- String firstLine;
- BufferedReader reader = null;
- try {
- is = client.getInputStream();
- reader = new BufferedReader(new InputStreamReader(is), 8192);
- firstLine = reader.readLine();
- } catch (IOException e) {
- Log.e(TAG, "Error parsing request", e);
- return request;
- }
-
- if (firstLine == null) {
- Log.i(TAG, "Proxy client closed connection without a request.");
- return request;
- }
-
- StringTokenizer st = new StringTokenizer(firstLine);
- String method = st.nextToken();
- String uri = st.nextToken();
- String realUri = uri.substring(1);
- Log.i(TAG, realUri);
- request = new BasicHttpRequest(method, realUri);
-
- // Get all of the headers
- try {
- String line;
- while((line = reader.readLine()) != null && !"".equals(line)) {
- String headerName = line.substring(0, line.indexOf(':'));
- String headerValue = line.substring(line.indexOf(": ") + 2);
- request.addHeader(headerName, headerValue);
- }
- } catch(IOException e) {
- // Don't really care once past first line
- }
-
- return request;
- }
-
- public boolean processRequest() {
- HttpRequest request = readRequest();
- if (request == null) {
- return false;
- }
-
- // Read HTTP headers
- Log.i(TAG, "Processing request");
-
- String localPath;
- try {
- localPath = URLDecoder.decode(request.getRequestLine().getUri(), Constants.UTF_8);
- } catch (UnsupportedEncodingException e) {
- Log.e(TAG, "Unsupported encoding", e);
- return false;
- }
-
- Log.i(TAG, "Processing request for file " + localPath);
- downloadFile = downloadService.getCurrentPlaying();
- File partialFile = new File(localPath);
- if (!partialFile.equals(downloadFile.getPartialFile())) {
- Log.e(TAG, "File " + localPath + " does not exist");
- return false;
- }
-
- // Use either partial or complete if downloading finished while StreamProxy was idle
- file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile();
-
- // Try to get range requested
- Header rangeHeader = request.getFirstHeader("Range");
-
- if(rangeHeader != null) {
- String range = rangeHeader.getValue();
- int index = range.indexOf("=");
- if(index >= 0) {
- range = range.substring(index + 1);
-
- index = range.indexOf("-");
- if(index > 0) {
- range = range.substring(0, index);
- }
-
- cbSkip = Integer.parseInt(range);
-
- // Make sure to not try to read past where the file is downloaded
- if(cbSkip >= file.length()) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- @Override
- public void run() {
- Log.i(TAG, "Streaming song in background");
- MusicDirectory.Entry song = downloadFile.getSong();
-
- Integer contentLength = downloadFile.getContentLength();
- if(contentLength == null && downloadFile.isWorkDone()) {
- contentLength = (int)file.length();
- }
-
- // Create HTTP header
- String headers;
- if(cbSkip == 0) {
- headers = "HTTP/1.0 200 OK\r\n";
- } else {
- headers = "HTTP/1.0 206 OK\r\n;";
- headers += "Content-Range: bytes " + cbSkip + "-" + (file.length() - 1) + "/";
- if(contentLength == null) {
- headers += "*";
- } else {
- headers += contentLength;
- }
-
- Log.i(TAG, "Streaming starts from: " + cbSkip);
- }
- headers += "Content-Type: " + "application/octet-stream" + "\r\n";
-
- long fileSize;
- if(contentLength == null) {
- fileSize = downloadFile.getBitRate() * ((song.getDuration() != null) ? song.getDuration() : 0) * 1000 / 8;
- } else {
- fileSize = contentLength;
- headers += "Content-Length: " + fileSize + "\r\n";
- }
- Log.i(TAG, "Streaming fileSize: " + fileSize);
-
- headers += "Connection: close\r\n";
- headers += "\r\n";
-
- long cbToSend = fileSize - cbSkip;
- OutputStream output = null;
- byte[] buff = new byte[64 * 1024];
- try {
- output = new BufferedOutputStream(client.getOutputStream(), 32*1024);
- output.write(headers.getBytes());
-
- // Make sure to have file lock
- downloadFile.setPlaying(true);
-
- // Loop as long as there's stuff to send
- while (isRunning && !client.isClosed()) {
-
- // See if there's more to send
- int cbSentThisBatch = 0;
- if (file.exists()) {
- FileInputStream input = new FileInputStream(file);
- input.skip(cbSkip);
- int cbToSendThisBatch = input.available();
- while (cbToSendThisBatch > 0) {
- int cbToRead = Math.min(cbToSendThisBatch, buff.length);
- int cbRead = input.read(buff, 0, cbToRead);
- if (cbRead == -1) {
- break;
- }
- cbToSendThisBatch -= cbRead;
- cbToSend -= cbRead;
- output.write(buff, 0, cbRead);
- output.flush();
- cbSkip += cbRead;
- cbSentThisBatch += cbRead;
- }
- input.close();
- }
-
- // Done regardless of whether or not it thinks it is
- if(downloadFile.isWorkDone() && cbSkip >= file.length()) {
- break;
- }
-
- // If we did nothing this batch, block for a second
- if (cbSentThisBatch == 0) {
- Log.d(TAG, "Blocking until more data appears (" + cbToSend + ")");
- Thread.sleep(1000);
- }
- }
-
- // Release file lock, use of stream proxy means nothing else is using it
- downloadFile.setPlaying(false);
- }
- catch (SocketException socketException) {
- Log.e(TAG, "SocketException() thrown, proxy client has probably closed. This can exit harmlessly");
-
- // Release file lock, use of stream proxy means nothing else is using it
- downloadFile.setPlaying(false);
- }
- catch (Exception e) {
- Log.e(TAG, "Exception thrown from streaming task:");
- Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
- }
-
- // Cleanup
- try {
- if (output != null) {
- output.close();
- }
- client.close();
- }
- catch (IOException e) {
- Log.e(TAG, "IOException while cleaning up streaming task:");
- Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
- }
- }
- }
-}
|