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:
parent
bedebf44f6
commit
f9ec3be09e
9 changed files with 25 additions and 26 deletions
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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. */
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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."
|
}
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue