diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index 1162e948..a7f9eed1 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -152,6 +152,8 @@ 6220D0C926D63F3700B8E046 /* Stinsen in Frameworks */ = {isa = PBXBuildFile; productRef = 6220D0C826D63F3700B8E046 /* Stinsen */; }; 6220D0CC26D640C400B8E046 /* AppURLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0CB26D640C400B8E046 /* AppURLHandler.swift */; }; 6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */; }; + 62400C4B287ED19600F6AD3D /* UDPBroadcast.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; }; + 62400C4C287ED19600F6AD3D /* UDPBroadcast.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 624C21752685CF60007F1390 /* SearchablePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 624C21742685CF60007F1390 /* SearchablePickerView.swift */; }; 62553429282190A00087FE20 /* PanDirectionGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62553428282190A00087FE20 /* PanDirectionGestureRecognizer.swift */; }; 625CB56F2678C23300530A6E /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB56E2678C23300530A6E /* HomeView.swift */; }; @@ -248,7 +250,6 @@ 62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; }; 62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */; }; 631759CF2879DB6A00A621AD /* PublicUserSignInCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631759CE2879DB6A00A621AD /* PublicUserSignInCellView.swift */; }; - 637FCAF4287B5B2600C0A353 /* UDPBroadcast.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; }; 637FCAF5287B5B2600C0A353 /* UDPBroadcast.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; }; AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; }; C400DB6A27FE894F007B65FE /* LiveTVChannelsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C400DB6927FE894F007B65FE /* LiveTVChannelsView.swift */; }; @@ -549,6 +550,7 @@ dstSubfolderSpec = 10; files = ( 62666E3D27E503F200EC0ECD /* GoogleCastSDK.xcframework in Embed Frameworks */, + 62400C4C287ED19600F6AD3D /* UDPBroadcast.xcframework in Embed Frameworks */, 62666DF827E5012C00EC0ECD /* MobileVLCKit.xcframework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -970,7 +972,7 @@ 62666E0C27E501A500EC0ECD /* OpenGLES.framework in Frameworks */, C409CE9E285044C800CABC12 /* SwiftUICollection in Frameworks */, 62666E0127E5016900EC0ECD /* CoreFoundation.framework in Frameworks */, - 637FCAF4287B5B2600C0A353 /* UDPBroadcast.xcframework in Frameworks */, + 62400C4B287ED19600F6AD3D /* UDPBroadcast.xcframework in Frameworks */, E1B6DCEA271A23880015B715 /* SwiftyJSON in Frameworks */, 62666E2427E501F300EC0ECD /* Foundation.framework in Frameworks */, 53352571265EA0A0006CCA86 /* Introspect in Frameworks */, diff --git a/Swiftfin/Views/VideoPlayer/Overlays/VLCPlayerOverlayView.swift b/Swiftfin/Views/VideoPlayer/Overlays/VLCPlayerOverlayView.swift index 2cb6ba19..5e9af22d 100644 --- a/Swiftfin/Views/VideoPlayer/Overlays/VLCPlayerOverlayView.swift +++ b/Swiftfin/Views/VideoPlayer/Overlays/VLCPlayerOverlayView.swift @@ -339,59 +339,96 @@ struct VLCPlayerOverlayView: View { .frame(height: 70) } - HStack { - if viewModel.overlayType == .compact { - HStack { - Button { - viewModel.playerOverlayDelegate?.didSelectBackward() - } label: { - Image(systemName: viewModel.jumpBackwardLength.backwardImageLabel) - .padding(.horizontal, 5) - } - - Button { - viewModel.playerOverlayDelegate?.didSelectMain() - } label: { - mainButtonView - .frame(minWidth: 30, maxWidth: 30) - .padding(.horizontal, 10) - } - - Button { - viewModel.playerOverlayDelegate?.didSelectForward() - } label: { - Image(systemName: viewModel.jumpForwardLength.forwardImageLabel) - .padding(.horizontal, 5) + VStack(alignment: .leading, spacing: 0) { + if viewModel.overlayType == .normal, + let currentChapter = viewModel.currentChapter + { + Button { + viewModel.playerOverlayDelegate?.didSelectChapters() + } label: { + HStack { + Text(currentChapter.name ?? "--") + Image(systemName: "chevron.right") } + .font(.system(size: 16, weight: .semibold, design: .default)) } - .font(.system(size: 24, weight: .semibold, design: .default)) + .padding(.leading, 16) } - Text(viewModel.leftLabelText) - .font(.system(size: 18, weight: .semibold, design: .default)) - .frame(minWidth: 70, maxWidth: 70) - .accessibilityLabel(L10n.currentPosition) - .accessibilityValue(viewModel.leftLabelText) + HStack { + if viewModel.overlayType == .compact { + HStack { + Button { + viewModel.playerOverlayDelegate?.didSelectBackward() + } label: { + Image(systemName: viewModel.jumpBackwardLength.backwardImageLabel) + .padding(.horizontal, 5) + } - ValueSlider(value: $viewModel.sliderPercentage, onEditingChanged: { editing in - viewModel.sliderIsScrubbing = editing - }) - .valueSliderStyle(HorizontalValueSliderStyle(track: - HorizontalValueTrack(view: - Capsule().foregroundColor(.purple)) - .background(Capsule().foregroundColor(Color.gray.opacity(0.25))) + Button { + viewModel.playerOverlayDelegate?.didSelectMain() + } label: { + mainButtonView + .frame(minWidth: 30, maxWidth: 30) + .padding(.horizontal, 10) + } + + Button { + viewModel.playerOverlayDelegate?.didSelectForward() + } label: { + Image(systemName: viewModel.jumpForwardLength.forwardImageLabel) + .padding(.horizontal, 5) + } + } + .font(.system(size: 24, weight: .semibold, design: .default)) + } + + Text(viewModel.leftLabelText) + .font(.system(size: 18, weight: .semibold, design: .default)) + .frame(minWidth: 70, maxWidth: 70) + .accessibilityLabel(L10n.currentPosition) + .accessibilityValue(viewModel.leftLabelText) + + ValueSlider(value: $viewModel.sliderPercentage, onEditingChanged: { editing in + viewModel.sliderIsScrubbing = editing + }) + .valueSliderStyle(HorizontalValueSliderStyle(track: + GeometryReader { proxy in + ZStack(alignment: .leading) { + HorizontalValueTrack(view: + Capsule().foregroundColor(.purple)) + .background(Capsule().foregroundColor(Color.gray.opacity(0.75))) + + if viewModel.overlayType == .normal { + // Chapters seek masks + ForEach(viewModel.chapters, id: \.startPositionTicks) { chapter in + let ticksRatio = CGFloat(chapter.startPositionTicks ?? 0) / + CGFloat(viewModel.item.runTimeTicks ?? 0) + let x = proxy.size.width * ticksRatio + if x != 0 { + Rectangle() + .blendMode(.destinationOut) + .offset(x: x - 1.5) + .frame(width: 3) + } + } + } + } + .compositingGroup() + } .frame(height: 4), - thumb: Circle().foregroundColor(.purple), - thumbSize: CGSize.Circle(radius: viewModel.sliderIsScrubbing ? 20 : 15), - thumbInteractiveSize: CGSize.Circle(radius: 40), - options: .defaultOptions)) - .frame(maxHeight: 50) + thumb: Circle().foregroundColor(.purple), + thumbSize: CGSize.Circle(radius: viewModel.sliderIsScrubbing ? 20 : 15), + thumbInteractiveSize: CGSize.Circle(radius: 40), + options: .defaultOptions)) + .frame(maxHeight: 50) - Text(viewModel.rightLabelText) - .font(.system(size: 18, weight: .semibold, design: .default)) - .frame(minWidth: 70, maxWidth: 70) - .accessibilityLabel(L10n.remainingTime) - .accessibilityValue(viewModel.rightLabelText) + Text(viewModel.rightLabelText) + .font(.system(size: 18, weight: .semibold, design: .default)) + .frame(minWidth: 70, maxWidth: 70) + .accessibilityLabel(L10n.remainingTime) + .accessibilityValue(viewModel.rightLabelText) + } } .padding(.horizontal, UIDevice.current.userInterfaceIdiom == .pad ? 30 : 0) .padding(.bottom, UIDevice.current.userInterfaceIdiom == .pad ? 10 : 0)