mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
WIP
This commit is contained in:
8
app.json
8
app.json
@@ -135,12 +135,10 @@
|
||||
["./plugins/withTrustLocalCerts.js"],
|
||||
["./plugins/withGradleProperties.js"],
|
||||
[
|
||||
"./plugins/addSPMDependenciesToMainTarget",
|
||||
"./plugins/withGitPod.js",
|
||||
{
|
||||
"version": "0.40.0",
|
||||
"repositoryUrl": "https://github.com/Alexk2309/mpvkit-private",
|
||||
"repoName": "mpvkit-private",
|
||||
"productName": "MPVKit"
|
||||
"podName": "MPVKit-GPL",
|
||||
"podspecUrl": "https://raw.githubusercontent.com/Alexk2309/MPVKit/0.40.0-av/MPVKit-GPL.podspec"
|
||||
}
|
||||
]
|
||||
],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
|
||||
s.static_framework = true
|
||||
|
||||
s.dependency 'ExpoModulesCore'
|
||||
s.dependency 'MPVKit', '~> 0.40.0'
|
||||
s.dependency 'MPVKit-GPL'
|
||||
|
||||
# Swift/Objective-C compatibility
|
||||
s.pod_target_xcconfig = {
|
||||
|
||||
@@ -148,12 +148,14 @@ class MpvPlayerView: ExpoView {
|
||||
func play() {
|
||||
intendedPlayState = true
|
||||
renderer?.play()
|
||||
pipController?.setPlaybackRate(1.0)
|
||||
pipController?.updatePlaybackState()
|
||||
}
|
||||
|
||||
func pause() {
|
||||
intendedPlayState = false
|
||||
renderer?.pausePlayback()
|
||||
pipController?.setPlaybackRate(0.0)
|
||||
pipController?.updatePlaybackState()
|
||||
}
|
||||
|
||||
@@ -283,9 +285,9 @@ extension MpvPlayerView: MPVSoftwareRendererDelegate {
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self else { return }
|
||||
// Only update PiP state when PiP is active
|
||||
// Update PiP current time for progress bar
|
||||
if self.pipController?.isPictureInPictureActive == true {
|
||||
self.pipController?.updatePlaybackState()
|
||||
self.pipController?.setCurrentTimeFromSeconds(position, duration: duration)
|
||||
}
|
||||
|
||||
self.onProgress([
|
||||
@@ -301,12 +303,14 @@ extension MpvPlayerView: MPVSoftwareRendererDelegate {
|
||||
guard let self else { return }
|
||||
// Don't update intendedPlayState here - it's only set by user actions (play/pause)
|
||||
// This prevents PiP UI flicker during seeking
|
||||
|
||||
// Sync timebase rate with actual playback state
|
||||
self.pipController?.setPlaybackRate(isPaused ? 0.0 : 1.0)
|
||||
|
||||
self.onPlaybackStateChange([
|
||||
"isPaused": isPaused,
|
||||
"isPlaying": !isPaused,
|
||||
])
|
||||
// Note: Don't call updatePlaybackState() here to avoid flicker
|
||||
// PiP queries pipControllerIsPlaying when it needs the state
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,12 +347,14 @@ extension MpvPlayerView: PiPControllerDelegate {
|
||||
print("PiP will start")
|
||||
// Sync timebase before PiP starts for smooth transition
|
||||
renderer?.syncTimebase()
|
||||
pipController?.updatePlaybackState()
|
||||
// Set current time for PiP progress bar
|
||||
pipController?.setCurrentTimeFromSeconds(cachedPosition, duration: cachedDuration)
|
||||
}
|
||||
|
||||
func pipController(_ controller: PiPController, didStartPictureInPicture: Bool) {
|
||||
print("PiP did start: \(didStartPictureInPicture)")
|
||||
pipController?.updatePlaybackState()
|
||||
// Ensure current time is synced when PiP starts
|
||||
pipController?.setCurrentTimeFromSeconds(cachedPosition, duration: cachedDuration)
|
||||
}
|
||||
|
||||
func pipController(_ controller: PiPController, willStopPictureInPicture: Bool) {
|
||||
@@ -371,12 +377,16 @@ extension MpvPlayerView: PiPControllerDelegate {
|
||||
|
||||
func pipControllerPlay(_ controller: PiPController) {
|
||||
print("PiP play requested")
|
||||
play()
|
||||
intendedPlayState = true
|
||||
renderer?.play()
|
||||
pipController?.setPlaybackRate(1.0)
|
||||
}
|
||||
|
||||
func pipControllerPause(_ controller: PiPController) {
|
||||
print("PiP pause requested")
|
||||
pause()
|
||||
intendedPlayState = false
|
||||
renderer?.pausePlayback()
|
||||
pipController?.setPlaybackRate(0.0)
|
||||
}
|
||||
|
||||
func pipController(_ controller: PiPController, skipByInterval interval: CMTime) {
|
||||
@@ -394,4 +404,8 @@ extension MpvPlayerView: PiPControllerDelegate {
|
||||
func pipControllerDuration(_ controller: PiPController) -> Double {
|
||||
return getDuration()
|
||||
}
|
||||
|
||||
func pipControllerCurrentPosition(_ controller: PiPController) -> Double {
|
||||
return getCurrentPosition()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ protocol PiPControllerDelegate: AnyObject {
|
||||
func pipController(_ controller: PiPController, skipByInterval interval: CMTime)
|
||||
func pipControllerIsPlaying(_ controller: PiPController) -> Bool
|
||||
func pipControllerDuration(_ controller: PiPController) -> Double
|
||||
func pipControllerCurrentPosition(_ controller: PiPController) -> Double
|
||||
}
|
||||
|
||||
final class PiPController: NSObject {
|
||||
@@ -20,6 +21,13 @@ final class PiPController: NSObject {
|
||||
|
||||
weak var delegate: PiPControllerDelegate?
|
||||
|
||||
// Timebase for PiP progress tracking
|
||||
private var timebase: CMTimebase?
|
||||
|
||||
// Track current time for PiP progress
|
||||
private var currentTime: CMTime = .zero
|
||||
private var currentDuration: Double = 0
|
||||
|
||||
var isPictureInPictureSupported: Bool {
|
||||
return AVPictureInPictureController.isPictureInPictureSupported()
|
||||
}
|
||||
@@ -35,9 +43,29 @@ final class PiPController: NSObject {
|
||||
init(sampleBufferDisplayLayer: AVSampleBufferDisplayLayer) {
|
||||
self.sampleBufferDisplayLayer = sampleBufferDisplayLayer
|
||||
super.init()
|
||||
setupTimebase()
|
||||
setupPictureInPicture()
|
||||
}
|
||||
|
||||
private func setupTimebase() {
|
||||
// Create a timebase for tracking playback time
|
||||
var newTimebase: CMTimebase?
|
||||
let status = CMTimebaseCreateWithSourceClock(
|
||||
allocator: kCFAllocatorDefault,
|
||||
sourceClock: CMClockGetHostTimeClock(),
|
||||
timebaseOut: &newTimebase
|
||||
)
|
||||
|
||||
if status == noErr, let tb = newTimebase {
|
||||
timebase = tb
|
||||
CMTimebaseSetTime(tb, time: .zero)
|
||||
CMTimebaseSetRate(tb, rate: 0) // Start paused
|
||||
|
||||
// Set the control timebase on the display layer
|
||||
sampleBufferDisplayLayer?.controlTimebase = tb
|
||||
}
|
||||
}
|
||||
|
||||
private func setupPictureInPicture() {
|
||||
guard isPictureInPictureSupported,
|
||||
let displayLayer = sampleBufferDisplayLayer else {
|
||||
@@ -89,6 +117,34 @@ final class PiPController: NSObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the current playback time for PiP progress display
|
||||
func setCurrentTime(_ time: CMTime) {
|
||||
currentTime = time
|
||||
|
||||
// Update the timebase to reflect current position
|
||||
if let tb = timebase {
|
||||
CMTimebaseSetTime(tb, time: time)
|
||||
}
|
||||
|
||||
// Always invalidate to refresh the PiP UI
|
||||
updatePlaybackState()
|
||||
}
|
||||
|
||||
/// Updates the current playback time from seconds
|
||||
func setCurrentTimeFromSeconds(_ seconds: Double, duration: Double) {
|
||||
guard seconds >= 0 else { return }
|
||||
currentDuration = duration
|
||||
let time = CMTime(seconds: seconds, preferredTimescale: 1000)
|
||||
setCurrentTime(time)
|
||||
}
|
||||
|
||||
/// Updates the playback rate on the timebase (1.0 = playing, 0.0 = paused)
|
||||
func setPlaybackRate(_ rate: Float) {
|
||||
if let tb = timebase {
|
||||
CMTimebaseSetRate(tb, rate: Float64(rate))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AVPictureInPictureControllerDelegate
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
const { withXcodeProject } = require("@expo/config-plugins");
|
||||
|
||||
const addSPMDependenciesToMainTarget = (config, options) =>
|
||||
withXcodeProject(config, (config) => {
|
||||
const { version, repositoryUrl, repoName, productName } = options;
|
||||
const xcodeProject = config.modResults;
|
||||
|
||||
// update XCRemoteSwiftPackageReference
|
||||
const spmReferences =
|
||||
xcodeProject.hash.project.objects.XCRemoteSwiftPackageReference;
|
||||
|
||||
if (!spmReferences) {
|
||||
xcodeProject.hash.project.objects.XCRemoteSwiftPackageReference = {};
|
||||
}
|
||||
|
||||
const packageReferenceUUID = xcodeProject.generateUuid();
|
||||
|
||||
xcodeProject.hash.project.objects.XCRemoteSwiftPackageReference[
|
||||
`${packageReferenceUUID} /* XCRemoteSwiftPackageReference "${repoName}" */`
|
||||
] = {
|
||||
isa: "XCRemoteSwiftPackageReference",
|
||||
repositoryURL: repositoryUrl,
|
||||
requirement: {
|
||||
kind: "upToNextMajorVersion",
|
||||
minimumVersion: version,
|
||||
},
|
||||
};
|
||||
|
||||
// update XCSwiftPackageProductDependency
|
||||
const spmProducts =
|
||||
xcodeProject.hash.project.objects.XCSwiftPackageProductDependency;
|
||||
|
||||
if (!spmProducts) {
|
||||
xcodeProject.hash.project.objects.XCSwiftPackageProductDependency = {};
|
||||
}
|
||||
|
||||
const packageUUID = xcodeProject.generateUuid();
|
||||
|
||||
xcodeProject.hash.project.objects.XCSwiftPackageProductDependency[
|
||||
`${packageUUID} /* ${productName} */`
|
||||
] = {
|
||||
isa: "XCSwiftPackageProductDependency",
|
||||
// from step before
|
||||
package: `${packageReferenceUUID} /* XCRemoteSwiftPackageReference "${repoName}" */`,
|
||||
productName: productName,
|
||||
};
|
||||
|
||||
// update PBXProject
|
||||
const projectId = Object.keys(
|
||||
xcodeProject.hash.project.objects.PBXProject,
|
||||
).at(0);
|
||||
|
||||
if (
|
||||
!xcodeProject.hash.project.objects.PBXProject[projectId].packageReferences
|
||||
) {
|
||||
xcodeProject.hash.project.objects.PBXProject[
|
||||
projectId
|
||||
].packageReferences = [];
|
||||
}
|
||||
|
||||
xcodeProject.hash.project.objects.PBXProject[projectId].packageReferences =
|
||||
[
|
||||
...xcodeProject.hash.project.objects.PBXProject[projectId]
|
||||
.packageReferences,
|
||||
`${packageReferenceUUID} /* XCRemoteSwiftPackageReference "${repoName}" */`,
|
||||
];
|
||||
|
||||
// update PBXBuildFile
|
||||
const frameworkUUID = xcodeProject.generateUuid();
|
||||
|
||||
xcodeProject.hash.project.objects.PBXBuildFile[`${frameworkUUID}_comment`] =
|
||||
`${productName} in Frameworks`;
|
||||
xcodeProject.hash.project.objects.PBXBuildFile[frameworkUUID] = {
|
||||
isa: "PBXBuildFile",
|
||||
productRef: packageUUID,
|
||||
productRef_comment: productName,
|
||||
};
|
||||
|
||||
// update PBXFrameworksBuildPhase
|
||||
const buildPhaseId = Object.keys(
|
||||
xcodeProject.hash.project.objects.PBXFrameworksBuildPhase,
|
||||
).at(0);
|
||||
|
||||
if (
|
||||
!xcodeProject.hash.project.objects.PBXFrameworksBuildPhase[buildPhaseId]
|
||||
.files
|
||||
) {
|
||||
xcodeProject.hash.project.objects.PBXFrameworksBuildPhase[
|
||||
buildPhaseId
|
||||
].files = [];
|
||||
}
|
||||
|
||||
xcodeProject.hash.project.objects.PBXFrameworksBuildPhase[
|
||||
buildPhaseId
|
||||
].files = [
|
||||
...xcodeProject.hash.project.objects.PBXFrameworksBuildPhase[buildPhaseId]
|
||||
.files,
|
||||
`${frameworkUUID} /* ${productName} in Frameworks */`,
|
||||
];
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
module.exports = addSPMDependenciesToMainTarget;
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "addSPMDependenciesToMainTarget",
|
||||
"version": "1.0.0",
|
||||
"main": "app.plugin.js"
|
||||
}
|
||||
24
plugins/withGitPod.js
Normal file
24
plugins/withGitPod.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const { withPodfile } = require("@expo/config-plugins");
|
||||
|
||||
const withGitPod = (config, { podName, podspecUrl }) => {
|
||||
return withPodfile(config, (config) => {
|
||||
const podfile = config.modResults.contents;
|
||||
|
||||
const podLine = ` pod '${podName}', :podspec => '${podspecUrl}'`;
|
||||
|
||||
// Check if already added
|
||||
if (podfile.includes(podLine)) {
|
||||
return config;
|
||||
}
|
||||
|
||||
// Insert after "use_expo_modules!"
|
||||
config.modResults.contents = podfile.replace(
|
||||
"use_expo_modules!",
|
||||
`use_expo_modules!\n${podLine}`,
|
||||
);
|
||||
|
||||
return config;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = withGitPod;
|
||||
Reference in New Issue
Block a user