|
|
|
@ -23,6 +23,7 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
|
|
|
|
@State var savetext = "save" |
|
|
|
@State var confirmationShown = false |
|
|
|
@State var dirtyShown = false |
|
|
|
@State var seekSmoothly = false |
|
|
|
@State var upsidedown = false |
|
|
|
@State var more = false |
|
|
|
@ -30,6 +31,7 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
@State var smoothTime = -1.0 |
|
|
|
@State var smoothSeekTime = -1.0 |
|
|
|
@State var timeCounter = 0 |
|
|
|
@State var timeSlomoCounter = 0 |
|
|
|
@State var lastScale = 1.25 |
|
|
|
@State var xoffs = 0.0 |
|
|
|
@State var rotazero = -1000.0 |
|
|
|
@ -60,21 +62,29 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
HStack { |
|
|
|
Group { |
|
|
|
Button(action: { |
|
|
|
model.baseItem.favorite = model.favorite |
|
|
|
player.pause() |
|
|
|
player.replaceCurrentItem(with: nil) |
|
|
|
model.currentURL = nil |
|
|
|
cleanup() |
|
|
|
completionHandler!(model.edit) |
|
|
|
if model.dirty { |
|
|
|
dirtyShown = true |
|
|
|
} else { |
|
|
|
let withSave = model.edit |
|
|
|
closePlayer(withSave: withSave) |
|
|
|
} |
|
|
|
}, label: { |
|
|
|
Text("cancel") |
|
|
|
}) |
|
|
|
.buttonStyle(BorderlessButtonStyle()) |
|
|
|
.buttonStyle(BorderlessButtonStyle()).confirmationDialog("Save?", isPresented: $dirtyShown) { |
|
|
|
Button("save") { |
|
|
|
closePlayer(withSave: true) |
|
|
|
} |
|
|
|
Button("cancel", role: .cancel) { |
|
|
|
closePlayer(withSave: false) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
KToggleButton(text: "more", binding: $more) |
|
|
|
|
|
|
|
Button(action: { |
|
|
|
model.favorite.toggle() |
|
|
|
model.dirty = true |
|
|
|
}, label: { |
|
|
|
Image(systemName: "heart.fill") |
|
|
|
}) |
|
|
|
@ -139,16 +149,6 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
.scaleEffect(model.scale) |
|
|
|
.rotation3DEffect(.degrees(upsidedown ? 180 : 0), axis: (x: 1, y: 0, z: 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 { |
|
|
|
@ -159,7 +159,6 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
model.dragOffset = CGSize.zero |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.gesture( |
|
|
|
DragGesture() |
|
|
|
.onChanged { gesture in |
|
|
|
@ -179,7 +178,6 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
let delta = val / self.lastScaleValue |
|
|
|
self.lastScaleValue = val |
|
|
|
self.model.scale = self.model.scale * delta |
|
|
|
//... anything else e.g. clamping the newScale |
|
|
|
} |
|
|
|
.onEnded { val in |
|
|
|
// without this the next gesture will be broken |
|
|
|
@ -212,17 +210,23 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
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] |
|
|
|
if model.speed == 1.0 && timeSlomoCounter == 0 { |
|
|
|
model.speed = 0.5 |
|
|
|
timeSlomoCounter = 200 |
|
|
|
} else { |
|
|
|
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")") |
|
|
|
}) |
|
|
|
@ -231,8 +235,7 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
Button(action: { |
|
|
|
if model.paused { |
|
|
|
player.play() |
|
|
|
} |
|
|
|
else { |
|
|
|
} else { |
|
|
|
player.pause() |
|
|
|
} |
|
|
|
model.paused = !model.paused |
|
|
|
@ -242,6 +245,35 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
.frame(height: 30) |
|
|
|
.foregroundColor(model.paused ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle()) |
|
|
|
|
|
|
|
Button(action: {}) { |
|
|
|
Text("start") |
|
|
|
} |
|
|
|
.frame(height: 30).buttonStyle(BorderlessButtonStyle()) |
|
|
|
.simultaneousGesture( |
|
|
|
LongPressGesture() |
|
|
|
.onEnded { _ in |
|
|
|
print("Loooong") |
|
|
|
} |
|
|
|
) |
|
|
|
.highPriorityGesture(TapGesture() |
|
|
|
.onEnded { _ in |
|
|
|
seekTimeSmoothly(model.currentSnapshot.time) |
|
|
|
}) |
|
|
|
|
|
|
|
Button(action: {}) { |
|
|
|
Text("end") |
|
|
|
} |
|
|
|
.frame(height: 30).buttonStyle(BorderlessButtonStyle()).foregroundColor(model.currentSnapshot.length > 0 ? Color.yellow : Color.blue) |
|
|
|
.simultaneousGesture( |
|
|
|
LongPressGesture() |
|
|
|
.onEnded { _ in |
|
|
|
setEnd() |
|
|
|
} |
|
|
|
) |
|
|
|
.highPriorityGesture(TapGesture() |
|
|
|
.onEnded { _ in |
|
|
|
seekTimeSmoothly(model.currentSnapshot.time + model.currentSnapshot.length) |
|
|
|
}) |
|
|
|
} |
|
|
|
.frame(width: 50, alignment: .top).offset(x: 0, y: 0), alignment: .topLeading) |
|
|
|
} else { |
|
|
|
@ -270,6 +302,9 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
if timeCounter >= 1 { |
|
|
|
timeCounter -= 1 |
|
|
|
} |
|
|
|
if timeSlomoCounter >= 1 { |
|
|
|
timeSlomoCounter -= 1 |
|
|
|
} |
|
|
|
if model.loop && model.currentSnapshot.length > 0 { |
|
|
|
if time.seconds > model.currentSnapshot.time + model.currentSnapshot.length { |
|
|
|
seekTime(model.currentSnapshot.time) |
|
|
|
@ -339,6 +374,15 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
private func closePlayer(withSave: Bool) { |
|
|
|
model.baseItem.favorite = model.favorite |
|
|
|
player.pause() |
|
|
|
player.replaceCurrentItem(with: nil) |
|
|
|
model.currentURL = nil |
|
|
|
cleanup() |
|
|
|
completionHandler!(withSave) |
|
|
|
} |
|
|
|
|
|
|
|
func doSnapshot() { |
|
|
|
let currentItem = player.currentItem! |
|
|
|
let asset = currentItem.asset |
|
|
|
@ -349,6 +393,7 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
let time = currentItem.currentTime() |
|
|
|
let cgImage = try imgGenerator.copyCGImage(at: time, actualTime: nil) |
|
|
|
let thumbnail = UIImage(cgImage: cgImage) |
|
|
|
model.dirty = true |
|
|
|
|
|
|
|
showThumbnail(currentItem: model.baseItem, thumbnail: thumbnail, time: time) |
|
|
|
|
|
|
|
@ -378,7 +423,7 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
} else { |
|
|
|
gotoSnapshot(model.allItems[0]) |
|
|
|
} |
|
|
|
timeCounter = 15 |
|
|
|
timeCounter = 50 |
|
|
|
print("jump") |
|
|
|
} |
|
|
|
} |
|
|
|
@ -392,13 +437,23 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
} |
|
|
|
|
|
|
|
if (start.y < 130) { |
|
|
|
let delta = dragged.width / 1000.0 |
|
|
|
let delta = dragged.width / 500.0 |
|
|
|
// print(delta) |
|
|
|
seekTimeSmoothly(smoothTime + delta) |
|
|
|
|
|
|
|
return false |
|
|
|
} else { |
|
|
|
return true |
|
|
|
if model.scale != 1.0 { |
|
|
|
return true |
|
|
|
} |
|
|
|
else { |
|
|
|
let dragWidth = 20.0 |
|
|
|
if dragged.width > dragWidth { |
|
|
|
seekTime(time + 4.0) |
|
|
|
} else if dragged.width < -dragWidth { |
|
|
|
seekTime(time - 5.0) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
return true |
|
|
|
@ -406,10 +461,6 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
} |
|
|
|
|
|
|
|
if let time = getCurrentTime() { |
|
|
|
// let sk = model.seeking |
|
|
|
// model.seeking = false |
|
|
|
|
|
|
|
// print("Start \(start.y) Drag \(dragged.width)))") |
|
|
|
let dragWidth = 20.0 |
|
|
|
if !model.seeking { |
|
|
|
if (start.y < 130) { |
|
|
|
@ -459,8 +510,10 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
// 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 |
|
|
|
player.play() |
|
|
|
player.rate = model.speed |
|
|
|
if !model.paused { |
|
|
|
player.play() |
|
|
|
player.rate = model.speed |
|
|
|
} |
|
|
|
self.model.seeking = false |
|
|
|
} |
|
|
|
|
|
|
|
@ -527,11 +580,13 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
func captureZoom() { |
|
|
|
model.currentSnapshot.scale = model.scale |
|
|
|
model.currentSnapshot.offset = CGPoint(x: model.dragOffset.width, y: model.dragOffset.height) |
|
|
|
model.dirty = true |
|
|
|
} |
|
|
|
|
|
|
|
func setStart() { |
|
|
|
if let time = getCurrentTime() { |
|
|
|
model.currentSnapshot.time = time |
|
|
|
model.dirty = true |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -541,6 +596,7 @@ struct SVideoPlayer: View, EditItemDelegate { |
|
|
|
if time > snapTime { |
|
|
|
model.currentSnapshot.length = time - snapTime |
|
|
|
} |
|
|
|
model.dirty = true |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|