Stop putting User-Agent on URLRequest so CFNetwork cannot replace it, and log the UA iOS actually sent.
Play-diagnose shows bedoeld vs verzonden; Don't go MPD fails with that UA on screen instead of playing the promo.
This commit is contained in:
parent
e303dc4d37
commit
24913bddf0
6 changed files with 206 additions and 13 deletions
|
|
@ -345,7 +345,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 70;
|
CURRENT_PROJECT_VERSION = 71;
|
||||||
DEVELOPMENT_TEAM = X25NYX3K49;
|
DEVELOPMENT_TEAM = X25NYX3K49;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
|
@ -360,7 +360,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks"
|
"@executable_path/Frameworks"
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.5.57;
|
MARKETING_VERSION = 0.5.58;
|
||||||
OTHER_LDFLAGS = ("$(inherited)", "-ObjC");
|
OTHER_LDFLAGS = ("$(inherited)", "-ObjC");
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = nl.vonas.mediacluster.ios;
|
PRODUCT_BUNDLE_IDENTIFIER = nl.vonas.mediacluster.ios;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
|
@ -378,7 +378,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 70;
|
CURRENT_PROJECT_VERSION = 71;
|
||||||
DEVELOPMENT_TEAM = X25NYX3K49;
|
DEVELOPMENT_TEAM = X25NYX3K49;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
|
@ -393,7 +393,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks"
|
"@executable_path/Frameworks"
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.5.57;
|
MARKETING_VERSION = 0.5.58;
|
||||||
OTHER_LDFLAGS = ("$(inherited)", "-ObjC");
|
OTHER_LDFLAGS = ("$(inherited)", "-ObjC");
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = nl.vonas.mediacluster.ios;
|
PRODUCT_BUNDLE_IDENTIFIER = nl.vonas.mediacluster.ios;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
|
|
||||||
|
|
@ -377,6 +377,18 @@ actor ApiClient {
|
||||||
return try await get("/api/v1/client/viaplay/content/\(enc)/play\(q)")
|
return try await get("/api/v1/client/viaplay/content/\(enc)/play\(q)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reportViaplayCdnDiag(_ d: CdnFetchDiag) async {
|
||||||
|
struct Ok: Decodable { let ok: Bool? }
|
||||||
|
_ = try? await post("/api/v1/client/viaplay/client-diag", json: [
|
||||||
|
"intendedUA": d.intendedUA,
|
||||||
|
"observedUA": d.observedUA,
|
||||||
|
"host": d.host,
|
||||||
|
"path": d.path,
|
||||||
|
"dontGo": d.dontGo,
|
||||||
|
"status": d.status,
|
||||||
|
]) as Ok
|
||||||
|
}
|
||||||
|
|
||||||
/// Encode path segment (incl. `:` in `custom:uuid`) — strenger dan urlPathAllowed.
|
/// Encode path segment (incl. `:` in `custom:uuid`) — strenger dan urlPathAllowed.
|
||||||
private static func encodePathSegment(_ raw: String) -> String {
|
private static func encodePathSegment(_ raw: String) -> String {
|
||||||
var allowed = CharacterSet.alphanumerics
|
var allowed = CharacterSet.alphanumerics
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ enum ViaplayPlaybackHeaders {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func apply(to req: inout URLRequest) {
|
static func apply(to req: inout URLRequest) {
|
||||||
req.setValue(userAgent, forHTTPHeaderField: "User-Agent")
|
// User-Agent NIET op URLRequest zetten: CFNetwork vervangt die dan door VMC/… CFNetwork/…
|
||||||
req.setValue(referer, forHTTPHeaderField: "Referer")
|
req.setValue(referer, forHTTPHeaderField: "Referer")
|
||||||
req.setValue(origin, forHTTPHeaderField: "Origin")
|
req.setValue(origin, forHTTPHeaderField: "Origin")
|
||||||
}
|
}
|
||||||
|
|
@ -27,8 +27,8 @@ enum ViaplayPlaybackHeaders {
|
||||||
|
|
||||||
private let proxyLog = Logger(subsystem: "nl.vonas.mediacluster.ios", category: "ClearKeyProxy")
|
private let proxyLog = Logger(subsystem: "nl.vonas.mediacluster.ios", category: "ClearKeyProxy")
|
||||||
|
|
||||||
/// CFNetwork volgt redirects met de app-UA; wij volgen zelf en zetten Chrome/122 opnieuw.
|
/// CFNetwork volgt redirects met de app-UA; wij volgen zelf. Log de écht verzonden UA.
|
||||||
private final class DenyRedirects: NSObject, URLSessionTaskDelegate {
|
private final class ViaplayCdnSessionDelegate: NSObject, URLSessionTaskDelegate {
|
||||||
func urlSession(
|
func urlSession(
|
||||||
_ session: URLSession,
|
_ session: URLSession,
|
||||||
task: URLSessionTask,
|
task: URLSessionTask,
|
||||||
|
|
@ -38,9 +38,22 @@ private final class DenyRedirects: NSObject, URLSessionTaskDelegate {
|
||||||
) {
|
) {
|
||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
|
||||||
|
let sent = metrics.transactionMetrics.last?.request
|
||||||
|
let ua = sent?.value(forHTTPHeaderField: "User-Agent")
|
||||||
|
?? task.currentRequest?.value(forHTTPHeaderField: "User-Agent")
|
||||||
|
?? "(geen)"
|
||||||
|
ClearKeyDashProxy.noteObservedUserAgent(ua)
|
||||||
|
proxyLog.info("CDN UA \(ua, privacy: .public)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let viaplayDenyRedirects = DenyRedirects()
|
private let viaplayCdnSessionDelegate = ViaplayCdnSessionDelegate()
|
||||||
|
|
||||||
|
private final class URLSessionTaskBox: @unchecked Sendable {
|
||||||
|
var task: URLSessionDataTask?
|
||||||
|
}
|
||||||
|
|
||||||
private final class ResumeOnce: @unchecked Sendable {
|
private final class ResumeOnce: @unchecked Sendable {
|
||||||
private var done = false
|
private var done = false
|
||||||
|
|
@ -54,11 +67,54 @@ private final class ResumeOnce: @unchecked Sendable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CdnFetchDiag: Sendable {
|
||||||
|
let intendedUA: String
|
||||||
|
let observedUA: String
|
||||||
|
let host: String
|
||||||
|
let path: String
|
||||||
|
let dontGo: Bool
|
||||||
|
let status: Int
|
||||||
|
}
|
||||||
|
|
||||||
/// Lokale HTTP-proxy: haalt DASH MPD of HLS m3u8 + CENC-segmenten op, strip DRM, decrypt ClearKey.
|
/// Lokale HTTP-proxy: haalt DASH MPD of HLS m3u8 + CENC-segmenten op, strip DRM, decrypt ClearKey.
|
||||||
/// VLC speelt daarna plain media vanaf 127.0.0.1.
|
/// VLC speelt daarna plain media vanaf 127.0.0.1.
|
||||||
final class ClearKeyDashProxy {
|
final class ClearKeyDashProxy {
|
||||||
static let shared = ClearKeyDashProxy()
|
static let shared = ClearKeyDashProxy()
|
||||||
|
|
||||||
|
private static let diagLock = NSLock()
|
||||||
|
private static var observedUA = "(onbekend)"
|
||||||
|
private static var _lastCdnDiag: CdnFetchDiag?
|
||||||
|
static var lastCdnDiag: CdnFetchDiag? {
|
||||||
|
diagLock.lock()
|
||||||
|
defer { diagLock.unlock() }
|
||||||
|
return _lastCdnDiag
|
||||||
|
}
|
||||||
|
|
||||||
|
static func noteObservedUserAgent(_ ua: String) {
|
||||||
|
diagLock.lock()
|
||||||
|
observedUA = ua
|
||||||
|
diagLock.unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func currentObservedUA() -> String {
|
||||||
|
diagLock.lock()
|
||||||
|
defer { diagLock.unlock() }
|
||||||
|
return observedUA
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func storeDiag(_ d: CdnFetchDiag) {
|
||||||
|
diagLock.lock()
|
||||||
|
_lastCdnDiag = d
|
||||||
|
diagLock.unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
static func resetCdnDiag() {
|
||||||
|
diagLock.lock()
|
||||||
|
_lastCdnDiag = nil
|
||||||
|
observedUA = "(onbekend)"
|
||||||
|
diagLock.unlock()
|
||||||
|
}
|
||||||
|
|
||||||
/// CMAF: video- én audio-init hebben vaak dezelfde track_ID (1).
|
/// CMAF: video- én audio-init hebben vaak dezelfde track_ID (1).
|
||||||
/// Gescheiden state voorkomt dat audio-crypto video-decrypt overschrijft → geluid zonder beeld.
|
/// Gescheiden state voorkomt dat audio-crypto video-decrypt overschrijft → geluid zonder beeld.
|
||||||
private enum CryptoLane: String {
|
private enum CryptoLane: String {
|
||||||
|
|
@ -132,7 +188,7 @@ final class ClearKeyDashProxy {
|
||||||
]
|
]
|
||||||
return URLSession(
|
return URLSession(
|
||||||
configuration: cfg,
|
configuration: cfg,
|
||||||
delegate: viaplayDenyRedirects,
|
delegate: viaplayCdnSessionDelegate,
|
||||||
delegateQueue: nil
|
delegateQueue: nil
|
||||||
)
|
)
|
||||||
}()
|
}()
|
||||||
|
|
@ -225,6 +281,7 @@ final class ClearKeyDashProxy {
|
||||||
throw ProxyError.noKeys
|
throw ProxyError.noKeys
|
||||||
}
|
}
|
||||||
let hls = Self.isHlsURL(manifestURL)
|
let hls = Self.isHlsURL(manifestURL)
|
||||||
|
Self.resetCdnDiag()
|
||||||
|
|
||||||
let bound: (port: UInt16, session: Int) = try await withCheckedThrowingContinuation { cont in
|
let bound: (port: UInt16, session: Int) = try await withCheckedThrowingContinuation { cont in
|
||||||
queue.async {
|
queue.async {
|
||||||
|
|
@ -1126,7 +1183,7 @@ final class ClearKeyDashProxy {
|
||||||
req.setValue("VMC-iOS-ClearKey/0.5.5", forHTTPHeaderField: "User-Agent")
|
req.setValue("VMC-iOS-ClearKey/0.5.5", forHTTPHeaderField: "User-Agent")
|
||||||
session = urlSession
|
session = urlSession
|
||||||
}
|
}
|
||||||
let (data, resp) = try await session.data(for: req)
|
let (data, resp) = try await Self.dataWithObservedUA(session: session, request: req)
|
||||||
guard let http = resp as? HTTPURLResponse else {
|
guard let http = resp as? HTTPURLResponse else {
|
||||||
return (data, current)
|
return (data, current)
|
||||||
}
|
}
|
||||||
|
|
@ -1149,22 +1206,97 @@ final class ClearKeyDashProxy {
|
||||||
throw ProxyError.upstream(310)
|
throw ProxyError.upstream(310)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// URLSession.data(for:) geeft de gemuteerde UA niet terug; currentRequest wel.
|
||||||
|
private static func dataWithObservedUA(
|
||||||
|
session: URLSession,
|
||||||
|
request: URLRequest
|
||||||
|
) async throws -> (Data, URLResponse) {
|
||||||
|
try await withCheckedThrowingContinuation { cont in
|
||||||
|
let box = URLSessionTaskBox()
|
||||||
|
let task = session.dataTask(with: request) { data, resp, err in
|
||||||
|
let ua = box.task?.currentRequest?.value(forHTTPHeaderField: "User-Agent")
|
||||||
|
?? request.value(forHTTPHeaderField: "User-Agent")
|
||||||
|
?? (session.configuration.httpAdditionalHeaders?["User-Agent"] as? String)
|
||||||
|
?? "(geen)"
|
||||||
|
noteObservedUserAgent(ua)
|
||||||
|
proxyLog.info("CDN currentRequest UA \(ua, privacy: .public)")
|
||||||
|
if let err {
|
||||||
|
cont.resume(throwing: err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let data, let resp else {
|
||||||
|
cont.resume(throwing: ProxyError.upstream(0))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cont.resume(returning: (data, resp))
|
||||||
|
}
|
||||||
|
box.task = task
|
||||||
|
task.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Manifest ophalen via de sticky eind-URL (zoals ExoPlayer); faalt die, één keer opnieuw via het origineel.
|
/// Manifest ophalen via de sticky eind-URL (zoals ExoPlayer); faalt die, één keer opnieuw via het origineel.
|
||||||
private func fetchManifest(_ original: URL) async throws -> (data: Data, baseURL: URL) {
|
private func fetchManifest(_ original: URL) async throws -> (data: Data, baseURL: URL) {
|
||||||
let sticky = withState { resolvedManifest }
|
let sticky = withState { resolvedManifest }
|
||||||
if let sticky, sticky != original {
|
if let sticky, sticky != original {
|
||||||
if let got = try? await fetchFollowingRedirects(sticky) {
|
if let got = try? await fetchFollowingRedirects(sticky) {
|
||||||
withState { resolvedManifest = got.finalURL }
|
try acceptManifest(got, original: original)
|
||||||
return (got.data, got.finalURL)
|
return (got.data, got.finalURL)
|
||||||
}
|
}
|
||||||
proxyLog.error("sticky manifest failed — re-resolving via original URL")
|
proxyLog.error("sticky manifest failed — re-resolving via original URL")
|
||||||
}
|
}
|
||||||
let got = try await fetchFollowingRedirects(original)
|
let got = try await fetchFollowingRedirects(original)
|
||||||
|
try acceptManifest(got, original: original)
|
||||||
|
return (got.data, got.finalURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func acceptManifest(_ got: (data: Data, finalURL: URL), original: URL) throws {
|
||||||
withState { resolvedManifest = got.finalURL }
|
withState { resolvedManifest = got.finalURL }
|
||||||
if got.finalURL != original {
|
if got.finalURL != original {
|
||||||
proxyLog.info("manifest redirected to \(got.finalURL.host ?? "?", privacy: .public)")
|
proxyLog.info("manifest redirected to \(got.finalURL.host ?? "?", privacy: .public)")
|
||||||
}
|
}
|
||||||
return (got.data, got.finalURL)
|
inspectViaplayManifest(got.data, url: got.finalURL, status: 200)
|
||||||
|
if withState({ useViaplayBrowserHeaders }),
|
||||||
|
Self.mpdLooksLikeDontGo(String(data: got.data, encoding: .utf8) ?? "", url: got.finalURL)
|
||||||
|
{
|
||||||
|
throw ProxyError.dontGo(
|
||||||
|
observedUA: Self.currentObservedUA(),
|
||||||
|
path: got.finalURL.path
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func mpdLooksLikeDontGo(_ xml: String, url: URL) -> Bool {
|
||||||
|
let s = url.absoluteString
|
||||||
|
if s.range(of: #"88000100-1778484912807|\b7c4cd64\b|/dont[-_]?go"#, options: .regularExpression) != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if xml.range(of: #"don'?t\s*go|viaplay\s*presents"#, options: [.regularExpression, .caseInsensitive]) != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return xml.contains("PT43M36S") || xml.contains("PT43M35S")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func inspectViaplayManifest(_ data: Data, url: URL, status: Int) {
|
||||||
|
guard withState({ useViaplayBrowserHeaders }) else { return }
|
||||||
|
let xml = String(data: data, encoding: .utf8) ?? ""
|
||||||
|
let observed = Self.currentObservedUA()
|
||||||
|
let dontGo = Self.mpdLooksLikeDontGo(xml, url: url)
|
||||||
|
let diag = CdnFetchDiag(
|
||||||
|
intendedUA: ViaplayPlaybackHeaders.userAgent,
|
||||||
|
observedUA: observed,
|
||||||
|
host: url.host ?? "?",
|
||||||
|
path: url.path,
|
||||||
|
dontGo: dontGo,
|
||||||
|
status: status
|
||||||
|
)
|
||||||
|
Self.storeDiag(diag)
|
||||||
|
proxyLog.info(
|
||||||
|
"MPD inspect dontGo=\(dontGo, privacy: .public) observedUA=\(observed, privacy: .public) host=\(url.host ?? "?", privacy: .public)"
|
||||||
|
)
|
||||||
|
if dontGo {
|
||||||
|
// niet throwen hier — fetchManifest heeft meerdere callers; throw in start na eerste inspect
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func rewriteMpd(_ xml: String, mpdURL: URL) -> String {
|
private func rewriteMpd(_ xml: String, mpdURL: URL) -> String {
|
||||||
|
|
@ -2141,6 +2273,7 @@ final class ClearKeyDashProxy {
|
||||||
case badPath
|
case badPath
|
||||||
case emptyManifest
|
case emptyManifest
|
||||||
case upstream(Int)
|
case upstream(Int)
|
||||||
|
case dontGo(observedUA: String, path: String)
|
||||||
|
|
||||||
var errorDescription: String? {
|
var errorDescription: String? {
|
||||||
switch self {
|
switch self {
|
||||||
|
|
@ -2150,6 +2283,8 @@ final class ClearKeyDashProxy {
|
||||||
case .badPath: return "Ongeldig proxy-pad"
|
case .badPath: return "Ongeldig proxy-pad"
|
||||||
case .emptyManifest: return "Leeg of ongeldig stream-manifest ontvangen"
|
case .emptyManifest: return "Leeg of ongeldig stream-manifest ontvangen"
|
||||||
case .upstream(let code): return "Upstream HTTP \(code)"
|
case .upstream(let code): return "Upstream HTTP \(code)"
|
||||||
|
case .dontGo(let ua, let path):
|
||||||
|
return "Don't go-MPD. Verzonden UA: \(ua). Pad: \(path)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -616,6 +616,9 @@ struct PlayerScreen: View {
|
||||||
waited += stepNs
|
waited += stepNs
|
||||||
}
|
}
|
||||||
if av.isPlaying && !av.hasError {
|
if av.isPlaying && !av.hasError {
|
||||||
|
if let diag = ClearKeyDashProxy.lastCdnDiag {
|
||||||
|
await app.api.reportViaplayCdnDiag(diag)
|
||||||
|
}
|
||||||
loading = false
|
loading = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -668,6 +671,9 @@ struct PlayerScreen: View {
|
||||||
}
|
}
|
||||||
try? await Task.sleep(nanoseconds: 250_000_000)
|
try? await Task.sleep(nanoseconds: 250_000_000)
|
||||||
loading = false
|
loading = false
|
||||||
|
if request.usesViaplayCdn, let diag = ClearKeyDashProxy.lastCdnDiag {
|
||||||
|
await app.api.reportViaplayCdnDiag(diag)
|
||||||
|
}
|
||||||
if !useClearKeyAV && !vlc.hasError {
|
if !useClearKeyAV && !vlc.hasError {
|
||||||
vlc.refreshTracks()
|
vlc.refreshTracks()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ struct SettingsScreen: View {
|
||||||
|
|
||||||
Section("Info") {
|
Section("Info") {
|
||||||
LabeledContent("App", value: "VMC iOS")
|
LabeledContent("App", value: "VMC iOS")
|
||||||
LabeledContent("Versie", value: "0.5.57")
|
LabeledContent("Versie", value: "0.5.58")
|
||||||
LabeledContent("Live TV", value: app.iptvEnabled ? "Aan" : "Uit")
|
LabeledContent("Live TV", value: app.iptvEnabled ? "Aan" : "Uit")
|
||||||
LabeledContent("Events", value: app.eventsEnabled ? "Aan" : "Uit")
|
LabeledContent("Events", value: app.eventsEnabled ? "Aan" : "Uit")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1043,6 +1043,46 @@ export function registerViewerRoutes(
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post("/api/v1/client/viaplay/client-diag", async (request) => {
|
||||||
|
await viewers.authFromBearer(request.headers.authorization);
|
||||||
|
const body = (request.body ?? {}) as {
|
||||||
|
intendedUA?: string;
|
||||||
|
observedUA?: string;
|
||||||
|
host?: string;
|
||||||
|
path?: string;
|
||||||
|
dontGo?: boolean;
|
||||||
|
status?: number;
|
||||||
|
};
|
||||||
|
const observed = String(body.observedUA || "").slice(0, 240);
|
||||||
|
const intended = String(body.intendedUA || "").slice(0, 240);
|
||||||
|
const host = String(body.host || "").slice(0, 120);
|
||||||
|
const path = String(body.path || "").slice(0, 180);
|
||||||
|
const dontGo = !!body.dontGo;
|
||||||
|
const { recordViaplayPlay, mediaPathOf } = await import("../viaplay/diag");
|
||||||
|
const { inventoryFromNames } = await import("../viaplay/cookies");
|
||||||
|
recordViaplayPlay({
|
||||||
|
kind: "live",
|
||||||
|
guid: "ios-cdn",
|
||||||
|
ok: !dontGo,
|
||||||
|
title: "iOS CDN-fetch",
|
||||||
|
accountId: null,
|
||||||
|
accountLabel: null,
|
||||||
|
httpStatus: Number.isFinite(body.status) ? Number(body.status) : null,
|
||||||
|
playHost: host || null,
|
||||||
|
mediaPath: path || mediaPathOf(host),
|
||||||
|
hasLicense: false,
|
||||||
|
dontGo,
|
||||||
|
dontGoReason: dontGo ? `iOS Don't go. UA=${observed}` : null,
|
||||||
|
productDurationMs: null,
|
||||||
|
mpdDurationMs: null,
|
||||||
|
cookies: inventoryFromNames([], null),
|
||||||
|
refreshOk: null,
|
||||||
|
error: `bedoeld=${intended} | verzonden=${observed}`,
|
||||||
|
resolver: "ios-cdn-ua",
|
||||||
|
});
|
||||||
|
return { ok: true };
|
||||||
|
});
|
||||||
|
|
||||||
app.get("/api/v1/client/viaplay/content/:guid/play", async (request) => {
|
app.get("/api/v1/client/viaplay/content/:guid/play", async (request) => {
|
||||||
const auth = await viewers.authFromBearer(request.headers.authorization);
|
const auth = await viewers.authFromBearer(request.headers.authorization);
|
||||||
const { guid: rawGuid } = request.params as { guid: string };
|
const { guid: rawGuid } = request.params as { guid: string };
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue