Browse Source

MasterDetail

master
marcoschmickler 2 years ago
parent
commit
408f446229
  1. 19
      kplayer/core/DatabaseManager.swift
  2. 6
      kplayer/core/KSettings.swift
  3. 1
      kplayer/core/KSettingsModel.swift
  4. 9
      kplayer/core/MediaItem.swift
  5. 34
      kplayer/detail/DetailView.swift
  6. 8
      kplayer/detail/EditItemView.swift
  7. 8
      kplayer/detail/ItemView.swift
  8. 31
      kplayer/master/KSettingsView.swift
  9. 97
      kplayer/master/MasterModel.swift
  10. 30
      kplayer/master/MasterSplitView.swift
  11. 7
      kplayer/master/MasterViewController.swift
  12. 9
      kplayer/master/NetworkDelegate.swift
  13. 1
      kplayer/photo/SPhotoView.swift
  14. 2
      kplayer/util/AsyncImage.swift
  15. 12
      kplayer/util/UIImageExtension.swift
  16. 1
      kplayer/video/SVideoModel.swift
  17. 187
      kplayer/video/SVideoPlayer.swift

19
kplayer/core/DatabaseManager.swift

@ -24,6 +24,24 @@ class DatabaseManager {
loadAllTags()
}
func renameCurrentTag(newName: String) {
let kFetch = KTag.fetchRequest()
kFetch.predicate = NSPredicate(format: "name == %@ and path == %@", currentTag, "")
let tags = try! managedObjectContext.fetch(kFetch)
if tags.isEmpty {
return
}
let tag = tags[0]
tag.name = newName
save()
print(newName)
}
func enrichItem(_ item: MediaItem) {
let fetchRequest = KItem.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "name == %@ AND path == %@ AND root == %@", item.name, item.path, item.root)
@ -329,6 +347,7 @@ rollback()
var res = [MediaItem]()
let fetchRequest = KTag.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
let results = try! managedObjectContext.fetch(fetchRequest)

6
kplayer/core/KSettings.swift

