mirror of
https://github.com/dirtydishes/dreamio.git
synced 2026-06-06 13:37:24 +00:00
fix native playback cocoa pods build
This commit is contained in:
parent
6e220e6df9
commit
511224bcd4
9 changed files with 386 additions and 5 deletions
|
|
@ -6,3 +6,4 @@
|
|||
{"id":"int-3dbe205a","kind":"field_change","created_at":"2026-05-25T03:23:00.515861Z","actor":"dirtydishes","issue_id":"dreamio-2lp","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Fixed Swift raw string escaping and guarded MobileVLCKit import for builds before pod install."}}
|
||||
{"id":"int-23df9e14","kind":"field_change","created_at":"2026-05-25T03:41:03.811099Z","actor":"dirtydishes","issue_id":"dreamio-vxs","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Resolved native playback stream URLs before opening VLC, added resolver selection tests, and documented validation limits."}}
|
||||
{"id":"int-76aa54ba","kind":"field_change","created_at":"2026-05-25T03:51:39.198446Z","actor":"dirtydishes","issue_id":"dreamio-8vi","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
||||
{"id":"int-74805ffd","kind":"field_change","created_at":"2026-05-25T04:21:42.440755Z","actor":"dirtydishes","issue_id":"dreamio-2k5","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Added native backend availability guard, installed CocoaPods, generated workspace metadata, documented setup, and validated available checks."}}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
{"_type":"issue","id":"dreamio-2k5","title":"Guard native playback when MobileVLCKit is unavailable","description":"Dreamio can currently present its native player from raw xcodeproj builds where MobileVLCKit is not linked, which leads to the fallback backend message instead of an actionable setup path. Add a runtime/build availability check, document the CocoaPods workspace requirement, and validate the fallback remains buildable.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T04:15:47Z","created_by":"dirtydishes","updated_at":"2026-05-25T04:21:42Z","started_at":"2026-05-25T04:15:56Z","closed_at":"2026-05-25T04:21:42Z","close_reason":"Added native backend availability guard, installed CocoaPods, generated workspace metadata, documented setup, and validated available checks.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"dreamio-8vi","title":"Fix URL redaction crash on percent-encoded paths","description":"## Why\nDreamio can crash while logging WebKit navigation and playback URLs because URLRedactor writes raw replacement text back into URLComponents.percentEncodedPath.\n\n## What needs to be done\n- Update URL redaction to avoid assigning invalid characters to percentEncodedPath\n- Preserve token/path redaction behavior for diagnostics\n- Add a regression test covering percent-encoded path input similar to the Stremio crash logs\n\n## Acceptance criteria\n- Redacting a URL with percent-encoded path segments does not crash\n- Diagnostics still remove query strings/fragments and redact token-like path segments\n- Tests cover the regression","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T03:50:04Z","created_by":"dirtydishes","updated_at":"2026-05-25T03:51:39Z","started_at":"2026-05-25T03:50:08Z","closed_at":"2026-05-25T03:51:39Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"dreamio-vxs","title":"Resolve final media URLs before native playback","description":"Dreamio native playback can pass addon resolver URLs into VLC instead of the final direct media URL. Resolve known Stremio addon stream responses before presenting the native player, preserve needed headers, and make startup failure recoverable.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T03:36:14Z","created_by":"dirtydishes","updated_at":"2026-05-25T03:41:04Z","started_at":"2026-05-25T03:36:19Z","closed_at":"2026-05-25T03:41:04Z","close_reason":"Resolved native playback stream URLs before opening VLC, added resolver selection tests, and documented validation limits.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"dreamio-2lp","title":"Fix native playback build blockers","description":"Correct Swift string escaping for the injected stream bridge and allow the VLC backend source to compile before MobileVLCKit is installed by guarding the import with canImport.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T03:22:52Z","created_by":"dirtydishes","updated_at":"2026-05-25T03:23:00Z","started_at":"2026-05-25T03:23:00Z","closed_at":"2026-05-25T03:23:00Z","close_reason":"Fixed Swift raw string escaping and guarded MobileVLCKit import for builds before pod install.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
2771CB8C5035D4D119051FEA /* Pods_Dreamio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 908FA15B08AB341C116BAB46 /* Pods_Dreamio.framework */; };
|
||||
6F2A2B362C00100100DREAMIO /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2A2B332C00100100DREAMIO /* AppDelegate.swift */; };
|
||||
6F2A2B372C00100100DREAMIO /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2A2B342C00100100DREAMIO /* SceneDelegate.swift */; };
|
||||
6F2A2B382C00100100DREAMIO /* DreamioWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2A2B352C00100100DREAMIO /* DreamioWebViewController.swift */; };
|
||||
|
|
@ -28,6 +29,9 @@
|
|||
6F2A2B482C00100100DREAMIO /* VLCNativePlaybackBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCNativePlaybackBackend.swift; sourceTree = "<group>"; };
|
||||
6F2A2B492C00100100DREAMIO /* NativePlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativePlayerViewController.swift; sourceTree = "<group>"; };
|
||||
6F2A2B512C00100100DREAMIO /* StreamResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamResolver.swift; sourceTree = "<group>"; };
|
||||
701702B9C2BFBEDE36E7F0A3 /* Pods-Dreamio.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Dreamio.release.xcconfig"; path = "Target Support Files/Pods-Dreamio/Pods-Dreamio.release.xcconfig"; sourceTree = "<group>"; };
|
||||
908FA15B08AB341C116BAB46 /* Pods_Dreamio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Dreamio.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BF0A4D5BAC9400AEEF3B0181 /* Pods-Dreamio.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Dreamio.debug.xcconfig"; path = "Target Support Files/Pods-Dreamio/Pods-Dreamio.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
|
@ -35,17 +39,38 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2771CB8C5035D4D119051FEA /* Pods_Dreamio.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
5DEC645FC7F60E33F3A4E21E /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
908FA15B08AB341C116BAB46 /* Pods_Dreamio.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6593E172E04E344E08B5CAA8 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF0A4D5BAC9400AEEF3B0181 /* Pods-Dreamio.debug.xcconfig */,
|
||||
701702B9C2BFBEDE36E7F0A3 /* Pods-Dreamio.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6F2A2B272C00100100DREAMIO = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6F2A2B322C00100100DREAMIO /* Dreamio */,
|
||||
6F2A2B312C00100100DREAMIO /* Products */,
|
||||
6593E172E04E344E08B5CAA8 /* Pods */,
|
||||
5DEC645FC7F60E33F3A4E21E /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
|
@ -80,9 +105,11 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 6F2A2B412C00100100DREAMIO /* Build configuration list for PBXNativeTarget "Dreamio" */;
|
||||
buildPhases = (
|
||||
9F808EDAD2C69568A9142D10 /* [CP] Check Pods Manifest.lock */,
|
||||
6F2A2B2C2C00100100DREAMIO /* Sources */,
|
||||
6F2A2B2D2C00100100DREAMIO /* Frameworks */,
|
||||
6F2A2B2E2C00100100DREAMIO /* Resources */,
|
||||
F26EA81D312D2AA38B06CF11 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -136,6 +163,48 @@
|
|||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
9F808EDAD2C69568A9142D10 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Dreamio-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F26EA81D312D2AA38B06CF11 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Dreamio/Pods-Dreamio-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Dreamio/Pods-Dreamio-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Dreamio/Pods-Dreamio-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
6F2A2B2C2C00100100DREAMIO /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
|
|
@ -191,7 +260,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
|
@ -252,7 +321,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
|
@ -272,6 +341,7 @@
|
|||
};
|
||||
6F2A2B3E2C00100100DREAMIO /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = BF0A4D5BAC9400AEEF3B0181 /* Pods-Dreamio.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
|
|
@ -293,6 +363,7 @@
|
|||
};
|
||||
6F2A2B3F2C00100100DREAMIO /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 701702B9C2BFBEDE36E7F0A3 /* Pods-Dreamio.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
|
|
|
|||
10
Dreamio.xcworkspace/contents.xcworkspacedata
generated
Normal file
10
Dreamio.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Dreamio.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -368,6 +368,12 @@ final class DreamioWebViewController: UIViewController {
|
|||
|
||||
@MainActor
|
||||
private func resolveAndPresentNativePlayback(_ request: NativePlaybackRequest) async {
|
||||
guard VLCNativePlaybackBackend.isAvailable else {
|
||||
lastNativePlaybackURL = nil
|
||||
showNativePlaybackUnavailableAlert()
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let resolved = try await streamResolver.resolve(request: request)
|
||||
#if DEBUG
|
||||
|
|
@ -407,6 +413,16 @@ final class DreamioWebViewController: UIViewController {
|
|||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
private func showNativePlaybackUnavailableAlert() {
|
||||
let alert = UIAlertController(
|
||||
title: "Native playback needs CocoaPods",
|
||||
message: "This build was opened from Dreamio.xcodeproj or built before MobileVLCKit was installed. Run pod install, open Dreamio.xcworkspace, then build again to play MKV, AVI, and WebM streams.",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: "Close", style: .cancel))
|
||||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private func logDiagnostic(type: String, payload: Any, pageURL: String?) {
|
||||
let redactedPageURL = pageURL.map(redactedURLString) ?? "unknown"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,14 @@ import MobileVLCKit
|
|||
#endif
|
||||
|
||||
final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend {
|
||||
static var isAvailable: Bool {
|
||||
#if canImport(MobileVLCKit)
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
}
|
||||
|
||||
let view = UIView()
|
||||
var onReady: (() -> Void)?
|
||||
var onFailure: ((Error) -> Void)?
|
||||
|
|
|
|||
16
Podfile.lock
Normal file
16
Podfile.lock
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
PODS:
|
||||
- MobileVLCKit (3.7.3)
|
||||
|
||||
DEPENDENCIES:
|
||||
- MobileVLCKit
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- MobileVLCKit
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
MobileVLCKit: 73d7ddb52238b6885b70b0f281cae75a0a6e3ac0
|
||||
|
||||
PODFILE CHECKSUM: 1e4ca4475e4e798e59c235cee9233ad9691c3bee
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
46
README.md
46
README.md
|
|
@ -22,11 +22,51 @@ WebKit commonly cannot play, especially MKV, AVI, and WebM debrid URLs. Keep
|
|||
using `Dreamio.xcworkspace` after installing pods so Xcode links the native
|
||||
playback backend.
|
||||
|
||||
If the app says "Native playback needs CocoaPods" or a player screen says
|
||||
"Native playback is not available in this build," the binary was built without
|
||||
MobileVLCKit linked. To resolve it, install CocoaPods, run `pod install` from
|
||||
this repository, open `Dreamio.xcworkspace` instead of `Dreamio.xcodeproj`, and
|
||||
build the workspace. Direct MKV, AVI, and WebM playback depends on that
|
||||
workspace build because the raw project intentionally keeps a fallback compile
|
||||
path for environments where CocoaPods has not been installed yet.
|
||||
|
||||
On macOS, install CocoaPods with RubyGems:
|
||||
|
||||
```bash
|
||||
sudo gem install cocoapods
|
||||
pod --version
|
||||
pod install
|
||||
open Dreamio.xcworkspace
|
||||
```
|
||||
|
||||
If the gem install fails because of a local Ruby or permissions issue, another
|
||||
common macOS option is Homebrew:
|
||||
|
||||
```bash
|
||||
brew install cocoapods
|
||||
pod --version
|
||||
pod install
|
||||
open Dreamio.xcworkspace
|
||||
```
|
||||
|
||||
The official CocoaPods getting started guide documents the RubyGems install
|
||||
path: https://guides.cocoapods.org/using/getting-started.html
|
||||
|
||||
## Validation Notes
|
||||
|
||||
The repository machine currently has Command Line Tools selected instead of full
|
||||
Xcode, and CocoaPods is not installed, so command-line `pod install` and
|
||||
`xcodebuild` validation are not available here.
|
||||
CocoaPods 1.16.2 was installed with Homebrew on this repository machine, and
|
||||
`pod install` generated `Dreamio.xcworkspace` plus `Podfile.lock` with
|
||||
MobileVLCKit 3.7.3. The workspace builds from the command line when full Xcode
|
||||
is selected for that command:
|
||||
|
||||
```bash
|
||||
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
|
||||
xcodebuild -workspace Dreamio.xcworkspace \
|
||||
-scheme Dreamio \
|
||||
-configuration Debug \
|
||||
-sdk iphonesimulator \
|
||||
build
|
||||
```
|
||||
|
||||
## Playback Validation Checklist
|
||||
|
||||
|
|
|
|||
218
docs/turns/2026-05-25-guard-native-playback-availability.html
Normal file
218
docs/turns/2026-05-25-guard-native-playback-availability.html
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Guard Native Playback Availability</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--ink: oklch(23% 0.024 282);
|
||||
--muted: oklch(48% 0.03 282);
|
||||
--paper: oklch(98% 0.007 285);
|
||||
--panel: oklch(95% 0.012 285);
|
||||
--line: oklch(84% 0.026 285);
|
||||
--accent: oklch(56% 0.16 292);
|
||||
--warn: oklch(62% 0.14 58);
|
||||
--good: oklch(54% 0.13 160);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
line-height: 1.55;
|
||||
}
|
||||
main {
|
||||
max-width: 1040px;
|
||||
margin: 0 auto;
|
||||
padding: 56px 28px 72px;
|
||||
}
|
||||
header {
|
||||
margin-bottom: 34px;
|
||||
}
|
||||
h1 {
|
||||
max-width: 820px;
|
||||
margin: 0 0 16px;
|
||||
font-size: clamp(2rem, 4vw, 3.8rem);
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
h2 {
|
||||
margin: 34px 0 10px;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
p {
|
||||
max-width: 74ch;
|
||||
}
|
||||
ul {
|
||||
max-width: 78ch;
|
||||
padding-left: 1.2rem;
|
||||
}
|
||||
code {
|
||||
font-family: "SFMono-Regular", Consolas, monospace;
|
||||
font-size: 0.95em;
|
||||
background: var(--panel);
|
||||
padding: 0.08rem 0.28rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
pre {
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: oklch(99% 0.004 285);
|
||||
padding: 16px;
|
||||
font-family: "SFMono-Regular", Consolas, monospace;
|
||||
font-size: 0.88rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.summary {
|
||||
max-width: 76ch;
|
||||
color: var(--muted);
|
||||
font-size: 1.08rem;
|
||||
}
|
||||
.meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
.pill {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 999px;
|
||||
padding: 5px 10px;
|
||||
color: var(--muted);
|
||||
background: oklch(96% 0.01 285);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.callout {
|
||||
max-width: 78ch;
|
||||
border: 1px solid oklch(78% 0.08 58);
|
||||
background: oklch(95% 0.035 68);
|
||||
border-radius: 8px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
.ok {
|
||||
border-color: oklch(78% 0.08 160);
|
||||
background: oklch(95% 0.032 160);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header>
|
||||
<h1>Guard Native Playback Availability</h1>
|
||||
<p class="summary">Dreamio now checks whether the MobileVLCKit-backed native player is actually linked before presenting the full-screen native player. Raw project builds stay buildable, but they now show a setup alert instead of opening a black player that can only fail.</p>
|
||||
<div class="meta">
|
||||
<span class="pill">Beads: dreamio-2k5</span>
|
||||
<span class="pill">Native playback</span>
|
||||
<span class="pill">CocoaPods setup</span>
|
||||
<span class="pill">2026-05-25</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<h2>Summary</h2>
|
||||
<p>Fixed the unavailable native playback build path by exposing a build-time availability check on <code>VLCNativePlaybackBackend</code> and using it before Dreamio presents native playback. CocoaPods was installed through Homebrew, <code>pod install</code> was run, and the generated workspace now links MobileVLCKit.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Changes Made</h2>
|
||||
<ul>
|
||||
<li>Added <code>VLCNativePlaybackBackend.isAvailable</code>, backed by the same <code>canImport(MobileVLCKit)</code> compile condition as the real VLC implementation.</li>
|
||||
<li>Updated <code>DreamioWebViewController</code> to check native backend availability before resolving and presenting the native player.</li>
|
||||
<li>Added an actionable setup alert for builds that do not link <code>MobileVLCKit</code>.</li>
|
||||
<li>Updated the README to explain that the exact unavailable-build message means the binary was built without the CocoaPods workspace.</li>
|
||||
<li>Installed CocoaPods 1.16.2 with Homebrew and ran <code>pod install</code>, generating <code>Dreamio.xcworkspace</code> and <code>Podfile.lock</code> with MobileVLCKit 3.7.3.</li>
|
||||
<li>Disabled Xcode user script sandboxing for the project so CocoaPods can embed MobileVLCKit during the framework copy phase.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Context</h2>
|
||||
<p>The repository has a <code>Podfile</code> declaring <code>MobileVLCKit</code>, but this checkout did not have a generated <code>Pods/</code> directory or <code>Dreamio.xcworkspace</code>. In that state, Swift takes the fallback compile path where <code>canImport(MobileVLCKit)</code> is false. Before this change, Dreamio could still present the native player, which then displayed the generic fallback error.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Important Implementation Details</h2>
|
||||
<ul>
|
||||
<li>The fallback backend remains intact so opening <code>Dreamio.xcodeproj</code> directly still compiles.</li>
|
||||
<li>The guard runs before stream resolution, avoiding unnecessary resolver network work when native playback cannot succeed in the current build.</li>
|
||||
<li>The duplicate playback key is cleared when the guard blocks playback, so the user can retry after rebuilding the app correctly.</li>
|
||||
<li>The generated workspace references <code>Dreamio.xcodeproj</code> and <code>Pods/Pods.xcodeproj</code>. The <code>Pods/</code> directory remains ignored, while <code>Podfile.lock</code> and workspace metadata are tracked.</li>
|
||||
<li><code>ENABLE_USER_SCRIPT_SANDBOXING</code> is set to <code>NO</code> because the CocoaPods embed frameworks script uses <code>rsync</code> to copy the MobileVLCKit framework into the app bundle.</li>
|
||||
<li>No public app-facing API changed.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Relevant Diff Snippets</h2>
|
||||
<p><code>@pierre/diffs</code> is installed as a library dependency, but its package does not expose a runnable CLI in this checkout, and <code>npx @pierre/diffs --help</code> failed with "could not determine executable to run." The plain diff below is the fallback snippet for the core behavior change.</p>
|
||||
<pre><code>diff --git a/Dreamio/DreamioWebViewController.swift b/Dreamio/DreamioWebViewController.swift
|
||||
@@ -368,6 +368,12 @@ final class DreamioWebViewController: UIViewController {
|
||||
@MainActor
|
||||
private func resolveAndPresentNativePlayback(_ request: NativePlaybackRequest) async {
|
||||
+ guard VLCNativePlaybackBackend.isAvailable else {
|
||||
+ lastNativePlaybackURL = nil
|
||||
+ showNativePlaybackUnavailableAlert()
|
||||
+ return
|
||||
+ }
|
||||
+
|
||||
do {
|
||||
let resolved = try await streamResolver.resolve(request: request)
|
||||
|
||||
diff --git a/Dreamio/VLCNativePlaybackBackend.swift b/Dreamio/VLCNativePlaybackBackend.swift
|
||||
@@ -5,6 +5,14 @@ import MobileVLCKit
|
||||
#endif
|
||||
|
||||
final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend {
|
||||
+ static var isAvailable: Bool {
|
||||
+#if canImport(MobileVLCKit)
|
||||
+ true
|
||||
+#else
|
||||
+ false
|
||||
+#endif
|
||||
+ }
|
||||
+
|
||||
let view = UIView()
|
||||
var onReady: (() -> Void)?
|
||||
var onFailure: ((Error) -> Void)?</code></pre>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Expected Impact for End-Users</h2>
|
||||
<p>Users who accidentally run a raw <code>.xcodeproj</code> build will see a clear CocoaPods setup message instead of a black native player with an unavailable-build failure. Users who build from <code>Dreamio.xcworkspace</code> with <code>MobileVLCKit</code> linked should continue into VLC-backed direct-file playback.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Validation</h2>
|
||||
<ul>
|
||||
<li>Ran <code>swiftc Dreamio/StreamCandidate.swift Dreamio/StreamResolver.swift Tests/StreamResolverTests.swift -o /tmp/dreamio-stream-tests && /tmp/dreamio-stream-tests</code>: passed.</li>
|
||||
<li>Ran <code>swiftc -typecheck Dreamio/StreamCandidate.swift Dreamio/StreamResolver.swift</code>: passed.</li>
|
||||
<li>Ran <code>git diff --check</code>: passed.</li>
|
||||
<li>Ran <code>HOMEBREW_NO_AUTO_UPDATE=1 brew install cocoapods</code>: passed, installing CocoaPods 1.16.2.</li>
|
||||
<li>Ran <code>pod --version && pod install</code>: passed, installing MobileVLCKit 3.7.3 and generating the workspace.</li>
|
||||
<li>Ran <code>DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -workspace Dreamio.xcworkspace -scheme Dreamio -configuration Debug -sdk iphonesimulator build</code>: passed.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Issues, Limitations, and Mitigations</h2>
|
||||
<div class="callout">The simulator workspace build passes. Real-device playback validation is still required for the actual VLC-backed stream behavior.</div>
|
||||
<ul>
|
||||
<li>The global <code>xcode-select</code> value still points at Command Line Tools because changing it requires sudo. Command-line builds can use <code>DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer</code>.</li>
|
||||
<li>The <code>Pods/</code> directory is intentionally ignored by git, so another checkout should run <code>pod install</code> after pulling.</li>
|
||||
<li>The native player still depends on MobileVLCKit behavior once the workspace build is available.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Follow-up Work</h2>
|
||||
<ul>
|
||||
<li>On device, select a direct MKV, AVI, or WebM stream and confirm the VLC-backed player starts.</li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue