Fix iOS Viaplay catch-up Don't go: send Chrome/122 on every CDN fetch.
Catch-up (live:false) skipped the ClearKey proxy so VLC hit the CDN without the working UA, and CFNetwork dropped URLRequest User-Agent unless it is set on the session.
This commit is contained in:
parent
19aa0853a3
commit
80ab9cdb76
5 changed files with 63 additions and 21 deletions
|
|
@ -345,7 +345,7 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 67;
|
||||
CURRENT_PROJECT_VERSION = 68;
|
||||
DEVELOPMENT_TEAM = X25NYX3K49;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
|
@ -360,7 +360,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks"
|
||||
);
|
||||
MARKETING_VERSION = 0.5.54;
|
||||
MARKETING_VERSION = 0.5.55;
|
||||
OTHER_LDFLAGS = ("$(inherited)", "-ObjC");
|
||||
PRODUCT_BUNDLE_IDENTIFIER = nl.vonas.mediacluster.ios;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
@ -378,7 +378,7 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 67;
|
||||
CURRENT_PROJECT_VERSION = 68;
|
||||
DEVELOPMENT_TEAM = X25NYX3K49;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
|
@ -393,7 +393,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks"
|
||||
);
|
||||
MARKETING_VERSION = 0.5.54;
|
||||
MARKETING_VERSION = 0.5.55;
|
||||
OTHER_LDFLAGS = ("$(inherited)", "-ObjC");
|
||||
PRODUCT_BUNDLE_IDENTIFIER = nl.vonas.mediacluster.ios;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
|
|||
|
|
@ -2,6 +2,24 @@ import Foundation
|
|||
import Network
|
||||
import os.log
|
||||
|
||||
enum ViaplayPlaybackHeaders {
|
||||
static let userAgent =
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
||||
static let referer = "https://viaplay.com/"
|
||||
static let origin = "https://viaplay.com"
|
||||
|
||||
static func matches(_ url: URL) -> Bool {
|
||||
let h = (url.host ?? "").lowercased()
|
||||
return h.contains("viaplay") || h.contains("viasat")
|
||||
}
|
||||
|
||||
static func apply(to req: inout URLRequest) {
|
||||
req.setValue(userAgent, forHTTPHeaderField: "User-Agent")
|
||||
req.setValue(referer, forHTTPHeaderField: "Referer")
|
||||
req.setValue(origin, forHTTPHeaderField: "Origin")
|
||||
}
|
||||
}
|
||||
|
||||
private let proxyLog = Logger(subsystem: "nl.vonas.mediacluster.ios", category: "ClearKeyProxy")
|
||||
|
||||
private final class ResumeOnce: @unchecked Sendable {
|
||||
|
|
@ -50,6 +68,8 @@ final class ClearKeyDashProxy {
|
|||
private var isHlsMode = false
|
||||
/// AVPlayer kan geen DASH: serve clear HLS (native of vanuit MPD gegenereerd).
|
||||
private var preferHlsOutput = false
|
||||
/// Segment-CDN's zonder "viaplay" in de host krijgen dezelfde browser-UA als het manifest.
|
||||
private var useViaplayBrowserHeaders = false
|
||||
private var dashVideoRep: DashHlsRep?
|
||||
private var dashAudioRep: DashHlsRep?
|
||||
/// Beschikbare audio-reps (UI); master playlist heeft er maar één actief.
|
||||
|
|
@ -77,6 +97,22 @@ final class ClearKeyDashProxy {
|
|||
return URLSession(configuration: cfg)
|
||||
}()
|
||||
|
||||
/// CFNetwork overschrijft User-Agent op URLRequest met de app-UA; alleen session-headers blijven staan.
|
||||
private let viaplayBrowserSession: URLSession = {
|
||||
let cfg = URLSessionConfiguration.ephemeral
|
||||
cfg.timeoutIntervalForRequest = 15
|
||||
cfg.timeoutIntervalForResource = 45
|
||||
cfg.httpMaximumConnectionsPerHost = 10
|
||||
cfg.requestCachePolicy = .reloadIgnoringLocalCacheData
|
||||
cfg.httpAdditionalHeaders = [
|
||||
"User-Agent": ViaplayPlaybackHeaders.userAgent,
|
||||
"Referer": ViaplayPlaybackHeaders.referer,
|
||||
"Origin": ViaplayPlaybackHeaders.origin,
|
||||
"Accept": "*/*",
|
||||
]
|
||||
return URLSession(configuration: cfg)
|
||||
}()
|
||||
|
||||
/// Serialiseert alleen manifest/playlist-requests (niet segment-decrypt).
|
||||
/// Voorheen alles via één gate → segmenten blokkeerden live-refresh → stall na ~10s + zware buffer.
|
||||
private let playlistGate = PayloadGate()
|
||||
|
|
@ -175,6 +211,7 @@ final class ClearKeyDashProxy {
|
|||
self.keyMap = map
|
||||
self.isHlsMode = hls
|
||||
self.preferHlsOutput = preferHlsForAVPlayer
|
||||
self.useViaplayBrowserHeaders = ViaplayPlaybackHeaders.matches(manifestURL)
|
||||
self.dashVideoRep = nil
|
||||
self.dashAudioRep = nil
|
||||
self.dashAudioReps = []
|
||||
|
|
@ -395,6 +432,7 @@ final class ClearKeyDashProxy {
|
|||
keyMap = nil
|
||||
isHlsMode = false
|
||||
preferHlsOutput = false
|
||||
useViaplayBrowserHeaders = false
|
||||
dashVideoRep = nil
|
||||
dashAudioRep = nil
|
||||
dashAudioReps = []
|
||||
|
|
@ -1047,19 +1085,17 @@ final class ClearKeyDashProxy {
|
|||
var req = URLRequest(url: url)
|
||||
req.timeoutInterval = 15
|
||||
req.cachePolicy = .reloadIgnoringLocalCacheData
|
||||
let host = url.host?.lowercased() ?? ""
|
||||
if host.contains("viaplay") {
|
||||
req.setValue(
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
||||
forHTTPHeaderField: "User-Agent"
|
||||
)
|
||||
req.setValue("https://viaplay.com/", forHTTPHeaderField: "Referer")
|
||||
req.setValue("https://viaplay.com", forHTTPHeaderField: "Origin")
|
||||
req.setValue("bytes", forHTTPHeaderField: "Accept-Ranges")
|
||||
let browser = withState { useViaplayBrowserHeaders } || ViaplayPlaybackHeaders.matches(url)
|
||||
let session: URLSession
|
||||
if browser {
|
||||
ViaplayPlaybackHeaders.apply(to: &req)
|
||||
session = viaplayBrowserSession
|
||||
} else {
|
||||
req.setValue("VMC-iOS-ClearKey/0.5.5", forHTTPHeaderField: "User-Agent")
|
||||
session = urlSession
|
||||
}
|
||||
req.setValue("bytes", forHTTPHeaderField: "Accept-Ranges")
|
||||
let (data, resp) = try await urlSession.data(for: req)
|
||||
let (data, resp) = try await session.data(for: req)
|
||||
if let http = resp as? HTTPURLResponse, !(200...299).contains(http.statusCode) {
|
||||
throw ProxyError.upstream(http.statusCode)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ struct PlayerScreen: View {
|
|||
@State private var clearKeyPreferAV = true
|
||||
|
||||
private var isLive: Bool { request.isLive }
|
||||
private var useClearKeyAV: Bool { request.isLive && request.needsClearKey && clearKeyPreferAV }
|
||||
private var useClearKeyAV: Bool { request.needsClearKey && clearKeyPreferAV }
|
||||
/// F1-replay e.d. komen via live-request binnen, maar hebben een echte duur.
|
||||
private var showSeekChrome: Bool { !isLive || (!useClearKeyAV && vlc.canSeek) }
|
||||
|
||||
|
|
@ -566,7 +566,7 @@ struct PlayerScreen: View {
|
|||
private func start() async {
|
||||
loading = true
|
||||
error = nil
|
||||
if request.isLive && request.needsClearKey {
|
||||
if request.needsClearKey {
|
||||
let keys = request.drm?.keys ?? []
|
||||
let fmt = (request.format ?? "").lowercased()
|
||||
// ESPN e.d.: native HLS + aparte AUDIO-rendition → AVPlayer bevriest beeld bij rotatie.
|
||||
|
|
|
|||
|
|
@ -239,6 +239,10 @@ final class VLCPlayerController: NSObject, ObservableObject, VLCMediaPlayerDeleg
|
|||
media.addOption(":freetype-rel-fontsize=\(Self.subtitleRelFontSize)")
|
||||
media.addOption(":freetype-bold=1")
|
||||
media.addOption(":sub-margin=0")
|
||||
if ViaplayPlaybackHeaders.matches(url) {
|
||||
media.addOption(":http-user-agent=\(ViaplayPlaybackHeaders.userAgent)")
|
||||
media.addOption(":http-referrer=\(ViaplayPlaybackHeaders.referer)")
|
||||
}
|
||||
if mediaPlayer.drawable as AnyObject? !== drawableHost {
|
||||
applyDrawable(force: true)
|
||||
}
|
||||
|
|
@ -316,6 +320,7 @@ final class VLCPlayerController: NSObject, ObservableObject, VLCMediaPlayerDeleg
|
|||
lengthMs = clamped
|
||||
lengthText = Self.format(ms: clamped)
|
||||
canSeek = true
|
||||
liveMode = false
|
||||
}
|
||||
// Lokale clear-HLS of remote HLS.
|
||||
if preferHls || ClearKeyDashProxy.isHlsURL(started.url) || ClearKeyDashProxy.isHlsURL(url) {
|
||||
|
|
@ -458,10 +463,11 @@ final class VLCPlayerController: NSObject, ObservableObject, VLCMediaPlayerDeleg
|
|||
media.addOption(":http-reconnect")
|
||||
media.addOption(":avcodec-hw=any")
|
||||
media.addOption(":freetype-rel-fontsize=\(subtitleRelFontSize)")
|
||||
let viaplay = url.host?.localizedCaseInsensitiveContains("viaplay") == true
|
||||
if viaplay && !viaLocalProxy {
|
||||
media.addOption(":http-user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36")
|
||||
media.addOption(":http-referrer=https://viaplay.com/")
|
||||
if viaLocalProxy {
|
||||
media.addOption(":http-user-agent=VLC/3.0.21 LibVLC/3.0.21")
|
||||
} else if ViaplayPlaybackHeaders.matches(url) {
|
||||
media.addOption(":http-user-agent=\(ViaplayPlaybackHeaders.userAgent)")
|
||||
media.addOption(":http-referrer=\(ViaplayPlaybackHeaders.referer)")
|
||||
} else {
|
||||
media.addOption(":http-user-agent=VLC/3.0.21 LibVLC/3.0.21")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ struct SettingsScreen: View {
|
|||
|
||||
Section("Info") {
|
||||
LabeledContent("App", value: "VMC iOS")
|
||||
LabeledContent("Versie", value: "0.5.54")
|
||||
LabeledContent("Versie", value: "0.5.55")
|
||||
LabeledContent("Live TV", value: app.iptvEnabled ? "Aan" : "Uit")
|
||||
LabeledContent("Events", value: app.eventsEnabled ? "Aan" : "Uit")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue