mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
feat(UI): Show Episode Runtime (#1207)
This commit is contained in:
parent
dfd127265a
commit
e3ff1cf455
6 changed files with 75 additions and 12 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue