mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-03 16:41:26 +01:00
fix(android,ios): the player names the episode, not its file
The header under the episode's numbering showed `Episode::detail`, which falls back to the file name — `[Judas] Chainsaw Man - S01E01.mkv` on a phone, where the desktop shows `Dog & Chainsaw`. It now shows the provider's name for the episode, else the one the file name gave it, and nothing rather than the file name: the release group's tag is no caption, and it is what a screenshot of the player would carry away.
This commit is contained in:
1 parent
79f0d74e1a
commit
b5ba59b41d
6 files changed
+60
-4
No files matched your search
@@ -97,6 +97,7 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import io.narl.protonstream.settings.SettingsStore
|
import io.narl.protonstream.settings.SettingsStore
|
||||||
import io.narl.protonstream.native.NativeRuntime
|
import io.narl.protonstream.native.NativeRuntime
|
||||||
|
import io.narl.protonstream.ui.shownName
|
||||||
import io.narl.protonstream.ui.theme.AccentButton
|
import io.narl.protonstream.ui.theme.AccentButton
|
||||||
import io.narl.protonstream.ui.theme.AccentTrack
|
import io.narl.protonstream.ui.theme.AccentTrack
|
||||||
import io.narl.protonstream.ui.theme.EdgedButton
|
import io.narl.protonstream.ui.theme.EdgedButton
|
||||||
@@ -567,8 +568,11 @@ fun PlayerScreen(
|
|||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
)
|
)
|
||||||
|
// The episode's name, never its file name: a release
|
||||||
|
// group's tag is no caption, and it is what a screenshot
|
||||||
|
// of the player would carry away.
|
||||||
Text(
|
Text(
|
||||||
listOfNotNull(title.displayName, episode.detail.takeIf(String::isNotBlank))
|
listOfNotNull(title.displayName, episode.shownName())
|
||||||
.joinToString(" • "),
|
.joinToString(" • "),
|
||||||
color = Color.White.copy(alpha = 0.7f),
|
color = Color.White.copy(alpha = 0.7f),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
|
|||||||
@@ -221,6 +221,13 @@ internal fun rememberQueueReason(key: Any?): String? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The episode's own name: the provider's, else the one the filename gave it.
|
||||||
|
* `null` rather than the filename, which is a release group's tag and the
|
||||||
|
* episode's numbering again.
|
||||||
|
*/
|
||||||
|
internal fun EpisodeRecord.shownName(): String? = providerName ?: detail.takeIf { it != name && it.isNotBlank() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What an episode row is headed with: "1. Mother and Children".
|
* What an episode row is headed with: "1. Mother and Children".
|
||||||
*
|
*
|
||||||
@@ -229,7 +236,7 @@ internal fun rememberQueueReason(key: Any?): String? {
|
|||||||
* of `[Group] Show - S01E01.mkv` is forty rows that differ in two characters.
|
* of `[Group] Show - S01E01.mkv` is forty rows that differ in two characters.
|
||||||
*/
|
*/
|
||||||
internal fun EpisodeRecord.heading(position: Int): String {
|
internal fun EpisodeRecord.heading(position: Int): String {
|
||||||
val named = providerName ?: detail.takeIf { it != name }
|
val named = shownName()
|
||||||
val number = number?.toInt()
|
val number = number?.toInt()
|
||||||
return when {
|
return when {
|
||||||
named != null && number != null -> "$number. $named"
|
named != null && number != null -> "$number. $named"
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package io.narl.protonstream.ui
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
|
import org.junit.Test
|
||||||
|
import uniffi.pstr_android.EpisodeRecord
|
||||||
|
|
||||||
|
class EpisodeNameTest {
|
||||||
|
private fun episode(name: String, detail: String, providerName: String? = null) = EpisodeRecord(
|
||||||
|
shareId = "s", volumeId = "v", linkId = "l", name = name, label = "S01E01", detail = detail,
|
||||||
|
season = 1u, number = 1u, size = null, progress = null, resumeAt = null,
|
||||||
|
watched = false, offline = false, providerName = providerName, providerOverview = null, stillUrl = null,
|
||||||
|
airDate = null, lastPlayed = 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `the player captions an episode with its name and never its filename`() {
|
||||||
|
val file = "[Group] Show - S01E01.mkv"
|
||||||
|
assertEquals("Dog & Chainsaw", episode(file, file, providerName = "Dog & Chainsaw").shownName())
|
||||||
|
assertEquals("Winter Is Coming", episode(file, "Winter Is Coming").shownName())
|
||||||
|
assertNull(episode(file, file).shownName())
|
||||||
|
assertNull(episode(file, "").shownName())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -210,7 +210,9 @@ struct PlayerView: View {
|
|||||||
// thing a viewer who has just woken the controls is checking.
|
// thing a viewer who has just woken the controls is checking.
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
Text(host.episode?.label ?? "").textStyle(.titleMedium).foregroundStyle(.white).lineLimit(1)
|
Text(host.episode?.label ?? "").textStyle(.titleMedium).foregroundStyle(.white).lineLimit(1)
|
||||||
Text([host.title?.displayName, host.episode?.detail].compactMap { $0 }.filter { !$0.isEmpty }.joined(separator: " • "))
|
// The episode's name, never its file name: a release group's
|
||||||
|
// tag is no caption, and it is what a screenshot would carry away.
|
||||||
|
Text([host.title?.displayName, host.episode?.shownName].compactMap { $0 }.filter { !$0.isEmpty }.joined(separator: " • "))
|
||||||
.textStyle(.bodySmall).foregroundStyle(.white.opacity(0.7)).lineLimit(1)
|
.textStyle(.bodySmall).foregroundStyle(.white.opacity(0.7)).lineLimit(1)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|||||||
@@ -342,12 +342,19 @@ func downloadStatusLine(_ download: RetainedDownload, waitingFor: String? = nil)
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension EpisodeRecord {
|
extension EpisodeRecord {
|
||||||
|
/// The episode's own name: the provider's, else the one the filename gave
|
||||||
|
/// it. `nil` rather than the filename, which is a release group's tag and
|
||||||
|
/// the episode's numbering again.
|
||||||
|
var shownName: String? {
|
||||||
|
providerName ?? (detail != name && !detail.isEmpty ? detail : nil)
|
||||||
|
}
|
||||||
|
|
||||||
/// What an episode row is headed with: "1. Mother and Children".
|
/// What an episode row is headed with: "1. Mother and Children".
|
||||||
///
|
///
|
||||||
/// The provider's name for the episode, else the name the filename gave it,
|
/// The provider's name for the episode, else the name the filename gave it,
|
||||||
/// else "Episode 1". The bare filename only when none of those exist.
|
/// else "Episode 1". The bare filename only when none of those exist.
|
||||||
func heading(_ position: Int) -> String {
|
func heading(_ position: Int) -> String {
|
||||||
let named = providerName ?? (detail != name ? detail : nil)
|
let named = shownName
|
||||||
switch (named, number) {
|
switch (named, number) {
|
||||||
case let (named?, number?): return "\(number). \(named)"
|
case let (named?, number?): return "\(number). \(named)"
|
||||||
case let (named?, nil): return named
|
case let (named?, nil): return named
|
||||||
|
|||||||
@@ -171,6 +171,18 @@ final class CommonTests: XCTestCase {
|
|||||||
XCTAssertEqual(bare.heading(0), "Episode 4")
|
XCTAssertEqual(bare.heading(0), "Episode 4")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testThePlayerCaptionsAnEpisodeWithItsNameAndNeverItsFilename() {
|
||||||
|
var named = episode("e")
|
||||||
|
named.providerName = "Dog & Chainsaw"
|
||||||
|
XCTAssertEqual(named.shownName, "Dog & Chainsaw")
|
||||||
|
var bare = episode("e")
|
||||||
|
bare.name = "[Group] Show - S01E01.mkv"
|
||||||
|
bare.detail = bare.name
|
||||||
|
XCTAssertNil(bare.shownName)
|
||||||
|
bare.detail = ""
|
||||||
|
XCTAssertNil(bare.shownName)
|
||||||
|
}
|
||||||
|
|
||||||
func testEpisodesAreNumberedBySeasonThenByPlaceInTheList() {
|
func testEpisodesAreNumberedBySeasonThenByPlaceInTheList() {
|
||||||
var numbered = episode("e", number: 38)
|
var numbered = episode("e", number: 38)
|
||||||
numbered.season = 3
|
numbered.season = 3
|
||||||
|
|||||||
Reference in new issue
Block a user