mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
added stuff to MainAPI
This commit is contained in:
parent
79328502b4
commit
8c0a6dffe9
9 changed files with 152 additions and 102 deletions
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
|
|
|
@ -10,13 +10,10 @@ import com.lagradost.cloudstream3.animeproviders.ShiroProvider
|
|||
import com.lagradost.cloudstream3.animeproviders.TenshiProvider
|
||||
import com.lagradost.cloudstream3.animeproviders.WcoProvider
|
||||
import com.lagradost.cloudstream3.movieproviders.HDMProvider
|
||||
import com.lagradost.cloudstream3.movieproviders.LookMovieProvider
|
||||
import com.lagradost.cloudstream3.movieproviders.MeloMovieProvider
|
||||
import com.lagradost.cloudstream3.movieproviders.TrailersToProvider
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; rv:68.0) Gecko/20100101 Firefox/68.0"
|
||||
val baseHeader = mapOf("User-Agent" to USER_AGENT)
|
||||
|
@ -38,7 +35,7 @@ object APIHolder {
|
|||
MeloMovieProvider(),
|
||||
DubbedAnimeProvider(),
|
||||
HDMProvider(),
|
||||
LookMovieProvider(),
|
||||
//LookMovieProvider(), // RECAPTCHA (Please allow up to 5 seconds...)
|
||||
TrailersToProvider(),
|
||||
)
|
||||
|
||||
|
@ -60,13 +57,23 @@ object APIHolder {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**Every provider will **not** have try catch built in, so handle exceptions when calling these functions*/
|
||||
abstract class MainAPI {
|
||||
open val name = "NONE"
|
||||
open val mainUrl = "NONE"
|
||||
open val instantLinkLoading = false // THIS IS IF THE LINK IS STORED IN THE "DATA"
|
||||
|
||||
/**If link is stored in the "data" string, so links can be instantly loaded*/
|
||||
open val instantLinkLoading = false
|
||||
|
||||
open val hasQuickSearch = false
|
||||
open fun search(query: String): ArrayList<SearchResponse>? { // SearchResponse
|
||||
|
||||
/**Set false if links require referer or for some reason cant be played on a chromecast*/
|
||||
open val hasChromecastSupport = true
|
||||
|
||||
/**If all links are m3u8 then set this to false*/
|
||||
open val hasDownloadSupport = true
|
||||
|
||||
open fun search(query: String): ArrayList<SearchResponse>? {
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -78,7 +85,7 @@ abstract class MainAPI {
|
|||
return null
|
||||
}
|
||||
|
||||
// callback is fired once a link is found, will return true if method is executed successfully
|
||||
/**Callback is fired once a link is found, will return true if method is executed successfully*/
|
||||
open fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
|
|
|
@ -10,6 +10,8 @@ class HDMProvider : MainAPI() {
|
|||
get() = "HD Movies"
|
||||
override val mainUrl: String
|
||||
get() = "https://hdm.to"
|
||||
override val hasDownloadSupport: Boolean
|
||||
get() = false
|
||||
|
||||
override fun search(query: String): ArrayList<SearchResponse> {
|
||||
val url = "$mainUrl/search/$query"
|
||||
|
|
|
@ -17,6 +17,8 @@ class MeloMovieProvider : MainAPI() {
|
|||
get() = true
|
||||
override val hasQuickSearch: Boolean
|
||||
get() = true
|
||||
override val hasChromecastSupport: Boolean
|
||||
get() = false // MKV FILES CANT BE PLAYED ON A CHROMECAST
|
||||
|
||||
data class MeloMovieSearchResult(
|
||||
@JsonProperty("id") val id: Int,
|
||||
|
|
|
@ -1,28 +1,17 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.widget.ContentLoadingProgressBar
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import com.google.android.gms.cast.framework.CastContext
|
||||
import com.google.android.gms.cast.framework.CastState
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.UIHelper.hideSystemUI
|
||||
import com.lagradost.cloudstream3.UIHelper.isCastApiAvailable
|
||||
import com.lagradost.cloudstream3.UIHelper.isConnectedToChromecast
|
||||
import com.lagradost.cloudstream3.utils.getId
|
||||
import com.lagradost.cloudstream3.R
|
||||
import kotlinx.android.synthetic.main.result_episode.view.episode_holder
|
||||
import kotlinx.android.synthetic.main.result_episode.view.episode_text
|
||||
import kotlinx.android.synthetic.main.result_episode_large.view.*
|
||||
|
@ -42,10 +31,13 @@ const val ACTION_COPY_LINK = 9
|
|||
|
||||
const val ACTION_SHOW_OPTIONS = 10
|
||||
|
||||
const val ACTION_CLICK_DEFAULT = 11
|
||||
|
||||
data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
|
||||
|
||||
class EpisodeAdapter(
|
||||
var cardList: List<ResultEpisode>,
|
||||
val hasDownloadSupport : Boolean,
|
||||
private val clickCallback: (EpisodeClickEvent) -> Unit,
|
||||
) :
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
@ -64,6 +56,7 @@ class EpisodeAdapter(
|
|||
|
||||
return CardViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(layout, parent, false),
|
||||
hasDownloadSupport,
|
||||
clickCallback
|
||||
)
|
||||
}
|
||||
|
@ -83,6 +76,7 @@ class EpisodeAdapter(
|
|||
class CardViewHolder
|
||||
constructor(
|
||||
itemView: View,
|
||||
private val hasDownloadSupport : Boolean,
|
||||
private val clickCallback: (EpisodeClickEvent) -> Unit,
|
||||
) : RecyclerView.ViewHolder(itemView) {
|
||||
private val episodeText: TextView = itemView.episode_text
|
||||
|
@ -131,13 +125,7 @@ class EpisodeAdapter(
|
|||
}
|
||||
|
||||
episodeHolder.setOnClickListener {
|
||||
episodeHolder.context?.let { ctx ->
|
||||
if (ctx.isConnectedToChromecast()) {
|
||||
clickCallback.invoke(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card))
|
||||
} else {
|
||||
clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
|
||||
}
|
||||
}
|
||||
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
||||
}
|
||||
|
||||
episodeHolder.setOnLongClickListener {
|
||||
|
@ -146,6 +134,8 @@ class EpisodeAdapter(
|
|||
return@setOnLongClickListener true
|
||||
}
|
||||
|
||||
episodeDownload?.visibility = if(hasDownloadSupport) View.VISIBLE else View.GONE
|
||||
|
||||
episodeDownload?.setOnClickListener {
|
||||
clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card))
|
||||
}
|
||||
|
|
|
@ -227,6 +227,23 @@ class ResultFragment : Fragment() {
|
|||
)
|
||||
result_back.layoutParams = backParameter
|
||||
|
||||
// activity?.fixPaddingStatusbar(result_toolbar)
|
||||
|
||||
url = arguments?.getString("url")
|
||||
val slug = arguments?.getString("slug")
|
||||
val apiName = arguments?.getString("apiName") ?: return
|
||||
|
||||
val api = getApiFromName(apiName)
|
||||
if (media_route_button != null) {
|
||||
val chromecastSupport = api.hasChromecastSupport
|
||||
|
||||
media_route_button?.alpha = if (chromecastSupport) 1f else 0.3f
|
||||
if (!chromecastSupport) {
|
||||
media_route_button.setOnClickListener {
|
||||
Toast.makeText(it.context, "This provider has no chromecast support", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
if (activity?.isCastApiAvailable() == true) {
|
||||
CastButtonFactory.setUpMediaRouteButton(activity, media_route_button)
|
||||
val castContext = CastContext.getSharedInstance(requireActivity().applicationContext)
|
||||
|
@ -240,12 +257,7 @@ class ResultFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
// activity?.fixPaddingStatusbar(result_toolbar)
|
||||
|
||||
url = arguments?.getString("url")
|
||||
val slug = arguments?.getString("slug")
|
||||
val apiName = arguments?.getString("apiName")
|
||||
|
||||
}
|
||||
result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, _ ->
|
||||
if (result_poster_blur == null) return@OnScrollChangeListener
|
||||
result_poster_blur.alpha = maxOf(0f, (0.7f - scrollY / 1000f))
|
||||
|
@ -397,7 +409,10 @@ class ResultFragment : Fragment() {
|
|||
|
||||
val epData = episodeClick.data
|
||||
ctx.setKey(
|
||||
getFolderName(DOWNLOAD_EPISODE_CACHE, (currentId ?: return@let).toString()), // 3 deep folder for faster acess
|
||||
getFolderName(
|
||||
DOWNLOAD_EPISODE_CACHE,
|
||||
(currentId ?: return@let).toString()
|
||||
), // 3 deep folder for faster acess
|
||||
epData.id.toString(),
|
||||
VideoDownloadHelper.DownloadEpisodeCached(
|
||||
epData.name,
|
||||
|
@ -431,6 +446,16 @@ class ResultFragment : Fragment() {
|
|||
if (!isLoaded) return@main // CANT LOAD
|
||||
|
||||
when (episodeClick.action) {
|
||||
ACTION_CLICK_DEFAULT -> {
|
||||
context?.let { ctx ->
|
||||
if (ctx.isConnectedToChromecast()) {
|
||||
handleAction(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, episodeClick.data))
|
||||
} else {
|
||||
handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, episodeClick.data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ACTION_SHOW_OPTIONS -> {
|
||||
val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom)
|
||||
var dialog: AlertDialog? = null
|
||||
|
@ -442,6 +467,8 @@ class ResultFragment : Fragment() {
|
|||
val verifiedOptions = ArrayList<String>()
|
||||
val verifiedOptionsValues = ArrayList<Int>()
|
||||
|
||||
val hasDownloadSupport = api.hasDownloadSupport
|
||||
|
||||
for (i in options.indices) {
|
||||
val opv = optionsValues[i]
|
||||
val op = options[i]
|
||||
|
@ -450,6 +477,8 @@ class ResultFragment : Fragment() {
|
|||
val add = when (opv) {
|
||||
ACTION_CHROME_CAST_EPISODE -> isConnected
|
||||
ACTION_CHROME_CAST_MIRROR -> isConnected
|
||||
ACTION_DOWNLOAD_EPISODE -> hasDownloadSupport
|
||||
ACTION_DOWNLOAD_MIRROR -> hasDownloadSupport
|
||||
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> context?.isAppInstalled(VLC_PACKAGE) ?: false
|
||||
else -> true
|
||||
}
|
||||
|
@ -482,7 +511,7 @@ class ResultFragment : Fragment() {
|
|||
|
||||
ACTION_PLAY_EPISODE_IN_BROWSER -> {
|
||||
aquireSingeExtractorLink("Play in Browser") { link ->
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
val i = Intent(ACTION_VIEW)
|
||||
i.data = Uri.parse(link.url)
|
||||
startActivity(i)
|
||||
}
|
||||
|
@ -595,11 +624,11 @@ class ResultFragment : Fragment() {
|
|||
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
|
||||
EpisodeAdapter(
|
||||
ArrayList(),
|
||||
api.hasDownloadSupport,
|
||||
) { episodeClick ->
|
||||
handleAction(episodeClick)
|
||||
}
|
||||
|
||||
|
||||
result_episodes.adapter = adapter
|
||||
result_episodes.layoutManager = GridLayoutManager(context, 1)
|
||||
|
||||
|
@ -678,17 +707,17 @@ class ResultFragment : Fragment() {
|
|||
currentIsMovie = !d.isEpisodeBased()
|
||||
|
||||
result_openinbrower.setOnClickListener {
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
val i = Intent(ACTION_VIEW)
|
||||
i.data = Uri.parse(d.url)
|
||||
startActivity(i)
|
||||
}
|
||||
|
||||
result_share.setOnClickListener {
|
||||
val i = Intent(Intent.ACTION_SEND)
|
||||
val i = Intent(ACTION_SEND)
|
||||
i.type = "text/plain"
|
||||
i.putExtra(Intent.EXTRA_SUBJECT, d.name)
|
||||
i.putExtra(Intent.EXTRA_TEXT, d.url)
|
||||
startActivity(Intent.createChooser(i, d.name))
|
||||
i.putExtra(EXTRA_SUBJECT, d.name)
|
||||
i.putExtra(EXTRA_TEXT, d.url)
|
||||
startActivity(createChooser(i, d.name))
|
||||
}
|
||||
|
||||
val metadataInfoArray = ArrayList<Pair<String, String>>()
|
||||
|
@ -713,6 +742,8 @@ class ResultFragment : Fragment() {
|
|||
val duration = d.duration
|
||||
if (duration != null) metadataInfoArray.add(Pair("Duration", duration))
|
||||
|
||||
metadataInfoArray.add(Pair("Site", d.apiName))
|
||||
|
||||
if (metadataInfoArray.size > 0) {
|
||||
result_metadata.visibility = VISIBLE
|
||||
val text = SpannableStringBuilder()
|
||||
|
@ -777,38 +808,24 @@ class ResultFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
when (d.type) {
|
||||
TvType.Movie -> {
|
||||
result_play_movie.visibility = VISIBLE
|
||||
if (d.type == TvType.Movie && d is MovieLoadResponse) {
|
||||
result_movie_parent.visibility = VISIBLE
|
||||
result_episodes_text.visibility = GONE
|
||||
result_episodes.visibility = GONE
|
||||
|
||||
result_play_movie.setOnClickListener {
|
||||
val card = currentEpisodes?.first() ?: return@setOnClickListener
|
||||
if (requireContext().isCastApiAvailable()) {
|
||||
val castContext = CastContext.getSharedInstance(requireContext())
|
||||
|
||||
if (castContext.castState == CastState.CONNECTED) {
|
||||
handleAction(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card))
|
||||
} else {
|
||||
handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
|
||||
handleAction(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
||||
}
|
||||
result_options.setOnClickListener {
|
||||
val card = currentEpisodes?.first() ?: return@setOnClickListener
|
||||
handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
|
||||
}
|
||||
} else {
|
||||
handleAction(
|
||||
EpisodeClickEvent(
|
||||
ACTION_PLAY_EPISODE_IN_PLAYER,
|
||||
card
|
||||
)
|
||||
) //TODO REDO TO MAIN
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
result_play_movie.visibility = GONE
|
||||
result_movie_parent.visibility = GONE
|
||||
result_episodes_text.visibility = VISIBLE
|
||||
result_episodes.visibility = VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
when (d) {
|
||||
is AnimeLoadResponse -> {
|
||||
|
@ -842,7 +859,7 @@ class ResultFragment : Fragment() {
|
|||
|
||||
if (url != null) {
|
||||
result_reload_connection_open_in_browser.setOnClickListener {
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
val i = Intent(ACTION_VIEW)
|
||||
i.data = Uri.parse(url)
|
||||
startActivity(i)
|
||||
}
|
||||
|
|
|
@ -165,9 +165,10 @@ class ResultViewModel : ViewModel() {
|
|||
updateEpisodes(
|
||||
context, mainId, arrayListOf(
|
||||
context.buildResultEpisode(
|
||||
d.name,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
0, null,
|
||||
d.dataUrl,
|
||||
d.apiName,
|
||||
(mainId + 1),
|
||||
|
@ -211,7 +212,7 @@ class ResultViewModel : ViewModel() {
|
|||
suspend fun loadEpisode(
|
||||
episode: ResultEpisode,
|
||||
isCasting: Boolean,
|
||||
) : Resource<ResultViewModel.EpisodeData> {
|
||||
): Resource<EpisodeData> {
|
||||
return loadEpisode(episode.id, episode.data, isCasting)
|
||||
}
|
||||
|
||||
|
@ -219,7 +220,7 @@ class ResultViewModel : ViewModel() {
|
|||
id: Int,
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
): Resource<ResultViewModel.EpisodeData> {
|
||||
): Resource<EpisodeData> {
|
||||
if (_allEpisodes.value?.contains(id) == true) {
|
||||
_allEpisodes.value?.remove(id)
|
||||
}
|
||||
|
|
|
@ -286,6 +286,11 @@
|
|||
android:layout_height="wrap_content">
|
||||
|
||||
</com.lagradost.cloudstream3.widget.FlowLayout>
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/result_movie_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:visibility="visible"
|
||||
android:layout_gravity="center_vertical"
|
||||
|
@ -293,7 +298,29 @@
|
|||
android:id="@+id/result_play_movie"
|
||||
android:text="@string/play_movie_button"
|
||||
|
||||
app:rippleColor="?attr/colorPrimary"
|
||||
app:rippleColor="?attr/textColor"
|
||||
android:textColor="?attr/textColor"
|
||||
app:iconTint="?attr/textColor"
|
||||
android:textAllCaps="false"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
|
||||
app:iconGravity="textStart"
|
||||
app:strokeColor="?attr/textColor"
|
||||
app:icon="@drawable/ic_baseline_play_arrow_24"
|
||||
android:backgroundTint="@color/transparent"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="45dp">
|
||||
</com.google.android.material.button.MaterialButton>
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:visibility="visible"
|
||||
android:layout_gravity="center_vertical"
|
||||
app:cornerRadius="4dp"
|
||||
android:id="@+id/result_options"
|
||||
android:text="@string/options"
|
||||
|
||||
app:rippleColor="?attr/textColor"
|
||||
android:textColor="?attr/textColor"
|
||||
app:iconTint="?attr/textColor"
|
||||
android:textAllCaps="false"
|
||||
|
@ -308,13 +335,15 @@
|
|||
android:layout_height="45dp">
|
||||
</com.google.android.material.button.MaterialButton>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:orientation="horizontal" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:visibility="visible"
|
||||
android:layout_gravity="center_vertical"
|
||||
app:cornerRadius="4dp"
|
||||
android:id="@+id/result_season_button"
|
||||
tools:text="Bookmark"
|
||||
tools:text="Subbed"
|
||||
|
||||
app:rippleColor="?attr/bitDarkerGrayBackground"
|
||||
android:textColor="?attr/textColor"
|
||||
|
|
|
@ -41,4 +41,5 @@
|
|||
<string name="download_descript">Download</string>
|
||||
<string name="error_loading_links">Error Loading Links</string>
|
||||
<string name="download_storage_text">Internal Storage</string>
|
||||
<string name="options">Options</string>
|
||||
</resources>
|
Loading…
Reference in a new issue