@ -24,6 +24,9 @@ class KSettings: ObservableObject {
@Published
var edit = false
@Published
var confirm = true
@Published
var automaticallyWaitsToMinimizeStalling = true
@ -37,10 +40,11 @@ class KSettings: ObservableObject {
zoomed = model.zoomed
jump = model.jump
slow = model.slow
confirm = model.confirm
}
func toModel() -> KSettingsModel {
KSettingsModel(scale: scale, autoloop: autoloop, zoomed: zoomed, jump: jump, slow: slow )
KSettingsModel(scale: scale, autoloop: autoloop, zoomed: zoomed, jump: jump, slow: slow, confirm: confirm)
}
func toJSON() -> String {

1
kplayer/core/KSettingsModel.swift

@ -11,4 +11,5 @@ struct KSettingsModel: Codable {
var zoomed = false
var jump = false
var slow = false
var confirm = false
}

9
kplayer/core/MediaItem.swift

@ -144,6 +144,15 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject, Identifiable, H
}
}
func tagList() -> String {
var list = ""
for t in tags {
list.append(t)
list.append(" ")
}
return list
}
func toMediaModel() -> MediaModel {
var c = [MediaModel]()

34
kplayer/detail/DetailView.swift

@ -6,6 +6,20 @@
import Foundation
import SwiftUI
struct SectionView : View {
let category : MediaItem
var body: some View {
HStack {
if (category.isPic()) {
Image(systemName: "photo")
}
Text(category.name)
Spacer()
}
}
}
struct DetailView : View {
@ObservedObject var model: MasterModel
@State var hiddenTrigger = false
@ -21,10 +35,24 @@ struct DetailView : View {
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
ForEach(model.detailItems, id: \.self) { category in
LazyVGrid(columns: columns, spacing: 10, pinnedViews: [.sectionHeaders]) {
Section(header: Text(category.name)) {
LazyVGrid(columns: columns, spacing: 10) {
Section(header: SectionView(category: category)) {
ForEach(category.children.count == 0 ? [category] : category.children, id: \.self) { item in
ItemView(category: category, item: item, model: model)
ItemView(category: category, item: item, model: model).dropDestination(for: String.self, action: {x, _ in
var doit = false
for t in x {
print(t)
if !item.tags.contains(t) {
item.tags.append(t)
doit = true
}
}
if doit {
model.delegate.saveItem(selectedItem: item)
}
return doit
})
}
}
}

8
kplayer/detail/EditItemView.swift

@ -19,15 +19,17 @@ struct TagEditor: View {
@ObservedObject
var item: MediaItem
var completionHandler: ((MediaItem) -> Void)?
var data = DatabaseManager.sharedInstance.allTags[""]!
init(item: MediaItem, completionHandler: ((MediaItem) -> Void)? = nil) {
self.item = item
self.completionHandler = completionHandler
data.sort()
}
var body: some View {
FlexibleView(
data: DatabaseManager.sharedInstance.allTags[""]!,
data: data,
spacing: 15,
alignment: .leading
) { tag in
@ -49,7 +51,7 @@ struct TagEditor: View {
RoundedRectangle(cornerRadius: 8)
.fill( item.tags.contains(tag) ?
Color.yellow.opacity(0.4) :
Color.gray.opacity(0.2)
Color.gray.opacity(0.4)
)
)
}).buttonStyle(BorderlessButtonStyle())
@ -144,7 +146,7 @@ struct EditItemView: View {
.onDisappear {
UITableView.appearance().backgroundColor = .systemBackground
}
.frame(height: 350, alignment: .top)
.frame(height: 800, alignment: .top)
//Spacer()
}

8
kplayer/detail/ItemView.swift

@ -16,7 +16,13 @@ struct ItemView : View {
print("click")
model.showDetails(sectionItem: category, selectedItem: item, categoryItem: category)
}, label: {
AsyncImage(item: item, small: false, placeholder: { Text("Loading ...") })
VStack {
AsyncImage(item: item, small: false, placeholder: { Text("Loading ...").frame(width: 15*16, height: 15*9).background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.gray.opacity(0.2))
) })
Text(item.tagList()).font(.system(size: 10))
}
})
}
}

31
kplayer/master/KSettingsView.swift

@ -10,12 +10,16 @@ struct KSettingsView: View {
@ObservedObject
var kSettings: KSettings
@State var newTag = ""
var completionHandler: (() -> Void)?
var body: some View {
Form {
Section(header: Text("K Settings")) {
HStack {
VStack {
Text("Size")
Slider(value: $kSettings.scale, in: 1...2)
Toggle(isOn: $kSettings.autoloop, label: {
@ -27,39 +31,64 @@ struct KSettingsView: View {
Toggle(isOn: $kSettings.jump, label: {
Text("Jump")
})
}.padding(30)
VStack {
Toggle(isOn: $kSettings.edit, label: {
Text("Edit")
})
Toggle(isOn: $kSettings.slow, label: {
Text("Slow")
})
Toggle(isOn: $kSettings.confirm, label: {
Text("Confirm")
})
Toggle(isOn: $kSettings.automaticallyWaitsToMinimizeStalling, label: {
Text("Stalling")
})
}.padding(30)
}
}
Section(header: Text("K Settings 3")) {
HStack {
Button(action: {
NetworkManager.sharedInstance.suspendLinkstation()
}, label: {
Text("suspend")
});
Spacer()
Button(action: {
NetworkManager.sharedInstance.wakeLinkstation()
}, label: {
Text("wake")
});
}
HStack {
Button(action: {
LocalManager.sharedInstance.saveSettings()
self.completionHandler?()
}, label: {
Text("ok")
});
})
.buttonStyle(BorderlessButtonStyle());
Spacer()
Button(action: {
LocalManager.sharedInstance.loadSettings()
self.completionHandler?()
}, label: {
Text("cancel")
})
.buttonStyle(BorderlessButtonStyle())
}
// HStack {
// TextField("Rename " + DatabaseManager.sharedInstance.currentTag, text: $newTag)
// Button(action: {
// DatabaseManager.sharedInstance.renameCurrentTag(newName: newTag)
// }, label: {
// Text("rename")
// }).buttonStyle(BorderlessButtonStyle())
// }
}
}
.onAppear {
}

97
kplayer/master/MasterModel.swift

@ -21,6 +21,8 @@ class MasterModel : ObservableObject {
var delegate: MasterDelegate
var detailDelegate: DetailDelegate
var tagModes = ["", "models", "compilations"]
var tagMode = 0
@Published var videoModel: SVideoModel = SVideoModel(allItems: [], currentSnapshot: MediaItem(name: "", path: "", root: "", type: .VIDEO), baseItem: MediaItem(name: "", path: "", root: "", type: .VIDEO))
@Published var photoModel = SPhotoModel(allItems: [MediaItem(name: "", path: "", root: "", type: .PICS)])
@ -29,10 +31,15 @@ class MasterModel : ObservableObject {
@Published var showPhoto = false
@Published var showFilePicker = false
@Published var showSettings = false
@Published var isTagging = false
@Published var selectedItemId : String?
{
didSet {
if isTagging {
return
}
if let selected = selectedItemId {
if !LocalManager.sharedInstance.authenticated {
// doAuthenticate()
@ -88,15 +95,36 @@ class MasterModel : ObservableObject {
@Published var detailItems: [MediaItem]
func back() {
if !isTagging {
if !parentItem.isEmpty {
parentItem.removeLast()
}
}
else {
isTagging = false
}
items = parentItem.last?.children ?? itemModel.items
selectedItemId = nil
selectedItem = nil
}
func tagIt() {
isTagging = true
let tagCategory = tagModes[tagMode]
tagMode+=1
if tagMode >= tagModes.count {
tagMode = 0
}
var newItems = [MediaItem]()
var tags = DatabaseManager.sharedInstance.allTags[tagCategory]!
tags.sort()
for t in tags {
newItems.append(MediaItem(name: t, path: t, root: "tags", type: ItemType.TAG))
}
items = newItems
}
private func gotoNextFolder(_ neu: MediaItem) {
DispatchQueue.main.async {
self.selectedItemId = nil
@ -104,7 +132,12 @@ class MasterModel : ObservableObject {
self.parentItem.append(neu)
// ItemModel().sortItems(selectedItem: neu, children: neu.children)
if (neu.type != ItemType.TAGROOT) {
neu.sort()
}
self.items = neu.children
// items.sort()
neu.printTree()
}
@ -176,6 +209,7 @@ class MasterModel : ObservableObject {
model.zoomed = delegate.settings().zoomed
model.jump = delegate.settings().jump
model.loop = delegate.settings().autoloop
model.tagging = isTagging
videoModel = model
showVideo = true
@ -183,7 +217,10 @@ class MasterModel : ObservableObject {
func saveVideo(saved: Bool) {
if saved {
if let baseItem=selectedDetail {
if var baseItem=selectedDetail {
if baseItem.type == ItemType.SNAPSHOT && baseItem.parent != nil {
baseItem = baseItem.parent!
}
baseItem.children = videoModel.allItems
delegate.saveItem(selectedItem: baseItem)
}
@ -191,8 +228,40 @@ class MasterModel : ObservableObject {
else {
//baseItem.children = clonedChildren
}
var next = false
var nextChild = false
if LocalManager.sharedInstance.settings.confirm == false {
for c in detailItems {
if next {
showVideo = false
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { timer in
self.showVideo(selectedItem: c)
}
break
}
if selectedDetail == c {
next = true
continue
}
for cc in c.children {
if selectedDetail == cc {
nextChild = true
continue
}
}
if nextChild {
next = true
continue
}
}
}
else {
showVideo = false
}
}
func showPhotos(_ im: [MediaItem], selectedItem: MediaItem, categoryItem: MediaItem, compilation: Bool = false) {
let base = MediaItem(name: "", path: "", root: "", type: ItemType.PICFOLDER)
@ -241,6 +310,30 @@ class MasterModel : ObservableObject {
}
}
func overview() {
if let item = selectedItem {
let composition = MediaItem(name: item.name, path: item.path, root: item.root, type: ItemType.VIDEO)
composition.compilation = true
for d in detailItems {
if d.children.isEmpty {
let clone = d.clone()
clone.parent = composition
clone.type = ItemType.SNAPSHOT
composition.children.append(clone)
} else {
for c in d.children {
let clone = c.clone()
clone.parent = composition
composition.children.append(clone)
}
}
}
showVideo(selectedItem: composition.children[0])
}
}
func doAuthenticate() {
let authenticationContext = LAContext()
var error: NSError?

30
kplayer/master/MasterSplitView.swift

@ -13,15 +13,36 @@ struct MasterSplitView: View {
var body: some View {
ZStack {
NavigationSplitView {
ZStack {
if model.isTagging {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 80))]) {
ForEach(model.items) { item in
Text(item.name).font(.system(size: 16)).bold().padding(EdgeInsets(top: 10, leading: 6, bottom: 10, trailing: 6))
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.gray.opacity(0.2))
).draggable(item.name)
}
}
}
}
else {
List(model.items, selection: $model.selectedItemId) { item in
Text(item.name).font(.title3).bold()
}.id(UUID()).toolbar {
}.id(UUID())
}
}.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: model.back, label: {
Image(systemName: "arrowshape.backward")
})
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: model.tagIt, label: {
Image(systemName: "tag")
})
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: { model.showSettings = true }, label: {
Image(systemName: "slider.horizontal.3")
@ -38,6 +59,11 @@ struct MasterSplitView: View {
HStack {
Text(model.selectedItem?.longName() ?? "").font(.title3)
Spacer()
if model.selectedItem != nil {
Button(action: model.overview, label: {
Text("Overview")
})
}
}.background(Color(UIColor.systemGray6))
DetailView(model: model)
}

7
kplayer/master/MasterViewController.swift

@ -177,6 +177,13 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa
let submitAction = UIAlertAction(title: "Submit", style: .default) { [unowned ac] _ in
let answer = ac.textFields![0].text!
if (item.root == "/models") {
DatabaseManager.sharedInstance.createTag(answer, path: "models")
let m = MediaItem(name: answer, path: answer, root: "models", type: ItemType.TAG)
m.local = true
item.children.append(m)
m.parent = item
} else
if (item.root == "/tags") {
DatabaseManager.sharedInstance.createTag(answer)
let m = MediaItem(name: answer, path: answer, root: "tags", type: ItemType.TAG)

9
kplayer/master/NetworkDelegate.swift

@ -115,7 +115,14 @@ class NetworkDelegate: MasterDelegate, DetailDelegate {
func saveItem(selectedItem: MediaItem) {
var item = selectedItem
if (selectedItem.local || (selectedItem.parent?.local != nil )) {
var isLocal = selectedItem.local
if (!isLocal) {
if let p = selectedItem.parent {
isLocal = p.local
}
}
if (isLocal) {
if item.type == ItemType.SNAPSHOT {
item = selectedItem.parent!
}

1
kplayer/photo/SPhotoView.swift

@ -74,6 +74,7 @@ struct SPhotoView: View {
print("3 tapped!")
if model.scale == 1 {
model.scale = lastScale
model.dragOffset = CGSize(width: 600, height: 0)
} else {
lastScale = model.scale
model.scale = 1

2
kplayer/util/AsyncImage.swift

@ -33,7 +33,7 @@ struct AsyncImage<Placeholder: View>: View {
image(item.thumbImage!.scaleToSize(66.0, height: 44.0))
}
else {
image(item.thumbImage!.scaleToSize(15 * 16, height: 15 * 9))
image(item.thumbImage!.scaleToHeight(15 * 16))
}
} else {
placeholder

12
kplayer/util/UIImageExtension.swift

@ -39,6 +39,18 @@ extension UIImage {
}
}
func scaleToHeight(_ max: CGFloat) -> UIImage {
let targetSize = CGSize(width: max, height: max)
// Compute the scaling ratio for the width and height separately
let widthScaleRatio = targetSize.width / size.width
let heightScaleRatio = targetSize.height / size.height
// To keep the aspect ratio, scale by the smaller scaling ratio
let scaleFactor = min(widthScaleRatio, heightScaleRatio)
return scaleToSize(size.width * scaleFactor, height: size.height * scaleFactor)
}
func decodeImage() -> UIImage? {
guard let newImage = self.cgImage else { return nil }

1
kplayer/video/SVideoModel.swift

@ -25,6 +25,7 @@ class SVideoModel : ObservableObject {
@Published var edit = false
@Published var dirty = false
@Published var loop = false
@Published var tagging = false
@Published var slow = false
@Published var zoomed = false
@Published var jump = false

187
kplayer/video/SVideoPlayer.swift

@ -25,6 +25,7 @@ struct SVideoPlayer: View, EditItemDelegate {
@State var confirmationShown = false
@State var dirtyShown = false
@State var blackShown = false
@State var showSettings = false
@State var truncateShown = false
@State var seekSmoothly = false
@State var upsidedown = false
@ -42,13 +43,18 @@ struct SVideoPlayer: View, EditItemDelegate {
@State var xoffs = 0.0
@State var rotazero = -1000.0
@State var loop5 = false
@State var loop5start = 0.0
@State var cutFlag = false
@State var lores = false
@State var second = false
@State var tap3 = false
@State var secondScale = 0.75
@State var secondOffset = CGSize(width: -70, height: 50)
@State var orientation = UIDevice.current.orientation
@State private var scrollTarget: Int?
let orientationChanged = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
.makeConnectable()
@ -65,6 +71,10 @@ struct SVideoPlayer: View, EditItemDelegate {
self.playerLooper = AVPlayerLooper(player: player, templateItem: model.currentPlayerItem())
}
func isEnd() -> Bool {
model.paused && model.currentSnapshot.time > 0
}
func cleanup() {
if model.observer != nil && model.observed != nil && model.observed! === player {
do {
@ -76,19 +86,41 @@ struct SVideoPlayer: View, EditItemDelegate {
}
}
var multiTapGesture: some Gesture {
SimultaneousGesture(TapGesture(count: 2), TapGesture(count: 3))
.onEnded { gestureValue in
if gestureValue.second != nil {
if !model.baseItem.compilation {
doSnapshot()
}
print("triple tap!")
} else if gestureValue.first != nil {
doubleTapZoom()
}
}
}
var body: some View {
VStack {
HStack {
Group {
VStack {
HStack {
Button(action: {
if model.dirty {
if LocalManager.sharedInstance.settings.confirm {
dirtyShown = true
} else {
cleanup()
closePlayer(withSave: true)
cleanup()
}
} else {
let withSave = model.edit
cleanup()
closePlayer(withSave: withSave)
}
}, label: {
Text("cancel")
Text(LocalManager.sharedInstance.settings.confirm ? "cancel" : "next")
})
.buttonStyle(BorderlessButtonStyle()).confirmationDialog("Save?", isPresented: $dirtyShown) {
Button("save") {
@ -99,8 +131,14 @@ struct SVideoPlayer: View, EditItemDelegate {
}
}
if !model.baseItem.compilation {
Button(action: { blackShown = true }) {
Text("black")
Button(action: {
if isEnd() {
setEnd()
} else {
blackShown = true
}
}) {
Text(isEnd() ? "end" : "black")
}
.foregroundColor(NetworkManager.sharedInstance.isBlack(model.baseItem) ? Color.yellow : Color.blue)
.frame(height: 30).buttonStyle(BorderlessButtonStyle()).confirmationDialog("Delete?", isPresented: $blackShown) {
@ -129,7 +167,8 @@ struct SVideoPlayer: View, EditItemDelegate {
}
}, label: {
Text(DatabaseManager.sharedInstance.currentTag)
}).foregroundColor(model.currentSnapshot.tags.contains(DatabaseManager.sharedInstance.currentTag) ? Color.yellow : Color.blue)
})
.foregroundColor(model.currentSnapshot.tags.contains(DatabaseManager.sharedInstance.currentTag) ? Color.yellow : Color.blue)
Button(action: {
model.favorite.toggle()
@ -139,7 +178,8 @@ struct SVideoPlayer: View, EditItemDelegate {
})
.foregroundColor(model.favorite ? Color.yellow : Color.blue).buttonStyle(BorderlessButtonStyle())
KToggleButton(text: "loop5", binding: $loop5)
KToggleButton(text: "cfg", binding: $showSettings).frame(height: 30)
Button(action: { confirmationShown = true }, label: {
Text(savetext)
})
@ -160,17 +200,19 @@ struct SVideoPlayer: View, EditItemDelegate {
}
KToggleButton(text: "embd", binding: $embedded).frame(height: 30)
Group {
Text(model.currentSnapshot.name).foregroundColor(Color.blue)
Text("""
(\(model.codec) \(model.height), \(model.nominalFrameRate), \(model.bitRate)m)\n\(model.scale, specifier: "%.2f")x (\(model.dragOffset.width, specifier: "%.0f"),\(model.dragOffset.height, specifier: "%.0f"))
""").foregroundColor(Color.blue)
}
HStack {
Text(model.currentSnapshot.name).foregroundColor(Color.blue).fontWeight(Font.Weight.light)
}
}
HStack {
ScrollViewReader { scrollvalue in
ScrollView(.horizontal, showsIndicators: false) {
HStack {
ForEach(model.allItems) { item in
Button(action: {
gotoSnapshot(item)
scrollvalue.scrollTo(item.id)
}) {
AsyncImage(item: item, placeholder: { Text("Loading ...") },
image: { Image(uiImage: $0).resizable() }).border(.yellow, width: (item === model.currentSnapshot) ? 1 : 0)
@ -179,10 +221,19 @@ struct SVideoPlayer: View, EditItemDelegate {
}
}
}
// .onChange(of: scrollTarget { target in
// if let tg = target {
// scrollTarget = nil
// withAnimation {
// scrollvalue.scrollTo(tg)
// }
// }
// })
}
Spacer()
VStack {
HStack {
Button(action: { model.edit.toggle() }, label: {
Text("edit")
})
@ -203,9 +254,13 @@ struct SVideoPlayer: View, EditItemDelegate {
}
}
Text("""
(\(model.codec) \(model.height), \(model.nominalFrameRate), \(model.bitRate)m)\n\(model.scale, specifier: "%.2f")x (\(model.dragOffset.width, specifier: "%.0f"),\(model.dragOffset.height, specifier: "%.0f"))
""").foregroundColor(Color.blue).fontWeight(Font.Weight.light)
}
}
}
.frame(height: 50)
}
GeometryReader { geometry in
VStack {
@ -214,25 +269,14 @@ struct SVideoPlayer: View, EditItemDelegate {
.scaleEffect(small ? 1 : model.scale)
.rotation3DEffect(.degrees(upsidedown ? 180 : 0), axis: (x: 1, y: 0, z: 0))
.offset(small ? CGSize.zero : model.dragOffset).modifier(KAnimate(dragOffset: model.dragOffset, spring: false)).offset(x: xoffs, y: 0)
.onTapGesture(count: 2) {
print("3 tapped!")
second = !second
if second {
model.scale = lastScale
} else {
lastScale = model.scale
if (lores) {
model.scale = secondScale
model.dragOffset = secondOffset
}
else {
model.scale = 1
model.dragOffset = CGSize.zero
}
}
.gesture(multiTapGesture)
.simultaneousGesture(
LongPressGesture()
.onEnded { _ in
print("Loooong")
togglePause()
}
)
.gesture(
DragGesture()
.onChanged { gesture in
@ -323,12 +367,7 @@ struct SVideoPlayer: View, EditItemDelegate {
.frame(height: 30)
.buttonStyle(BorderlessButtonStyle())
Button(action: {
if model.paused {
player.play()
} else {
player.pause()
}
model.paused = !model.paused
togglePause()
}, label: {
Text("pause")
})
@ -366,7 +405,8 @@ struct SVideoPlayer: View, EditItemDelegate {
}
Button(action: { cut() }) {
Text("cut")
}.foregroundColor(cutFlag ? Color.yellow : Color.blue)
}
.foregroundColor(cutFlag ? Color.yellow : Color.blue)
.frame(height: 30).buttonStyle(BorderlessButtonStyle())
}
@ -401,6 +441,12 @@ struct SVideoPlayer: View, EditItemDelegate {
})
}
.frame(width: 50, alignment: .top).offset(x: 0, y: 0), alignment: .topLeading)
} else if model.tagging {
v.overlay(ScrollView() {
TagEditor(item: model.currentSnapshot, completionHandler: NetworkManager.sharedInstance.saveItem)
}
.frame(width: 170, alignment: .top).offset(x: 0, y: 0),
alignment: .topLeading)
} else if frames && model.zoomed {
v.overlay(VStack {
ForEach(model.frames) { f in
@ -449,6 +495,11 @@ struct SVideoPlayer: View, EditItemDelegate {
.clipped()
}
}
.sheet(isPresented: $showSettings, onDismiss: { showSettings = false }) {
KSettingsView(kSettings: LocalManager.sharedInstance.settings, completionHandler: {
showSettings = false
})
}
.onAppear() {
model.observed = player
@ -491,10 +542,46 @@ struct SVideoPlayer: View, EditItemDelegate {
}
model.bitRate = Int(lastEvent.indicatedBitrate / 1024 / 1024)
}.onChange(of: loop5) { new in
if new {
if model.paused {
model.paused = false
player.play()
player.rate = 0.8
}
loop5start = player.currentTime().seconds
}
}
}
private func togglePause() {
if model.paused {
player.play()
} else {
player.pause()
}
model.paused = !model.paused
}
private func doubleTapZoom() {
second = !second
if second {
model.scale = lastScale
} else {
lastScale = model.scale
if (lores) {
model.scale = secondScale
model.dragOffset = secondOffset
} else {
model.scale = 1
model.dragOffset = CGSize.zero
}
}
}
private func timeObserver() -> (CMTime) -> () {
{ time in
if timeCounter >= 1 {
@ -503,6 +590,11 @@ struct SVideoPlayer: View, EditItemDelegate {
if timeSlomoCounter >= 1 {
timeSlomoCounter -= 1
}
if loop5 {
if time.seconds > loop5start + 5.0 {
seekTime(loop5start)
}
}
if model.loop && !model.paused && model.currentSnapshot.length > 0 {
if time.seconds > model.currentSnapshot.time + model.currentSnapshot.length {
seekTime(model.currentSnapshot.time)
@ -596,6 +688,7 @@ struct SVideoPlayer: View, EditItemDelegate {
newItem.external = currentItem.external
model.allItems.append(newItem)
model.currentSnapshot = newItem
scrollTarget = model.allItems.count
}
private func move(_ dragged: CGSize, start: CGPoint) -> Bool {
@ -606,7 +699,7 @@ struct SVideoPlayer: View, EditItemDelegate {
}
if (start.y < 170) {
let delta = (dragged.width + dragged.height) / 200.0
let delta = (dragged.width + dragged.height) / 100.0
seekTimeSmoothly(smoothTime + delta)
return false
@ -614,7 +707,7 @@ struct SVideoPlayer: View, EditItemDelegate {
if second || small {
return true
} else {
let delta = (dragged.width + dragged.height) / 200.0
let delta = (dragged.width + dragged.height) / 30.0
seekTimeSmoothly(smoothTime + delta)
return false
@ -646,7 +739,7 @@ struct SVideoPlayer: View, EditItemDelegate {
if i - 1 >= 0 {
gotoSnapshot(model.allItems[i - 1])
} else {
gotoSnapshot(model.allItems[model.allItems.count-1])
gotoSnapshot(model.allItems[model.allItems.count - 1])
}
timeCounter = 50
print("jump")
@ -797,7 +890,7 @@ struct SVideoPlayer: View, EditItemDelegate {
}
model.height = Int(height)
lores = (height <= 1080) && (height*1.5 < heightSpace)
lores = (height <= 1080) && (height * 1.5 < heightSpace)
var f = height / heightSpace
print("h \(height) \(heightSpace) \(f)")
@ -815,8 +908,7 @@ struct SVideoPlayer: View, EditItemDelegate {
model.scale = f
model.dragOffset.width = 0
model.dragOffset.height = 0
}
else {
} else {
if (model.scale != 1) {
lastScale = model.scale
}
@ -824,8 +916,7 @@ struct SVideoPlayer: View, EditItemDelegate {
if (lores) {
model.scale = secondScale
model.dragOffset = secondOffset
}
else {
} else {
model.scale = 1
model.dragOffset = CGSize.zero
}

Loading…
Cancel
Save