Fix Viaplay VOD live-edge seek and slow Token Tray typing.

Clips no longer use DASH live configuration (was jumping to end). Tray 0.1.2 types slower; no inter-account pause.
This commit is contained in:
Jos Vooges | STH 2026-09-23 02:38:26 +02:00
parent bedebf44f6
commit f9ec3be09e
9 changed files with 25 additions and 26 deletions

View file

@ -20,8 +20,8 @@ android {
applicationId = "nl.vonas.mediacluster.tv" applicationId = "nl.vonas.mediacluster.tv"
minSdk = 24 minSdk = 24
targetSdk = 36 targetSdk = 36
versionCode = 147 versionCode = 148
versionName = "0.14.84" versionName = "0.14.85"
buildConfigField("String", "DEFAULT_API_BASE", "\"https://master.vonas.nl\"") buildConfigField("String", "DEFAULT_API_BASE", "\"https://master.vonas.nl\"")
ndk { ndk {
abiFilters += listOf("arm64-v8a", "armeabi-v7a") abiFilters += listOf("arm64-v8a", "armeabi-v7a")

View file

@ -340,12 +340,16 @@ private fun bytesToUuid(bytes: ByteArray): UUID {
return UUID(buf.long, buf.long) return UUID(buf.long, buf.long)
} }
/** TS / HLS / DASH(+ClearKey). */ /** TS / HLS / DASH(+ClearKey).
* @param live live-edge seek (IPTV/events). Zet false voor VOD/clips — anders landt
* ExoPlayer op het einde van een static/VOD-DASH (lijkt op “afgelopen”-scherm).
*/
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
fun liveMediaItem( fun liveMediaItem(
url: String, url: String,
format: String, format: String,
drm: DrmInfo? = null, drm: DrmInfo? = null,
live: Boolean = true,
): MediaItem { ): MediaItem {
// URL wint: ESPN/FAAB levert .m3u8 terwijl de API soms format=dash hardcodet. // URL wint: ESPN/FAAB levert .m3u8 terwijl de API soms format=dash hardcodet.
val isHls = url.contains("m3u8", ignoreCase = true) || val isHls = url.contains("m3u8", ignoreCase = true) ||
@ -359,7 +363,7 @@ fun liveMediaItem(
else -> MimeTypes.VIDEO_MP2T else -> MimeTypes.VIDEO_MP2T
} }
val builder = MediaItem.Builder().setUri(url).setMimeType(mime) val builder = MediaItem.Builder().setUri(url).setMimeType(mime)
if (isHls) { if (live && isHls) {
builder.setLiveConfiguration( builder.setLiveConfiguration(
MediaItem.LiveConfiguration.Builder() MediaItem.LiveConfiguration.Builder()
.setTargetOffsetMs(2_000) .setTargetOffsetMs(2_000)
@ -368,7 +372,7 @@ fun liveMediaItem(
.build(), .build(),
) )
} }
if (isDash) { if (live && isDash) {
builder.setLiveConfiguration( builder.setLiveConfiguration(
MediaItem.LiveConfiguration.Builder() MediaItem.LiveConfiguration.Builder()
.setTargetOffsetMs(3_000) .setTargetOffsetMs(3_000)

View file

@ -168,8 +168,10 @@ fun EventPlayerScreen(
return@LaunchedEffect return@LaunchedEffect
} }
// ClearKey optioneel: F1TV plain HLS heeft drm=null. // ClearKey optioneel: F1TV plain HLS heeft drm=null.
// VOD/clips: geen live-config — anders seek naar “live edge” = eindscherm.
val asVod = hubPlay && eventId.startsWith("viaplay-content:", ignoreCase = true)
val item = try { val item = try {
liveMediaItem(url, play.format, play.drm) liveMediaItem(url, play.format, play.drm, live = !asVod)
} catch (e: Exception) { } catch (e: Exception) {
error = e.message ?: "Kon mediastream niet openen" error = e.message ?: "Kon mediastream niet openen"
return@LaunchedEffect return@LaunchedEffect

View file

@ -1,6 +1,6 @@
{ {
"name": "@media-cluster/token-tray", "name": "@media-cluster/token-tray",
"version": "0.1.1", "version": "0.1.2",
"private": true, "private": true,
"description": "Windows system tray helper to refresh Viaplay/F1TV session tokens", "description": "Windows system tray helper to refresh Viaplay/F1TV session tokens",
"author": "Vonas Media Cluster", "author": "Vonas Media Cluster",

View file

@ -3,7 +3,6 @@ import type { TrayConfig } from "./config";
import { saveConfig } from "./config"; import { saveConfig } from "./config";
import { loginViaplayUi } from "./login/viaplay"; import { loginViaplayUi } from "./login/viaplay";
import { loginF1tvUi } from "./login/f1tv"; import { loginF1tvUi } from "./login/f1tv";
import { betweenAccounts } from "./login/human";
export type AgentAccount = { export type AgentAccount = {
provider: "viaplay" | "f1tv"; provider: "viaplay" | "f1tv";
@ -145,17 +144,11 @@ export async function refreshAccountsSilent(
const notify = opts?.notify !== false; const notify = opts?.notify !== false;
const summary: RefreshSummary = { ok: 0, failed: 0, skipped: 0, errors: [] }; const summary: RefreshSummary = { ok: 0, failed: 0, skipped: 0, errors: [] };
let attempted = 0;
for (const acc of accounts) { for (const acc of accounts) {
if (!acc.password?.trim()) { if (!acc.password?.trim()) {
summary.skipped += 1; summary.skipped += 1;
continue; continue;
} }
if (attempted > 0) {
// Captcha-vriendelijk: niet back-to-back inloggen op meerdere accounts
await betweenAccounts();
}
attempted += 1;
try { try {
await refreshOne(cfg, acc, headless); await refreshOne(cfg, acc, headless);
summary.ok += 1; summary.ok += 1;

View file

@ -84,7 +84,7 @@ async function launchProfile(userDataDir: string, headless: boolean) {
headless, headless,
viewport: { width: 1280, height: 900 } as const, viewport: { width: 1280, height: 900 } as const,
args: ["--disable-blink-features=AutomationControlled"], args: ["--disable-blink-features=AutomationControlled"],
slowMo: 40 + Math.floor(Math.random() * 50), slowMo: 15 + Math.floor(Math.random() * 25),
}; };
for (const channel of ["chrome", "msedge"] as const) { for (const channel of ["chrome", "msedge"] as const) {
try { try {

View file

@ -18,7 +18,7 @@ export function afterNavigate(): Promise<void> {
return humanDelay(1800, 4200); return humanDelay(1800, 4200);
} }
/** Pauze tussen accounts bij batch-refresh (captcha-vriendelijk). */ /** Pauze tussen accounts bij batch-refresh (optioneel; standaard niet gebruikt). */
export function betweenAccounts(): Promise<void> { export function betweenAccounts(): Promise<void> {
return humanDelay(12_000, 28_000); return humanDelay(12_000, 28_000);
} }
@ -46,11 +46,11 @@ export async function humanType(
delay: 60 + Math.floor(Math.random() * 100), delay: 60 + Math.floor(Math.random() * 100),
}); });
await humanDelay(200, 550); await humanDelay(200, 550);
// Per-character delay; geen instant fill() // Per-character delay; geen instant fill() — expres traag tegen CAPTCHA
await locator.pressSequentially(text, { await locator.pressSequentially(text, {
delay: 55 + Math.floor(Math.random() * 90), delay: 110 + Math.floor(Math.random() * 160),
}); });
await humanDelay(280, 800); await humanDelay(400, 1100);
} }
/** Wacht tot password zichtbaar is, met menselijke poll i.p.v. agressief spammen. */ /** Wacht tot password zichtbaar is, met menselijke poll i.p.v. agressief spammen. */

View file

@ -105,7 +105,8 @@ async function launchProfile(userDataDir: string, headless: boolean) {
headless, headless,
viewport: { width: 1280, height: 900 } as const, viewport: { width: 1280, height: 900 } as const,
args: ["--disable-blink-features=AutomationControlled"], args: ["--disable-blink-features=AutomationControlled"],
slowMo: 40 + Math.floor(Math.random() * 50), // Lichte slowMo; echte CAPTCHA-fix zit in humanType (toetsaanslagen)
slowMo: 15 + Math.floor(Math.random() * 25),
}; };
for (const channel of ["chrome", "msedge"] as const) { for (const channel of ["chrome", "msedge"] as const) {
try { try {

View file

@ -1,9 +1,8 @@
{ {
"version": "0.0.0", "version": "0.1.2",
"filename": "token-tray-setup.exe", "filename": "token-tray-setup.exe",
"sizeBytes": 0, "sizeBytes": 84309324,
"builtAt": null, "builtAt": "2026-09-23T00:37:43.088Z",
"productName": "Media Cluster Token Tray", "productName": "Media Cluster Token Tray",
"available": false, "available": true
"note": "Run apps/token-tray pack:win + publish:installer on Windows, copy token-tray-setup.exe into the Dokploy agent_install volume (/app/install/agent/), then refresh /agent." }
}