From a707547ca9be35539973f2ab58af928400972ea7 Mon Sep 17 00:00:00 2001 From: marcoschmickler Date: Sun, 3 Apr 2022 09:06:45 +0200 Subject: [PATCH] more --- kplayer.xcodeproj/project.pbxproj | 4 + kplayer/video/KToggleButton.swift | 23 ++ kplayer/video/SVideoPlayer.swift | 354 +++++++++++++++++------------- 3 files changed, 225 insertions(+), 156 deletions(-) create mode 100644 kplayer/video/KToggleButton.swift diff --git a/kplayer.xcodeproj/project.pbxproj b/kplayer.xcodeproj/project.pbxproj index d644e40..466fd30 100644 --- a/kplayer.xcodeproj/project.pbxproj +++ b/kplayer.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 1C73646F87B495A47D7943C7 /* NetData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7369EC16B19B32B515169E /* NetData.swift */; }; 1C736503B656C999E5E12081 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */; }; 1C73654C9EA6D255CFC039C5 /* NetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73620D01687FB4F1811C5C /* NetworkHelper.swift */; }; + 1C736559059B6FBA1C661191 /* KToggleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73661C3F9F4E53645551AD /* KToggleButton.swift */; }; 1C7365885FAF292F2221ED44 /* PhotoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73673DC671535E3A049F54 /* PhotoController.swift */; }; 1C7365C59F72C29EA41C8717 /* SVideoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736EEC570C71AAC50F2E95 /* SVideoModel.swift */; }; 1C7365CE76693E7772585CA8 /* SVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73621E431C9BEC1440B936 /* SVideoPlayer.swift */; }; @@ -113,6 +114,7 @@ 1C736595533B56039C417E0D /* ServerDownloadDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerDownloadDelegate.swift; sourceTree = ""; }; 1C73659CC9B523B957E58DC6 /* LocalManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalManager.swift; sourceTree = ""; }; 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; + 1C73661C3F9F4E53645551AD /* KToggleButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KToggleButton.swift; sourceTree = ""; }; 1C736677D4EF2437358B2387 /* Utility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = ""; }; 1C7366C09381DC0052B52B69 /* EditItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditItemView.swift; sourceTree = ""; }; 1C73673DC671535E3A049F54 /* PhotoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoController.swift; sourceTree = ""; }; @@ -281,6 +283,7 @@ 1C736EEC570C71AAC50F2E95 /* SVideoModel.swift */, 1C73621E431C9BEC1440B936 /* SVideoPlayer.swift */, 1C73675F8DDFA82DEADB542E /* VideoPlayerView.swift */, + 1C73661C3F9F4E53645551AD /* KToggleButton.swift */, ); path = video; sourceTree = ""; @@ -592,6 +595,7 @@ 1C7363C2E744C2318879127D /* FlexibleView.swift in Sources */, 1C736C8DAD6C2FBB9A2EA625 /* SearchItemView.swift in Sources */, 1C736690D123BD4B24874394 /* pathfinder.scpt in Sources */, + 1C736559059B6FBA1C661191 /* KToggleButton.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/kplayer/video/KToggleButton.swift b/kplayer/video/KToggleButton.swift new file mode 100644 index 0000000..bc2e4d2 --- /dev/null +++ b/kplayer/video/KToggleButton.swift @@ -0,0 +1,23 @@ +// +// Created by Marco Schmickler on 03.04.22. +// Copyright (c) 2022 Marco Schmickler. All rights reserved. +// + +import Foundation + +import SwiftUI + +struct KToggleButton : View { + let text : String + @Binding var binding: Bool + + var body: some View { + Button(action: { + binding.toggle() + }, label: { + Text(text) + }) + .foregroundColor(binding ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle()) + } + +} \ No newline at end of file diff --git a/kplayer/video/SVideoPlayer.swift b/kplayer/video/SVideoPlayer.swift index b021b9c..5eaf734 100644 --- a/kplayer/video/SVideoPlayer.swift +++ b/kplayer/video/SVideoPlayer.swift @@ -11,7 +11,7 @@ import CoreMotion struct SVideoPlayer: View, EditItemDelegate { // url: URL(string: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8")! var player = AVQueuePlayer(items: [AVPlayerItem]()) - var playerLooper : AVPlayerLooper + var playerLooper: AVPlayerLooper var completionHandler: ((Bool) -> Void)? let motionManager = CMMotionManager() @@ -25,10 +25,12 @@ struct SVideoPlayer: View, EditItemDelegate { @State var confirmationShown = false @State var seekSmoothly = false @State var upsidedown = false + @State var more = false @State var tilt = false @State var smoothTime = -1.0 @State var smoothSeekTime = -1.0 @State var timeCounter = 0 + @State var lastScale = 1.25 @State var xoffs = 0.0 @State var rotazero = -1000.0 @@ -38,12 +40,12 @@ struct SVideoPlayer: View, EditItemDelegate { .makeConnectable() .autoconnect() - let steps : [Float] = [0.25, 0.5, 1.0, 2.0 ] + let steps: [Float] = [0.25, 0.5, 1.0, 2.0] init(completionHandler: ((Bool) -> ())?, model: SVideoModel) { self.completionHandler = completionHandler self.model = model - self.playerLooper = AVPlayerLooper(player: player , templateItem: model.currentPlayerItem()) + self.playerLooper = AVPlayerLooper(player: player, templateItem: model.currentPlayerItem()) } func cleanup() { @@ -68,103 +70,67 @@ struct SVideoPlayer: View, EditItemDelegate { Text("cancel") }) .buttonStyle(BorderlessButtonStyle()) - Button(action: { - model.loop.toggle() - }, label: { - Text("loop") - }) - .foregroundColor(model.loop ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle()) - Button(action: { - model.zoomed.toggle() - }, label: { - Text("zoom") - }) - .foregroundColor(model.zoomed ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle()) - Button(action: { - upsidedown.toggle() - }, label: { - Text("flip") - }).foregroundColor(upsidedown ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle()) - Button(action: { - tilt.toggle() - if tilt { - motionManager.startDeviceMotionUpdates(using: .xMagneticNorthZVertical) - } - else { - motionManager.stopDeviceMotionUpdates() - } - }, label: { - Text("tilt") - }).foregroundColor(tilt ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle()) + + KToggleButton(text: "more", binding: $more) + Button(action: { model.favorite.toggle() }, label: { Image(systemName: "heart.fill") }) .foregroundColor(model.favorite ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle()) - } - Button(action: { - for s in steps { - if model.speed < s { - model.speed = s - break - } - else { - if s == 2.0 { - model.speed = steps[0] - } - } - } - player.rate = model.speed - }, label: { - Text("\(model.speed, specifier: "%.2f")") - }).buttonStyle(BorderlessButtonStyle()) - - Button(action: { confirmationShown = true }, label: { - Text(savetext) - }).buttonStyle(BorderlessButtonStyle()) - .confirmationDialog("Save to folder", isPresented: $confirmationShown) { - Button("1") { - save(currentSnapshot: model.currentSnapshot, name: "1") - } - Button("2") { - save(currentSnapshot: model.currentSnapshot, name: "2") - } - Button("3") { - save(currentSnapshot: model.currentSnapshot, name: "3") - } - Button("cancel", role: .cancel) {} - } + Button(action: { confirmationShown = true }, label: { + Text(savetext) + }) + .buttonStyle(BorderlessButtonStyle()) + .confirmationDialog("Save to folder", isPresented: $confirmationShown) { + Button("1") { + save(currentSnapshot: model.currentSnapshot, name: "1") + } + Button("2") { + save(currentSnapshot: model.currentSnapshot, name: "2") + } + Button("3") { + save(currentSnapshot: model.currentSnapshot, name: "3") + } + + Button("cancel", role: .cancel) { + } + } - Text(model.currentSnapshot.name).foregroundColor(Color.blue) + Text(model.currentSnapshot.name).foregroundColor(Color.blue) - ScrollView(.horizontal, showsIndicators: false) { - HStack { - ForEach(model.allItems) { item in - Button(action: { - gotoSnapshot(item) - }) { - AsyncImage(item: item, placeholder: { Text("Loading ...") }, - image: { Image(uiImage: $0).resizable() }) + ScrollView(.horizontal, showsIndicators: false) { + HStack { + ForEach(model.allItems) { item in + Button(action: { + gotoSnapshot(item) + }) { + AsyncImage(item: item, placeholder: { Text("Loading ...") }, + image: { Image(uiImage: $0).resizable() }) + } } } } - } - Spacer() + Spacer() - Button(action: { model.edit.toggle() }, label: { - Text("edit") - }).buttonStyle(BorderlessButtonStyle()); - if !model.baseItem.compilation { - Button(action: doSnapshot, label: { - Text("snap") - }).buttonStyle(BorderlessButtonStyle()); + Button(action: { model.edit.toggle() }, label: { + Text("edit") + }) + .buttonStyle(BorderlessButtonStyle()); + if !model.baseItem.compilation { + Button(action: doSnapshot, label: { + Text("snap") + }) + .buttonStyle(BorderlessButtonStyle()); + } } - }.frame(height: 50) + .frame(height: 50) + } GeometryReader { geometry in VStack { @@ -172,7 +138,28 @@ struct SVideoPlayer: View, EditItemDelegate { player: player) .scaleEffect(model.scale) .rotation3DEffect(.degrees(upsidedown ? 180 : 0), axis: (x: 1, y: 0, z: 0)) - .offset(model.dragOffset).offset(x: xoffs, y:0) + .offset(model.dragOffset).offset(x: xoffs, y: 0) + // .onTapGesture(count: 2) { + // print("Double tapped!") + // if model.paused { + // player.play() + // } + // else { + // player.pause() + // } + // model.paused = !model.paused + // } + .onTapGesture(count: 2) { + print("3 tapped!") + if model.scale == 1 { + model.scale = lastScale + } else { + lastScale = model.scale + model.scale = 1 + model.dragOffset = CGSize.zero + } + } + .gesture( DragGesture() .onChanged { gesture in @@ -204,14 +191,75 @@ struct SVideoPlayer: View, EditItemDelegate { v.overlay(EditItemView(item: model.currentSnapshot, len: model.videoDuration, delegate: self) .frame(width: 400, alignment: .top).offset(x: 0, y: -50), alignment: .topTrailing) } else { - v + if more { + v.overlay(VStack { + + Button(action: { + tilt.toggle() + if tilt { + motionManager.startDeviceMotionUpdates(using: .xMagneticNorthZVertical) + } else { + motionManager.stopDeviceMotionUpdates() + } + }, label: { + Text("tilt") + }) + .frame(height: 30) + .foregroundColor(tilt ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle()) + + KToggleButton(text: "zoom", binding: $model.zoomed).frame(height: 30) + KToggleButton(text: "loop", binding: $model.loop).frame(height: 30) + KToggleButton(text: "flip", binding: $upsidedown).frame(height: 30) + + Button(action: { + for s in steps { + if model.speed < s { + model.speed = s + break + } else { + if s == 2.0 { + model.speed = steps[0] + } + } + } + player.rate = model.speed + }, label: { + Text("\(model.speed, specifier: "%.2f")") + }) + .frame(height: 30) + .buttonStyle(BorderlessButtonStyle()) + Button(action: { + if model.paused { + player.play() + } + else { + player.pause() + } + model.paused = !model.paused + }, label: { + Text("pause") + }) + .frame(height: 30) + .foregroundColor(model.paused ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle()) + + } + .frame(width: 50, alignment: .top).offset(x: 0, y: 0), alignment: .topLeading) + } else { + v + } } - VideoPlayerControlsView(model: model, + let controlsView = VideoPlayerControlsView(model: model, player: player) - }.onAppear { + if model.scale <= 1.0 { + controlsView + } else { + controlsView.hidden() + } + } + .onAppear { model.proxy = geometry - print ("geo \(geometry.size.height)"); + print("geo \(geometry.size.height)"); } .clipped() } @@ -228,32 +276,32 @@ struct SVideoPlayer: View, EditItemDelegate { } } if tilt { - if let data = motionManager.deviceMotion { - var rotation = atan2(data.gravity.x, - data.gravity.y) - .pi - - if rotation < (-1 * .pi) { - rotation += .pi + .pi + if let data = motionManager.deviceMotion { + var rotation = atan2(data.gravity.x, + data.gravity.y) - .pi + + if rotation < (-1 * .pi) { + rotation += .pi + .pi + } + rotation *= 100.0 + if rotazero == -1000 { + rotazero = rotation + } + + rotation -= rotazero + + // let roll = accelerometerData.attitude.yaw + if rotation > 30 { + xoffs += 3; + // print(xoffs) + } + if rotation < -30 { + xoffs -= 3; + // print(xoffs) + } + // print(Int(rotation * 100.0) ) } - rotation *= 100.0 - if rotazero == -1000 { - rotazero = rotation - } - - rotation -= rotazero - - // let roll = accelerometerData.attitude.yaw - if rotation > 30 { - xoffs += 3; - // print(xoffs) - } - if rotation < -30 { - xoffs -= 3; - // print(xoffs) - } - // print(Int(rotation * 100.0) ) - }} - else { + } else { xoffs = 0.0 rotazero = -1000 } @@ -288,6 +336,7 @@ struct SVideoPlayer: View, EditItemDelegate { .onReceive(orientationChanged) { _ in self.orientation = UIDevice.current.orientation } + } func doSnapshot() { @@ -325,9 +374,8 @@ struct SVideoPlayer: View, EditItemDelegate { if timeCounter == 0 { if let i = model.allItems.index(where: { m in m === model.currentSnapshot }) { if i + 1 < model.allItems.count { - gotoSnapshot(model.allItems[i+1]) - } - else { + gotoSnapshot(model.allItems[i + 1]) + } else { gotoSnapshot(model.allItems[0]) } timeCounter = 15 @@ -345,24 +393,23 @@ struct SVideoPlayer: View, EditItemDelegate { if (start.y < 130) { let delta = dragged.width / 1000.0 - // print(delta) + // print(delta) seekTimeSmoothly(smoothTime + delta) return false } else { return true } - } - else { + } else { return true } } if let time = getCurrentTime() { - // let sk = model.seeking - // model.seeking = false + // let sk = model.seeking + // model.seeking = false - // print("Start \(start.y) Drag \(dragged.width)))") + // print("Start \(start.y) Drag \(dragged.width)))") let dragWidth = 20.0 if !model.seeking { if (start.y < 130) { @@ -370,9 +417,9 @@ struct SVideoPlayer: View, EditItemDelegate { return true } if dragged.width > dragWidth { - seekTime(time + 8.0) + seekTime(time + 5.0) } else if dragged.width < -dragWidth { - seekTime(time - 10.0) + seekTime(time - 8.0) } } else { if dragged.width > dragWidth { @@ -383,7 +430,7 @@ struct SVideoPlayer: View, EditItemDelegate { } } - // model.seeking = sk + // model.seeking = sk } @@ -402,13 +449,13 @@ struct SVideoPlayer: View, EditItemDelegate { } self.model.seeking = true player.pause() - // player.cancelPendingPrerolls() + // player.cancelPendingPrerolls() if let item = player.currentItem { item.cancelPendingSeeks() } - player.seek(to: CMTime(seconds: time, preferredTimescale: CMTimeScale(600))){ _ in + player.seek(to: CMTime(seconds: time, preferredTimescale: CMTimeScale(600))) { _ in // player.seek(to: CMTime(seconds: time, preferredTimescale: CMTimeScale(10000)), // toleranceBefore: CMTime(seconds: 0.3, preferredTimescale: CMTimeScale(10000)), // toleranceAfter: CMTime(seconds: 1.0, preferredTimescale: CMTimeScale(10000))){ _ in @@ -419,21 +466,21 @@ struct SVideoPlayer: View, EditItemDelegate { } - func seekTimeSmoothly(_ time: Double) { - print("smooth \(time)") - if seekSmoothly { - smoothSeekTime = time - print("delay \(smoothSeekTime)") - return - } - if smoothSeekTime == time { - print("finished \(smoothSeekTime)") - smoothSeekTime = -1.0 - return - } - seekSmoothly = true - - print("seek \(time)") + func seekTimeSmoothly(_ time: Double) { + print("smooth \(time)") + if seekSmoothly { + smoothSeekTime = time + print("delay \(smoothSeekTime)") + return + } + if smoothSeekTime == time { + print("finished \(smoothSeekTime)") + smoothSeekTime = -1.0 + return + } + seekSmoothly = true + + print("seek \(time)") player.seek(to: CMTime(seconds: time, preferredTimescale: CMTimeScale(10000)), toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero) { _ in seekSmoothly = false @@ -453,8 +500,7 @@ struct SVideoPlayer: View, EditItemDelegate { player.replaceCurrentItem(with: model.currentPlayerItem()) playerLooper seekTime(currentSnapshot.time) - } - else { + } else { seekTime(currentSnapshot.time) } let height = player.currentItem!.presentationSize.height @@ -466,9 +512,8 @@ struct SVideoPlayer: View, EditItemDelegate { model.scale = CGFloat(currentSnapshot.scale) model.dragOffset.width = currentSnapshot.offset.x model.dragOffset.height = currentSnapshot.offset.y - // player.transformLayer() - } - else { + // player.transformLayer() + } else { if model.zoomed && f > 0.0 { if f < 0.6 { f = f * CGFloat(LocalManager.sharedInstance.settings.scale) @@ -516,7 +561,7 @@ struct SVideoPlayer: View, EditItemDelegate { if file.pathExtension != "mp4" { file = file.appendingPathExtension("mp4") } - print (file) + print(file) var dur = c.length if (dur < 0) { return @@ -526,11 +571,10 @@ struct SVideoPlayer: View, EditItemDelegate { progress: { p in let percent = Int(p * 100) savetext = "\(percent)" - }) - { url in + }) { url in savetext = "saved" } - var s : MediaModel; + var s: MediaModel; if (c.type == ItemType.SNAPSHOT && c.parent != nil) { s = c.parent!.toMediaModel() do { @@ -543,12 +587,10 @@ struct SVideoPlayer: View, EditItemDelegate { .appendingPathExtension("jpg") try local.write(to: tfile) } - } - catch { + } catch { // ignore } - } - else { + } else { s = c.toMediaModel() } @@ -571,7 +613,7 @@ struct SVideoPlayer: View, EditItemDelegate { struct SVideoPlayer_Previews: PreviewProvider { static var previews: some View { - SVideoPlayer(completionHandler: {b in}, model: SVideoModel(allItems: [MediaItem](), + SVideoPlayer(completionHandler: { b in }, model: SVideoModel(allItems: [MediaItem](), currentSnapshot: MediaItem(name: "extern", path: "", root: "", type: ItemType.FAVROOT), baseItem: MediaItem(name: "extern", path: "", root: "", type: ItemType.FAVROOT) ))