fix(mobile): zapas hls4 dla sextapes, przycisk OK w klawiaturze PIN-u
sextapes: hls2 na zewnetrznym CDN dawal 502 albo byl blokowany filtrem DNS (Failed to connect ... [::]); resolver sprawdza manifest i przechodzi na hls4 z domeny embedu (segmenty to TS z doklejonym PNG, ExoPlayer je gra; sprawdzone na emulatorze). PIN: na TV w poziomie Cancel/Next byly pod krawedzia ekranu, OK siedzi teraz w pustym polu klawiatury. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
34138ff82f
commit
5e203e7e98
3 changed files with 68 additions and 9 deletions
|
|
@ -35,6 +35,14 @@ export type ChangelogEntry = {
|
|||
};
|
||||
|
||||
export const CHANGELOG: ChangelogEntry[] = [
|
||||
{
|
||||
id: '2026-10-16a',
|
||||
date: 'October 2026',
|
||||
items: [
|
||||
'xtapes movies fall back to a second stream when the first one fails or is blocked on your network.',
|
||||
'TV: setting a PIN works with the remote. An OK key now sits in the keypad, the Next button was below the edge of the screen.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '2026-10-15e',
|
||||
date: 'October 2026',
|
||||
|
|
|
|||
|
|
@ -102,22 +102,57 @@ const AD_RE = /\/(?:preroll|midroll|postroll|preplay|ads?|advert|promo)\d*\.(?:m
|
|||
// (hls4) doklejony do domeny embedu.
|
||||
const LINKS_ENTRY_RE = /["']?(hls\d+)["']?\s*:\s*["']([^"']+\.m3u8[^"']*)["']/gi;
|
||||
|
||||
function linksObjectUrl(js: string, embedUrl: string): string | null {
|
||||
let relative: string | null = null;
|
||||
/** Kandydaci z obiektu `links`: najpierw pelne adresy (hls2, CDN), potem wzgledne (hls4,
|
||||
* sciezka na domenie embedu) zamienione na pelne. */
|
||||
function linksObjectCandidates(js: string, embedUrl: string): string[] {
|
||||
const absolute: string[] = [];
|
||||
const relative: string[] = [];
|
||||
let m: RegExpExecArray | null;
|
||||
LINKS_ENTRY_RE.lastIndex = 0;
|
||||
while ((m = LINKS_ENTRY_RE.exec(js)) !== null) {
|
||||
const u = m[2];
|
||||
if (AD_RE.test(u)) continue;
|
||||
if (/^https?:\/\//i.test(u)) return u;
|
||||
if (!relative && u.startsWith('/')) relative = u;
|
||||
}
|
||||
if (!relative) return null;
|
||||
if (/^https?:\/\//i.test(u)) absolute.push(u);
|
||||
else if (u.startsWith('/')) {
|
||||
try {
|
||||
return new URL(relative, embedUrl).toString();
|
||||
relative.push(new URL(u, embedUrl).toString());
|
||||
} catch {
|
||||
return null;
|
||||
// pomijamy
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...absolute, ...relative];
|
||||
}
|
||||
|
||||
/** Czy manifest pod adresem odpowiada (krotki GET z limitem czasu). */
|
||||
async function manifestReachable(url: string, referer: string): Promise<boolean> {
|
||||
const ctrl = new AbortController();
|
||||
const t = setTimeout(() => ctrl.abort(), 4000);
|
||||
try {
|
||||
const r = await fetch(url, { headers: { 'User-Agent': UA, Referer: referer }, signal: ctrl.signal });
|
||||
if (!r.ok) return false;
|
||||
return (await r.text()).trimStart().startsWith('#EXTM3U');
|
||||
} catch {
|
||||
return false;
|
||||
} finally {
|
||||
clearTimeout(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adres z obiektu `links`, sprawdzony z telefonu. sextapes (2026-10-16): hls2 lezy na
|
||||
* zewnetrznym CDN (premilkyway, cdn-centaurus), ktory potrafi dac 502 albo byc zablokowany
|
||||
* przez filtr reklam w DNS (blad "Failed to connect to ...premilkyway.com/[::]"). Wtedy
|
||||
* bierzemy hls4 z domeny embedu (tam strona sama zaczyna). Segmenty hls4 to TS z doklejonym
|
||||
* na poczatku 70-bajtowym PNG, ExoPlayer i tak znajduje synchronizacje TS.
|
||||
*/
|
||||
async function linksObjectUrl(js: string, embedUrl: string, referer: string): Promise<string | null> {
|
||||
const candidates = linksObjectCandidates(js, embedUrl);
|
||||
if (candidates.length <= 1) return candidates[0] || null;
|
||||
for (const c of candidates) {
|
||||
if (await manifestReachable(c, referer)) return c;
|
||||
}
|
||||
return candidates[0];
|
||||
}
|
||||
|
||||
export async function resolvePackerHoster(
|
||||
|
|
@ -164,7 +199,7 @@ export async function resolvePackerHoster(
|
|||
}
|
||||
const um = unpacked.match(FILE_RE);
|
||||
if (um && !AD_RE.test(um[1])) videoUrl = um[1];
|
||||
if (!videoUrl) videoUrl = linksObjectUrl(unpacked, url);
|
||||
if (!videoUrl) videoUrl = await linksObjectUrl(unpacked, url, referer);
|
||||
}
|
||||
|
||||
if (!videoUrl) return { error: 'no_video_url' };
|
||||
|
|
|
|||
|
|
@ -44,7 +44,21 @@ export function PinEntry({ title, error, onSubmit, onCancel, footerAction }: Pro
|
|||
<View style={styles.pad}>
|
||||
{KEYS.map((k, i) =>
|
||||
k === null ? (
|
||||
<View key={i} style={[styles.key, styles.keyEmpty]} />
|
||||
// Zatwierdzenie tez w samej klawiaturze: na telewizorze w poziomie przyciski
|
||||
// Cancel/Next lezaly pod dolna krawedzia ekranu i nie dalo sie przejsc dalej
|
||||
// (zgloszenie 94305101, Fire TV AFTMM: "where is ok").
|
||||
<Pressable
|
||||
key={i}
|
||||
disabled={pin.length < 4}
|
||||
style={({ pressed }) => [
|
||||
styles.key,
|
||||
pin.length < 4 ? styles.keyEmpty : styles.keyOk,
|
||||
pressed && styles.keyPressed,
|
||||
]}
|
||||
onPress={() => onSubmit(pin)}
|
||||
>
|
||||
{pin.length >= 4 ? <Text style={styles.keyOkText}>OK</Text> : null}
|
||||
</Pressable>
|
||||
) : (
|
||||
<Pressable
|
||||
key={i}
|
||||
|
|
@ -114,6 +128,8 @@ const styles = StyleSheet.create({
|
|||
justifyContent: 'center',
|
||||
},
|
||||
keyEmpty: { backgroundColor: 'transparent', borderColor: 'transparent' },
|
||||
keyOk: { backgroundColor: theme.accent, borderColor: theme.accentText },
|
||||
keyOkText: { color: theme.fg, fontSize: 20, fontWeight: '700' },
|
||||
keyPressed: { backgroundColor: theme.bgElevated, borderColor: theme.borderFocus },
|
||||
keyText: { color: theme.fg, fontSize: 26, fontWeight: '500' },
|
||||
keyBack: { color: theme.muted, fontSize: 22 },
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue