feat(UI): Show Episode Runtime (#1207)

This commit is contained in:
KingLucius 2024-07-25 21:23:49 +03:00 committed by GitHub
parent dfd127265a
commit e3ff1cf455
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 75 additions and 12 deletions

View file

@ -236,6 +236,7 @@ open class TraktProvider : MainAPI() {
posterUrl = fixPath(episode.images?.screenshot?.firstOrNull()), posterUrl = fixPath(episode.images?.screenshot?.firstOrNull()),
rating = episode.rating?.times(10)?.roundToInt(), rating = episode.rating?.times(10)?.roundToInt(),
description = episode.overview, description = episode.overview,
runTime = episode.runtime
).apply { ).apply {
this.addDate(episode.firstAired, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") this.addDate(episode.firstAired, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
if (nextAir == null && this.date != null && this.date!! > unixTimeMS && this.season != 0) { if (nextAir == null && this.date != null && this.date!! > unixTimeMS && this.season != 0) {

View file

@ -27,7 +27,8 @@ import com.lagradost.cloudstream3.utils.UIHelper.toPx
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import java.text.DateFormat import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.Date
import java.util.Locale
const val ACTION_PLAY_EPISODE_IN_PLAYER = 1 const val ACTION_PLAY_EPISODE_IN_PLAYER = 1
const val ACTION_PLAY_EPISODE_IN_VLC_PLAYER = 2 const val ACTION_PLAY_EPISODE_IN_VLC_PLAYER = 2
@ -58,6 +59,7 @@ const val ACTION_MARK_AS_WATCHED = 18
const val ACTION_FCAST = 19 const val ACTION_FCAST = 19
const val TV_EP_SIZE = 400 const val TV_EP_SIZE = 400
data class EpisodeClickEvent(val action: Int, val data: ResultEpisode) data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
class EpisodeAdapter( class EpisodeAdapter(
@ -274,7 +276,10 @@ class EpisodeAdapter(
episodeDate.setText( episodeDate.setText(
txt( txt(
R.string.episode_upcoming_format, R.string.episode_upcoming_format,
secondsToReadable(card.airDate.minus(unixTimeMS).div(1000).toInt(), "") secondsToReadable(
card.airDate.minus(unixTimeMS).div(1000).toInt(),
""
)
) )
) )
} else { } else {
@ -292,6 +297,12 @@ class EpisodeAdapter(
episodeDate.isVisible = false episodeDate.isVisible = false
} }
episodeRuntime.setText(
txt(
card.runTime?.times(60L)?.toInt()?.let { secondsToReadable(it, "") }
)
)
if (isLayout(EMULATOR or PHONE)) { if (isLayout(EMULATOR or PHONE)) {
episodePoster.setOnClickListener { episodePoster.setOnClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))

View file

@ -51,6 +51,7 @@ data class ResultEpisode(
/** Sum of all previous season episode counts + episode */ /** Sum of all previous season episode counts + episode */
val totalEpisodeIndex: Int? = null, val totalEpisodeIndex: Int? = null,
val airDate: Long? = null, val airDate: Long? = null,
val runTime: Int? = null,
) )
fun ResultEpisode.getRealPosition(): Long { fun ResultEpisode.getRealPosition(): Long {
@ -87,6 +88,7 @@ fun buildResultEpisode(
parentId: Int, parentId: Int,
totalEpisodeIndex: Int? = null, totalEpisodeIndex: Int? = null,
airDate: Long? = null, airDate: Long? = null,
runTime: Int? = null,
): ResultEpisode { ): ResultEpisode {
val posDur = getViewPos(id) val posDur = getViewPos(id)
val videoWatchState = getVideoWatchState(id) ?: VideoWatchState.None val videoWatchState = getVideoWatchState(id) ?: VideoWatchState.None
@ -111,6 +113,7 @@ fun buildResultEpisode(
videoWatchState, videoWatchState,
totalEpisodeIndex, totalEpisodeIndex,
airDate, airDate,
runTime,
) )
} }

View file

@ -2371,7 +2371,8 @@ class ResultViewModel2 : ViewModel() {
loadResponse.type, loadResponse.type,
mainId, mainId,
totalIndex, totalIndex,
airDate = i.date airDate = i.date,
runTime = i.runTime,
) )
val season = eps.seasonIndex ?: 0 val season = eps.seasonIndex ?: 0
@ -2426,7 +2427,8 @@ class ResultViewModel2 : ViewModel() {
loadResponse.type, loadResponse.type,
mainId, mainId,
totalIndex, totalIndex,
airDate = episode.date airDate = episode.date,
runTime = episode.runTime,
) )
val season = ep.seasonIndex ?: 0 val season = ep.seasonIndex ?: 0

View file

@ -44,7 +44,7 @@
android:nextFocusRight="@id/download_button" android:nextFocusRight="@id/download_button"
android:scaleType="centerCrop" android:scaleType="centerCrop"
tools:src="@drawable/example_poster" tools:src="@drawable/example_poster"
tools:visibility="invisible"/> tools:visibility="invisible" />
<ImageView <ImageView
android:id="@+id/episode_play_icon" android:id="@+id/episode_play_icon"
@ -53,7 +53,7 @@
android:layout_gravity="center" android:layout_gravity="center"
android:contentDescription="@string/play_episode" android:contentDescription="@string/play_episode"
android:src="@drawable/play_button" android:src="@drawable/play_button"
tools:visibility="invisible"/> tools:visibility="invisible" />
<ImageView <ImageView
android:id="@+id/episode_upcoming_icon" android:id="@+id/episode_upcoming_icon"
@ -106,19 +106,37 @@
tools:text="1. Jobless" /> tools:text="1. Jobless" />
</LinearLayout> </LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:orientation="horizontal">
<TextView <TextView
android:id="@+id/episode_rating" android:id="@+id/episode_rating"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:textColor="?attr/grayTextColor" android:textColor="?attr/grayTextColor"
tools:text="Rated: 8.8" /> tools:text="Rated: 8.8" />
<TextView
android:id="@+id/episode_runtime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:textColor="?attr/grayTextColor"
tools:text="80min" />
</LinearLayout>
<TextView <TextView
android:id="@+id/episode_date" android:id="@+id/episode_date"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="?attr/grayTextColor" android:textColor="?attr/grayTextColor"
tools:text="15 Apr 2024" /> tools:text="15 Apr 2024" />
</LinearLayout> </LinearLayout>
<com.lagradost.cloudstream3.ui.download.button.PieFetchButton <com.lagradost.cloudstream3.ui.download.button.PieFetchButton

View file

@ -1700,7 +1700,17 @@ suspend fun MainAPI.newMovieLoadResponse(
builder.initializer() builder.initializer()
return builder return builder
} }
/** Episode information that will be passed to LoadLinks function & showed on UI
* @property data string used as main LoadLinks fun parameter.
* @property name Name of the Episode.
* @property season Season number.
* @property episode Episode number.
* @property posterUrl URL of Episode's poster image.
* @property rating Episode rating.
* @property date Episode air date, see addDate.
* @property runTime Episode runtime in seconds.
* @see[addDate]
* */
data class Episode( data class Episode(
var data: String, var data: String,
var name: String? = null, var name: String? = null,
@ -1710,7 +1720,25 @@ data class Episode(
var rating: Int? = null, var rating: Int? = null,
var description: String? = null, var description: String? = null,
var date: Long? = null, var date: Long? = null,
) var runTime: Int? = null,
) {
/**
* Secondary constructor for backwards compatibility without runTime.
* TODO Remove this constructor after there is a new stable release and extensions are updated to support runTime.
*/
constructor(
data: String,
name: String? = null,
season: Int? = null,
episode: Int? = null,
posterUrl: String? = null,
rating: Int? = null,
description: String? = null,
date: Long? = null,
) : this(
data, name, season, episode, posterUrl, rating, description, date, null
)
}
fun Episode.addDate(date: String?, format: String = "yyyy-MM-dd") { fun Episode.addDate(date: String?, format: String = "yyyy-MM-dd") {
try { try {