From c891f2f1eda4ade08bda3e74d9454b98743980b4 Mon Sep 17 00:00:00 2001 From: kapodamy Date: Sat, 28 Sep 2019 18:11:05 -0300 Subject: [PATCH] long-term downloads resume * recovery infrastructure * bump serialVersionUID of DownloadMission * misc cleanup in DownloadMission.java * remove unused/redundant from strings.xml --- .../newpipe/download/DownloadDialog.java | 34 ++- .../giga/get/DownloadInitializer.java | 15 ++ .../us/shandian/giga/get/DownloadMission.java | 96 ++++++-- .../giga/get/DownloadMissionRecover.java | 222 ++++++++++++++++++ .../shandian/giga/get/DownloadRunnable.java | 27 ++- .../giga/get/DownloadRunnableFallback.java | 11 +- .../giga/get/MissionRecoveryInfo.java | 79 +++++++ .../giga/service/DownloadManager.java | 1 - .../giga/service/DownloadManagerService.java | 36 ++- .../giga/ui/adapter/MissionAdapter.java | 12 +- app/src/main/res/values-ar/strings.xml | 1 - app/src/main/res/values-be/strings.xml | 1 - app/src/main/res/values-cmn/strings.xml | 1 - app/src/main/res/values-cs/strings.xml | 1 - app/src/main/res/values-da/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-el/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 9 +- app/src/main/res/values-et/strings.xml | 1 - app/src/main/res/values-eu/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-he/strings.xml | 1 - app/src/main/res/values-hr/strings.xml | 1 - app/src/main/res/values-id/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-ja/strings.xml | 1 - app/src/main/res/values-ko/strings.xml | 1 - app/src/main/res/values-ms/strings.xml | 1 - app/src/main/res/values-nb-rNO/strings.xml | 2 +- app/src/main/res/values-nl-rBE/strings.xml | 1 - app/src/main/res/values-nl/strings.xml | 1 - app/src/main/res/values-pa/strings.xml | 1 - app/src/main/res/values-pl/strings.xml | 1 - app/src/main/res/values-pt-rBR/strings.xml | 1 - app/src/main/res/values-pt/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-sk/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-uk/strings.xml | 1 - app/src/main/res/values-vi/strings.xml | 1 - app/src/main/res/values-zh-rTW/strings.xml | 1 - app/src/main/res/values/strings.xml | 2 +- 42 files changed, 478 insertions(+), 97 deletions(-) create mode 100644 app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java create mode 100644 app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.java diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index 90258a6dc..0006b3c12 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -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 secondaryStream = videoStreamsAdapter + SecondaryStreamHelper 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(); } diff --git a/app/src/main/java/us/shandian/giga/get/DownloadInitializer.java b/app/src/main/java/us/shandian/giga/get/DownloadInitializer.java index 247faeb6d..593feafa7 100644 --- a/app/src/main/java/us/shandian/giga/get/DownloadInitializer.java +++ b/app/src/main/java/us/shandian/giga/get/DownloadInitializer.java @@ -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) { diff --git a/app/src/main/java/us/shandian/giga/get/DownloadMission.java b/app/src/main/java/us/shandian/giga/get/DownloadMission.java index d78f8e32b..77b417118 100644 --- a/app/src/main/java/us/shandian/giga/get/DownloadMission.java +++ b/app/src/main/java/us/shandian/giga/get/DownloadMission.java @@ -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 { diff --git a/app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java b/app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java new file mode 100644 index 000000000..9abd93717 --- /dev/null +++ b/app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java @@ -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 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 + } + } + } +} diff --git a/app/src/main/java/us/shandian/giga/get/DownloadRunnable.java b/app/src/main/java/us/shandian/giga/get/DownloadRunnable.java index f5b9b06d4..1d2a4eee7 100644 --- a/app/src/main/java/us/shandian/giga/get/DownloadRunnable.java +++ b/app/src/main/java/us/shandian/giga/get/DownloadRunnable.java @@ -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"); diff --git a/app/src/main/java/us/shandian/giga/get/DownloadRunnableFallback.java b/app/src/main/java/us/shandian/giga/get/DownloadRunnableFallback.java index 7fb1f0c77..b5937c577 100644 --- a/app/src/main/java/us/shandian/giga/get/DownloadRunnableFallback.java +++ b/app/src/main/java/us/shandian/giga/get/DownloadRunnableFallback.java @@ -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; diff --git a/app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.java b/app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.java new file mode 100644 index 000000000..553ba6d89 --- /dev/null +++ b/app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.java @@ -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 CREATOR = new Parcelable.Creator() { + @Override + public MissionRecoveryInfo createFromParcel(Parcel source) { + return new MissionRecoveryInfo(source); + } + + @Override + public MissionRecoveryInfo[] newArray(int size) { + return new MissionRecoveryInfo[size]; + } + }; +} diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManager.java b/app/src/main/java/us/shandian/giga/service/DownloadManager.java index 3d34411b9..a859a87ca 100644 --- a/app/src/main/java/us/shandian/giga/service/DownloadManager.java +++ b/app/src/main/java/us/shandian/giga/service/DownloadManager.java @@ -177,7 +177,6 @@ public class DownloadManager { mis.psAlgorithm.setTemporalDir(pickAvailableTemporalDir(ctx)); } - mis.recovered = exists; mis.metadata = sub; mis.maxRetry = mPrefMaxRetry; mis.mHandler = mHandler; diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java index 461787b62..ea9029c0b 100755 --- a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java +++ b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java @@ -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)); diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java index 6d1169031..6c6198750 100644 --- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java +++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java @@ -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 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 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 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 implements Handler.Callb delete.setVisible(true); - boolean flag = !mission.isPsFailed(); + boolean flag = !mission.isPsFailed() && mission.urls.length > 0; start.setVisible(flag); queue.setVisible(flag); } diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 7156d08ba..43b45d15e 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -468,7 +468,6 @@ لا يمكن الاتصال بالخادم الخادم لايقوم بإرسال البيانات الخادم لا يقبل التنزيل المتعدد، إعادة المحاولة مع @string/msg_threads = 1 - عدم استيفاء النطاق المطلوب غير موجود فشلت المعالجة الاولية حذف التنزيلات المنتهية diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 93307cbcf..3c79a96d3 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -455,7 +455,6 @@ Немагчыма злучыцца з серверам Не атрымалася атрымаць дадзеныя з сервера Сервер не падтрымлівае шматструменную загрузку, паспрабуйце з @string/msg_threads = 1 - Запытаны дыяпазон недапушчальны Не знойдзена Пасляапрацоўка не ўдалася Ачысціць завершаныя diff --git a/app/src/main/res/values-cmn/strings.xml b/app/src/main/res/values-cmn/strings.xml index 49801a190..bcb145c16 100644 --- a/app/src/main/res/values-cmn/strings.xml +++ b/app/src/main/res/values-cmn/strings.xml @@ -460,7 +460,6 @@ NewPipe 更新可用! 无法创建目标文件夹 服务器不接受多线程下载, 请使用 @string/msg_threads = 1重试 - 请求范围无法满足 继续进行%s个待下载转移 切换至移动数据时有用,尽管一些下载无法被暂停 显示评论 diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index d539923fe..b741e0d16 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -463,7 +463,6 @@ otevření ve vyskakovacím okně Nelze se připojit k serveru Server neposílá data Server neakceptuje vícevláknové stahování, opakujte akci s @string/msg_threads = 1 - Požadovaný rozsah nelze splnit Nenalezeno Post-processing selhal Vyčistit dokončená stahování diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 42ffd474b..199c2f85d 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -380,7 +380,6 @@ Kan ikke forbinde til serveren Serveren sender ikke data Serveren accepterer ikke multitrådede downloads; prøv igen med @string/msg_threads = 1 - Det anmodede interval er ikke gyldigt Ikke fundet Efterbehandling fejlede Stop diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 2d6b5b6d2..3279e919c 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -454,7 +454,6 @@ Kann nicht mit dem Server verbinden Der Server sendet keine Daten Der Server erlaubt kein mehrfädiges Herunterladen – wiederhole mit @string/msg_threads = 1 - Gewünschter Bereich ist nicht verfügbar Nicht gefunden Nachbearbeitung fehlgeschlagen Um fertige Downloads bereinigen diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 4f3499cfd..372cbb1a2 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -456,7 +456,6 @@ Αδυναμία σύνδεσης με τον εξυπηρετητή Ο εξυπηρετητής δεν μπορεί να στείλει τα δεδομένα Ο εξυπηρετητής δέν υποστηρίζει πολυνηματικές λήψεις, ξαναπροσπαθήστε με @string/msg_threads = 1 - Το ζητούμενο εύρος δεν μπορεί να εξυπηρετηθεί Δεν βρέθηκε Μετεπεξεργασία απέτυχε Εκκαθάριση ολοκληρωμένων λήψεων diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3aa0bac66..2f69e62cb 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -351,8 +351,8 @@ \n3. Inicie sesión cuando se le pida \n4. Copie la URL del perfil a la que fue redireccionado. suID, soundcloud.com/suID - Observe que esta operación puede causar un uso intensivo de la red. -\n + Observe que esta operación puede causar un uso intensivo de la red. +\n \n¿Quiere continuar\? Cargar miniaturas 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. @@ -444,8 +444,8 @@ Fallo la conexión segura No se pudo encontrar el servidor No se puede conectar con el servidor - El servidor no está enviando datos - El servidor no acepta descargas multiproceso; intente de nuevo con @string/msg_threads = 1 + El servidor no devolvio datos + El servidor no acepta descargas multi-hilos, intente de nuevo con @string/msg_threads = 1 No se puede satisfacer el intervalo seleccionado No encontrado Falló el posprocesamiento @@ -453,6 +453,7 @@ No hay suficiente espacio disponible en el dispositivo Se perdió el progreso porque el archivo fue eliminado Tiempo de espera excedido + El recurso solicitado ya no esta disponible Preguntar dónde descargar Se preguntará dónde guardar cada descarga Se le preguntará dónde guardar cada descarga. diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index baad94b5d..4dfcc3d0e 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -457,7 +457,6 @@ Serveriga ei saadud ühendust Server ei saada andmeid Server ei toeta mitmelõimelisi allalaadimisi. Proovi uuesti kasutades @string/msg_threads = 1 - Taotletud vahemik ei ole rahuldatav Ei leitud Järeltöötlemine nurjus Eemalda lõpetatud allalaadimised diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 7da39393e..7b636d383 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -456,7 +456,6 @@ Ezin da zerbitzariarekin konektatu Zerbitzariak ez du daturik bidaltzen Zerbitzariak ez ditu hainbat hariko deskargak onartzen, saiatu @string/msg_threads = 1 erabilita - Eskatutako barrutia ezin da bete Ez aurkitua Post-prozesuak huts egin du Garbitu amaitutako deskargak diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 147502088..b4388e39f 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -467,7 +467,6 @@ Utilisation des onglets par défaut, erreur lors de la lecture des onglets enregistrés Le serveur n’accepte pas les téléchargements multi-fils, veuillez réessayer avec @string/msg_threads = 1 Continuer vos %s transferts en attente depuis Téléchargement - Le domaine désiré n\'est pas disponible Afficher les commentaires Désactiver pour ne pas afficher les commentaires Lecture automatique diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index b5a0778d4..5e340d8b3 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -461,7 +461,6 @@ לא ניתן להתחבר לשרת השרת לא שולח נתונים "השרת לא מקבל הורדות רב ערוציות, מוטב לנסות שוב עם ‎@string/msg_threads = 1 " - הטווח המבוקש לא מתאים לא נמצא העיבוד המאוחר נכשל פינוי ההורדות שהסתיימו diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index e85d5810e..aa4ff9113 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -454,7 +454,6 @@ Nije moguće povezati se s serverom Server ne šalje podatke Poslužitelj ne prihvaća preuzimanja s više niti, pokušaj ponovo s @string/msg_threads = 1 - Traženi raspon nije zadovoljavajući Nije pronađeno Naknadna obrada nije uspjela Obriši završena preuzimanja diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index db738d749..d52f5fafa 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -450,7 +450,6 @@ Tidak dapat terhubung ke server Server tidak mengirim data Server tidak menerima unduhan multi-utas, coba lagi dengan @string/msg_threads = 1 - Rentang yang diminta tidak memuaskan Tidak ditemukan Pengolahan-pasca gagal Hapus unduhan yang sudah selesai diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 35fdebeda..c92292f99 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -454,7 +454,6 @@ Impossibile connettersi al server Il server non invia dati Il server non accetta download multipli, riprovare con @string/msg_threads = 1 - Intervallo richiesto non soddisfatto Non trovato Post-processing fallito Pulisci i download completati diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index b67da798c..58ca2ebff 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -440,7 +440,6 @@ サーバに接続できません サーバがデータを送信していません サーバが同時接続ダウンロードを受け付けません。再試行してください @string/msg_threads = 1 - 必要な範囲が満たされていません 見つかりません 保存処理に失敗しました 完了済みを一覧から削除します diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 333891910..fdc76b04e 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -451,7 +451,6 @@ 서버에 접속할 수 없습니다 서버가 데이터를 전송하지 않고 있습니다 서버가 다중 스레드 다운로드를 받아들이지 않습니다, @string/msg_threads = 1 를 사용해 다시 시도해보세요 - 요청된 HTTP 범위가 충분하지 않습니다 HTTP 찾을 수 없습니다 후처리 작업이 실패하였습니다 완료된 다운로드 비우기 diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index c7fa5de92..daa120ea2 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -450,7 +450,6 @@ Tidak dapat menyambung ke server Server tidak menghantar data Server tidak menerima muat turun berbilang thread, cuba lagi dengan @string/msg_threads = 1 - Julat yang diminta tidak memuaskan Tidak ditemui Pemprosesan-pasca gagal Hapuskan senarai muat turun yang selesai diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index d26886844..6262480b0 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -496,7 +496,7 @@ Sett nedlastinger på pause Spør om hvor ting skal lastes ned til Du vil bli spurt om hvor hver nedlasting skal plasseres - Du vil bli spurt om hvor hver nedlasting skal plasseres. + Du vil bli spurt om hvor hver nedlasting skal plasseres. \nSkru på SAF hvis du vil laste ned til eksternt SD-kort Bruk SAF Lagringstilgangsrammeverk (SAF) tillater nedlastinger til eksternt SD-kort. diff --git a/app/src/main/res/values-nl-rBE/strings.xml b/app/src/main/res/values-nl-rBE/strings.xml index 94feb4915..f64ff6bf9 100644 --- a/app/src/main/res/values-nl-rBE/strings.xml +++ b/app/src/main/res/values-nl-rBE/strings.xml @@ -454,7 +454,6 @@ Kan geen verbinding maken met de server De server verzendt geen gegevens De server aanvaardt geen meerdradige downloads, probeert het opnieuw met @string/msg_threads = 1 - Gevraagd bereik niet beschikbaar Niet gevonden Nabewerking mislukt Voltooide downloads wissen diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index f7acba6ae..6aecc2cd1 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -454,7 +454,6 @@ Kan niet met de server verbinden De server verzendt geen gegevens De server accepteert geen multi-threaded downloads, probeer het opnieuw met @string/msg_threads = 1 - Gevraagde bereik niet beschikbaar Niet gevonden Nabewerking mislukt Voltooide downloads wissen diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index c31eb805d..b57564eba 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -450,7 +450,6 @@ ਸਰਵਰ ਨਾਲ ਜੁੜ ਨਹੀਂ ਸਕਦਾ ਸਰਵਰ ਨੇ ਡਾਟਾ ਨਹੀਂ ਭੇਜਿਆ ਸਰਵਰ ਮਲਟੀ-Threaded ਡਾਊਨਲੋਡਸ ਨੂੰ ਸਵੀਕਾਰ ਨਹੀਂ ਕਰਦਾ, ਇਸ ਨਾਲ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ @string/msg_threads = 1 - ਬੇਨਤੀ ਕੀਤੀ ਸੀਮਾ ਤਸੱਲੀਬਖਸ਼ ਨਹੀਂ ਹੈ ਨਹੀਂ ਲਭਿਆ Post-processing ਫੇਲ੍ਹ ਮੁਕੰਮਲ ਹੋਈਆਂ ਡਾਊਨਲੋਡ ਸਾਫ਼ ਕਰੋ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index d3c84aa22..ca1e52ff2 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -456,7 +456,6 @@ Nie można połączyć się z serwerem Serwer nie wysyła danych Serwer nie akceptuje pobierania wielowątkowego, spróbuj ponownie za pomocą @string/msg_threads = 1 - Niewłaściwy zakres Nie znaleziono Przetwarzanie końcowe nie powiodło się Wyczyść ukończone pobieranie diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index aaac4fd4c..0bdf4d006 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -463,7 +463,6 @@ abrir em modo popup Não foi possível conectar ao servidor O servidor não envia dados O servidor não aceita downloads em multi-thread, tente com @string/msg_threads = 1 - Intervalo solicitado não aceito Não encontrado Falha no pós processamento Limpar downloads finalizados diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 5d7cd8146..6d55023d1 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -452,7 +452,6 @@ Não é possível ligar ao servidor O servidor não envia dados O servidor não aceita transferências de vários processos, tente novamente com @string/msg_threads = 1 - Intervalo solicitado não satisfatório Não encontrado Pós-processamento falhado Limpar transferências concluídas diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6f079a221..51771e1b1 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -454,7 +454,6 @@ Доступ запрещён системой Сервер не найден Сервер не принимает многопоточные загрузки, повторная попытка с @string/msg_threads = 1 - Запрашиваемый диапазон недопустим Не найдено Очистить завершённые Остановить diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 09502f60a..36c0afd84 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -462,7 +462,6 @@ Nepodarilo sa pripojiť k serveru Server neposiela údaje Server neakceptuje preberanie viacerých vlákien, zopakujte s @string/msg_threads = 1 - Požadovaný rozsah nie je uspokojivý Nenájdené Post-spracovanie zlyhalo Vyčistiť dokončené sťahovania diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index c17b58f50..6c9c66f69 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -449,7 +449,6 @@ Sunucuya bağlanılamıyor Sunucu veri göndermiyor Sunucu, çok iş parçacıklı indirmeleri kabul etmez, @string/msg_threads = 1 ile yeniden deneyin - İstenen aralık karşılanamıyor Bulunamadı İşlem sonrası başarısız Tamamlanan indirmeleri temizle diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 375557b04..fcce99e89 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -471,7 +471,6 @@ Помилка зчитування збережених вкладок. Використовую типові вкладки. Вкладки, що відображаються на головній сторінці Показувати сповіщення з пропозицією оновити застосунок за наявності нової версії - Запитуваний діапазон неприпустимий Продовжити ваші %s відкладених переміщень із Завантажень Корисно під час переходу на мобільні дані, хоча деякі завантаження не можуть бути призупинені Показувати коментарі diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 74b8b395c..f8860acfd 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -449,7 +449,6 @@ Không thế kết nối với máy chủ Máy chủ không gửi dữ liệu về Máy chủ không chấp nhận tải đa luồng, thử lại với số luồng = 1 - (HTTP) Không thể đáp ứng khoảng dữ liệu đã yêu cầu Không tìm thấy Xử lý thất bại Dọn các tải về đã hoàn thành diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index fe4c1b00a..310bae3a3 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -447,7 +447,6 @@ 無法連線到伺服器 伺服器沒有傳送資料 伺服器不接受多執行緒下載,請以 @string/msg_threads = 1 重試 - 請求範圍無法滿足 找不到 後處理失敗 清除已結束的下載 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a34b00ea9..2917fb9fd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -551,13 +551,13 @@ Can not connect to the server The server does not send data The server does not accept multi-threaded downloads, retry with @string/msg_threads = 1 - Requested range not satisfiable Not found Post-processing failed NewPipe was closed while working on the file No space left on device Progress lost, because the file was deleted Connection timeout + The solicited resource is not available anymore Clear finished downloads Are you sure? Continue your %s pending transfers from Downloads