long-term downloads resume
* recovery infrastructure * bump serialVersionUID of DownloadMission * misc cleanup in DownloadMission.java * remove unused/redundant from strings.xml
This commit is contained in:
parent
0cdfa6e377
commit
c891f2f1ed
42 changed files with 478 additions and 97 deletions
|
@ -68,6 +68,7 @@ import java.util.Locale;
|
|||
import icepick.Icepick;
|
||||
import icepick.State;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import us.shandian.giga.get.MissionRecoveryInfo;
|
||||
import us.shandian.giga.io.StoredDirectoryHelper;
|
||||
import us.shandian.giga.io.StoredFileHelper;
|
||||
import us.shandian.giga.postprocessing.Postprocessing;
|
||||
|
@ -762,12 +763,13 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
|||
}
|
||||
|
||||
Stream selectedStream;
|
||||
Stream secondaryStream = null;
|
||||
char kind;
|
||||
int threads = threadsSeekBar.getProgress() + 1;
|
||||
String[] urls;
|
||||
MissionRecoveryInfo[] recoveryInfo;
|
||||
String psName = null;
|
||||
String[] psArgs = null;
|
||||
String secondaryStreamUrl = null;
|
||||
long nearLength = 0;
|
||||
|
||||
// more download logic: select muxer, subtitle converter, etc.
|
||||
|
@ -786,12 +788,12 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
|||
kind = 'v';
|
||||
selectedStream = videoStreamsAdapter.getItem(selectedVideoIndex);
|
||||
|
||||
SecondaryStreamHelper<AudioStream> secondaryStream = videoStreamsAdapter
|
||||
SecondaryStreamHelper<AudioStream> secondary = videoStreamsAdapter
|
||||
.getAllSecondary()
|
||||
.get(wrappedVideoStreams.getStreamsList().indexOf(selectedStream));
|
||||
|
||||
if (secondaryStream != null) {
|
||||
secondaryStreamUrl = secondaryStream.getStream().getUrl();
|
||||
if (secondary != null) {
|
||||
secondaryStream = secondary.getStream();
|
||||
|
||||
if (selectedStream.getFormat() == MediaFormat.MPEG_4)
|
||||
psName = Postprocessing.ALGORITHM_MP4_FROM_DASH_MUXER;
|
||||
|
@ -803,8 +805,8 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
|||
|
||||
// set nearLength, only, if both sizes are fetched or known. This probably
|
||||
// does not work on slow networks but is later updated in the downloader
|
||||
if (secondaryStream.getSizeInBytes() > 0 && videoSize > 0) {
|
||||
nearLength = secondaryStream.getSizeInBytes() + videoSize;
|
||||
if (secondary.getSizeInBytes() > 0 && videoSize > 0) {
|
||||
nearLength = secondary.getSizeInBytes() + videoSize;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -826,13 +828,25 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
|||
return;
|
||||
}
|
||||
|
||||
if (secondaryStreamUrl == null) {
|
||||
urls = new String[]{selectedStream.getUrl()};
|
||||
if (secondaryStream == null) {
|
||||
urls = new String[]{
|
||||
selectedStream.getUrl()
|
||||
};
|
||||
recoveryInfo = new MissionRecoveryInfo[]{
|
||||
new MissionRecoveryInfo(selectedStream)
|
||||
};
|
||||
} else {
|
||||
urls = new String[]{selectedStream.getUrl(), secondaryStreamUrl};
|
||||
urls = new String[]{
|
||||
selectedStream.getUrl(), secondaryStream.getUrl()
|
||||
};
|
||||
recoveryInfo = new MissionRecoveryInfo[]{
|
||||
new MissionRecoveryInfo(selectedStream), new MissionRecoveryInfo(secondaryStream)
|
||||
};
|
||||
}
|
||||
|
||||
DownloadManagerService.startMission(context, urls, storage, kind, threads, currentInfo.getUrl(), psName, psArgs, nearLength);
|
||||
DownloadManagerService.startMission(
|
||||
context, urls, storage, kind, threads, currentInfo.getUrl(), psName, psArgs, nearLength, recoveryInfo
|
||||
);
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package us.shandian.giga.get;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.schabi.newpipe.streams.io.SharpStream;
|
||||
|
@ -151,6 +152,20 @@ public class DownloadInitializer extends Thread {
|
|||
|
||||
if (!mMission.running || Thread.interrupted()) return;
|
||||
|
||||
if (!mMission.unknownLength && mMission.recoveryInfo != null) {
|
||||
String entityTag = mConn.getHeaderField("ETAG");
|
||||
String lastModified = mConn.getHeaderField("Last-Modified");
|
||||
MissionRecoveryInfo recovery = mMission.recoveryInfo[mMission.current];
|
||||
|
||||
if (!TextUtils.isEmpty(entityTag)) {
|
||||
recovery.validateCondition = entityTag;
|
||||
} else if (!TextUtils.isEmpty(lastModified)) {
|
||||
recovery.validateCondition = lastModified;// Note: this is less precise
|
||||
} else {
|
||||
recovery.validateCondition = null;
|
||||
}
|
||||
}
|
||||
|
||||
mMission.running = false;
|
||||
break;
|
||||
} catch (InterruptedIOException | ClosedByInterruptException e) {
|
||||
|
|
|
@ -27,7 +27,7 @@ import us.shandian.giga.util.Utility;
|
|||
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
||||
|
||||
public class DownloadMission extends Mission {
|
||||
private static final long serialVersionUID = 5L;// last bump: 30 june 2019
|
||||
private static final long serialVersionUID = 6L;// last bump: 28 september 2019
|
||||
|
||||
static final int BUFFER_SIZE = 64 * 1024;
|
||||
static final int BLOCK_SIZE = 512 * 1024;
|
||||
|
@ -51,8 +51,9 @@ public class DownloadMission extends Mission {
|
|||
public static final int ERROR_INSUFFICIENT_STORAGE = 1010;
|
||||
public static final int ERROR_PROGRESS_LOST = 1011;
|
||||
public static final int ERROR_TIMEOUT = 1012;
|
||||
public static final int ERROR_RESOURCE_GONE = 1013;
|
||||
public static final int ERROR_HTTP_NO_CONTENT = 204;
|
||||
public static final int ERROR_HTTP_UNSUPPORTED_RANGE = 206;
|
||||
static final int ERROR_HTTP_FORBIDDEN = 403;
|
||||
|
||||
/**
|
||||
* The urls of the file to download
|
||||
|
@ -125,6 +126,11 @@ public class DownloadMission extends Mission {
|
|||
*/
|
||||
public int threadCount = 3;
|
||||
|
||||
/**
|
||||
* information required to recover a download
|
||||
*/
|
||||
public MissionRecoveryInfo[] recoveryInfo;
|
||||
|
||||
private transient int finishCount;
|
||||
public transient boolean running;
|
||||
public boolean enqueued;
|
||||
|
@ -132,7 +138,6 @@ public class DownloadMission extends Mission {
|
|||
public int errCode = ERROR_NOTHING;
|
||||
public Exception errObject = null;
|
||||
|
||||
public transient boolean recovered;
|
||||
public transient Handler mHandler;
|
||||
private transient boolean mWritingToFile;
|
||||
private transient boolean[] blockAcquired;
|
||||
|
@ -197,9 +202,9 @@ public class DownloadMission extends Mission {
|
|||
}
|
||||
|
||||
/**
|
||||
* Open connection
|
||||
* Opens a connection
|
||||
*
|
||||
* @param threadId id of the calling thread, used only for debug
|
||||
* @param threadId id of the calling thread, used only for debugging
|
||||
* @param rangeStart range start
|
||||
* @param rangeEnd range end
|
||||
* @return a {@link java.net.URLConnection URLConnection} linking to the URL.
|
||||
|
@ -251,7 +256,7 @@ public class DownloadMission extends Mission {
|
|||
case 204:
|
||||
case 205:
|
||||
case 207:
|
||||
throw new HttpError(conn.getResponseCode());
|
||||
throw new HttpError(statusCode);
|
||||
case 416:
|
||||
return;// let the download thread handle this error
|
||||
default:
|
||||
|
@ -270,10 +275,6 @@ public class DownloadMission extends Mission {
|
|||
synchronized void notifyProgress(long deltaLen) {
|
||||
if (!running) return;
|
||||
|
||||
if (recovered) {
|
||||
recovered = false;
|
||||
}
|
||||
|
||||
if (unknownLength) {
|
||||
length += deltaLen;// Update length before proceeding
|
||||
}
|
||||
|
@ -344,7 +345,6 @@ public class DownloadMission extends Mission {
|
|||
|
||||
if (running) {
|
||||
running = false;
|
||||
recovered = true;
|
||||
if (threads != null) selfPause();
|
||||
}
|
||||
}
|
||||
|
@ -409,12 +409,13 @@ public class DownloadMission extends Mission {
|
|||
* Start downloading with multiple threads.
|
||||
*/
|
||||
public void start() {
|
||||
if (running || isFinished()) return;
|
||||
if (running || isFinished() || urls.length < 1) return;
|
||||
|
||||
// ensure that the previous state is completely paused.
|
||||
joinForThread(init);
|
||||
int maxWait = 10000;// 10 seconds
|
||||
joinForThread(init, maxWait);
|
||||
if (threads != null) {
|
||||
for (Thread thread : threads) joinForThread(thread);
|
||||
for (Thread thread : threads) joinForThread(thread, maxWait);
|
||||
threads = null;
|
||||
}
|
||||
|
||||
|
@ -431,6 +432,11 @@ public class DownloadMission extends Mission {
|
|||
return;
|
||||
}
|
||||
|
||||
if (urls[current] == null) {
|
||||
doRecover(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (blocks == null) {
|
||||
initializer();
|
||||
return;
|
||||
|
@ -478,7 +484,6 @@ public class DownloadMission extends Mission {
|
|||
}
|
||||
|
||||
running = false;
|
||||
recovered = true;
|
||||
|
||||
if (init != null && init.isAlive()) {
|
||||
// NOTE: if start() method is running ¡will no have effect!
|
||||
|
@ -563,7 +568,7 @@ public class DownloadMission extends Mission {
|
|||
* Write this {@link DownloadMission} to the meta file asynchronously
|
||||
* if no thread is already running.
|
||||
*/
|
||||
private void writeThisToFile() {
|
||||
void writeThisToFile() {
|
||||
synchronized (LOCK) {
|
||||
if (deleted) return;
|
||||
Utility.writeToFile(metadata, DownloadMission.this);
|
||||
|
@ -667,6 +672,7 @@ public class DownloadMission extends Mission {
|
|||
* @return {@code true} is this mission its "healthy", otherwise, {@code false}
|
||||
*/
|
||||
public boolean isCorrupt() {
|
||||
if (urls.length < 1) return false;
|
||||
return (isPsFailed() || errCode == ERROR_POSTPROCESSING_HOLD) || isFinished();
|
||||
}
|
||||
|
||||
|
@ -710,6 +716,48 @@ public class DownloadMission extends Mission {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to recover the download
|
||||
*
|
||||
* @param fromError exception which require update the url from the source
|
||||
*/
|
||||
void doRecover(Exception fromError) {
|
||||
Log.i(TAG, "Attempting to recover the mission: " + storage.getName());
|
||||
|
||||
if (recoveryInfo == null) {
|
||||
if (fromError == null)
|
||||
notifyError(ERROR_RESOURCE_GONE, null);
|
||||
else
|
||||
notifyError(fromError);
|
||||
|
||||
urls = new String[0];// mark this mission as dead
|
||||
return;
|
||||
}
|
||||
|
||||
if (threads != null) {
|
||||
for (Thread thread : threads) {
|
||||
if (thread == Thread.currentThread()) continue;
|
||||
thread.interrupt();
|
||||
joinForThread(thread, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// set the current download url to null in case if the recovery
|
||||
// process is canceled. Next time start() method is called the
|
||||
// recovery will be executed, saving time
|
||||
urls[current] = null;
|
||||
|
||||
if (recoveryInfo[current].attempts >= maxRetry) {
|
||||
recoveryInfo[current].attempts = 0;
|
||||
notifyError(fromError);
|
||||
return;
|
||||
}
|
||||
|
||||
threads = new Thread[]{
|
||||
runAsync(DownloadMissionRecover.mID, new DownloadMissionRecover(this, fromError))
|
||||
};
|
||||
}
|
||||
|
||||
private boolean deleteThisFromFile() {
|
||||
synchronized (LOCK) {
|
||||
return metadata.delete();
|
||||
|
@ -749,7 +797,13 @@ public class DownloadMission extends Mission {
|
|||
return who;
|
||||
}
|
||||
|
||||
private void joinForThread(Thread thread) {
|
||||
/**
|
||||
* Waits at most {@code millis} milliseconds for the thread to die
|
||||
*
|
||||
* @param thread the desired thread
|
||||
* @param millis the time to wait in milliseconds
|
||||
*/
|
||||
private void joinForThread(Thread thread, int millis) {
|
||||
if (thread == null || !thread.isAlive()) return;
|
||||
if (thread == Thread.currentThread()) return;
|
||||
|
||||
|
@ -764,7 +818,7 @@ public class DownloadMission extends Mission {
|
|||
// start() method called quickly after pause()
|
||||
|
||||
try {
|
||||
thread.join(10000);
|
||||
thread.join(millis);
|
||||
} catch (InterruptedException e) {
|
||||
Log.d(TAG, "timeout on join : " + thread.getName());
|
||||
throw new RuntimeException("A thread is still running:\n" + thread.getName());
|
||||
|
@ -785,9 +839,9 @@ public class DownloadMission extends Mission {
|
|||
}
|
||||
}
|
||||
|
||||
static class Block {
|
||||
int position;
|
||||
int done;
|
||||
public static class Block {
|
||||
public int position;
|
||||
public int done;
|
||||
}
|
||||
|
||||
private static class Lock implements Serializable {
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
package us.shandian.giga.get;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.StreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
import java.util.List;
|
||||
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_RESOURCE_GONE;
|
||||
|
||||
public class DownloadMissionRecover extends Thread {
|
||||
private static final String TAG = "DownloadMissionRecover";
|
||||
static final int mID = -3;
|
||||
|
||||
private final DownloadMission mMission;
|
||||
private final MissionRecoveryInfo mRecovery;
|
||||
private final Exception mFromError;
|
||||
private HttpURLConnection mConn;
|
||||
|
||||
DownloadMissionRecover(DownloadMission mission, Exception originError) {
|
||||
mMission = mission;
|
||||
mFromError = originError;
|
||||
mRecovery = mission.recoveryInfo[mission.current];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mMission.source == null) {
|
||||
mMission.notifyError(mFromError);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
/*if (mMission.source.startsWith(MissionRecoveryInfo.DIRECT_SOURCE)) {
|
||||
resolve(mMission.source.substring(MissionRecoveryInfo.DIRECT_SOURCE.length()));
|
||||
return;
|
||||
}*/
|
||||
|
||||
StreamingService svr = NewPipe.getServiceByUrl(mMission.source);
|
||||
|
||||
if (svr == null) {
|
||||
throw new RuntimeException("Unknown source service");
|
||||
}
|
||||
|
||||
StreamExtractor extractor = svr.getStreamExtractor(mMission.source);
|
||||
extractor.fetchPage();
|
||||
|
||||
if (!mMission.running || super.isInterrupted()) return;
|
||||
|
||||
String url = null;
|
||||
|
||||
switch (mMission.kind) {
|
||||
case 'a':
|
||||
for (AudioStream audio : extractor.getAudioStreams()) {
|
||||
if (audio.average_bitrate == mRecovery.desiredBitrate && audio.getFormat() == mRecovery.format) {
|
||||
url = audio.getUrl();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
List<VideoStream> videoStreams;
|
||||
if (mRecovery.desired2)
|
||||
videoStreams = extractor.getVideoOnlyStreams();
|
||||
else
|
||||
videoStreams = extractor.getVideoStreams();
|
||||
for (VideoStream video : videoStreams) {
|
||||
if (video.resolution.equals(mRecovery.desired) && video.getFormat() == mRecovery.format) {
|
||||
url = video.getUrl();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
for (SubtitlesStream subtitles : extractor.getSubtitles(mRecovery.format)) {
|
||||
String tag = subtitles.getLanguageTag();
|
||||
if (tag.equals(mRecovery.desired) && subtitles.isAutoGenerated() == mRecovery.desired2) {
|
||||
url = subtitles.getURL();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unknown stream type");
|
||||
}
|
||||
|
||||
resolve(url);
|
||||
} catch (Exception e) {
|
||||
if (!mMission.running || e instanceof ClosedByInterruptException) return;
|
||||
mRecovery.attempts++;
|
||||
mMission.notifyError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void resolve(String url) throws IOException, DownloadMission.HttpError {
|
||||
if (mRecovery.validateCondition == null) {
|
||||
Log.w(TAG, "validation condition not defined, the resource can be stale");
|
||||
}
|
||||
|
||||
if (mMission.unknownLength || mRecovery.validateCondition == null) {
|
||||
recover(url, false);
|
||||
return;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
////// Validate the http resource doing a range request
|
||||
/////////////////////
|
||||
try {
|
||||
mConn = mMission.openConnection(url, mID, mMission.length - 10, mMission.length);
|
||||
mConn.setRequestProperty("If-Range", mRecovery.validateCondition);
|
||||
mMission.establishConnection(mID, mConn);
|
||||
|
||||
int code = mConn.getResponseCode();
|
||||
|
||||
switch (code) {
|
||||
case 200:
|
||||
case 413:
|
||||
// stale
|
||||
recover(url, true);
|
||||
return;
|
||||
case 206:
|
||||
// in case of validation using the Last-Modified date, check the resource length
|
||||
long[] contentRange = parseContentRange(mConn.getHeaderField("Content-Range"));
|
||||
boolean lengthMismatch = contentRange[2] != -1 && contentRange[2] != mMission.length;
|
||||
|
||||
recover(url, lengthMismatch);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new DownloadMission.HttpError(code);
|
||||
} catch (Exception e) {
|
||||
if (!mMission.running || e instanceof ClosedByInterruptException) return;
|
||||
throw e;
|
||||
} finally {
|
||||
this.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private void recover(String url, boolean stale) {
|
||||
Log.i(TAG,
|
||||
String.format("download recovered name=%s isStale=%s url=%s", mMission.storage.getName(), stale, url)
|
||||
);
|
||||
|
||||
if (url == null) {
|
||||
mMission.notifyError(ERROR_RESOURCE_GONE, null);
|
||||
return;
|
||||
}
|
||||
|
||||
mMission.urls[mMission.current] = url;
|
||||
mRecovery.attempts = 0;
|
||||
|
||||
if (stale) {
|
||||
mMission.resetState(false, false, DownloadMission.ERROR_NOTHING);
|
||||
}
|
||||
|
||||
mMission.writeThisToFile();
|
||||
|
||||
if (!mMission.running || super.isInterrupted()) return;
|
||||
|
||||
mMission.running = false;
|
||||
mMission.start();
|
||||
}
|
||||
|
||||
private long[] parseContentRange(String value) {
|
||||
long[] range = new long[3];
|
||||
|
||||
if (value == null) {
|
||||
// this never should happen
|
||||
return range;
|
||||
}
|
||||
|
||||
try {
|
||||
value = value.trim();
|
||||
|
||||
if (!value.startsWith("bytes")) {
|
||||
return range;// unknown range type
|
||||
}
|
||||
|
||||
int space = value.lastIndexOf(' ') + 1;
|
||||
int dash = value.indexOf('-', space) + 1;
|
||||
int bar = value.indexOf('/', dash);
|
||||
|
||||
// start
|
||||
range[0] = Long.parseLong(value.substring(space, dash - 1));
|
||||
|
||||
// end
|
||||
range[1] = Long.parseLong(value.substring(dash, bar));
|
||||
|
||||
// resource length
|
||||
value = value.substring(bar + 1);
|
||||
if (value.equals("*")) {
|
||||
range[2] = -1;// unknown length received from the server but should be valid
|
||||
} else {
|
||||
range[2] = Long.parseLong(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupt() {
|
||||
super.interrupt();
|
||||
if (mConn != null) {
|
||||
try {
|
||||
mConn.disconnect();
|
||||
} catch (Exception e) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,8 +10,10 @@ import java.net.HttpURLConnection;
|
|||
import java.nio.channels.ClosedByInterruptException;
|
||||
|
||||
import us.shandian.giga.get.DownloadMission.Block;
|
||||
import us.shandian.giga.get.DownloadMission.HttpError;
|
||||
|
||||
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_HTTP_FORBIDDEN;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -19,7 +21,7 @@ import static org.schabi.newpipe.BuildConfig.DEBUG;
|
|||
* an error occurs or the process is stopped.
|
||||
*/
|
||||
public class DownloadRunnable extends Thread {
|
||||
private static final String TAG = DownloadRunnable.class.getSimpleName();
|
||||
private static final String TAG = "DownloadRunnable";
|
||||
|
||||
private final DownloadMission mMission;
|
||||
private final int mId;
|
||||
|
@ -41,13 +43,7 @@ public class DownloadRunnable extends Thread {
|
|||
public void run() {
|
||||
boolean retry = false;
|
||||
Block block = null;
|
||||
|
||||
int retryCount = 0;
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":recovered: " + mMission.recovered);
|
||||
}
|
||||
|
||||
SharpStream f;
|
||||
|
||||
try {
|
||||
|
@ -133,6 +129,17 @@ public class DownloadRunnable extends Thread {
|
|||
} catch (Exception e) {
|
||||
if (!mMission.running || e instanceof ClosedByInterruptException) break;
|
||||
|
||||
if (e instanceof HttpError && ((HttpError) e).statusCode == ERROR_HTTP_FORBIDDEN) {
|
||||
// for youtube streams. The url has expired, recover
|
||||
f.close();
|
||||
|
||||
if (mId == 1) {
|
||||
// only the first thread will execute the recovery procedure
|
||||
mMission.doRecover(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (retryCount++ >= mMission.maxRetry) {
|
||||
mMission.notifyError(e);
|
||||
break;
|
||||
|
@ -144,11 +151,7 @@ public class DownloadRunnable extends Thread {
|
|||
}
|
||||
}
|
||||
|
||||
try {
|
||||
f.close();
|
||||
} catch (Exception err) {
|
||||
// ¿ejected media storage? ¿file deleted? ¿storage ran out of space?
|
||||
}
|
||||
f.close();
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "thread " + mId + " exited from main download loop");
|
||||
|
|
|
@ -10,9 +10,11 @@ import java.io.InputStream;
|
|||
import java.net.HttpURLConnection;
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
|
||||
import us.shandian.giga.get.DownloadMission.HttpError;
|
||||
import us.shandian.giga.util.Utility;
|
||||
|
||||
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_HTTP_FORBIDDEN;
|
||||
|
||||
/**
|
||||
* Single-threaded fallback mode
|
||||
|
@ -85,7 +87,7 @@ public class DownloadRunnableFallback extends Thread {
|
|||
|
||||
mIs = mConn.getInputStream();
|
||||
|
||||
byte[] buf = new byte[64 * 1024];
|
||||
byte[] buf = new byte[DownloadMission.BUFFER_SIZE];
|
||||
int len = 0;
|
||||
|
||||
while (mMission.running && (len = mIs.read(buf, 0, buf.length)) != -1) {
|
||||
|
@ -103,6 +105,13 @@ public class DownloadRunnableFallback extends Thread {
|
|||
|
||||
if (!mMission.running || e instanceof ClosedByInterruptException) return;
|
||||
|
||||
if (e instanceof HttpError && ((HttpError) e).statusCode == ERROR_HTTP_FORBIDDEN) {
|
||||
// for youtube streams. The url has expired, recover
|
||||
mMission.doRecover(e);
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRetryCount++ >= mMission.maxRetry) {
|
||||
mMission.notifyError(e);
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
package us.shandian.giga.get;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.Stream;
|
||||
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class MissionRecoveryInfo implements Serializable, Parcelable {
|
||||
private static final long serialVersionUID = 0L;
|
||||
//public static final String DIRECT_SOURCE = "direct-source://";
|
||||
|
||||
public MediaFormat format;
|
||||
String desired;
|
||||
boolean desired2;
|
||||
int desiredBitrate;
|
||||
|
||||
transient int attempts = 0;
|
||||
|
||||
String validateCondition = null;
|
||||
|
||||
public MissionRecoveryInfo(@NonNull Stream stream) {
|
||||
if (stream instanceof AudioStream) {
|
||||
desiredBitrate = ((AudioStream) stream).average_bitrate;
|
||||
desired2 = false;
|
||||
} else if (stream instanceof VideoStream) {
|
||||
desired = ((VideoStream) stream).getResolution();
|
||||
desired2 = ((VideoStream) stream).isVideoOnly();
|
||||
} else if (stream instanceof SubtitlesStream) {
|
||||
desired = ((SubtitlesStream) stream).getLanguageTag();
|
||||
desired2 = ((SubtitlesStream) stream).isAutoGenerated();
|
||||
} else {
|
||||
throw new RuntimeException("Unknown stream kind");
|
||||
}
|
||||
|
||||
format = stream.getFormat();
|
||||
if (format == null) throw new NullPointerException("Stream format cannot be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeInt(this.format.ordinal());
|
||||
parcel.writeString(this.desired);
|
||||
parcel.writeInt(this.desired2 ? 0x01 : 0x00);
|
||||
parcel.writeInt(this.desiredBitrate);
|
||||
parcel.writeString(this.validateCondition);
|
||||
}
|
||||
|
||||
private MissionRecoveryInfo(Parcel parcel) {
|
||||
this.format = MediaFormat.values()[parcel.readInt()];
|
||||
this.desired = parcel.readString();
|
||||
this.desired2 = parcel.readInt() != 0x00;
|
||||
this.desiredBitrate = parcel.readInt();
|
||||
this.validateCondition = parcel.readString();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<MissionRecoveryInfo> CREATOR = new Parcelable.Creator<MissionRecoveryInfo>() {
|
||||
@Override
|
||||
public MissionRecoveryInfo createFromParcel(Parcel source) {
|
||||
return new MissionRecoveryInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MissionRecoveryInfo[] newArray(int size) {
|
||||
return new MissionRecoveryInfo[size];
|
||||
}
|
||||
};
|
||||
}
|
|
@ -177,7 +177,6 @@ public class DownloadManager {
|
|||
mis.psAlgorithm.setTemporalDir(pickAvailableTemporalDir(ctx));
|
||||
}
|
||||
|
||||
mis.recovered = exists;
|
||||
mis.metadata = sub;
|
||||
mis.maxRetry = mPrefMaxRetry;
|
||||
mis.mHandler = mHandler;
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.os.Handler;
|
|||
import android.os.Handler.Callback;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Parcelable;
|
||||
import android.preference.PreferenceManager;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -40,8 +41,11 @@ import org.schabi.newpipe.player.helper.LockManager;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import us.shandian.giga.get.DownloadMission;
|
||||
import us.shandian.giga.get.MissionRecoveryInfo;
|
||||
import us.shandian.giga.io.StoredDirectoryHelper;
|
||||
import us.shandian.giga.io.StoredFileHelper;
|
||||
import us.shandian.giga.postprocessing.Postprocessing;
|
||||
|
@ -73,6 +77,7 @@ public class DownloadManagerService extends Service {
|
|||
private static final String EXTRA_PATH = "DownloadManagerService.extra.storagePath";
|
||||
private static final String EXTRA_PARENT_PATH = "DownloadManagerService.extra.storageParentPath";
|
||||
private static final String EXTRA_STORAGE_TAG = "DownloadManagerService.extra.storageTag";
|
||||
private static final String EXTRA_RECOVERY_INFO = "DownloadManagerService.extra.recoveryInfo";
|
||||
|
||||
private static final String ACTION_RESET_DOWNLOAD_FINISHED = APPLICATION_ID + ".reset_download_finished";
|
||||
private static final String ACTION_OPEN_DOWNLOADS_FINISHED = APPLICATION_ID + ".open_downloads_finished";
|
||||
|
@ -364,18 +369,20 @@ public class DownloadManagerService extends Service {
|
|||
/**
|
||||
* Start a new download mission
|
||||
*
|
||||
* @param context the activity context
|
||||
* @param urls the list of urls to download
|
||||
* @param storage where the file is saved
|
||||
* @param kind type of file (a: audio v: video s: subtitle ?: file-extension defined)
|
||||
* @param threads the number of threads maximal used to download chunks of the file.
|
||||
* @param psName the name of the required post-processing algorithm, or {@code null} to ignore.
|
||||
* @param source source url of the resource
|
||||
* @param psArgs the arguments for the post-processing algorithm.
|
||||
* @param nearLength the approximated final length of the file
|
||||
* @param context the activity context
|
||||
* @param urls array of urls to download
|
||||
* @param storage where the file is saved
|
||||
* @param kind type of file (a: audio v: video s: subtitle ?: file-extension defined)
|
||||
* @param threads the number of threads maximal used to download chunks of the file.
|
||||
* @param psName the name of the required post-processing algorithm, or {@code null} to ignore.
|
||||
* @param source source url of the resource
|
||||
* @param psArgs the arguments for the post-processing algorithm.
|
||||
* @param nearLength the approximated final length of the file
|
||||
* @param recoveryInfo array of MissionRecoveryInfo, in case is required recover the download
|
||||
*/
|
||||
public static void startMission(Context context, String[] urls, StoredFileHelper storage, char kind,
|
||||
int threads, String source, String psName, String[] psArgs, long nearLength) {
|
||||
public static void startMission(Context context, String[] urls, StoredFileHelper storage,
|
||||
char kind, int threads, String source, String psName,
|
||||
String[] psArgs, long nearLength, MissionRecoveryInfo[] recoveryInfo) {
|
||||
Intent intent = new Intent(context, DownloadManagerService.class);
|
||||
intent.setAction(Intent.ACTION_RUN);
|
||||
intent.putExtra(EXTRA_URLS, urls);
|
||||
|
@ -385,6 +392,7 @@ public class DownloadManagerService extends Service {
|
|||
intent.putExtra(EXTRA_POSTPROCESSING_NAME, psName);
|
||||
intent.putExtra(EXTRA_POSTPROCESSING_ARGS, psArgs);
|
||||
intent.putExtra(EXTRA_NEAR_LENGTH, nearLength);
|
||||
intent.putExtra(EXTRA_RECOVERY_INFO, recoveryInfo);
|
||||
|
||||
intent.putExtra(EXTRA_PARENT_PATH, storage.getParentUri());
|
||||
intent.putExtra(EXTRA_PATH, storage.getUri());
|
||||
|
@ -404,6 +412,7 @@ public class DownloadManagerService extends Service {
|
|||
String source = intent.getStringExtra(EXTRA_SOURCE);
|
||||
long nearLength = intent.getLongExtra(EXTRA_NEAR_LENGTH, 0);
|
||||
String tag = intent.getStringExtra(EXTRA_STORAGE_TAG);
|
||||
Parcelable[] parcelRecovery = intent.getParcelableArrayExtra(EXTRA_RECOVERY_INFO);
|
||||
|
||||
StoredFileHelper storage;
|
||||
try {
|
||||
|
@ -418,10 +427,15 @@ public class DownloadManagerService extends Service {
|
|||
else
|
||||
ps = Postprocessing.getAlgorithm(psName, psArgs);
|
||||
|
||||
MissionRecoveryInfo[] recovery = new MissionRecoveryInfo[parcelRecovery.length];
|
||||
for (int i = 0; i < parcelRecovery.length; i++)
|
||||
recovery[i] = (MissionRecoveryInfo) parcelRecovery[i];
|
||||
|
||||
final DownloadMission mission = new DownloadMission(urls, storage, kind, ps);
|
||||
mission.threadCount = threads;
|
||||
mission.source = source;
|
||||
mission.nearLength = nearLength;
|
||||
mission.recoveryInfo = recovery;
|
||||
|
||||
if (ps != null)
|
||||
ps.setTemporalDir(DownloadManager.pickAvailableTemporalDir(this));
|
||||
|
|
|
@ -62,7 +62,6 @@ import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
|||
import static us.shandian.giga.get.DownloadMission.ERROR_CONNECT_HOST;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_FILE_CREATION;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_HTTP_NO_CONTENT;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_HTTP_UNSUPPORTED_RANGE;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_INSUFFICIENT_STORAGE;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_NOTHING;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_PATH_CREATION;
|
||||
|
@ -71,6 +70,7 @@ import static us.shandian.giga.get.DownloadMission.ERROR_POSTPROCESSING;
|
|||
import static us.shandian.giga.get.DownloadMission.ERROR_POSTPROCESSING_HOLD;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_POSTPROCESSING_STOPPED;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_PROGRESS_LOST;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_RESOURCE_GONE;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_SSL_EXCEPTION;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_TIMEOUT;
|
||||
import static us.shandian.giga.get.DownloadMission.ERROR_UNKNOWN_EXCEPTION;
|
||||
|
@ -430,7 +430,7 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
|
|||
|
||||
switch (mission.errCode) {
|
||||
case 416:
|
||||
msg = R.string.error_http_requested_range_not_satisfiable;
|
||||
msg = R.string.error_http_unsupported_range;
|
||||
break;
|
||||
case 404:
|
||||
msg = R.string.error_http_not_found;
|
||||
|
@ -443,9 +443,6 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
|
|||
case ERROR_HTTP_NO_CONTENT:
|
||||
msg = R.string.error_http_no_content;
|
||||
break;
|
||||
case ERROR_HTTP_UNSUPPORTED_RANGE:
|
||||
msg = R.string.error_http_unsupported_range;
|
||||
break;
|
||||
case ERROR_PATH_CREATION:
|
||||
msg = R.string.error_path_creation;
|
||||
break;
|
||||
|
@ -480,6 +477,9 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
|
|||
case ERROR_TIMEOUT:
|
||||
msg = R.string.error_timeout;
|
||||
break;
|
||||
case ERROR_RESOURCE_GONE:
|
||||
msg = R.string.error_download_resource_gone;
|
||||
break;
|
||||
default:
|
||||
if (mission.errCode >= 100 && mission.errCode < 600) {
|
||||
msgEx = "HTTP " + mission.errCode;
|
||||
|
@ -859,7 +859,7 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
|
|||
|
||||
delete.setVisible(true);
|
||||
|
||||
boolean flag = !mission.isPsFailed();
|
||||
boolean flag = !mission.isPsFailed() && mission.urls.length > 0;
|
||||
start.setVisible(flag);
|
||||
queue.setVisible(flag);
|
||||
}
|
||||
|
|
|
@ -468,7 +468,6 @@
|
|||
<string name="error_connect_host">لا يمكن الاتصال بالخادم</string>
|
||||
<string name="error_http_no_content">الخادم لايقوم بإرسال البيانات</string>
|
||||
<string name="error_http_unsupported_range">الخادم لا يقبل التنزيل المتعدد، إعادة المحاولة مع @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">عدم استيفاء النطاق المطلوب</string>
|
||||
<string name="error_http_not_found">غير موجود</string>
|
||||
<string name="error_postprocessing_failed">فشلت المعالجة الاولية</string>
|
||||
<string name="clear_finished_download">حذف التنزيلات المنتهية</string>
|
||||
|
|
|
@ -455,7 +455,6 @@
|
|||
<string name="error_connect_host">Немагчыма злучыцца з серверам</string>
|
||||
<string name="error_http_no_content">Не атрымалася атрымаць дадзеныя з сервера</string>
|
||||
<string name="error_http_unsupported_range">Сервер не падтрымлівае шматструменную загрузку, паспрабуйце з @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Запытаны дыяпазон недапушчальны</string>
|
||||
<string name="error_http_not_found">Не знойдзена</string>
|
||||
<string name="error_postprocessing_failed">Пасляапрацоўка не ўдалася</string>
|
||||
<string name="clear_finished_download">Ачысціць завершаныя</string>
|
||||
|
|
|
@ -460,7 +460,6 @@
|
|||
<string name="app_update_notification_content_title">NewPipe 更新可用!</string>
|
||||
<string name="error_path_creation">无法创建目标文件夹</string>
|
||||
<string name="error_http_unsupported_range">服务器不接受多线程下载, 请使用 @string/msg_threads = 1重试</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">请求范围无法满足</string>
|
||||
<string name="msg_pending_downloads">继续进行%s个待下载转移</string>
|
||||
<string name="pause_downloads_on_mobile_desc">切换至移动数据时有用,尽管一些下载无法被暂停</string>
|
||||
<string name="show_comments_title">显示评论</string>
|
||||
|
|
|
@ -463,7 +463,6 @@ otevření ve vyskakovacím okně</string>
|
|||
<string name="error_connect_host">Nelze se připojit k serveru</string>
|
||||
<string name="error_http_no_content">Server neposílá data</string>
|
||||
<string name="error_http_unsupported_range">Server neakceptuje vícevláknové stahování, opakujte akci s @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Požadovaný rozsah nelze splnit</string>
|
||||
<string name="error_http_not_found">Nenalezeno</string>
|
||||
<string name="error_postprocessing_failed">Post-processing selhal</string>
|
||||
<string name="clear_finished_download">Vyčistit dokončená stahování</string>
|
||||
|
|
|
@ -380,7 +380,6 @@
|
|||
<string name="error_connect_host">Kan ikke forbinde til serveren</string>
|
||||
<string name="error_http_no_content">Serveren sender ikke data</string>
|
||||
<string name="error_http_unsupported_range">Serveren accepterer ikke multitrådede downloads; prøv igen med @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Det anmodede interval er ikke gyldigt</string>
|
||||
<string name="error_http_not_found">Ikke fundet</string>
|
||||
<string name="error_postprocessing_failed">Efterbehandling fejlede</string>
|
||||
<string name="stop">Stop</string>
|
||||
|
|
|
@ -454,7 +454,6 @@
|
|||
<string name="error_connect_host">Kann nicht mit dem Server verbinden</string>
|
||||
<string name="error_http_no_content">Der Server sendet keine Daten</string>
|
||||
<string name="error_http_unsupported_range">Der Server erlaubt kein mehrfädiges Herunterladen – wiederhole mit @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Gewünschter Bereich ist nicht verfügbar</string>
|
||||
<string name="error_http_not_found">Nicht gefunden</string>
|
||||
<string name="error_postprocessing_failed">Nachbearbeitung fehlgeschlagen</string>
|
||||
<string name="clear_finished_download">Um fertige Downloads bereinigen</string>
|
||||
|
|
|
@ -456,7 +456,6 @@
|
|||
<string name="error_connect_host">Αδυναμία σύνδεσης με τον εξυπηρετητή</string>
|
||||
<string name="error_http_no_content">Ο εξυπηρετητής δεν μπορεί να στείλει τα δεδομένα</string>
|
||||
<string name="error_http_unsupported_range">Ο εξυπηρετητής δέν υποστηρίζει πολυνηματικές λήψεις, ξαναπροσπαθήστε με @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Το ζητούμενο εύρος δεν μπορεί να εξυπηρετηθεί</string>
|
||||
<string name="error_http_not_found">Δεν βρέθηκε</string>
|
||||
<string name="error_postprocessing_failed">Μετεπεξεργασία απέτυχε</string>
|
||||
<string name="clear_finished_download">Εκκαθάριση ολοκληρωμένων λήψεων</string>
|
||||
|
|
|
@ -351,8 +351,8 @@
|
|||
\n3. Inicie sesión cuando se le pida
|
||||
\n4. Copie la URL del perfil a la que fue redireccionado.</string>
|
||||
<string name="import_soundcloud_instructions_hint">suID, soundcloud.com/suID</string>
|
||||
<string name="import_network_expensive_warning">Observe que esta operación puede causar un uso intensivo de la red.
|
||||
\n
|
||||
<string name="import_network_expensive_warning">Observe que esta operación puede causar un uso intensivo de la red.
|
||||
\n
|
||||
\n¿Quiere continuar\?</string>
|
||||
<string name="download_thumbnail_title">Cargar miniaturas</string>
|
||||
<string name="download_thumbnail_summary">Desactívela para evitar la carga de miniaturas y ahorrar datos y memoria. Se vaciará la antememoria de imágenes en la memoria volátil y en el disco.</string>
|
||||
|
@ -444,8 +444,8 @@
|
|||
<string name="error_ssl_exception">Fallo la conexión segura</string>
|
||||
<string name="error_unknown_host">No se pudo encontrar el servidor</string>
|
||||
<string name="error_connect_host">No se puede conectar con el servidor</string>
|
||||
<string name="error_http_no_content">El servidor no está enviando datos</string>
|
||||
<string name="error_http_unsupported_range">El servidor no acepta descargas multiproceso; intente de nuevo con @string/msg_threads = 1</string>
|
||||
<string name="error_http_no_content">El servidor no devolvio datos</string>
|
||||
<string name="error_http_unsupported_range">El servidor no acepta descargas multi-hilos, intente de nuevo con @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">No se puede satisfacer el intervalo seleccionado</string>
|
||||
<string name="error_http_not_found">No encontrado</string>
|
||||
<string name="error_postprocessing_failed">Falló el posprocesamiento</string>
|
||||
|
@ -453,6 +453,7 @@
|
|||
<string name="error_insufficient_storage">No hay suficiente espacio disponible en el dispositivo</string>
|
||||
<string name="error_progress_lost">Se perdió el progreso porque el archivo fue eliminado</string>
|
||||
<string name="error_timeout">Tiempo de espera excedido</string>
|
||||
<string name="error_download_resource_gone">El recurso solicitado ya no esta disponible</string>
|
||||
<string name="downloads_storage_ask_title">Preguntar dónde descargar</string>
|
||||
<string name="downloads_storage_ask_summary">Se preguntará dónde guardar cada descarga</string>
|
||||
<string name="downloads_storage_ask_summary_kitkat">Se le preguntará dónde guardar cada descarga.
|
||||
|
|
|
@ -457,7 +457,6 @@
|
|||
<string name="error_connect_host">Serveriga ei saadud ühendust</string>
|
||||
<string name="error_http_no_content">Server ei saada andmeid</string>
|
||||
<string name="error_http_unsupported_range">Server ei toeta mitmelõimelisi allalaadimisi. Proovi uuesti kasutades @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Taotletud vahemik ei ole rahuldatav</string>
|
||||
<string name="error_http_not_found">Ei leitud</string>
|
||||
<string name="error_postprocessing_failed">Järeltöötlemine nurjus</string>
|
||||
<string name="clear_finished_download">Eemalda lõpetatud allalaadimised</string>
|
||||
|
|
|
@ -456,7 +456,6 @@
|
|||
<string name="error_connect_host">Ezin da zerbitzariarekin konektatu</string>
|
||||
<string name="error_http_no_content">Zerbitzariak ez du daturik bidaltzen</string>
|
||||
<string name="error_http_unsupported_range">Zerbitzariak ez ditu hainbat hariko deskargak onartzen, saiatu @string/msg_threads = 1 erabilita</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Eskatutako barrutia ezin da bete</string>
|
||||
<string name="error_http_not_found">Ez aurkitua</string>
|
||||
<string name="error_postprocessing_failed">Post-prozesuak huts egin du</string>
|
||||
<string name="clear_finished_download">Garbitu amaitutako deskargak</string>
|
||||
|
|
|
@ -467,7 +467,6 @@
|
|||
<string name="saved_tabs_invalid_json">Utilisation des onglets par défaut, erreur lors de la lecture des onglets enregistrés</string>
|
||||
<string name="error_http_unsupported_range">Le serveur n’accepte pas les téléchargements multi-fils, veuillez réessayer avec @string/msg_threads = 1</string>
|
||||
<string name="msg_pending_downloads">Continuer vos %s transferts en attente depuis Téléchargement</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Le domaine désiré n\'est pas disponible</string>
|
||||
<string name="show_comments_title">Afficher les commentaires</string>
|
||||
<string name="show_comments_summary">Désactiver pour ne pas afficher les commentaires</string>
|
||||
<string name="autoplay_title">Lecture automatique</string>
|
||||
|
|
|
@ -461,7 +461,6 @@
|
|||
<string name="error_connect_host">לא ניתן להתחבר לשרת</string>
|
||||
<string name="error_http_no_content">השרת לא שולח נתונים</string>
|
||||
<string name="error_http_unsupported_range">"השרת לא מקבל הורדות רב ערוציות, מוטב לנסות שוב עם @string/msg_threads = 1 "</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">הטווח המבוקש לא מתאים</string>
|
||||
<string name="error_http_not_found">לא נמצא</string>
|
||||
<string name="error_postprocessing_failed">העיבוד המאוחר נכשל</string>
|
||||
<string name="clear_finished_download">פינוי ההורדות שהסתיימו</string>
|
||||
|
|
|
@ -454,7 +454,6 @@
|
|||
<string name="error_connect_host">Nije moguće povezati se s serverom</string>
|
||||
<string name="error_http_no_content">Server ne šalje podatke</string>
|
||||
<string name="error_http_unsupported_range">Poslužitelj ne prihvaća preuzimanja s više niti, pokušaj ponovo s @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Traženi raspon nije zadovoljavajući</string>
|
||||
<string name="error_http_not_found">Nije pronađeno</string>
|
||||
<string name="error_postprocessing_failed">Naknadna obrada nije uspjela</string>
|
||||
<string name="clear_finished_download">Obriši završena preuzimanja</string>
|
||||
|
|
|
@ -450,7 +450,6 @@
|
|||
<string name="error_connect_host">Tidak dapat terhubung ke server</string>
|
||||
<string name="error_http_no_content">Server tidak mengirim data</string>
|
||||
<string name="error_http_unsupported_range">Server tidak menerima unduhan multi-utas, coba lagi dengan @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Rentang yang diminta tidak memuaskan</string>
|
||||
<string name="error_http_not_found">Tidak ditemukan</string>
|
||||
<string name="error_postprocessing_failed">Pengolahan-pasca gagal</string>
|
||||
<string name="clear_finished_download">Hapus unduhan yang sudah selesai</string>
|
||||
|
|
|
@ -454,7 +454,6 @@
|
|||
<string name="error_connect_host">Impossibile connettersi al server</string>
|
||||
<string name="error_http_no_content">Il server non invia dati</string>
|
||||
<string name="error_http_unsupported_range">Il server non accetta download multipli, riprovare con @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Intervallo richiesto non soddisfatto</string>
|
||||
<string name="error_http_not_found">Non trovato</string>
|
||||
<string name="error_postprocessing_failed">Post-processing fallito</string>
|
||||
<string name="clear_finished_download">Pulisci i download completati</string>
|
||||
|
|
|
@ -440,7 +440,6 @@
|
|||
<string name="error_connect_host">サーバに接続できません</string>
|
||||
<string name="error_http_no_content">サーバがデータを送信していません</string>
|
||||
<string name="error_http_unsupported_range">サーバが同時接続ダウンロードを受け付けません。再試行してください @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">必要な範囲が満たされていません</string>
|
||||
<string name="error_http_not_found">見つかりません</string>
|
||||
<string name="error_postprocessing_failed">保存処理に失敗しました</string>
|
||||
<string name="clear_finished_download">完了済みを一覧から削除します</string>
|
||||
|
|
|
@ -451,7 +451,6 @@
|
|||
<string name="error_connect_host">서버에 접속할 수 없습니다</string>
|
||||
<string name="error_http_no_content">서버가 데이터를 전송하지 않고 있습니다</string>
|
||||
<string name="error_http_unsupported_range">서버가 다중 스레드 다운로드를 받아들이지 않습니다, @string/msg_threads = 1 를 사용해 다시 시도해보세요</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">요청된 HTTP 범위가 충분하지 않습니다</string>
|
||||
<string name="error_http_not_found">HTTP 찾을 수 없습니다</string>
|
||||
<string name="error_postprocessing_failed">후처리 작업이 실패하였습니다</string>
|
||||
<string name="clear_finished_download">완료된 다운로드 비우기</string>
|
||||
|
|
|
@ -450,7 +450,6 @@
|
|||
<string name="error_connect_host">Tidak dapat menyambung ke server</string>
|
||||
<string name="error_http_no_content">Server tidak menghantar data</string>
|
||||
<string name="error_http_unsupported_range">Server tidak menerima muat turun berbilang thread, cuba lagi dengan @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Julat yang diminta tidak memuaskan</string>
|
||||
<string name="error_http_not_found">Tidak ditemui</string>
|
||||
<string name="error_postprocessing_failed">Pemprosesan-pasca gagal</string>
|
||||
<string name="clear_finished_download">Hapuskan senarai muat turun yang selesai</string>
|
||||
|
|
|
@ -496,7 +496,7 @@
|
|||
<string name="pause_downloads">Sett nedlastinger på pause</string>
|
||||
<string name="downloads_storage_ask_title">Spør om hvor ting skal lastes ned til</string>
|
||||
<string name="downloads_storage_ask_summary">Du vil bli spurt om hvor hver nedlasting skal plasseres</string>
|
||||
<string name="downloads_storage_ask_summary_kitkat">Du vil bli spurt om hvor hver nedlasting skal plasseres.
|
||||
<string name="downloads_storage_ask_summary_kitkat">Du vil bli spurt om hvor hver nedlasting skal plasseres.
|
||||
\nSkru på SAF hvis du vil laste ned til eksternt SD-kort</string>
|
||||
<string name="downloads_storage_use_saf_title">Bruk SAF</string>
|
||||
<string name="downloads_storage_use_saf_summary">Lagringstilgangsrammeverk (SAF) tillater nedlastinger til eksternt SD-kort.
|
||||
|
|
|
@ -454,7 +454,6 @@
|
|||
<string name="error_connect_host">Kan geen verbinding maken met de server</string>
|
||||
<string name="error_http_no_content">De server verzendt geen gegevens</string>
|
||||
<string name="error_http_unsupported_range">De server aanvaardt geen meerdradige downloads, probeert het opnieuw met @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Gevraagd bereik niet beschikbaar</string>
|
||||
<string name="error_http_not_found">Niet gevonden</string>
|
||||
<string name="error_postprocessing_failed">Nabewerking mislukt</string>
|
||||
<string name="clear_finished_download">Voltooide downloads wissen</string>
|
||||
|
|
|
@ -454,7 +454,6 @@
|
|||
<string name="error_connect_host">Kan niet met de server verbinden</string>
|
||||
<string name="error_http_no_content">De server verzendt geen gegevens</string>
|
||||
<string name="error_http_unsupported_range">De server accepteert geen multi-threaded downloads, probeer het opnieuw met @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Gevraagde bereik niet beschikbaar</string>
|
||||
<string name="error_http_not_found">Niet gevonden</string>
|
||||
<string name="error_postprocessing_failed">Nabewerking mislukt</string>
|
||||
<string name="clear_finished_download">Voltooide downloads wissen</string>
|
||||
|
|
|
@ -450,7 +450,6 @@
|
|||
<string name="error_connect_host">ਸਰਵਰ ਨਾਲ ਜੁੜ ਨਹੀਂ ਸਕਦਾ</string>
|
||||
<string name="error_http_no_content">ਸਰਵਰ ਨੇ ਡਾਟਾ ਨਹੀਂ ਭੇਜਿਆ</string>
|
||||
<string name="error_http_unsupported_range">ਸਰਵਰ ਮਲਟੀ-Threaded ਡਾਊਨਲੋਡਸ ਨੂੰ ਸਵੀਕਾਰ ਨਹੀਂ ਕਰਦਾ, ਇਸ ਨਾਲ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">ਬੇਨਤੀ ਕੀਤੀ ਸੀਮਾ ਤਸੱਲੀਬਖਸ਼ ਨਹੀਂ ਹੈ</string>
|
||||
<string name="error_http_not_found">ਨਹੀਂ ਲਭਿਆ</string>
|
||||
<string name="error_postprocessing_failed">Post-processing ਫੇਲ੍ਹ</string>
|
||||
<string name="clear_finished_download">ਮੁਕੰਮਲ ਹੋਈਆਂ ਡਾਊਨਲੋਡ ਸਾਫ਼ ਕਰੋ</string>
|
||||
|
|
|
@ -456,7 +456,6 @@
|
|||
<string name="error_connect_host">Nie można połączyć się z serwerem</string>
|
||||
<string name="error_http_no_content">Serwer nie wysyła danych</string>
|
||||
<string name="error_http_unsupported_range">Serwer nie akceptuje pobierania wielowątkowego, spróbuj ponownie za pomocą @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Niewłaściwy zakres</string>
|
||||
<string name="error_http_not_found">Nie znaleziono</string>
|
||||
<string name="error_postprocessing_failed">Przetwarzanie końcowe nie powiodło się</string>
|
||||
<string name="clear_finished_download">Wyczyść ukończone pobieranie</string>
|
||||
|
|
|
@ -463,7 +463,6 @@ abrir em modo popup</string>
|
|||
<string name="error_connect_host">Não foi possível conectar ao servidor</string>
|
||||
<string name="error_http_no_content">O servidor não envia dados</string>
|
||||
<string name="error_http_unsupported_range">O servidor não aceita downloads em multi-thread, tente com @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Intervalo solicitado não aceito</string>
|
||||
<string name="error_http_not_found">Não encontrado</string>
|
||||
<string name="error_postprocessing_failed">Falha no pós processamento</string>
|
||||
<string name="clear_finished_download">Limpar downloads finalizados</string>
|
||||
|
|
|
@ -452,7 +452,6 @@
|
|||
<string name="error_connect_host">Não é possível ligar ao servidor</string>
|
||||
<string name="error_http_no_content">O servidor não envia dados</string>
|
||||
<string name="error_http_unsupported_range">O servidor não aceita transferências de vários processos, tente novamente com @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Intervalo solicitado não satisfatório</string>
|
||||
<string name="error_http_not_found">Não encontrado</string>
|
||||
<string name="error_postprocessing_failed">Pós-processamento falhado</string>
|
||||
<string name="clear_finished_download">Limpar transferências concluídas</string>
|
||||
|
|
|
@ -454,7 +454,6 @@
|
|||
<string name="error_permission_denied">Доступ запрещён системой</string>
|
||||
<string name="error_unknown_host">Сервер не найден</string>
|
||||
<string name="error_http_unsupported_range">Сервер не принимает многопоточные загрузки, повторная попытка с @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Запрашиваемый диапазон недопустим</string>
|
||||
<string name="error_http_not_found">Не найдено</string>
|
||||
<string name="clear_finished_download">Очистить завершённые</string>
|
||||
<string name="stop">Остановить</string>
|
||||
|
|
|
@ -462,7 +462,6 @@
|
|||
<string name="error_connect_host">Nepodarilo sa pripojiť k serveru</string>
|
||||
<string name="error_http_no_content">Server neposiela údaje</string>
|
||||
<string name="error_http_unsupported_range">Server neakceptuje preberanie viacerých vlákien, zopakujte s @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Požadovaný rozsah nie je uspokojivý</string>
|
||||
<string name="error_http_not_found">Nenájdené</string>
|
||||
<string name="error_postprocessing_failed">Post-spracovanie zlyhalo</string>
|
||||
<string name="clear_finished_download">Vyčistiť dokončené sťahovania</string>
|
||||
|
|
|
@ -449,7 +449,6 @@
|
|||
<string name="error_connect_host">Sunucuya bağlanılamıyor</string>
|
||||
<string name="error_http_no_content">Sunucu veri göndermiyor</string>
|
||||
<string name="error_http_unsupported_range">Sunucu, çok iş parçacıklı indirmeleri kabul etmez, @string/msg_threads = 1 ile yeniden deneyin</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">İstenen aralık karşılanamıyor</string>
|
||||
<string name="error_http_not_found">Bulunamadı</string>
|
||||
<string name="error_postprocessing_failed">İşlem sonrası başarısız</string>
|
||||
<string name="clear_finished_download">Tamamlanan indirmeleri temizle</string>
|
||||
|
|
|
@ -471,7 +471,6 @@
|
|||
<string name="saved_tabs_invalid_json">Помилка зчитування збережених вкладок. Використовую типові вкладки.</string>
|
||||
<string name="main_page_content_summary">Вкладки, що відображаються на головній сторінці</string>
|
||||
<string name="updates_setting_description">Показувати сповіщення з пропозицією оновити застосунок за наявності нової версії</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Запитуваний діапазон неприпустимий</string>
|
||||
<string name="msg_pending_downloads">Продовжити ваші %s відкладених переміщень із Завантажень</string>
|
||||
<string name="pause_downloads_on_mobile_desc">Корисно під час переходу на мобільні дані, хоча деякі завантаження не можуть бути призупинені</string>
|
||||
<string name="show_comments_title">Показувати коментарі</string>
|
||||
|
|
|
@ -449,7 +449,6 @@
|
|||
<string name="error_connect_host">Không thế kết nối với máy chủ</string>
|
||||
<string name="error_http_no_content">Máy chủ không gửi dữ liệu về</string>
|
||||
<string name="error_http_unsupported_range">Máy chủ không chấp nhận tải đa luồng, thử lại với số luồng = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">(HTTP) Không thể đáp ứng khoảng dữ liệu đã yêu cầu</string>
|
||||
<string name="error_http_not_found">Không tìm thấy</string>
|
||||
<string name="error_postprocessing_failed">Xử lý thất bại</string>
|
||||
<string name="clear_finished_download">Dọn các tải về đã hoàn thành</string>
|
||||
|
|
|
@ -447,7 +447,6 @@
|
|||
<string name="error_connect_host">無法連線到伺服器</string>
|
||||
<string name="error_http_no_content">伺服器沒有傳送資料</string>
|
||||
<string name="error_http_unsupported_range">伺服器不接受多執行緒下載,請以 @string/msg_threads = 1 重試</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">請求範圍無法滿足</string>
|
||||
<string name="error_http_not_found">找不到</string>
|
||||
<string name="error_postprocessing_failed">後處理失敗</string>
|
||||
<string name="clear_finished_download">清除已結束的下載</string>
|
||||
|
|
|
@ -551,13 +551,13 @@
|
|||
<string name="error_connect_host">Can not connect to the server</string>
|
||||
<string name="error_http_no_content">The server does not send data</string>
|
||||
<string name="error_http_unsupported_range">The server does not accept multi-threaded downloads, retry with @string/msg_threads = 1</string>
|
||||
<string name="error_http_requested_range_not_satisfiable">Requested range not satisfiable</string>
|
||||
<string name="error_http_not_found">Not found</string>
|
||||
<string name="error_postprocessing_failed">Post-processing failed</string>
|
||||
<string name="error_postprocessing_stopped">NewPipe was closed while working on the file</string>
|
||||
<string name="error_insufficient_storage">No space left on device</string>
|
||||
<string name="error_progress_lost">Progress lost, because the file was deleted</string>
|
||||
<string name="error_timeout">Connection timeout</string>
|
||||
<string name="error_download_resource_gone">The solicited resource is not available anymore</string>
|
||||
<string name="clear_finished_download">Clear finished downloads</string>
|
||||
<string name="confirm_prompt">Are you sure?</string>
|
||||
<string name="msg_pending_downloads">Continue your %s pending transfers from Downloads</string>
|
||||
|
|
Loading…
Reference in a new issue