From 0c4a73a72fbd94d5dd5c7e5bc785d38cfbab42c4 Mon Sep 17 00:00:00 2001 From: LagradOst <11805592+LagradOst@users.noreply.github.com> Date: Fri, 29 Jul 2022 17:51:28 +0200 Subject: [PATCH 1/4] delete bookmarks and watch history --- .../cloudstream3/ui/home/HomeFragment.kt | 51 +++++++++++++++++-- .../cloudstream3/utils/DataStoreHelper.kt | 13 +++++ .../res/layout/home_episodes_expanded.xml | 24 +++++++-- 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index 3700eab4..c84679f1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.ui.home import android.annotation.SuppressLint import android.app.Activity import android.content.Context +import android.content.DialogInterface import android.content.Intent import android.content.res.Configuration import android.net.Uri @@ -11,6 +12,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.* +import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.SearchView import androidx.core.view.isGone import androidx.core.view.isVisible @@ -50,6 +52,8 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStoreHelper +import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllBookmarkedData +import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllResumeStateIds import com.lagradost.cloudstream3.utils.DataStoreHelper.removeLastWatched import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState import com.lagradost.cloudstream3.utils.Event @@ -91,6 +95,7 @@ import kotlinx.android.synthetic.main.fragment_home.home_watch_holder import kotlinx.android.synthetic.main.fragment_home.home_watch_parent_item_title import kotlinx.android.synthetic.main.fragment_home.result_error_text import kotlinx.android.synthetic.main.fragment_home_tv.* +import kotlinx.android.synthetic.main.home_episodes_expanded.* import java.util.* const val HOME_BOOKMARK_VALUE_LIST = "home_bookmarked_last_list" @@ -117,7 +122,7 @@ class HomeFragment : Fragment() { val errorProfilePic = errorProfilePics.random() - fun Activity.loadHomepageList(item: HomePageList) { + fun Activity.loadHomepageList(item: HomePageList, deleteCallback: (() -> Unit)? = null) { val context = this val bottomSheetDialogBuilder = BottomSheetDialog(context) bottomSheetDialogBuilder.setContentView(R.layout.home_episodes_expanded) @@ -128,10 +133,44 @@ class HomeFragment : Fragment() { val titleHolder = bottomSheetDialogBuilder.findViewById(R.id.home_expanded_drag_down)!! + val delete = bottomSheetDialogBuilder.home_expanded_delete + delete.isGone = deleteCallback == null + if (deleteCallback != null) { + delete.setOnClickListener { + try { + val builder: AlertDialog.Builder = AlertDialog.Builder(context) + val dialogClickListener = + DialogInterface.OnClickListener { _, which -> + when (which) { + DialogInterface.BUTTON_POSITIVE -> { + deleteCallback.invoke() + bottomSheetDialogBuilder.dismissSafe(this) + } + DialogInterface.BUTTON_NEGATIVE -> {} + } + } + + builder.setTitle(R.string.delete_file) + .setMessage( + context.getString(R.string.delete_message).format( + item.name + ) + ) + .setPositiveButton(R.string.delete, dialogClickListener) + .setNegativeButton(R.string.cancel, dialogClickListener) + .show() + } catch (e: Exception) { + logError(e) + // ye you somehow fucked up formatting did you? + } + } + } + titleHolder.setOnClickListener { bottomSheetDialogBuilder.dismissSafe(this) } + // Span settings recycle.spanCount = currentSpan @@ -642,7 +681,10 @@ class HomeFragment : Fragment() { getString(R.string.error_bookmarks_text), //home_bookmarked_parent_item_title?.text?.toString() ?: getString(R.string.error_bookmarks_text), bookmarks ) - ) + ) { + deleteAllBookmarkedData() + homeViewModel.loadStoredData(null) + } } } @@ -665,7 +707,10 @@ class HomeFragment : Fragment() { ?: getString(R.string.continue_watching), resumeWatching ) - ) + ) { + deleteAllResumeStateIds() + homeViewModel.loadResumeWatching() + } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt index 3fef6756..9367189b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey +import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.DubStatus import com.lagradost.cloudstream3.SearchQuality @@ -76,6 +77,18 @@ object DataStoreHelper { } } + fun deleteAllResumeStateIds() { + val folder = "$currentAccount/$RESULT_RESUME_WATCHING" + removeKeys(folder) + } + + fun deleteAllBookmarkedData() { + val folder1 = "$currentAccount/$RESULT_WATCH_STATE" + val folder2 = "$currentAccount/$RESULT_WATCH_STATE_DATA" + removeKeys(folder1) + removeKeys(folder2) + } + fun getAllResumeStateIds(): List? { val folder = "$currentAccount/$RESULT_RESUME_WATCHING" return getKeys(folder)?.mapNotNull { diff --git a/app/src/main/res/layout/home_episodes_expanded.xml b/app/src/main/res/layout/home_episodes_expanded.xml index d016b9a1..4f028481 100644 --- a/app/src/main/res/layout/home_episodes_expanded.xml +++ b/app/src/main/res/layout/home_episodes_expanded.xml @@ -1,6 +1,7 @@ - + + + + + + Date: Fri, 29 Jul 2022 19:35:44 +0200 Subject: [PATCH 2/4] Added AniPlay provider and fixed M3u8Helper.selectBest (#1362) * Added AniPlay provider and fixed M3u8Helper selectBest * Code changes for better code quality --- .../com/lagradost/cloudstream3/MainAPI.kt | 1 + .../animeproviders/AniPlayProvider.kt | 215 ++++++++++++++++++ .../cloudstream3/utils/M3u8Helper.kt | 2 +- 3 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/animeproviders/AniPlayProvider.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index fa05f4d3..4a751932 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -131,6 +131,7 @@ object APIHolder { NineAnimeProvider(), AnimeWorldProvider(), AnimeSaturnProvider(), + AniPlayProvider(), ZoroProvider(), DubbedAnimeProvider(), MonoschinosProvider(), diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AniPlayProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AniPlayProvider.kt new file mode 100644 index 00000000..cb04d3d2 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AniPlayProvider.kt @@ -0,0 +1,215 @@ +package com.lagradost.cloudstream3.animeproviders + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId +import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration +import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper +import com.lagradost.cloudstream3.utils.Qualities + +class AniPlayProvider : MainAPI() { + override var mainUrl = "https://aniplay.it" + override var name = "AniPlay" + override var lang = "it" + override val hasMainPage = true + private val dubIdentifier = " (ITA)" + + override val supportedTypes = setOf( + TvType.Anime, + TvType.AnimeMovie, + TvType.OVA + ) + + companion object { + fun getStatus(t: String?): ShowStatus? { + return when (t?.lowercase()) { + "completato" -> ShowStatus.Completed + "in corso" -> ShowStatus.Ongoing + else -> null // "annunciato" + } + } + fun getType(t: String?): TvType { + return when (t?.lowercase()) { + "ona" -> TvType.OVA + "movie" -> TvType.AnimeMovie + else -> TvType.Anime //"serie", "special" + } + } + } + + private fun isDub(title: String): Boolean{ + return title.contains(dubIdentifier) + } + + data class ApiPoster( + @JsonProperty("imageFull") val posterUrl: String + ) + + data class ApiMainPageAnime( + @JsonProperty("animeId") val id: Int, + @JsonProperty("episodeNumber") val episode: String?, + @JsonProperty("animeTitle") val title: String, + @JsonProperty("animeType") val type: String, + @JsonProperty("fullHd") val fullHD: Boolean, + @JsonProperty("animeVerticalImages") val posters: List + ) + + data class ApiSearchResult( + @JsonProperty("id") val id: Int, + @JsonProperty("title") val title: String, + @JsonProperty("status") val status: String, + @JsonProperty("type") val type: String, + @JsonProperty("verticalImages") val posters: List + ) + + data class ApiGenres( + @JsonProperty("description") val name: String + ) + data class ApiWebsite( + @JsonProperty("listWebsiteId") val websiteId: Int, + @JsonProperty("url") val url: String + ) + + data class ApiEpisode( + @JsonProperty("id") val id: Int, + @JsonProperty("title") val title: String?, + @JsonProperty("episodeNumber") val number: String, + ) + + private fun ApiEpisode.toEpisode() : Episode? { + val number = this.number.toIntOrNull() ?: return null + return Episode( + data = "$mainUrl/api/episode/${this.id}", + episode = number, + name = this.title + ) + } + + data class ApiSeason( + @JsonProperty("id") val id: Int, + @JsonProperty("name") val name: String + ) + + private suspend fun ApiSeason.toEpisodeList(url: String) : List { + return app.get("$url/season/${this.id}").parsed>().mapNotNull { it.toEpisode() } + } + + data class ApiAnime( + @JsonProperty("title") val title: String, + @JsonProperty("alternativeTitle") val japTitle: String?, + @JsonProperty("episodeDuration") val duration: Int, + @JsonProperty("storyline") val plot: String, + @JsonProperty("type") val type: String, + @JsonProperty("status") val status: String, + @JsonProperty("genres") val genres: List, + @JsonProperty("verticalImages") val posters: List, + @JsonProperty("listWebsites") val websites: List, + @JsonProperty("episodes") val episodes: List, + @JsonProperty("seasons") val seasons: List? + ) + + data class ApiEpisodeUrl( + @JsonProperty("videoUrl") val url: String + ) + + override suspend fun getMainPage(): HomePageResponse { + val response = app.get("$mainUrl/api/home/latest-episodes?page=0").parsed>() + + val results = response.map{ + val isDub = isDub(it.title) + newAnimeSearchResponse( + name = if (isDub) it.title.replace(dubIdentifier, "") else it.title, + url = "$mainUrl/api/anime/${it.id}", + type = getType(it.type), + ){ + addDubStatus(isDub, it.episode?.toIntOrNull()) + this.posterUrl = it.posters.first().posterUrl + this.quality = if (it.fullHD) SearchQuality.HD else null + } + } + return HomePageResponse(listOf(HomePageList("Ultime uscite",results))) + } + + override suspend fun search(query: String): List { + val response = app.get("$mainUrl/api/anime/advanced-search?page=0&size=36&query=$query").parsed>() + + return response.map { + val isDub = isDub(it.title) + + newAnimeSearchResponse( + name = if (isDub) it.title.replace(dubIdentifier, "") else it.title, + url = "$mainUrl/api/anime/${it.id}", + type = getType(it.type), + ){ + addDubStatus(isDub) + this.posterUrl = it.posters.first().posterUrl + } + } + } + + override suspend fun load(url: String): LoadResponse { + + val response = app.get(url).parsed() + + val tags: List = response.genres.map { it.name } + + val malId: Int? = response.websites.find { it.websiteId == 1 }?.url?.removePrefix("https://myanimelist.net/anime/")?.split("/")?.first()?.toIntOrNull() + val aniListId: Int? = response.websites.find { it.websiteId == 4 }?.url?.removePrefix("https://anilist.co/anime/")?.split("/")?.first()?.toIntOrNull() + + val episodes = if (response.seasons.isNullOrEmpty()) response.episodes.mapNotNull { it.toEpisode() } else response.seasons.map{ it.toEpisodeList(url) }.flatten() + val isDub = isDub(response.title) + + return newAnimeLoadResponse(response.title, url, getType(response.type)) { + this.name = if (isDub) response.title.replace(dubIdentifier, "") else response.title + this.japName = response.japTitle + this.plot = response.plot + this.tags = tags + this.showStatus = getStatus(response.status) + addPoster(response.posters.first().posterUrl) + addEpisodes(if (isDub) DubStatus.Dubbed else DubStatus.Subbed, episodes) + addMalId(malId) + addAniListId(aniListId) + addDuration(response.duration.toString()) + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + val episode = app.get(data).parsed() + + if(episode.url.contains(".m3u8")){ + val m3u8Helper = M3u8Helper() + val streams = m3u8Helper.m3u8Generation(M3u8Helper.M3u8Stream(episode.url,Qualities.Unknown.value), false) + + streams.forEach { + callback.invoke( + ExtractorLink( + name, + name, + it.streamUrl, + referer = mainUrl, + quality = it.quality ?: Qualities.Unknown.value, + isM3u8 = it.streamUrl.contains(".m3u8"))) } + return true + } + + callback.invoke( + ExtractorLink( + name, + name, + episode.url, + referer = mainUrl, + quality = Qualities.Unknown.value, + isM3u8 = false, + ) + ) + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/M3u8Helper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/M3u8Helper.kt index 99dfe233..d8065f0c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/M3u8Helper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/M3u8Helper.kt @@ -103,7 +103,7 @@ class M3u8Helper { }.filter { listOf("m3u", "m3u8").contains(absoluteExtensionDetermination(it.streamUrl)) } - return result.getOrNull(0) + return result.lastOrNull() } private fun getParentLink(uri: String): String { From 334af5acfbf0ecd72606e9134736920c2c28af4b Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 29 Jul 2022 17:35:59 +0000 Subject: [PATCH 3/4] chore(docs): update list of sites --- docs/providers.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/providers.json b/docs/providers.json index 5c19425c..86efa83d 100644 --- a/docs/providers.json +++ b/docs/providers.json @@ -23,6 +23,12 @@ "status": 1, "url": "https://altadefinizione.tienda" }, + "AniPlayProvider": { + "language": "it", + "name": "AniPlay", + "status": 1, + "url": "https://aniplay.it" + }, "AniflixProvider": { "language": "en", "name": "Aniflix", From f94cf6c8523643b46a8eb06cf5e31e8b7316dd07 Mon Sep 17 00:00:00 2001 From: Davide <49226282+pizidavi@users.noreply.github.com> Date: Fri, 29 Jul 2022 22:31:00 +0200 Subject: [PATCH 4/4] Updated Italian translation (#1364) --- app/src/main/res/values-it/strings.xml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index e5733112..89159a12 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -36,6 +36,7 @@ %d min + Riproduci con CloudStream Home Cerca Scaricati @@ -62,6 +63,7 @@ Riguardando Riproduci film + Riproduci Livestream Stream Torrent Fonti Sottotitoli @@ -185,7 +187,7 @@ Errore nel backup %s Cerca - Accounts e Crediti + Accounts Aggiornamenti e Backup Cos\'è Nginx? Nginx è un software che può essere utilizzato per visualizzare i file da un server di proprietà. Fare clic per vedere la guida all\'installazione di Nginx @@ -204,9 +206,9 @@ Aggiorna alle prerelease Cerca per aggiornamenti alle prerelease invice di cercare solo le release complete GitHub - App di light novel degli stessi sviluppatori - App di anime dagli stessi sviluppatori - Entra Discord + App per Light Novel degli stessi sviluppatori + App per Anime degli stessi sviluppatori + Entra in Discord Dai una banana agli sviluppatori Dai una banana @@ -263,6 +265,7 @@ Torrent Documentari Drama Asiatici + Livestreams Film @@ -273,6 +276,7 @@ Torrent Documentario Drama Asiatico + Livestream Source error Remote error @@ -445,11 +449,14 @@ Titolo e Risoluzione Titolo Risoluzione - ID invalido + ID non valido Dati non validi + Url non valido Errore Trailer + Link allo stream +