feat(telemetry): playback_events.via, skad przyszedl adres w odtwarzaczu
Po przejsciu porndoe na resolver po stronie telefonu 35 urzadzen dalej dostawalo wylacznie 403 i nie dalo sie odroznic, czy pada nowa droga, zapas przez WebView, czy stara paczka. Apka wysyla teraz via: phone, backend, hoster, webview, proxy, movie; '-re' po ponownym rozwiazaniu w tym samym ekranie, '<droga>>wv' gdy adres wyciagnal WebView jako zapas. NULL = paczka sprzed tej zmiany. Migracja 0053 (kolumna nullable, sama zmiana metadanych). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
189dbdf17d
commit
2feef620a1
8 changed files with 77 additions and 5 deletions
32
alembic/versions/20261006_0053_playback_events_via.py
Normal file
32
alembic/versions/20261006_0053_playback_events_via.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
"""playback_events.via: skad przyszedl adres, ktory gral (albo padl) w odtwarzaczu.
|
||||||
|
|
||||||
|
Po przejsciu porndoe na resolver po stronie telefonu (2026-10-02) 35 urzadzen dalej
|
||||||
|
dostawalo wylacznie 403 i nie dalo sie odroznic, czy pada nowa droga (link pobrany na
|
||||||
|
telefonie), zapas przez WebView, czy stara paczka. Apka wysyla teraz `via`
|
||||||
|
('phone', 'backend', 'hoster', 'webview', 'proxy', z koncowka '-re' po ponownym
|
||||||
|
rozwiazaniu); NULL = paczka sprzed tej zmiany.
|
||||||
|
|
||||||
|
Kolumna bez DEFAULT, nullable: ADD COLUMN to sama zmiana metadanych.
|
||||||
|
|
||||||
|
Revision ID: 0053_playback_events_via
|
||||||
|
Revises: 0052_clips_feed_counters
|
||||||
|
Create Date: 2026-10-06
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
revision = "0053_playback_events_via"
|
||||||
|
down_revision: str | None = "0052_clips_feed_counters"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
op.execute("SET LOCAL lock_timeout = '5s'")
|
||||||
|
op.execute("ALTER TABLE playback_events ADD COLUMN IF NOT EXISTS via varchar(16)")
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.execute("SET LOCAL lock_timeout = '5s'")
|
||||||
|
op.execute("ALTER TABLE playback_events DROP COLUMN IF EXISTS via")
|
||||||
|
|
@ -32,6 +32,9 @@ class PlaybackEventIn(BaseModel):
|
||||||
# padów których nie da się odtworzyć na emulatorze (np. watchporn player_error).
|
# padów których nie da się odtworzyć na emulatorze (np. watchporn player_error).
|
||||||
error_detail: str | None = Field(default=None, max_length=512)
|
error_detail: str | None = Field(default=None, max_length=512)
|
||||||
ttff_ms: int | None = Field(default=None, ge=0, le=600_000)
|
ttff_ms: int | None = Field(default=None, ge=0, le=600_000)
|
||||||
|
# Skad przyszedl adres w odtwarzaczu (patrz PlaybackEvent.via). Opcjonalne:
|
||||||
|
# starsze paczki go nie wysylaja.
|
||||||
|
via: str | None = Field(default=None, max_length=16)
|
||||||
|
|
||||||
|
|
||||||
@router.post("", status_code=204)
|
@router.post("", status_code=204)
|
||||||
|
|
@ -49,6 +52,7 @@ def post_playback_event(
|
||||||
error_detail=body.error_detail if body.status == "error" else None,
|
error_detail=body.error_detail if body.status == "error" else None,
|
||||||
ttff_ms=body.ttff_ms if body.status == "success" else None,
|
ttff_ms=body.ttff_ms if body.status == "success" else None,
|
||||||
device_id=device_id,
|
device_id=device_id,
|
||||||
|
via=body.via,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@ class PlaybackEvent(Base):
|
||||||
error_detail: Mapped[str | None] = mapped_column(String(512), nullable=True)
|
error_detail: Mapped[str | None] = mapped_column(String(512), nullable=True)
|
||||||
# time-to-first-frame [ms], "szybkość" (tylko przy success; None gdy nie zmierzono).
|
# time-to-first-frame [ms], "szybkość" (tylko przy success; None gdy nie zmierzono).
|
||||||
ttff_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
ttff_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||||
|
# Skad przyszedl adres w odtwarzaczu: 'phone', 'backend', 'hoster', 'webview',
|
||||||
|
# 'proxy' (z '-re' po ponownym rozwiazaniu). NULL = paczka sprzed 2026-10-06.
|
||||||
|
via: Mapped[str | None] = mapped_column(String(16), nullable=True)
|
||||||
device_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
device_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||||
created_at: Mapped[datetime] = mapped_column(
|
created_at: Mapped[datetime] = mapped_column(
|
||||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||||
|
|
|
||||||
|
|
@ -333,6 +333,7 @@ export class GoonClient {
|
||||||
error_kind?: string;
|
error_kind?: string;
|
||||||
error_detail?: string;
|
error_detail?: string;
|
||||||
ttff_ms?: number;
|
ttff_ms?: number;
|
||||||
|
via?: string;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await this.request('/playback-events', {
|
await this.request('/playback-events', {
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,10 @@ export type RootStackParamList = {
|
||||||
// się automatycznie na WebView z tym URL'em, tam JS embed page sam pobiera
|
// się automatycznie na WebView z tym URL'em, tam JS embed page sam pobiera
|
||||||
// m3u8 we własnym kontekście (Cookies + JA3 + IP set ok).
|
// m3u8 we własnym kontekście (Cookies + JA3 + IP set ok).
|
||||||
fallbackEmbedUrl?: string;
|
fallbackEmbedUrl?: string;
|
||||||
|
// Skad przyszedl adres (telemetria playback_events.via): 'phone' (resolver na
|
||||||
|
// telefonie w SceneDetail), 'backend', 'hoster' (resolver hostera w Playerze),
|
||||||
|
// 'webview' (wyciagniety ze strony), 'proxy' (zapasowe proxy), 'movie'.
|
||||||
|
via?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,7 @@ function WatchChip({
|
||||||
const pDirect = p.direct_url;
|
const pDirect = p.direct_url;
|
||||||
const pIsDirect = !!pDirect && pDirect !== p.stream_url;
|
const pIsDirect = !!pDirect && pDirect !== p.stream_url;
|
||||||
navigation.navigate('Player', {
|
navigation.navigate('Player', {
|
||||||
|
via: 'movie',
|
||||||
url: pDirect || p.stream_url || p.embed_url || pb.page_url,
|
url: pDirect || p.stream_url || p.embed_url || pb.page_url,
|
||||||
origin: pb.origin,
|
origin: pb.origin,
|
||||||
sceneId: movieId,
|
sceneId: movieId,
|
||||||
|
|
@ -239,6 +240,7 @@ function WatchChip({
|
||||||
const best = links[0];
|
const best = links[0];
|
||||||
if (best && (best.direct_url || best.stream_url)) {
|
if (best && (best.direct_url || best.stream_url)) {
|
||||||
navigation.navigate('Player', {
|
navigation.navigate('Player', {
|
||||||
|
via: 'movie',
|
||||||
url: best.direct_url || best.stream_url!,
|
url: best.direct_url || best.stream_url!,
|
||||||
origin: pb.origin,
|
origin: pb.origin,
|
||||||
sceneId: movieId,
|
sceneId: movieId,
|
||||||
|
|
@ -290,6 +292,7 @@ function WatchChip({
|
||||||
const target = bestDirect || res.best?.stream_url || res.best?.embed_url || pb.page_url;
|
const target = bestDirect || res.best?.stream_url || res.best?.embed_url || pb.page_url;
|
||||||
const fallbackEmbed = res.best?.embed_url || pb.embed_url || pb.page_url;
|
const fallbackEmbed = res.best?.embed_url || pb.embed_url || pb.page_url;
|
||||||
navigation.navigate('Player', {
|
navigation.navigate('Player', {
|
||||||
|
via: 'movie',
|
||||||
url: target,
|
url: target,
|
||||||
origin: pb.origin,
|
origin: pb.origin,
|
||||||
// sceneId pozostaje nazwą param-u (legacy z kiedy Player obsługiwał tylko sceny),
|
// sceneId pozostaje nazwą param-u (legacy z kiedy Player obsługiwał tylko sceny),
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,8 @@ interface RouteParams {
|
||||||
headers?: Record<string, string>;
|
headers?: Record<string, string>;
|
||||||
fallbackProxyUrl?: string;
|
fallbackProxyUrl?: string;
|
||||||
fallbackEmbedUrl?: string;
|
fallbackEmbedUrl?: string;
|
||||||
|
// Skad przyszedl adres, do telemetrii (patrz RootStackParamList.Player.via).
|
||||||
|
via?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wspólny helper dla native + WebView ekranu błędu: oznacza playback source jako
|
// Wspólny helper dla native + WebView ekranu błędu: oznacza playback source jako
|
||||||
|
|
@ -264,6 +266,8 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) {
|
||||||
// przyciskow siedzi z boku, stad tez lewy i prawy.
|
// przyciskow siedzi z boku, stad tez lewy i prawy.
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { url, sceneId, origin: playOrigin, resolvePageUrl, entityKind, durationSec, refererHost, title, fallbackEmbedUrl, headers: paramHeaders, fallbackProxyUrl } = params;
|
const { url, sceneId, origin: playOrigin, resolvePageUrl, entityKind, durationSec, refererHost, title, fallbackEmbedUrl, headers: paramHeaders, fallbackProxyUrl } = params;
|
||||||
|
// Skad przyszedl adres, do telemetrii; ponowne rozwiazanie w tym samym ekranie dopisuje '-re'.
|
||||||
|
const viaRef = React.useRef(params.via || 'other');
|
||||||
const { markBroken, canMark, busy: markBusy } = useMarkSourceBroken(params);
|
const { markBroken, canMark, busy: markBusy } = useMarkSourceBroken(params);
|
||||||
// 'movie' → /movies/{id}/progress, 'scene' (default) → /scenes/{id}/progress.
|
// 'movie' → /movies/{id}/progress, 'scene' (default) → /scenes/{id}/progress.
|
||||||
const upsertProgress = React.useCallback(
|
const upsertProgress = React.useCallback(
|
||||||
|
|
@ -517,6 +521,7 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) {
|
||||||
if (!cancelled && freshUrl && freshUrl !== url) {
|
if (!cancelled && freshUrl && freshUrl !== url) {
|
||||||
player.replace(fresh?.headers ? { uri: freshUrl, headers: fresh.headers } : freshUrl);
|
player.replace(fresh?.headers ? { uri: freshUrl, headers: fresh.headers } : freshUrl);
|
||||||
player.play();
|
player.play();
|
||||||
|
viaRef.current = `${viaRef.current.replace(/-re$/, '')}-re`;
|
||||||
return; // sukces → status zmieni się z 'error', łańcuch fallback nie ruszy
|
return; // sukces → status zmieni się z 'error', łańcuch fallback nie ruszy
|
||||||
}
|
}
|
||||||
// Phone-resolve zadziałał, ale dał TEN SAM URL, czyli strona parsuje się dobrze,
|
// Phone-resolve zadziałał, ale dał TEN SAM URL, czyli strona parsuje się dobrze,
|
||||||
|
|
@ -534,6 +539,7 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) {
|
||||||
.find((u): u is string => !!u && u !== url);
|
.find((u): u is string => !!u && u !== url);
|
||||||
if (!cancelled && viaProxy) {
|
if (!cancelled && viaProxy) {
|
||||||
player.replace(viaProxy);
|
player.replace(viaProxy);
|
||||||
|
viaRef.current = 'proxy-re';
|
||||||
player.play();
|
player.play();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -605,6 +611,7 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) {
|
||||||
headers: undefined,
|
headers: undefined,
|
||||||
fallbackProxyUrl: undefined, // już użyty
|
fallbackProxyUrl: undefined, // już użyty
|
||||||
fallbackEmbedUrl, // zostawiamy do step 3
|
fallbackEmbedUrl, // zostawiamy do step 3
|
||||||
|
via: 'proxy',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -621,6 +628,8 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) {
|
||||||
refererHost,
|
refererHost,
|
||||||
title,
|
title,
|
||||||
mode: 'webview',
|
mode: 'webview',
|
||||||
|
// Droga, ktora wlasnie padla; WebView dopisze do niej '>wv', gdy wyciagnie adres.
|
||||||
|
via: viaRef.current,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [status, fallbackProxyUrl, fallbackEmbedUrl, url, nav, sceneId, durationSec, refererHost, title, player, source, playerError, resolvePageUrl, reResolveApplicable, reResolveDone, playOrigin]);
|
}, [status, fallbackProxyUrl, fallbackEmbedUrl, url, nav, sceneId, durationSec, refererHost, title, player, source, playerError, resolvePageUrl, reResolveApplicable, reResolveDone, playOrigin]);
|
||||||
|
|
@ -642,6 +651,7 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) {
|
||||||
status: 'success',
|
status: 'success',
|
||||||
scene_id: sceneId,
|
scene_id: sceneId,
|
||||||
ttff_ms: Date.now() - (playTelemetryMountRef.current ?? Date.now()),
|
ttff_ms: Date.now() - (playTelemetryMountRef.current ?? Date.now()),
|
||||||
|
via: viaRef.current,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [status, playOrigin, client, sceneId]);
|
}, [status, playOrigin, client, sceneId]);
|
||||||
|
|
@ -658,6 +668,7 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) {
|
||||||
scene_id: sceneId,
|
scene_id: sceneId,
|
||||||
error_kind: isGoneError(playerError?.message) ? 'gone' : 'player_error',
|
error_kind: isGoneError(playerError?.message) ? 'gone' : 'player_error',
|
||||||
error_detail: playerError?.message ? String(playerError.message).slice(0, 512) : undefined,
|
error_detail: playerError?.message ? String(playerError.message).slice(0, 512) : undefined,
|
||||||
|
via: viaRef.current,
|
||||||
});
|
});
|
||||||
}, [status, playOrigin, fallbackProxyUrl, fallbackEmbedUrl, url, playerError, client, sceneId]);
|
}, [status, playOrigin, fallbackProxyUrl, fallbackEmbedUrl, url, playerError, client, sceneId]);
|
||||||
|
|
||||||
|
|
@ -1855,6 +1866,9 @@ function EmbedWebViewPlayer({ params }: { params: RouteParams }) {
|
||||||
[client, sceneId, entityKind],
|
[client, sceneId, entityKind],
|
||||||
);
|
);
|
||||||
const [extractedUrl, setExtractedUrl] = React.useState<string | null>(null);
|
const [extractedUrl, setExtractedUrl] = React.useState<string | null>(null);
|
||||||
|
// Telemetria: adres wyciagniety z tej strony. Gdy WebView byl zapasem po padnietym
|
||||||
|
// odtwarzaczu, zostaje slad poprzedniej drogi (np. 'phone>wv').
|
||||||
|
const webViewVia = params.via ? `${params.via}>wv`.slice(0, 16) : 'webview';
|
||||||
const [resolveStatus, setResolveStatus] = React.useState<'idle' | 'pending' | 'failed'>('idle');
|
const [resolveStatus, setResolveStatus] = React.useState<'idle' | 'pending' | 'failed'>('idle');
|
||||||
const [resolveError, setResolveError] = React.useState<string | null>(null);
|
const [resolveError, setResolveError] = React.useState<string | null>(null);
|
||||||
const [skipResolve, setSkipResolve] = React.useState(false);
|
const [skipResolve, setSkipResolve] = React.useState(false);
|
||||||
|
|
@ -1968,6 +1982,7 @@ function EmbedWebViewPlayer({ params }: { params: RouteParams }) {
|
||||||
refererHost,
|
refererHost,
|
||||||
headers: result.headers,
|
headers: result.headers,
|
||||||
mode: 'video',
|
mode: 'video',
|
||||||
|
via: 'hoster',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Cichy resolver padl. Zamiast slepego zaulka z przyciskiem wchodzimy OD RAZU do
|
// Cichy resolver padl. Zamiast slepego zaulka z przyciskiem wchodzimy OD RAZU do
|
||||||
|
|
@ -2098,8 +2113,9 @@ function EmbedWebViewPlayer({ params }: { params: RouteParams }) {
|
||||||
durationSec,
|
durationSec,
|
||||||
refererHost: refererHost || (initialHostRef.current || undefined),
|
refererHost: refererHost || (initialHostRef.current || undefined),
|
||||||
mode: 'video',
|
mode: 'video',
|
||||||
|
via: webViewVia,
|
||||||
});
|
});
|
||||||
}, [extractedUrl, nav, sceneId, durationSec, refererHost, params.origin, params.title, params.playbackId, entityKind]);
|
}, [extractedUrl, nav, sceneId, durationSec, refererHost, params.origin, params.title, params.playbackId, entityKind, webViewVia]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
upsertWatchProgress({ position_sec: 0, duration_sec: durationSec ?? undefined }).catch(
|
upsertWatchProgress({ position_sec: 0, duration_sec: durationSec ?? undefined }).catch(
|
||||||
|
|
@ -2268,6 +2284,7 @@ function EmbedWebViewPlayer({ params }: { params: RouteParams }) {
|
||||||
durationSec,
|
durationSec,
|
||||||
refererHost,
|
refererHost,
|
||||||
mode: 'video',
|
mode: 'video',
|
||||||
|
via: webViewVia,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -566,6 +566,8 @@ function PlaybackButton({
|
||||||
const { defaultQuality } = usePreferences();
|
const { defaultQuality } = usePreferences();
|
||||||
const [resolving, setResolving] = React.useState(false);
|
const [resolving, setResolving] = React.useState(false);
|
||||||
const [qualityLinks, setQualityLinks] = React.useState<StreamLink[] | null>(null);
|
const [qualityLinks, setQualityLinks] = React.useState<StreamLink[] | null>(null);
|
||||||
|
// Skad pochodza linki w pickerze jakosci (telemetria via): resolver na telefonie czy serwer.
|
||||||
|
const [qualityVia, setQualityVia] = React.useState<'phone' | 'backend'>('backend');
|
||||||
|
|
||||||
// Auto-quality: 1 link → graj; wiele → spróbuj dopasować domyślną jakość usera
|
// Auto-quality: 1 link → graj; wiele → spróbuj dopasować domyślną jakość usera
|
||||||
// (null gdy 'auto'/brak dopasowania → pokaż picker jak dotąd). markStarted zostaje
|
// (null gdy 'auto'/brak dopasowania → pokaż picker jak dotąd). markStarted zostaje
|
||||||
|
|
@ -616,6 +618,7 @@ function PlaybackButton({
|
||||||
link: StreamLink,
|
link: StreamLink,
|
||||||
fallbackEmbedUrl?: string,
|
fallbackEmbedUrl?: string,
|
||||||
allLinks?: StreamLink[],
|
allLinks?: StreamLink[],
|
||||||
|
via: 'phone' | 'backend' = 'backend',
|
||||||
) => {
|
) => {
|
||||||
let refererHost: string | undefined;
|
let refererHost: string | undefined;
|
||||||
try {
|
try {
|
||||||
|
|
@ -657,6 +660,7 @@ function PlaybackButton({
|
||||||
headers: isDirect && link.headers ? link.headers : undefined,
|
headers: isDirect && link.headers ? link.headers : undefined,
|
||||||
fallbackProxyUrl: isDirect ? link.stream_url || undefined : undefined,
|
fallbackProxyUrl: isDirect ? link.stream_url || undefined : undefined,
|
||||||
fallbackEmbedUrl,
|
fallbackEmbedUrl,
|
||||||
|
via,
|
||||||
// Pozostałe jakości → przełącznik w playerze (bez powrotu do listy źródeł).
|
// Pozostałe jakości → przełącznik w playerze (bez powrotu do listy źródeł).
|
||||||
qualityLinks: (allLinks && allLinks.length > 1)
|
qualityLinks: (allLinks && allLinks.length > 1)
|
||||||
? allLinks
|
? allLinks
|
||||||
|
|
@ -711,8 +715,11 @@ function PlaybackButton({
|
||||||
if (links.length > 0) {
|
if (links.length > 0) {
|
||||||
markStarted();
|
markStarted();
|
||||||
const pick = pickAuto(links);
|
const pick = pickAuto(links);
|
||||||
if (pick) await openAsVideo(pick, source.page_url);
|
if (pick) await openAsVideo(pick, source.page_url, undefined, 'phone');
|
||||||
else setQualityLinks(links);
|
else {
|
||||||
|
setQualityVia('phone');
|
||||||
|
setQualityLinks(links);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// pusto → spadnij na backend resolve poniżej (HosterDead → mark dead dla
|
// pusto → spadnij na backend resolve poniżej (HosterDead → mark dead dla
|
||||||
|
|
@ -792,9 +799,10 @@ function PlaybackButton({
|
||||||
const autoPick = pickAuto(directLinks);
|
const autoPick = pickAuto(directLinks);
|
||||||
if (autoPick) {
|
if (autoPick) {
|
||||||
markStarted();
|
markStarted();
|
||||||
await openAsVideo(autoPick, fallbackEmbedUrl, directLinks);
|
await openAsVideo(autoPick, fallbackEmbedUrl, directLinks, 'backend');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setQualityVia('backend');
|
||||||
setQualityLinks(directLinks);
|
setQualityLinks(directLinks);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 410 = backend zaznaczył link jako PERMANENT dead (HosterDead exception
|
// 410 = backend zaznaczył link jako PERMANENT dead (HosterDead exception
|
||||||
|
|
@ -855,7 +863,7 @@ function PlaybackButton({
|
||||||
// direct link dał 403. `qualityLinks` zawiera tylko stream_url-e, więc
|
// direct link dał 403. `qualityLinks` zawiera tylko stream_url-e, więc
|
||||||
// szukamy embed'a w `links` na resolve response wcześniej; tu już go nie
|
// szukamy embed'a w `links` na resolve response wcześniej; tu już go nie
|
||||||
// mamy, ale to kompromis (rzadki przypadek multi-quality + dead direct).
|
// mamy, ale to kompromis (rzadki przypadek multi-quality + dead direct).
|
||||||
await openAsVideo(link, undefined, qualityLinks || undefined);
|
await openAsVideo(link, undefined, qualityLinks || undefined, qualityVia);
|
||||||
} else if (link.embed_url) {
|
} else if (link.embed_url) {
|
||||||
let refererHost: string | undefined;
|
let refererHost: string | undefined;
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue