forked from recloudstream/cloudstream
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
f3e7163436
19 changed files with 395 additions and 34 deletions
|
@ -10,7 +10,6 @@ import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
|
||||||
import com.lagradost.cloudstream3.animeproviders.*
|
import com.lagradost.cloudstream3.animeproviders.*
|
||||||
import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider
|
import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider
|
||||||
import com.lagradost.cloudstream3.movieproviders.*
|
import com.lagradost.cloudstream3.movieproviders.*
|
||||||
|
@ -93,6 +92,7 @@ object APIHolder {
|
||||||
TantifilmProvider(),
|
TantifilmProvider(),
|
||||||
CineblogProvider(),
|
CineblogProvider(),
|
||||||
AltadefinizioneProvider(),
|
AltadefinizioneProvider(),
|
||||||
|
FilmpertuttiProvider(),
|
||||||
HDMovie5(),
|
HDMovie5(),
|
||||||
RebahinProvider(),
|
RebahinProvider(),
|
||||||
LayarKacaProvider(),
|
LayarKacaProvider(),
|
||||||
|
@ -1004,7 +1004,7 @@ interface LoadResponse {
|
||||||
|
|
||||||
fun LoadResponse?.isEpisodeBased(): Boolean {
|
fun LoadResponse?.isEpisodeBased(): Boolean {
|
||||||
if (this == null) return false
|
if (this == null) return false
|
||||||
return (this is AnimeLoadResponse || this is TvSeriesLoadResponse) && this.type.isEpisodeBased()
|
return this is EpisodeResponse && this.type.isEpisodeBased()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun LoadResponse?.isAnimeBased(): Boolean {
|
fun LoadResponse?.isAnimeBased(): Boolean {
|
||||||
|
@ -1017,6 +1017,17 @@ fun TvType?.isEpisodeBased(): Boolean {
|
||||||
return (this == TvType.TvSeries || this == TvType.Anime)
|
return (this == TvType.TvSeries || this == TvType.Anime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class NextAiring(
|
||||||
|
val episode: Int,
|
||||||
|
val unixTime: Long,
|
||||||
|
)
|
||||||
|
|
||||||
|
interface EpisodeResponse {
|
||||||
|
var showStatus: ShowStatus?
|
||||||
|
var nextAiring: NextAiring?
|
||||||
|
}
|
||||||
|
|
||||||
data class TorrentLoadResponse(
|
data class TorrentLoadResponse(
|
||||||
override var name: String,
|
override var name: String,
|
||||||
override var url: String,
|
override var url: String,
|
||||||
|
@ -1050,7 +1061,7 @@ data class AnimeLoadResponse(
|
||||||
override var year: Int? = null,
|
override var year: Int? = null,
|
||||||
|
|
||||||
var episodes: MutableMap<DubStatus, List<Episode>> = mutableMapOf(),
|
var episodes: MutableMap<DubStatus, List<Episode>> = mutableMapOf(),
|
||||||
var showStatus: ShowStatus? = null,
|
override var showStatus: ShowStatus? = null,
|
||||||
|
|
||||||
override var plot: String? = null,
|
override var plot: String? = null,
|
||||||
override var tags: List<String>? = null,
|
override var tags: List<String>? = null,
|
||||||
|
@ -1064,7 +1075,8 @@ data class AnimeLoadResponse(
|
||||||
override var comingSoon: Boolean = false,
|
override var comingSoon: Boolean = false,
|
||||||
override var syncData: MutableMap<String, String> = mutableMapOf(),
|
override var syncData: MutableMap<String, String> = mutableMapOf(),
|
||||||
override var posterHeaders: Map<String, String>? = null,
|
override var posterHeaders: Map<String, String>? = null,
|
||||||
) : LoadResponse
|
override var nextAiring: NextAiring? = null,
|
||||||
|
) : LoadResponse, EpisodeResponse
|
||||||
|
|
||||||
fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List<Episode>?) {
|
fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List<Episode>?) {
|
||||||
if (episodes == null) return
|
if (episodes == null) return
|
||||||
|
@ -1222,7 +1234,7 @@ data class TvSeriesLoadResponse(
|
||||||
override var year: Int? = null,
|
override var year: Int? = null,
|
||||||
override var plot: String? = null,
|
override var plot: String? = null,
|
||||||
|
|
||||||
var showStatus: ShowStatus? = null,
|
override var showStatus: ShowStatus? = null,
|
||||||
override var rating: Int? = null,
|
override var rating: Int? = null,
|
||||||
override var tags: List<String>? = null,
|
override var tags: List<String>? = null,
|
||||||
override var duration: Int? = null,
|
override var duration: Int? = null,
|
||||||
|
@ -1232,7 +1244,8 @@ data class TvSeriesLoadResponse(
|
||||||
override var comingSoon: Boolean = false,
|
override var comingSoon: Boolean = false,
|
||||||
override var syncData: MutableMap<String, String> = mutableMapOf(),
|
override var syncData: MutableMap<String, String> = mutableMapOf(),
|
||||||
override var posterHeaders: Map<String, String>? = null,
|
override var posterHeaders: Map<String, String>? = null,
|
||||||
) : LoadResponse
|
override var nextAiring: NextAiring? = null,
|
||||||
|
) : LoadResponse, EpisodeResponse
|
||||||
|
|
||||||
suspend fun MainAPI.newTvSeriesLoadResponse(
|
suspend fun MainAPI.newTvSeriesLoadResponse(
|
||||||
name: String,
|
name: String,
|
||||||
|
|
|
@ -14,6 +14,9 @@ class DoodCxExtractor : DoodLaExtractor() {
|
||||||
class DoodShExtractor : DoodLaExtractor() {
|
class DoodShExtractor : DoodLaExtractor() {
|
||||||
override var mainUrl = "https://dood.sh"
|
override var mainUrl = "https://dood.sh"
|
||||||
}
|
}
|
||||||
|
class DoodWatchExtractor : DoodLaExtractor() {
|
||||||
|
override var mainUrl = "https://dood.watch"
|
||||||
|
}
|
||||||
|
|
||||||
class DoodPmExtractor : DoodLaExtractor() {
|
class DoodPmExtractor : DoodLaExtractor() {
|
||||||
override var mainUrl = "https://dood.pm"
|
override var mainUrl = "https://dood.pm"
|
||||||
|
|
|
@ -10,6 +10,9 @@ class MixDropBz : MixDrop(){
|
||||||
class MixDropCh : MixDrop(){
|
class MixDropCh : MixDrop(){
|
||||||
override var mainUrl = "https://mixdrop.ch"
|
override var mainUrl = "https://mixdrop.ch"
|
||||||
}
|
}
|
||||||
|
class MixDropTo : MixDrop(){
|
||||||
|
override var mainUrl = "https://mixdrop.to"
|
||||||
|
}
|
||||||
|
|
||||||
open class MixDrop : ExtractorApi() {
|
open class MixDrop : ExtractorApi() {
|
||||||
override var name = "MixDrop"
|
override var name = "MixDrop"
|
||||||
|
|
|
@ -114,7 +114,7 @@ class AltadefinizioneProvider : MainAPI() {
|
||||||
|
|
||||||
val tags: List<String> = document.select("#details > li:nth-child(1) > a").map { it.text() }
|
val tags: List<String> = document.select("#details > li:nth-child(1) > a").map { it.text() }
|
||||||
|
|
||||||
val trailerurl = document.selectFirst("#showtrailer > div > div > iframe")!!.attr("src")
|
val trailerurl = document.selectFirst("#showtrailer > div > div > iframe")?.attr("src")
|
||||||
|
|
||||||
return newMovieLoadResponse(
|
return newMovieLoadResponse(
|
||||||
title,
|
title,
|
||||||
|
|
|
@ -5,6 +5,7 @@ import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||||
|
|
||||||
|
|
||||||
class CineblogProvider : MainAPI() {
|
class CineblogProvider : MainAPI() {
|
||||||
override var lang = "it"
|
override var lang = "it"
|
||||||
override var mainUrl = "https://cb01.rip"
|
override var mainUrl = "https://cb01.rip"
|
||||||
|
@ -28,6 +29,7 @@ class CineblogProvider : MainAPI() {
|
||||||
val home = soup.select("article.item.movies").map {
|
val home = soup.select("article.item.movies").map {
|
||||||
val title = it.selectFirst("div.data > h3 > a")!!.text().substringBefore("(")
|
val title = it.selectFirst("div.data > h3 > a")!!.text().substringBefore("(")
|
||||||
val link = it.selectFirst("div.poster > a")!!.attr("href")
|
val link = it.selectFirst("div.poster > a")!!.attr("href")
|
||||||
|
val quality = getQualityFromString(it.selectFirst("span.quality")?.text())
|
||||||
TvSeriesSearchResponse(
|
TvSeriesSearchResponse(
|
||||||
title,
|
title,
|
||||||
link,
|
link,
|
||||||
|
@ -36,6 +38,7 @@ class CineblogProvider : MainAPI() {
|
||||||
it.selectFirst("img")!!.attr("src"),
|
it.selectFirst("img")!!.attr("src"),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
quality = quality
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,8 +85,7 @@ class CineblogProvider : MainAPI() {
|
||||||
href,
|
href,
|
||||||
this.name,
|
this.name,
|
||||||
TvType.Movie,
|
TvType.Movie,
|
||||||
poster,
|
poster
|
||||||
null
|
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -96,7 +98,6 @@ class CineblogProvider : MainAPI() {
|
||||||
val title = document.selectFirst("div.data > h1")!!.text().substringBefore("(")
|
val title = document.selectFirst("div.data > h1")!!.text().substringBefore("(")
|
||||||
val description = document.select("#info > div.wp-content > p").html().toString()
|
val description = document.select("#info > div.wp-content > p").html().toString()
|
||||||
val rating = null
|
val rating = null
|
||||||
|
|
||||||
var year = document.selectFirst(" div.data > div.extra > span.date")!!.text().substringAfter(",")
|
var year = document.selectFirst(" div.data > div.extra > span.date")!!.text().substringAfter(",")
|
||||||
.filter { it.isDigit() }
|
.filter { it.isDigit() }
|
||||||
if (year.length > 4) {
|
if (year.length > 4) {
|
||||||
|
@ -114,8 +115,7 @@ class CineblogProvider : MainAPI() {
|
||||||
href,
|
href,
|
||||||
this.name,
|
this.name,
|
||||||
TvType.Movie,
|
TvType.Movie,
|
||||||
posterUrl,
|
posterUrl
|
||||||
null
|
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
package com.lagradost.cloudstream3.movieproviders
|
||||||
|
import androidx.core.text.parseAsHtml
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
import com.lagradost.cloudstream3.app
|
||||||
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addRating
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||||
|
import com.lagradost.nicehttp.NiceResponse
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
|
||||||
|
|
||||||
|
class FilmpertuttiProvider : MainAPI() {
|
||||||
|
override var lang = "it"
|
||||||
|
override var mainUrl = "https://www.filmpertutti.buzz"
|
||||||
|
override var name = "Filmpertutti"
|
||||||
|
override val hasMainPage = true
|
||||||
|
override val hasChromecastSupport = true
|
||||||
|
override val supportedTypes = setOf(
|
||||||
|
TvType.Movie,
|
||||||
|
TvType.TvSeries
|
||||||
|
)
|
||||||
|
|
||||||
|
override suspend fun getMainPage(): HomePageResponse {
|
||||||
|
val items = ArrayList<HomePageList>()
|
||||||
|
val urls = listOf(
|
||||||
|
Pair("$mainUrl/category/serie-tv/", "Serie Tv"),
|
||||||
|
Pair("$mainUrl/category/film/azione/", "Azione"),
|
||||||
|
Pair("$mainUrl/category/film/avventura/", "Avventura"),
|
||||||
|
)
|
||||||
|
for ((url, name) in urls) {
|
||||||
|
try {
|
||||||
|
val soup = app.get(url).document
|
||||||
|
val home = soup.select("ul.posts > li").map {
|
||||||
|
val title = it.selectFirst("div.title")!!.text().substringBeforeLast("(").substringBeforeLast("[")
|
||||||
|
val link = it.selectFirst("a")!!.attr("href")
|
||||||
|
val image = it.selectFirst("a")!!.attr("data-thumbnail")
|
||||||
|
val qualitydata = it.selectFirst("div.hd")
|
||||||
|
val quality = if (qualitydata!= null) {
|
||||||
|
getQualityFromString(qualitydata?.text())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
newTvSeriesSearchResponse(
|
||||||
|
title,
|
||||||
|
link) {
|
||||||
|
this.posterUrl = image
|
||||||
|
this.quality = quality
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items.add(HomePageList(name, home))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (items.size <= 0) throw ErrorLoadingException()
|
||||||
|
return HomePageResponse(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
|
val queryformatted = query.replace(" ", "+")
|
||||||
|
val url = "$mainUrl/?s=$queryformatted"
|
||||||
|
val doc = app.get(url).document
|
||||||
|
return doc.select("ul.posts > li").map {
|
||||||
|
val title = it.selectFirst("div.title")!!.text().substringBeforeLast("(").substringBeforeLast("[")
|
||||||
|
val link = it.selectFirst("a")!!.attr("href")
|
||||||
|
val image = it.selectFirst("a")!!.attr("data-thumbnail")
|
||||||
|
val quality = getQualityFromString(it.selectFirst("div.hd")?.text())
|
||||||
|
|
||||||
|
MovieSearchResponse(
|
||||||
|
title,
|
||||||
|
link,
|
||||||
|
this.name,
|
||||||
|
quality = quality,
|
||||||
|
posterUrl = image
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun load(url: String): LoadResponse {
|
||||||
|
val document = app.get(url).document
|
||||||
|
val type =
|
||||||
|
if (document.selectFirst("a.taxonomy.category")!!.attr("href").contains("serie-tv")
|
||||||
|
.not()
|
||||||
|
) TvType.Movie else TvType.TvSeries
|
||||||
|
val title = document.selectFirst("#content > h1")!!.text().substringBeforeLast("(")
|
||||||
|
.substringBeforeLast("[")
|
||||||
|
|
||||||
|
val description = document.selectFirst("i.fa.fa-file-text-o.fa-fw")?.parent()?.nextSibling()?.toString()?.parseAsHtml().toString()
|
||||||
|
|
||||||
|
|
||||||
|
val rating = document.selectFirst("div.rating > div.value")?.text()
|
||||||
|
|
||||||
|
val year =
|
||||||
|
document.selectFirst("#content > h1")?.text()?.substringAfterLast("(")?.filter { it.isDigit() }?.toIntOrNull() ?:
|
||||||
|
description.substringAfter("trasmessa nel").take(6).filter { it.isDigit() }.toIntOrNull() ?:
|
||||||
|
(document.selectFirst("i.fa.fa-calendar.fa-fw")?.parent()?.nextSibling() as Element?)?.text()?.substringAfterLast(" ")?.filter { it.isDigit() }?.toIntOrNull()
|
||||||
|
|
||||||
|
|
||||||
|
val poster = document.selectFirst("div.meta > div > img")?.attr("data-src")
|
||||||
|
|
||||||
|
|
||||||
|
val trailerurl = document.selectFirst("div.youtube-player")?.attr("data-id")?.let{ urldata->
|
||||||
|
"https://www.youtube.com/watch?v=$urldata"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == TvType.TvSeries) {
|
||||||
|
|
||||||
|
val episodeList = ArrayList<Episode>()
|
||||||
|
document.select("div.accordion-item").filter{it.selectFirst("#season > ul > li.s_title > span")!!.text().isNotEmpty()}.map { element ->
|
||||||
|
val season =
|
||||||
|
element.selectFirst("#season > ul > li.s_title > span")!!.text().toInt()
|
||||||
|
element.select("div.episode-wrap").map { episode ->
|
||||||
|
val href =
|
||||||
|
episode.select("#links > div > div > table > tbody:nth-child(2) > tr")
|
||||||
|
.map { it.selectFirst("a")!!.attr("href") }.toJson()
|
||||||
|
val epNum = episode.selectFirst("li.season-no")!!.text().substringAfter("x")
|
||||||
|
.filter { it.isDigit() }.toIntOrNull()
|
||||||
|
val epTitle = episode.selectFirst("li.other_link > a")?.text()
|
||||||
|
|
||||||
|
val posterUrl = episode.selectFirst("figure > img")?.attr("data-src")
|
||||||
|
episodeList.add(
|
||||||
|
Episode(
|
||||||
|
href,
|
||||||
|
epTitle,
|
||||||
|
season,
|
||||||
|
epNum,
|
||||||
|
posterUrl,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newTvSeriesLoadResponse(
|
||||||
|
title,
|
||||||
|
url, type, episodeList
|
||||||
|
) {
|
||||||
|
this.posterUrl = poster
|
||||||
|
this.year = year
|
||||||
|
this.plot = description
|
||||||
|
addRating(rating)
|
||||||
|
addTrailer(trailerurl)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
val urls0 = document.select("div.embed-player")
|
||||||
|
val urls = if (urls0.isNotEmpty()){
|
||||||
|
urls0.map { it.attr("data-id") }.toJson()
|
||||||
|
}
|
||||||
|
else{ document.select("#info > ul > li ").mapNotNull { it.selectFirst("a")?.attr("href") }.toJson() }
|
||||||
|
|
||||||
|
return newMovieLoadResponse(
|
||||||
|
title,
|
||||||
|
url,
|
||||||
|
type,
|
||||||
|
urls
|
||||||
|
) {
|
||||||
|
posterUrl = fixUrlNull(poster)
|
||||||
|
this.year = year
|
||||||
|
this.plot = description
|
||||||
|
addRating(rating)
|
||||||
|
addTrailer(trailerurl)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to be updated when UnshortenUrl is ready
|
||||||
|
suspend fun unshorten_linkup(uri: String): String {
|
||||||
|
var r: NiceResponse? = null
|
||||||
|
var uri = uri
|
||||||
|
when{
|
||||||
|
uri.contains("/tv/") -> uri = uri.replace("/tv/", "/tva/")
|
||||||
|
uri.contains("delta") -> uri = uri.replace("/delta/", "/adelta/")
|
||||||
|
(uri.contains("/ga/") || uri.contains("/ga2/")) -> uri = base64Decode(uri.split('/').last()).trim()
|
||||||
|
uri.contains("/speedx/") -> uri = uri.replace("http://linkup.pro/speedx", "http://speedvideo.net")
|
||||||
|
else -> {
|
||||||
|
r = app.get(uri, allowRedirects = true)
|
||||||
|
uri = r.url
|
||||||
|
val link =
|
||||||
|
Regex("<iframe[^<>]*src=\\'([^'>]*)\\'[^<>]*>").find(r.text)?.value ?:
|
||||||
|
Regex("""action="(?:[^/]+.*?/[^/]+/([a-zA-Z0-9_]+))">""").find(r.text)?.value ?:
|
||||||
|
Regex("""href","((.|\\n)*?)"""").findAll(r.text).elementAtOrNull(1)?.groupValues?.get(1)
|
||||||
|
|
||||||
|
if (link!=null) {
|
||||||
|
uri = link
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val short = Regex("""^https?://.*?(https?://.*)""").find(uri)?.value
|
||||||
|
if (short!=null){
|
||||||
|
uri = short
|
||||||
|
}
|
||||||
|
if (r==null){
|
||||||
|
r = app.get(
|
||||||
|
uri,
|
||||||
|
allowRedirects = false)
|
||||||
|
if (r.headers["location"]!= null){
|
||||||
|
uri = r.headers["location"].toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (uri.contains("snip.")) {
|
||||||
|
if (uri.contains("out_generator")) {
|
||||||
|
uri = Regex("url=(.*)\$").find(uri)!!.value
|
||||||
|
}
|
||||||
|
else if (uri.contains("/decode/")) {
|
||||||
|
uri = app.get(uri, allowRedirects = true).url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uri
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
|
tryParseJson<List<String>>(data)?.forEach { id ->
|
||||||
|
if (id.contains("buckler")){
|
||||||
|
val id2 = unshorten_linkup(id).trim().replace("/v/","/e/").replace("/f/","/e/")
|
||||||
|
loadExtractor(id2, data, callback)
|
||||||
|
}
|
||||||
|
else if (id.contains("isecure")){
|
||||||
|
val doc1 = app.get(id).document
|
||||||
|
val id2 = doc1.selectFirst("iframe")!!.attr("src")
|
||||||
|
loadExtractor(id2, data, callback)
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
loadExtractor(id, data, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
|
|
||||||
class TantifilmProvider : MainAPI() {
|
class TantifilmProvider : MainAPI() {
|
||||||
override var lang = "it"
|
override var lang = "it"
|
||||||
override var mainUrl = "https://www.tantifilm.rodeo"
|
override var mainUrl = "https://www.tantifilm.pics"
|
||||||
override var name = "Tantifilm"
|
override var name = "Tantifilm"
|
||||||
override val hasMainPage = true
|
override val hasMainPage = true
|
||||||
override val hasChromecastSupport = true
|
override val hasChromecastSupport = true
|
||||||
|
|
|
@ -36,11 +36,6 @@ interface SyncAPI : OAuth2API {
|
||||||
override var id: Int? = null,
|
override var id: Int? = null,
|
||||||
) : SearchResponse
|
) : SearchResponse
|
||||||
|
|
||||||
data class SyncNextAiring(
|
|
||||||
val episode: Int,
|
|
||||||
val unixTime: Long,
|
|
||||||
)
|
|
||||||
|
|
||||||
data class SyncStatus(
|
data class SyncStatus(
|
||||||
val status: Int,
|
val status: Int,
|
||||||
/** 1-10 */
|
/** 1-10 */
|
||||||
|
@ -63,7 +58,7 @@ interface SyncAPI : OAuth2API {
|
||||||
var duration: Int? = null,
|
var duration: Int? = null,
|
||||||
var synopsis: String? = null,
|
var synopsis: String? = null,
|
||||||
var airStatus: ShowStatus? = null,
|
var airStatus: ShowStatus? = null,
|
||||||
var nextAiring: SyncNextAiring? = null,
|
var nextAiring: NextAiring? = null,
|
||||||
var studio: List<String>? = null,
|
var studio: List<String>? = null,
|
||||||
var genres: List<String>? = null,
|
var genres: List<String>? = null,
|
||||||
var synonyms: List<String>? = null,
|
var synonyms: List<String>? = null,
|
||||||
|
|
|
@ -98,7 +98,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
return SyncAPI.SyncResult(
|
return SyncAPI.SyncResult(
|
||||||
season.id.toString(),
|
season.id.toString(),
|
||||||
nextAiring = season.nextAiringEpisode?.let {
|
nextAiring = season.nextAiringEpisode?.let {
|
||||||
SyncAPI.SyncNextAiring(
|
NextAiring(
|
||||||
it.episode ?: return@let null,
|
it.episode ?: return@let null,
|
||||||
(it.timeUntilAiring ?: return@let null) + unixTime
|
(it.timeUntilAiring ?: return@let null) + unixTime
|
||||||
)
|
)
|
||||||
|
|
|
@ -300,14 +300,14 @@ class CS3IPlayer : IPlayer {
|
||||||
|
|
||||||
saveData()
|
saveData()
|
||||||
exoPlayer?.pause()
|
exoPlayer?.pause()
|
||||||
releasePlayer()
|
//releasePlayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
Log.i(TAG, "onPause")
|
Log.i(TAG, "onPause")
|
||||||
saveData()
|
saveData()
|
||||||
exoPlayer?.pause()
|
exoPlayer?.pause()
|
||||||
releasePlayer()
|
//releasePlayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume(context: Context) {
|
override fun onResume(context: Context) {
|
||||||
|
|
|
@ -88,6 +88,7 @@ const val DOUBLE_TAB_PAUSE_PERCENTAGE = 0.15 // in both directions
|
||||||
open class FullScreenPlayer : AbstractPlayerFragment() {
|
open class FullScreenPlayer : AbstractPlayerFragment() {
|
||||||
protected open var lockRotation = true
|
protected open var lockRotation = true
|
||||||
protected open var isFullScreenPlayer = true
|
protected open var isFullScreenPlayer = true
|
||||||
|
protected open var isTv = false
|
||||||
|
|
||||||
// state of player UI
|
// state of player UI
|
||||||
protected var isShowing = false
|
protected var isShowing = false
|
||||||
|
@ -1055,7 +1056,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
||||||
|
|
||||||
// netflix capture back and hide ~monke
|
// netflix capture back and hide ~monke
|
||||||
KeyEvent.KEYCODE_BACK -> {
|
KeyEvent.KEYCODE_BACK -> {
|
||||||
if (isShowing) {
|
if (isShowing && isTv) {
|
||||||
onClickChange()
|
onClickChange()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1257,6 +1258,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
||||||
player_intro_play?.setOnClickListener {
|
player_intro_play?.setOnClickListener {
|
||||||
player_intro_play?.isGone = true
|
player_intro_play?.isGone = true
|
||||||
player.handleEvent(CSPlayerEvent.Play)
|
player.handleEvent(CSPlayerEvent.Play)
|
||||||
|
updateUIVisibility()
|
||||||
}
|
}
|
||||||
|
|
||||||
// it is !not! a bug that you cant touch the right side, it does not register inputs on navbar or status bar
|
// it is !not! a bug that you cant touch the right side, it does not register inputs on navbar or status bar
|
||||||
|
|
|
@ -914,8 +914,9 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
// this is used instead of layout-television to follow the settings and some TV devices are not classified as TV for some reason
|
// this is used instead of layout-television to follow the settings and some TV devices are not classified as TV for some reason
|
||||||
|
isTv = context?.isTvSettings() == true
|
||||||
layout =
|
layout =
|
||||||
if (context?.isTvSettings() == true) R.layout.fragment_player_tv else R.layout.fragment_player
|
if (isTv) R.layout.fragment_player_tv else R.layout.fragment_player
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this)[PlayerGeneratorViewModel::class.java]
|
viewModel = ViewModelProvider(this)[PlayerGeneratorViewModel::class.java]
|
||||||
sync = ViewModelProvider(this)[SyncViewModel::class.java]
|
sync = ViewModelProvider(this)[SyncViewModel::class.java]
|
||||||
|
|
|
@ -41,6 +41,7 @@ import com.google.android.material.button.MaterialButton
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||||
import com.lagradost.cloudstream3.APIHolder.getId
|
import com.lagradost.cloudstream3.APIHolder.getId
|
||||||
|
import com.lagradost.cloudstream3.APIHolder.unixTime
|
||||||
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.CommonActivity.getCastSession
|
import com.lagradost.cloudstream3.CommonActivity.getCastSession
|
||||||
|
@ -100,6 +101,8 @@ import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
|
||||||
const val MAX_SYNO_LENGH = 1000
|
const val MAX_SYNO_LENGH = 1000
|
||||||
|
|
||||||
|
@ -654,6 +657,55 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
loadTrailer()
|
loadTrailer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setNextEpisode(nextAiring: NextAiring?) {
|
||||||
|
result_next_airing_holder?.isVisible =
|
||||||
|
if (nextAiring == null || nextAiring.episode <= 0 || nextAiring.unixTime <= unixTime) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
val seconds = nextAiring.unixTime - unixTime
|
||||||
|
val days = TimeUnit.SECONDS.toDays(seconds)
|
||||||
|
val hours: Long = TimeUnit.SECONDS.toHours(seconds) - days * 24
|
||||||
|
val minute =
|
||||||
|
TimeUnit.SECONDS.toMinutes(seconds) - TimeUnit.SECONDS.toHours(seconds) * 60
|
||||||
|
// val second =
|
||||||
|
// TimeUnit.SECONDS.toSeconds(seconds) - TimeUnit.SECONDS.toMinutes(seconds) * 60
|
||||||
|
try {
|
||||||
|
val ctx = context
|
||||||
|
if (ctx == null) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
when {
|
||||||
|
days > 0 -> {
|
||||||
|
ctx.getString(R.string.next_episode_time_day_format).format(
|
||||||
|
days,
|
||||||
|
hours,
|
||||||
|
minute
|
||||||
|
)
|
||||||
|
}
|
||||||
|
hours > 0 -> ctx.getString(R.string.next_episode_time_hour_format)
|
||||||
|
.format(
|
||||||
|
hours,
|
||||||
|
minute
|
||||||
|
)
|
||||||
|
minute > 0 -> ctx.getString(R.string.next_episode_time_min_format)
|
||||||
|
.format(
|
||||||
|
minute
|
||||||
|
)
|
||||||
|
else -> null
|
||||||
|
}?.also { text ->
|
||||||
|
result_next_airing_time?.text = text
|
||||||
|
result_next_airing?.text =
|
||||||
|
ctx.getString(R.string.next_episode_format).format(nextAiring.episode)
|
||||||
|
} != null
|
||||||
|
}
|
||||||
|
} catch (e: Exception) { // mistranslation
|
||||||
|
result_next_airing_holder?.isVisible = false
|
||||||
|
logError(e)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setActors(actors: List<ActorData>?) {
|
private fun setActors(actors: List<ActorData>?) {
|
||||||
if (actors.isNullOrEmpty()) {
|
if (actors.isNullOrEmpty()) {
|
||||||
result_cast_text?.isVisible = false
|
result_cast_text?.isVisible = false
|
||||||
|
@ -1801,7 +1853,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
setRating(d.rating)
|
setRating(d.rating)
|
||||||
setRecommendations(d.recommendations, null)
|
setRecommendations(d.recommendations, null)
|
||||||
setActors(d.actors)
|
setActors(d.actors)
|
||||||
|
setNextEpisode(if (d is EpisodeResponse) d.nextAiring else null)
|
||||||
setTrailers(d.trailers)
|
setTrailers(d.trailers)
|
||||||
|
|
||||||
if (syncModel.addSyncs(d.syncData)) {
|
if (syncModel.addSyncs(d.syncData)) {
|
||||||
|
|
|
@ -85,7 +85,6 @@ open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreen
|
||||||
isFullScreenPlayer = fullscreen
|
isFullScreenPlayer = fullscreen
|
||||||
lockRotation = fullscreen
|
lockRotation = fullscreen
|
||||||
player_fullscreen?.setImageResource(if (fullscreen) R.drawable.baseline_fullscreen_exit_24 else R.drawable.baseline_fullscreen_24)
|
player_fullscreen?.setImageResource(if (fullscreen) R.drawable.baseline_fullscreen_exit_24 else R.drawable.baseline_fullscreen_24)
|
||||||
uiReset()
|
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
enterFullscreen()
|
enterFullscreen()
|
||||||
result_top_bar?.isVisible = false
|
result_top_bar?.isVisible = false
|
||||||
|
@ -106,6 +105,7 @@ open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreen
|
||||||
exitFullscreen()
|
exitFullscreen()
|
||||||
}
|
}
|
||||||
fixPlayerSize()
|
fixPlayerSize()
|
||||||
|
uiReset()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||||
|
@ -29,7 +28,6 @@ import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.ui.player.IGenerator
|
import com.lagradost.cloudstream3.ui.player.IGenerator
|
||||||
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
|
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
|
||||||
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
|
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
|
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
|
||||||
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
|
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
|
@ -144,6 +142,10 @@ class ResultViewModel : ViewModel() {
|
||||||
posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl
|
posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl
|
||||||
actors = actors ?: meta.actors
|
actors = actors ?: meta.actors
|
||||||
|
|
||||||
|
if (this is EpisodeResponse) {
|
||||||
|
nextAiring = nextAiring ?: meta.nextAiring
|
||||||
|
}
|
||||||
|
|
||||||
for ((k, v) in syncs ?: emptyMap()) {
|
for ((k, v) in syncs ?: emptyMap()) {
|
||||||
syncData[k] = v
|
syncData[k] = v
|
||||||
}
|
}
|
||||||
|
@ -162,7 +164,6 @@ class ResultViewModel : ViewModel() {
|
||||||
argamap({
|
argamap({
|
||||||
addTrailer(meta.trailers)
|
addTrailer(meta.trailers)
|
||||||
}, {
|
}, {
|
||||||
|
|
||||||
if (this !is AnimeLoadResponse) return@argamap
|
if (this !is AnimeLoadResponse) return@argamap
|
||||||
val map = getEpisodesDetails(getMalId(), getAniListId(), isResponseRequired = false)
|
val map = getEpisodesDetails(getMalId(), getAniListId(), isResponseRequired = false)
|
||||||
if (map.isNullOrEmpty()) return@argamap
|
if (map.isNullOrEmpty()) return@argamap
|
||||||
|
|
|
@ -140,6 +140,8 @@ val extractorApis: Array<ExtractorApi> = arrayOf(
|
||||||
//mixdrop extractors
|
//mixdrop extractors
|
||||||
MixDropBz(),
|
MixDropBz(),
|
||||||
MixDropCh(),
|
MixDropCh(),
|
||||||
|
MixDropTo(),
|
||||||
|
|
||||||
MixDrop(),
|
MixDrop(),
|
||||||
|
|
||||||
Mcloud(),
|
Mcloud(),
|
||||||
|
@ -186,6 +188,7 @@ val extractorApis: Array<ExtractorApi> = arrayOf(
|
||||||
DoodLaExtractor(),
|
DoodLaExtractor(),
|
||||||
DoodWsExtractor(),
|
DoodWsExtractor(),
|
||||||
DoodShExtractor(),
|
DoodShExtractor(),
|
||||||
|
DoodWatchExtractor(),
|
||||||
|
|
||||||
AsianLoad(),
|
AsianLoad(),
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,7 @@
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.facebook.shimmer.ShimmerFrameLayout
|
<com.facebook.shimmer.ShimmerFrameLayout
|
||||||
|
tools:visibility="gone"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:id="@+id/result_trailer_loading"
|
android:id="@+id/result_trailer_loading"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -148,8 +149,7 @@
|
||||||
app:shimmer_auto_start="true"
|
app:shimmer_auto_start="true"
|
||||||
app:shimmer_base_alpha="0.2"
|
app:shimmer_base_alpha="0.2"
|
||||||
app:shimmer_duration="@integer/loading_time"
|
app:shimmer_duration="@integer/loading_time"
|
||||||
app:shimmer_highlight_alpha="0.3"
|
app:shimmer_highlight_alpha="0.3">
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -423,6 +423,7 @@
|
||||||
tools:text="Cast: Joe Ligma" />
|
tools:text="Cast: Joe Ligma" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
tools:visibility="gone"
|
||||||
android:nextFocusUp="@id/result_bookmark_button"
|
android:nextFocusUp="@id/result_bookmark_button"
|
||||||
android:nextFocusDown="@id/result_play_movie"
|
android:nextFocusDown="@id/result_play_movie"
|
||||||
|
|
||||||
|
@ -669,6 +670,7 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/result_resume_series_button"
|
android:id="@+id/result_resume_series_button"
|
||||||
style="@style/WhiteButton"
|
style="@style/WhiteButton"
|
||||||
|
@ -747,6 +749,7 @@
|
||||||
tools:text="69m\nremaining" />
|
tools:text="69m\nremaining" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/result_episodes_tab"
|
android:id="@+id/result_episodes_tab"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -765,6 +768,7 @@
|
||||||
style="@style/MultiSelectButton"
|
style="@style/MultiSelectButton"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_marginStart="0dp"
|
android:layout_marginStart="0dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
android:nextFocusLeft="@id/result_episode_select"
|
android:nextFocusLeft="@id/result_episode_select"
|
||||||
android:nextFocusRight="@id/result_episode_select"
|
android:nextFocusRight="@id/result_episode_select"
|
||||||
android:nextFocusUp="@id/result_description"
|
android:nextFocusUp="@id/result_description"
|
||||||
|
@ -779,6 +783,8 @@
|
||||||
|
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_marginStart="0dp"
|
android:layout_marginStart="0dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
|
||||||
android:nextFocusLeft="@id/result_season_button"
|
android:nextFocusLeft="@id/result_season_button"
|
||||||
android:nextFocusRight="@id/result_season_button"
|
android:nextFocusRight="@id/result_season_button"
|
||||||
|
|
||||||
|
@ -794,6 +800,7 @@
|
||||||
|
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_marginStart="0dp"
|
android:layout_marginStart="0dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
android:nextFocusLeft="@id/result_season_button"
|
android:nextFocusLeft="@id/result_season_button"
|
||||||
android:nextFocusRight="@id/result_season_button"
|
android:nextFocusRight="@id/result_season_button"
|
||||||
|
|
||||||
|
@ -816,6 +823,37 @@
|
||||||
tools:text="8 Episodes" />
|
tools:text="8 Episodes" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<!--TODO add next airing-->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/result_next_airing_holder"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:paddingBottom="15dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
android:gravity="center"
|
||||||
|
|
||||||
|
android:id="@+id/result_next_airing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="?attr/grayTextColor"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textStyle="normal"
|
||||||
|
android:text="Episode 1022 will be released in" />
|
||||||
|
<TextView
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:id="@+id/result_next_airing_time"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textStyle="normal"
|
||||||
|
tools:text="5d 3h 30m" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<com.facebook.shimmer.ShimmerFrameLayout
|
<com.facebook.shimmer.ShimmerFrameLayout
|
||||||
android:id="@+id/result_episode_loading"
|
android:id="@+id/result_episode_loading"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -67,6 +67,10 @@
|
||||||
<string name="year_format" translatable="false" formatted="true">%d</string>
|
<string name="year_format" translatable="false" formatted="true">%d</string>
|
||||||
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
|
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
|
||||||
<string name="cast_format" formatted="true">Cast: %s</string>
|
<string name="cast_format" formatted="true">Cast: %s</string>
|
||||||
|
<string name="next_episode_format" formatted="true">Episode %d will be released in</string>
|
||||||
|
<string name="next_episode_time_day_format" formatted="true">%dd %dh %dm</string>
|
||||||
|
<string name="next_episode_time_hour_format" formatted="true">%dh %dm</string>
|
||||||
|
<string name="next_episode_time_min_format" formatted="true">%dm</string>
|
||||||
|
|
||||||
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS -->
|
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS -->
|
||||||
<string name="result_poster_img_des">Poster</string>
|
<string name="result_poster_img_des">Poster</string>
|
||||||
|
|
|
@ -184,6 +184,12 @@
|
||||||
"status": 1,
|
"status": 1,
|
||||||
"url": "https://filman.cc"
|
"url": "https://filman.cc"
|
||||||
},
|
},
|
||||||
|
"FilmpertuttiProvider": {
|
||||||
|
"language": "it",
|
||||||
|
"name": "Filmpertutti",
|
||||||
|
"status": 1,
|
||||||
|
"url": "https://www.filmpertutti.buzz"
|
||||||
|
},
|
||||||
"FmoviesToProvider": {
|
"FmoviesToProvider": {
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"name": "Fmovies.to",
|
"name": "Fmovies.to",
|
||||||
|
@ -420,7 +426,7 @@
|
||||||
"language": "it",
|
"language": "it",
|
||||||
"name": "Tantifilm",
|
"name": "Tantifilm",
|
||||||
"status": 1,
|
"status": 1,
|
||||||
"url": "https://www.tantifilm.rodeo"
|
"url": "https://www.tantifilm.pics"
|
||||||
},
|
},
|
||||||
"TenshiProvider": {
|
"TenshiProvider": {
|
||||||
"language": "en",
|
"language": "en",
|
||||||
|
|
Loading…
Reference in a new issue