mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-11 09:27:03 +01:00
feat(core,app,android,cli,ios): sync the library and settings, verify a share before adding it
Sync carried only watch positions, so a second device started empty. The per-installation history file now also carries per-key registers (pstr-core::profile): every share with its link and custom password, the playback, appearance and metadata settings, the TMDB key, hand-picked matches and per-title track choices. Newer updated_at wins per key, a removal travels as an empty register, and a first sync stamps at 0 so a fresh install takes the library the other devices have instead of overwriting it with defaults. Volume, mute and the device-only settings (cache budget, Wi-Fi only, background audio, hardware decoding) stay out. The secrets live only in the end-to-end encrypted Drive file, never in shares.json. FORMAT_VERSION stays 1; an older build ignores the registers. Adding a share stored it before opening the link, so a first failure left a broken entry and every retry said "already configured". add_verified opens the link first and stores nothing on failure, and only the new share is crawled afterwards, so another broken share cannot fail the add. Android drops picture-in-picture: the window sized the video wrongly and background audio already covers leaving the app. iOS sizes the Metal drawable on every layout, so the picture no longer stretches after a rotation or a Split View resize. Settings show a save at once rather than after the match run, and a failed save reverts and says why. Both phone clients gain an activity banner naming what is running, and the add-share, metadata and change-match forms show progress and errors in place.
This commit is contained in:
1 parent
dc70cafbb7
commit
8e42906a7f
37 files changed
+2018
-594
No files matched your search
@@ -109,6 +109,9 @@ final class MpvPlayer {
|
||||
private init() {
|
||||
layer.framebufferOnly = true
|
||||
layer.backgroundColor = UIColor.black.cgColor
|
||||
// For the frames between a resize and mpv's next one: a drawable of
|
||||
// the old shape is letterboxed rather than stretched.
|
||||
layer.contentsGravity = .resizeAspect
|
||||
guard let mpv = mpv_create() else { return }
|
||||
// mpv takes the layer's address as an int64, not a Swift reference.
|
||||
var wid = Int64(Int(bitPattern: Unmanaged.passUnretained(layer).toOpaque()))
|
||||
|
||||
@@ -567,9 +567,9 @@ struct MpvView: UIViewRepresentable {
|
||||
func updateUIView(_: LayerView, context _: Context) {}
|
||||
|
||||
final class LayerView: UIView {
|
||||
private let video: CALayer
|
||||
private let video: CAMetalLayer
|
||||
|
||||
init(layer video: CALayer) {
|
||||
init(layer video: CAMetalLayer) {
|
||||
self.video = video
|
||||
super.init(frame: .zero)
|
||||
backgroundColor = .black
|
||||
@@ -580,14 +580,30 @@ struct MpvView: UIViewRepresentable {
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { nil }
|
||||
|
||||
/// The drawable is sized here, not left to the layer. MoltenVK sets
|
||||
/// `drawableSize` once when it builds the swapchain and Core Animation
|
||||
/// stops tracking the bounds from then on, while mpv decides its
|
||||
/// output size from `drawableSize` alone. Left alone, a rotation, a
|
||||
/// Split View resize or the player reopening at another size kept the
|
||||
/// old drawable, which Core Animation stretched over the new bounds —
|
||||
/// a squashed or cropped picture until something else resized it.
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
let scale = window?.screen.nativeScale ?? UIScreen.main.nativeScale
|
||||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
video.frame = bounds
|
||||
video.contentsScale = window?.screen.scale ?? UIScreen.main.scale
|
||||
video.contentsScale = scale
|
||||
video.drawableSize = CGSize(width: bounds.width * scale, height: bounds.height * scale)
|
||||
CATransaction.commit()
|
||||
}
|
||||
|
||||
override func didMoveToWindow() {
|
||||
super.didMoveToWindow()
|
||||
// Reattached after the player was minimised: the layer is the one
|
||||
// from before, still sized for wherever it last was.
|
||||
setNeedsLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user