diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt index 5c52cc9bd..883ac6081 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt @@ -20,17 +20,20 @@ import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadService import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.source.AnimeSourceManager import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.ui.anime.AnimeController import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.watcher.WatcherActivity +import eu.kanade.tachiyomi.util.lang.awaitSingle import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.system.notificationManager import eu.kanade.tachiyomi.util.system.toast +import kotlinx.coroutines.runBlocking import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy @@ -433,7 +436,8 @@ class NotificationReceiver : BroadcastReceiver() { * @param episode episode that needs to be opened */ internal fun openEpisodePendingActivity(context: Context, anime: Anime, episode: Episode): PendingIntent { - val newIntent = WatcherActivity.newIntent(context, anime, episode) + val source = Injekt.get().getOrStub(anime.source) + val newIntent = WatcherActivity.newIntent(context, anime, episode, runBlocking { source.fetchEpisodeLink(episode).awaitSingle() }) return PendingIntent.getActivity(context, anime.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/AnimeSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/AnimeSource.kt index a8e654bdf..3d7d81112 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/AnimeSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/AnimeSource.kt @@ -37,13 +37,21 @@ interface AnimeSource : tachiyomi.source.AnimeSource { fun fetchAnimeDetails(anime: SAnime): Observable /** - * Returns an observable with all the available chapters for a anime. + * Returns an observable with all the available episodes for an anime. * * @param anime the anime to update. */ @Deprecated("Use getEpisodeList instead") fun fetchEpisodeList(anime: SAnime): Observable> + /** + * Returns an observable with a link for the episode of an anime. + * + * @param episode the episode to get the link for. + */ + @Deprecated("Use getEpisodeList instead") + fun fetchEpisodeLink(episode: SEpisode): Observable + /** * Returns an observable with the list of pages a chapter has. * @@ -64,7 +72,7 @@ interface AnimeSource : tachiyomi.source.AnimeSource { } /** - * [1.x API] Get all the available chapters for a anime. + * [1.x API] Get all the available episodes for a anime. */ @Suppress("DEPRECATION") override suspend fun getEpisodeList(anime: AnimeInfo): List { @@ -72,6 +80,14 @@ interface AnimeSource : tachiyomi.source.AnimeSource { .map { it.toEpisodeInfo() } } + /** + * [1.x API] Get a link for the episode of an anime. + */ + @Suppress("DEPRECATION") + override suspend fun getEpisodeLink(episode: EpisodeInfo): String { + return fetchEpisodeLink(episode.toSEpisode()).awaitSingle() + } + /** * [1.x API] Get the list of pages a chapter has. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/AnimeSourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/AnimeSourceManager.kt index 5c5eaff48..1b5173f25 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/AnimeSourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/AnimeSourceManager.kt @@ -62,6 +62,10 @@ open class AnimeSourceManager(private val context: Context) { return Observable.error(getSourceNotInstalledException()) } + override fun fetchEpisodeLink(episode: SEpisode): Observable { + return Observable.error(getSourceNotInstalledException()) + } + override fun fetchPageList(episode: SEpisode): Observable> { return Observable.error(getSourceNotInstalledException()) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/LocalAnimeSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/LocalAnimeSource.kt index 609a68afe..63425948e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/LocalAnimeSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/LocalAnimeSource.kt @@ -203,6 +203,12 @@ class LocalAnimeSource(private val context: Context) : AnimeCatalogueSource { return Observable.just(episodes) } + override fun fetchEpisodeLink(episode: SEpisode): Observable { + val link = episode.url + + return Observable.just(link) + } + /** * Strips the anime title from a episode name, matching only based on alphanumeric and whitespace * characters. diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/AnimeHttpSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/AnimeHttpSource.kt index 7cb4955ab..a0be443dd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/AnimeHttpSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/AnimeHttpSource.kt @@ -218,6 +218,14 @@ abstract class AnimeHttpSource : AnimeCatalogueSource { } } + override fun fetchEpisodeLink(episode: SEpisode): Observable { + return client.newCall(episodeLinkRequest(episode)) + .asObservableSuccess() + .map { response -> + episodeLinkParse(response) + } + } + /** * Returns the request for updating the episode list. Override only if it's needed to override * the url, send different headers or request method like POST. @@ -228,6 +236,16 @@ abstract class AnimeHttpSource : AnimeCatalogueSource { return GET(baseUrl + anime.url, headers) } + /** + * Returns the request for getting the episode link. Override only if it's needed to override + * the url, send different headers or request method like POST. + * + * @param episode the episode to look for links. + */ + protected open fun episodeLinkRequest(episode: SEpisode): Request { + return GET(baseUrl + episode.url, headers) + } + /** * Parses the response from the site and returns a list of episodes. * @@ -235,6 +253,13 @@ abstract class AnimeHttpSource : AnimeCatalogueSource { */ protected abstract fun episodeListParse(response: Response): List + /** + * Parses the response from the site and returns a list of episodes. + * + * @param response the response from the site. + */ + protected abstract fun episodeLinkParse(response: Response): String + /** * Returns an observable with the page list for a episode. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/ParsedAnimeHttpSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/ParsedAnimeHttpSource.kt index cb15a8cda..e773124dc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/ParsedAnimeHttpSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/ParsedAnimeHttpSource.kt @@ -159,6 +159,21 @@ abstract class ParsedAnimeHttpSource : AnimeHttpSource() { */ protected abstract fun episodeListSelector(): String + /** + * Parses the response from the site and returns a list of episodes. + * + * @param response the response from the site. + */ + override fun episodeLinkParse(response: Response): String { + val document = response.asJsoup() + return linkFromElement(document.select(episodeLinkSelector()).first()) + } + + /** + * Returns the Jsoup selector that returns a list of [Element] corresponding to each episode. + */ + protected abstract fun episodeLinkSelector(): String + /** * Returns a episode from the given element. * @@ -166,6 +181,13 @@ abstract class ParsedAnimeHttpSource : AnimeHttpSource() { */ protected abstract fun episodeFromElement(element: Element): SEpisode + /** + * Returns a episode from the given element. + * + * @param element an element obtained from [episodeListSelector]. + */ + protected abstract fun linkFromElement(element: Element): String + /** * Parses the response from the site and returns the page list. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/anime/AnimeController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/anime/AnimeController.kt index 2025b126b..c643d875a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/anime/AnimeController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/anime/AnimeController.kt @@ -79,6 +79,9 @@ import eu.kanade.tachiyomi.util.view.shrinkOnScroll import eu.kanade.tachiyomi.util.view.snack import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import okhttp3.Callback import reactivecircus.flowbinding.recyclerview.scrollEvents import reactivecircus.flowbinding.swiperefreshlayout.refreshes import timber.log.Timber @@ -722,6 +725,14 @@ class AnimeController : } } + private fun fetchEpisodeLinksFromSource(manualFetch: Boolean = false, episode: Episode): String { + return presenter.fetchEpisodeLinksFromSource(manualFetch, episode) + } + + fun onFetchEpisodeLinksError(error: Throwable) { + activity?.toast("no links found") + } + fun onEpisodeDownloadUpdate(download: AnimeDownload) { episodesAdapter?.currentItems?.find { it.id == download.episode.id }?.let { episodesAdapter?.updateItem(it, it.status) @@ -730,11 +741,16 @@ class AnimeController : fun openEpisode(episode: Episode, hasAnimation: Boolean = false) { val activity = activity ?: return - val intent = WatcherActivity.newIntent(activity, presenter.anime, episode) - if (hasAnimation) { - intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) + runBlocking { + launch { + val url = fetchEpisodeLinksFromSource(false, episode) + val intent = WatcherActivity.newIntent(activity, presenter.anime, episode, url) + if (hasAnimation) { + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) + } + startActivityForResult(intent, REQUEST_SECONDS) + } } - startActivityForResult(intent, REQUEST_SECONDS) } override fun onItemClick(view: View?, position: Int): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/anime/AnimePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/anime/AnimePresenter.kt index 0e5d6a379..058e84165 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/anime/AnimePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/anime/AnimePresenter.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.anime import android.content.Context import android.net.Uri import android.os.Bundle +import android.util.Log import com.jakewharton.rxrelay.PublishRelay import eu.kanade.tachiyomi.data.cache.AnimeCoverCache import eu.kanade.tachiyomi.data.database.AnimeDatabaseHelper @@ -19,6 +20,7 @@ import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.source.AnimeSource import eu.kanade.tachiyomi.source.LocalAnimeSource +import eu.kanade.tachiyomi.source.model.toEpisodeInfo import eu.kanade.tachiyomi.source.model.toSAnime import eu.kanade.tachiyomi.source.model.toSEpisode import eu.kanade.tachiyomi.ui.anime.episode.EpisodeItem @@ -31,10 +33,7 @@ import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.withUIContext import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.State -import kotlinx.coroutines.Job -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.supervisorScope +import kotlinx.coroutines.* import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -43,6 +42,8 @@ import timber.log.Timber import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.Date +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine class AnimePresenter( val anime: Anime, @@ -83,6 +84,11 @@ class AnimePresenter( */ private var fetchEpisodesJob: Job? = null + /** + * Subscription to retrieve the new link of episodes from the source. + */ + private var fetchEpisodeLinksJob: Job? = null + /** * Subscription to observe download status changes. */ @@ -391,6 +397,28 @@ class AnimePresenter( } } + /** + * Requests an updated list of episodes from the source. + */ + fun fetchEpisodeLinksFromSource(manualFetch: Boolean = false, episode: Episode): String { + hasRequested = true + var link = runBlocking { + return@runBlocking suspendCoroutine { continuation -> + var link: String + presenterScope.launchIO { + try { + link = source.getEpisodeLink(episode.toEpisodeInfo()) + continuation.resume(link) + } catch (e: Throwable) { + withUIContext { view?.onFetchEpisodeLinksError(e) } + } + } + } + } + Log.i("lol", "omg" + link) + return link + } + /** * Updates the UI after applying the filters. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index f3983e981..728514d4b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -43,8 +43,8 @@ import eu.kanade.tachiyomi.ui.base.controller.TabbedController import eu.kanade.tachiyomi.ui.base.controller.ToolbarLiftOnScrollController import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.browse.BrowseController -import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceController +import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController import eu.kanade.tachiyomi.ui.download.DownloadController import eu.kanade.tachiyomi.ui.library.LibraryController diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/watcher/WatcherActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/watcher/WatcherActivity.kt index 97b1966ab..c832b4487 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/watcher/WatcherActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/watcher/WatcherActivity.kt @@ -42,6 +42,7 @@ class WatcherActivity : AppCompatActivity() { setContentView(R.layout.watcher_activity) playerView = findViewById(R.id.player_view) dataSourceFactory = DefaultDataSourceFactory(this, Util.getUserAgent(this, "xyz.jmir.tachiyomi.mi")) + Log.i("uri is ", intent.getStringExtra("uri")) mediaItem = MediaItem.Builder() .setUri(intent.getStringExtra("uri")) .setMimeType(MimeTypes.VIDEO_MP4) @@ -127,12 +128,13 @@ class WatcherActivity : AppCompatActivity() { } companion object { - fun newIntent(context: Context, anime: Anime, episode: Episode): Intent { + fun newIntent(context: Context, anime: Anime, episode: Episode, url: String): Intent { return Intent(context, WatcherActivity::class.java).apply { putExtra("anime", anime.id) putExtra("episode", episode) putExtra("second", episode.last_second_seen) - putExtra("uri", episode.url) + putExtra("uri", url) + Log.i("bruhh", url) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) } } diff --git a/app/src/main/java/tachiyomi/source/AnimeSource.kt b/app/src/main/java/tachiyomi/source/AnimeSource.kt index 672bcb887..24d37ba82 100644 --- a/app/src/main/java/tachiyomi/source/AnimeSource.kt +++ b/app/src/main/java/tachiyomi/source/AnimeSource.kt @@ -36,6 +36,13 @@ interface AnimeSource { */ suspend fun getEpisodeList(anime: AnimeInfo): List + /** + * Returns an observable with all the available chapters for a anime. + * + * @param anime the anime to update. + */ + suspend fun getEpisodeLink(episode: EpisodeInfo): String + /** * Returns an observable with the list of pages a chapter has. *