From 7b4ac7998a4d535662d616a546c9b7b6595a2e74 Mon Sep 17 00:00:00 2001 From: inorichi Date: Sun, 18 Feb 2018 17:34:22 +0100 Subject: [PATCH] Remove simultaneous downloads --- .../data/download/DownloadNotifier.kt | 63 +----- .../tachiyomi/data/download/Downloader.kt | 52 ++--- .../data/preference/PreferenceKeys.kt | 2 - .../data/preference/PreferencesHelper.kt | 2 - .../ui/setting/SettingsDownloadController.kt | 12 +- .../util/DynamicConcurrentMergeOperator.java | 196 ------------------ app/src/main/res/values/strings.xml | 1 - 7 files changed, 22 insertions(+), 306 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/util/DynamicConcurrentMergeOperator.java diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt index aeec8e33c3..cc896a978e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt @@ -36,7 +36,6 @@ internal class DownloadNotifier(private val context: Context) { * The size of queue on start download. */ var initialQueueSize = 0 - get() = field set(value) { if (value != 0){ isSingleChapter = (value == 1) @@ -44,11 +43,6 @@ internal class DownloadNotifier(private val context: Context) { field = value } - /** - * Simultaneous download setting > 1. - */ - var multipleDownloadThreads = false - /** * Updated when error is thrown */ @@ -91,36 +85,10 @@ internal class DownloadNotifier(private val context: Context) { /** * Called when download progress changes. - * Note: Only accepted when multi download active. - * - * @param queue the queue containing downloads. - */ - fun onProgressChange(queue: DownloadQueue) { - if (multipleDownloadThreads) { - doOnProgressChange(null, queue) - } - } - - /** - * Called when download progress changes. - * Note: Only accepted when single download active. * * @param download download object containing download information. - * @param queue the queue containing downloads. */ - fun onProgressChange(download: Download, queue: DownloadQueue) { - if (!multipleDownloadThreads) { - doOnProgressChange(download, queue) - } - } - - /** - * Show notification progress of chapter. - * - * @param download download object containing download information. - * @param queue the queue containing downloads. - */ - private fun doOnProgressChange(download: Download?, queue: DownloadQueue) { + fun onProgressChange(download: Download) { // Create notification with(notification) { // Check if first call. @@ -133,28 +101,13 @@ internal class DownloadNotifier(private val context: Context) { isDownloading = true } - if (multipleDownloadThreads) { - setContentTitle(context.getString(R.string.app_name)) - - // Reset the queue size if the download progress is negative - if ((initialQueueSize - queue.size) < 0) - initialQueueSize = queue.size - - setContentText(context.getString(R.string.chapter_downloading_progress) - .format(initialQueueSize - queue.size, initialQueueSize)) - setProgress(initialQueueSize, initialQueueSize - queue.size, false) - } else { - download?.let { - val title = it.manga.title.chop(15) - val quotedTitle = Pattern.quote(title) - val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "") - setContentTitle("$title - $chapter".chop(30)) - setContentText(context.getString(R.string.chapter_downloading_progress) - .format(it.downloadedImages, it.pages!!.size)) - setProgress(it.pages!!.size, it.downloadedImages, false) - - } - } + val title = download.manga.title.chop(15) + val quotedTitle = Pattern.quote(title) + val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "") + setContentTitle("$title - $chapter".chop(30)) + setContentText(context.getString(R.string.chapter_downloading_progress) + .format(download.downloadedImages, download.pages!!.size)) + setProgress(download.pages!!.size, download.downloadedImages, false) } // Displays the progress bar on notification notification.show() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index 88f32553bc..86642f269c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -9,8 +9,6 @@ import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.DownloadQueue -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.online.HttpSource @@ -21,7 +19,6 @@ import okhttp3.Response import rx.Observable import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers -import rx.subjects.BehaviorSubject import rx.subscriptions.CompositeSubscription import timber.log.Timber import uy.kohesive.injekt.injectLazy @@ -39,9 +36,11 @@ import uy.kohesive.injekt.injectLazy * @param provider the downloads directory provider. * @param cache the downloads cache, used to add the downloads to the cache after their completion. */ -class Downloader(private val context: Context, - private val provider: DownloadProvider, - private val cache: DownloadCache) { +class Downloader( + private val context: Context, + private val provider: DownloadProvider, + private val cache: DownloadCache +) { /** * Store for persisting downloads across restarts. @@ -58,11 +57,6 @@ class Downloader(private val context: Context, */ private val sourceManager: SourceManager by injectLazy() - /** - * Preferences. - */ - private val preferences: PreferencesHelper by injectLazy() - /** * Notifier for the downloader state and progress. */ @@ -73,11 +67,6 @@ class Downloader(private val context: Context, */ private val subscriptions = CompositeSubscription() - /** - * Subject to do a live update of the number of simultaneous downloads. - */ - private val threadsSubject = BehaviorSubject.create() - /** * Relay to send a list of downloads to the downloader. */ @@ -116,9 +105,6 @@ class Downloader(private val context: Context, val pending = queue.filter { it.status != Download.DOWNLOADED } pending.forEach { if (it.status != Download.QUEUE) it.status = Download.QUEUE } - // Show download notification when simultaneous download > 1. - notifier.onProgressChange(queue) - downloadsRelay.call(pending) return !pending.isEmpty() } @@ -185,14 +171,8 @@ class Downloader(private val context: Context, subscriptions.clear() - subscriptions += preferences.downloadThreads().asObservable() - .subscribe { - threadsSubject.onNext(it) - notifier.multipleDownloadThreads = it > 1 - } - - subscriptions += downloadsRelay.flatMap { Observable.from(it) } - .lift(DynamicConcurrentMergeOperator({ downloadChapter(it) }, threadsSubject)) + subscriptions += downloadsRelay.concatMapIterable { it } + .concatMap { downloadChapter(it).subscribeOn(Schedulers.io()) } .onBackpressureBuffer() .observeOn(AndroidSchedulers.mainThread()) .subscribe({ completeDownload(it) @@ -250,15 +230,9 @@ class Downloader(private val context: Context, // Initialize queue size. notifier.initialQueueSize = queue.size - // Initial multi-thread - notifier.multipleDownloadThreads = preferences.downloadThreads().getOrDefault() > 1 - if (isRunning) { // Send the list of downloads to the downloader. downloadsRelay.call(chaptersToQueue) - } else { - // Show initial notification. - notifier.onProgressChange(queue) } // Start downloader if needed @@ -273,7 +247,8 @@ class Downloader(private val context: Context, * * @param download the chapter to be downloaded. */ - private fun downloadChapter(download: Download): Observable { + private fun downloadChapter(download: Download): Observable = Observable.defer { + Timber.e("Thread: ${Thread.currentThread()}") val chapterDirname = provider.getChapterDirName(download.chapter) val mangaDir = provider.getMangaDir(download.manga, download.source) val tmpDir = mangaDir.createDirectory("${chapterDirname}_tmp") @@ -292,7 +267,7 @@ class Downloader(private val context: Context, Observable.just(download.pages!!) } - return pageListObservable + pageListObservable .doOnNext { _ -> // Delete all temporary (unfinished) files tmpDir.listFiles() @@ -307,7 +282,7 @@ class Downloader(private val context: Context, // Start downloading images, consider we can have downloaded images already .concatMap { page -> getOrDownloadImage(page, download, tmpDir) } // Do when page is downloaded. - .doOnNext { notifier.onProgressChange(download, queue) } + .doOnNext { notifier.onProgressChange(download) } .toList() .map { _ -> download } // Do after download completes @@ -318,7 +293,7 @@ class Downloader(private val context: Context, notifier.onError(error.message, download.chapter.name) download } - .subscribeOn(Schedulers.io()) + } /** @@ -448,7 +423,6 @@ class Downloader(private val context: Context, if (download.status == Download.DOWNLOADED) { // remove downloaded chapter from queue queue.remove(download) - notifier.onProgressChange(queue) } if (areAllDownloadsFinished()) { if (notifier.isSingleChapter && !notifier.errorThrown) { @@ -465,4 +439,4 @@ class Downloader(private val context: Context, return queue.none { it.status <= Download.DOWNLOADING } } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index d3d6e00372..66e9d4181c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -67,8 +67,6 @@ object PreferenceKeys { const val downloadsDirectory = "download_directory" - const val downloadThreads = "pref_download_slots_key" - const val downloadOnlyOverWifi = "pref_download_only_over_wifi_key" const val numberOfBackups = "backup_slots" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index c2869510bd..ed10fa7d32 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -123,8 +123,6 @@ class PreferencesHelper(val context: Context) { fun downloadsDirectory() = rxPrefs.getString(Keys.downloadsDirectory, defaultDownloadsDir.toString()) - fun downloadThreads() = rxPrefs.getInteger(Keys.downloadThreads, 1) - fun downloadOnlyOverWifi() = prefs.getBoolean(Keys.downloadOnlyOverWifi, true) fun numberOfBackups() = rxPrefs.getInteger(Keys.numberOfBackups, 1) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt index 0fc58ec006..1b6119b13c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt @@ -12,7 +12,6 @@ import android.support.v4.content.ContextCompat import android.support.v7.preference.PreferenceScreen import com.afollestad.materialdialogs.MaterialDialog import com.hippo.unifile.UniFile -import com.nononsenseapps.filepicker.FilePickerActivity import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper @@ -20,7 +19,6 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.util.DiskUtil import eu.kanade.tachiyomi.util.getFilePicker -import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy @@ -63,14 +61,6 @@ class SettingsDownloadController : SettingsController() { titleRes = R.string.pref_download_only_over_wifi defaultValue = true } - intListPreference { - key = Keys.downloadThreads - titleRes = R.string.pref_download_slots - entries = arrayOf("1", "2", "3") - entryValues = arrayOf("1", "2", "3") - defaultValue = "1" - summary = "%s" - } preferenceCategory { titleRes = R.string.pref_remove_after_read @@ -206,4 +196,4 @@ class SettingsDownloadController : SettingsController() { const val DOWNLOAD_DIR_PRE_L = 103 const val DOWNLOAD_DIR_L = 104 } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/DynamicConcurrentMergeOperator.java b/app/src/main/java/eu/kanade/tachiyomi/util/DynamicConcurrentMergeOperator.java deleted file mode 100644 index 2b18e417b9..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/util/DynamicConcurrentMergeOperator.java +++ /dev/null @@ -1,196 +0,0 @@ -package eu.kanade.tachiyomi.util; - -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import rx.Observable; -import rx.Observable.Operator; -import rx.Subscriber; -import rx.Subscription; -import rx.functions.Action0; -import rx.functions.Action1; -import rx.functions.Func1; -import rx.subscriptions.CompositeSubscription; -import rx.subscriptions.Subscriptions; - -public class DynamicConcurrentMergeOperator implements Operator { - private final Func1> mapper; - private final Observable workerCount; - - public DynamicConcurrentMergeOperator( - Func1> mapper, - Observable workerCount) { - this.mapper = mapper; - this.workerCount = workerCount; - } - - @Override - public Subscriber call(Subscriber t) { - DynamicConcurrentMerge parent = new DynamicConcurrentMerge<>(t, mapper); - t.add(parent); - parent.init(workerCount); - - return parent; - } - - static final class DynamicConcurrentMerge extends Subscriber { - private final Subscriber actual; - private final Func1> mapper; - private final Queue queue; - private final CopyOnWriteArrayList> workers; - private final CompositeSubscription composite; - private final AtomicInteger wipActive; - private final AtomicBoolean once; - private long id; - - public DynamicConcurrentMerge(Subscriber actual, - Func1> mapper) { - this.actual = actual; - this.mapper = mapper; - this.queue = new ConcurrentLinkedQueue<>(); - this.workers = new CopyOnWriteArrayList<>(); - this.composite = new CompositeSubscription(); - this.wipActive = new AtomicInteger(1); - this.once = new AtomicBoolean(); - this.add(composite); - this.request(0); - } - - public void init(Observable workerCount) { - Subscription wc = workerCount.subscribe(new Action1() { - @Override - public void call(Integer n) { - int n0 = workers.size(); - if (n0 < n) { - for (int i = n0; i < n; i++) { - DynamicWorker dw = new DynamicWorker<>(++id, DynamicConcurrentMerge.this); - workers.add(dw); - DynamicConcurrentMerge.this.request(1); - dw.tryNext(); - } - } else if (n0 > n) { - for (int i = 0; i < n; i++) { - workers.get(i).start(); - } - - for (int i = n0 - 1; i >= n; i--) { - workers.get(i).stop(); - } - } - - if (!once.get() && once.compareAndSet(false, true)) { - DynamicConcurrentMerge.this.request(n); - } - } - }, new Action1() { - @Override - public void call(Throwable e) {DynamicConcurrentMerge.this.onError(e);} - }); - - composite.add(wc); - } - - void requestMore(long n) { - request(n); - } - - @Override - public void onNext(T t) { - queue.offer(t); - wipActive.getAndIncrement(); - for (DynamicWorker w : workers) { - w.tryNext(); - } - } - - @Override - public void onError(Throwable e) { - composite.unsubscribe(); - actual.onError(e); - } - - @Override - public void onCompleted() { - if (wipActive.decrementAndGet() == 0) { - actual.onCompleted(); - } - } - } - - static final class DynamicWorker { - private final long id; - private final AtomicBoolean running; - private final DynamicConcurrentMerge parent; - private final AtomicBoolean stop; - - public DynamicWorker(long id, DynamicConcurrentMerge parent) { - this.id = id; - this.parent = parent; - this.stop = new AtomicBoolean(); - this.running = new AtomicBoolean(); - } - - public void tryNext() { - if (!running.get() && running.compareAndSet(false, true)) { - T t; - if (stop.get()) { - parent.workers.remove(this); - return; - } - t = parent.queue.poll(); - if (t == null) { - running.set(false); - return; - } - - Observable out = parent.mapper.call(t); - - final Subscriber s = new Subscriber() { - @Override - public void onNext(R t) { - parent.actual.onNext(t); - } - - @Override - public void onError(Throwable e) { - parent.onError(e); - } - - @Override - public void onCompleted() { - parent.onCompleted(); - if (parent.wipActive.get() != 0) { - running.set(false); - parent.requestMore(1); - tryNext(); - } - } - }; - - parent.composite.add(s); - s.add(Subscriptions.create(new Action0() { - @Override - public void call() {parent.composite.remove(s);} - })); - - out.subscribe(s); - } - } - - public void start() { - stop.set(false); - tryNext(); - } - - public void stop() { - stop.set(true); - if (running.compareAndSet(false, true)) { - parent.workers.remove(this); - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 24bbb0fad3..f626104517 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -219,7 +219,6 @@ Downloads directory - Simultaneous downloads Only download over Wi-Fi Remove when marked as read Remove after read