Use new subsonic api stream call.
Signed-off-by: Yahor Berdnikau <egorr.berd@gmail.com>
This commit is contained in:
parent
ba412721ac
commit
3e2529bd25
|
@ -21,7 +21,6 @@ package org.moire.ultrasonic.service;
|
|||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.moire.ultrasonic.domain.Bookmark;
|
||||
import org.moire.ultrasonic.domain.ChatMessage;
|
||||
import org.moire.ultrasonic.domain.Genre;
|
||||
|
@ -43,11 +42,14 @@ import org.moire.ultrasonic.util.ProgressListener;
|
|||
import org.moire.ultrasonic.util.TimeLimitedCache;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import kotlin.Pair;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
|
@ -314,7 +316,7 @@ public class CachedMusicService implements MusicService
|
|||
}
|
||||
|
||||
@Override
|
||||
public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception
|
||||
public Pair<InputStream, Boolean> getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception
|
||||
{
|
||||
return musicService.getDownloadInputStream(context, song, offset, maxBitrate, task);
|
||||
}
|
||||
|
|
|
@ -29,10 +29,6 @@ import org.moire.ultrasonic.util.CancellableTask;
|
|||
import org.moire.ultrasonic.util.FileUtil;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -40,6 +36,8 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
import kotlin.Pair;
|
||||
|
||||
import static android.content.Context.POWER_SERVICE;
|
||||
import static android.os.PowerManager.ON_AFTER_RELEASE;
|
||||
import static android.os.PowerManager.SCREEN_DIM_WAKE_LOCK;
|
||||
|
@ -66,7 +64,6 @@ public class DownloadFile
|
|||
private volatile boolean isPlaying;
|
||||
private volatile boolean saveWhenDone;
|
||||
private volatile boolean completeWhenDone;
|
||||
private Integer contentLength;
|
||||
|
||||
public DownloadFile(Context context, MusicDirectory.Entry song, boolean save)
|
||||
{
|
||||
|
@ -105,11 +102,6 @@ public class DownloadFile
|
|||
return song.getBitRate() == null ? 160 : song.getBitRate();
|
||||
}
|
||||
|
||||
public Integer getContentLength()
|
||||
{
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
public synchronized void download()
|
||||
{
|
||||
FileUtil.createDirectoryForParent(saveFile);
|
||||
|
@ -369,30 +361,17 @@ public class DownloadFile
|
|||
if (compare)
|
||||
{
|
||||
// Attempt partial HTTP GET, appending to the file if it exists.
|
||||
HttpResponse response = musicService.getDownloadInputStream(context, song, partialFile.length(), bitRate, DownloadTask.this);
|
||||
Header contentLengthHeader = response.getFirstHeader("Content-Length");
|
||||
Pair<InputStream, Boolean> response = musicService
|
||||
.getDownloadInputStream(context, song, partialFile.length(), bitRate,
|
||||
DownloadTask.this);
|
||||
|
||||
if (contentLengthHeader != null)
|
||||
{
|
||||
String contentLengthString = contentLengthHeader.getValue();
|
||||
|
||||
if (contentLengthString != null)
|
||||
{
|
||||
Log.i(TAG, "Content Length: " + contentLengthString);
|
||||
contentLength = Integer.parseInt(contentLengthString);
|
||||
}
|
||||
}
|
||||
|
||||
in = response.getEntity().getContent();
|
||||
boolean partial = response.getStatusLine().getStatusCode() == HttpStatus.SC_PARTIAL_CONTENT;
|
||||
|
||||
if (partial)
|
||||
if (response.getSecond())
|
||||
{
|
||||
Log.i(TAG, String.format("Executed partial HTTP GET, skipping %d bytes", partialFile.length()));
|
||||
}
|
||||
|
||||
out = new FileOutputStream(partialFile, partial);
|
||||
long n = copy(in, out);
|
||||
out = new FileOutputStream(partialFile, response.getSecond());
|
||||
long n = copy(response.getFirst(), out);
|
||||
Log.i(TAG, String.format("Downloaded %d bytes to %s", n, partialFile));
|
||||
out.flush();
|
||||
out.close();
|
||||
|
|
|
@ -39,8 +39,11 @@ import org.moire.ultrasonic.domain.UserInfo;
|
|||
import org.moire.ultrasonic.util.CancellableTask;
|
||||
import org.moire.ultrasonic.util.ProgressListener;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import kotlin.Pair;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
|
@ -101,7 +104,11 @@ public interface MusicService
|
|||
|
||||
Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, boolean highQuality, ProgressListener progressListener) throws Exception;
|
||||
|
||||
HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception;
|
||||
/**
|
||||
* Return response {@link InputStream} and a {@link Boolean} that indicates if this response is
|
||||
* partial.
|
||||
*/
|
||||
Pair<InputStream, Boolean> getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception;
|
||||
|
||||
String getVideoUrl(Context context, String id, boolean useFlash) throws Exception;
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@ import org.apache.http.conn.scheme.SchemeRegistry;
|
|||
import org.apache.http.conn.scheme.SocketFactory;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.params.BasicHttpParams;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
|
@ -133,13 +132,13 @@ import java.io.Reader;
|
|||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import kotlin.Pair;
|
||||
import retrofit2.Response;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
@ -154,12 +153,8 @@ public class RESTMusicService implements MusicService
|
|||
|
||||
private static final int SOCKET_CONNECT_TIMEOUT = 10 * 1000;
|
||||
private static final int SOCKET_READ_TIMEOUT_DEFAULT = 10 * 1000;
|
||||
private static final int SOCKET_READ_TIMEOUT_DOWNLOAD = 30 * 1000;
|
||||
private static final int SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS = 60 * 1000;
|
||||
private static final int SOCKET_READ_TIMEOUT_GET_PLAYLIST = 60 * 1000;
|
||||
|
||||
// Allow 20 seconds extra timeout per MB offset.
|
||||
private static final double TIMEOUT_MILLIS_PER_OFFSET_BYTE = 20000.0 / 1000000.0;
|
||||
private static final int SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS = 60 * 1000;
|
||||
|
||||
/**
|
||||
* URL from which to fetch latest versions.
|
||||
|
@ -785,14 +780,7 @@ public class RESTMusicService implements MusicService
|
|||
}
|
||||
|
||||
StreamResponse response = subsonicAPIClient.getCoverArt(id, (long) size);
|
||||
if (response.hasError() || response.getStream() == null) {
|
||||
if (response.getApiError() != null) {
|
||||
throw new SubsonicRESTException(response.getApiError().getCode(), "rest error");
|
||||
} else {
|
||||
throw new IOException("Failed to make endpoint request, code: " +
|
||||
response.getRequestErrorCode());
|
||||
}
|
||||
}
|
||||
checkStreamResponseError(response);
|
||||
|
||||
if (response.getStream() == null) {
|
||||
return null; // Failed to load
|
||||
|
@ -826,47 +814,38 @@ public class RESTMusicService implements MusicService
|
|||
}
|
||||
}
|
||||
|
||||
private void checkStreamResponseError(StreamResponse response)
|
||||
throws SubsonicRESTException, IOException {
|
||||
if (response.hasError() || response.getStream() == null) {
|
||||
if (response.getApiError() != null) {
|
||||
throw new SubsonicRESTException(response.getApiError().getCode(), "rest error");
|
||||
} else {
|
||||
throw new IOException("Failed to make endpoint request, code: " +
|
||||
response.getResponseHttpCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception
|
||||
{
|
||||
|
||||
String url = Util.getRestUrl(context, "stream");
|
||||
|
||||
// Set socket read timeout. Note: The timeout increases as the offset gets larger. This is
|
||||
// to avoid the thrashing effect seen when offset is combined with transcoding/downsampling on the server.
|
||||
// In that case, the server uses a long time before sending any data, causing the client to time out.
|
||||
HttpParams params = new BasicHttpParams();
|
||||
int timeout = (int) (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE);
|
||||
HttpConnectionParams.setSoTimeout(params, timeout);
|
||||
|
||||
// Add "Range" header if offset is given.
|
||||
Collection<Header> headers = new ArrayList<Header>();
|
||||
|
||||
if (offset > 0)
|
||||
{
|
||||
headers.add(new BasicHeader("Range", String.format("bytes=%d-", offset)));
|
||||
public Pair<InputStream, Boolean> getDownloadInputStream(final Context context,
|
||||
final MusicDirectory.Entry song,
|
||||
final long offset,
|
||||
final int maxBitrate,
|
||||
final CancellableTask task)
|
||||
throws Exception {
|
||||
if (song == null) {
|
||||
throw new IllegalArgumentException("Song for download is null!");
|
||||
}
|
||||
long songOffset = offset < 0 ? 0 : offset;
|
||||
|
||||
List<String> parameterNames = asList("id", "maxBitRate");
|
||||
List<Object> parameterValues = Arrays.<Object>asList(song.getId(), maxBitrate);
|
||||
HttpResponse response = getResponseForURL(context, url, params, parameterNames, parameterValues, headers, null, task);
|
||||
StreamResponse response = subsonicAPIClient.stream(song.getId(), maxBitrate, songOffset);
|
||||
checkStreamResponseError(response);
|
||||
if (response.getStream() == null) {
|
||||
throw new IOException("Null stream response");
|
||||
}
|
||||
Boolean partial = response.getResponseHttpCode() == 206;
|
||||
|
||||
// If content type is XML, an error occurred. Get it.
|
||||
String contentType = Util.getContentType(response.getEntity());
|
||||
if (contentType != null && contentType.startsWith("text/xml"))
|
||||
{
|
||||
InputStream in = response.getEntity().getContent();
|
||||
try
|
||||
{
|
||||
new ErrorParser(context).parse(new InputStreamReader(in, Constants.UTF_8));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Util.close(in);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
return new Pair<>(response.getStream(), partial);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -980,11 +959,6 @@ public class RESTMusicService implements MusicService
|
|||
return getReader(context, progressListener, method, requestParams, Collections.<String>emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
private Reader getReader(Context context, ProgressListener progressListener, String method, HttpParams requestParams, String parameterName, Object parameterValue) throws Exception
|
||||
{
|
||||
return getReader(context, progressListener, method, requestParams, Collections.singletonList(parameterName), Collections.singletonList(parameterValue));
|
||||
}
|
||||
|
||||
private Reader getReader(Context context, ProgressListener progressListener, String method, HttpParams requestParams, List<String> parameterNames, List<Object> parameterValues) throws Exception
|
||||
{
|
||||
|
||||
|
|
Loading…
Reference in New Issue