5 Commits

Author SHA1 Message Date
marcoschmickler 71a90b1a34 haneke weg 3 weeks ago
marcoschmickler b55e621540 haneke weg 2 months ago
marcoschmickler a2f2b901dc haneke weg 2 months ago
marcoschmickler 8d2e16485a haneke weg 2 months ago
marcoschmickler 300d9847cb haneke weg 2 months ago
  1. 8
      .idea/kplayer.iml
  2. 3
      Podfile
  3. 10
      kplayer.xcodeproj/project.pbxproj
  4. 42
      kplayer/core/FaceManager.swift
  5. 13
      kplayer/core/MediaItem.swift
  6. 75
      kplayer/core/openapi.json
  7. 203
      kplayer/detail/EditItemView.swift
  8. 18
      kplayer/detail/ItemCell.swift
  9. 28
      kplayer/photo/SPhotoAlbumView.swift
  10. 4
      kplayer/photo/SPhotoModel.swift
  11. 1
      kplayer/photo/SPhotoScrubber.swift
  12. 1
      kplayer/util/AsyncImage.swift
  13. 32
      kplayer/util/HanekeFetchOperation.swift

8
.idea/kplayer.iml

@ -1,2 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="AppCode" type="CIDR_MODULE" version="4" />
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.9" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

3
Podfile

@ -5,8 +5,7 @@ use_frameworks!
target 'kplayer' do
pod 'Alamofire' #, '4.7'
pod 'HanekeSwift', :git => 'https://github.com/Haneke/HanekeSwift.git'
pod 'FileBrowser'
pod 'FileBrowser'
pod 'ZIPFoundation'
end

10
kplayer.xcodeproj/project.pbxproj

@ -60,9 +60,7 @@
1C736C00693A05747578DF87 /* grabber.js in Sources */ = {isa = PBXBuildFile; fileRef = 1C73619BBFA9295A11C9ACBA /* grabber.js */; };
1C736C76C1D80B649474F0A5 /* SPhotoAlbumView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360A759DFDE11F631E0B6 /* SPhotoAlbumView.swift */; };
1C736C8DAD6C2FBB9A2EA625 /* SearchItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73654AB95A2D629833BEC5 /* SearchItemView.swift */; };
1C736C9821DA743C2E3F3B07 /* kplayer.txt in Resources */ = {isa = PBXBuildFile; fileRef = 1C7360F9649E40B7C2EAB581 /* kplayer.txt */; };
1C736D0A14C365F3E874420C /* SelfSizingHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736C72CDF8902484856B3B /* SelfSizingHostingController.swift */; };
1C736D16E81BA1FB325200E0 /* HanekeFetchOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */; };
1C736D24891597F2728230EE /* ImageLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */; };
1C736D24B49451141CD4B64D /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */; };
1C736D5A7C7CB9B14AF0ECA6 /* DetailViewController+Show.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736B17A8E9FB352B90A903 /* DetailViewController+Show.swift */; };
@ -114,11 +112,9 @@
1C7360295486647982CFEACF /* UIViewController+Alert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Alert.swift"; sourceTree = "<group>"; };
1C736059262A57AADE6AB761 /* Kirschkeks-256x256.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Kirschkeks-256x256.png"; path = "kplayer/Kirschkeks-256x256.png"; sourceTree = "<group>"; };
1C736069C214E9522BB1BD97 /* ItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemCell.swift; sourceTree = "<group>"; };
1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HanekeFetchOperation.swift; sourceTree = "<group>"; };
1C7360A759DFDE11F631E0B6 /* SPhotoAlbumView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPhotoAlbumView.swift; sourceTree = "<group>"; };
1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageLoadOperation.swift; sourceTree = "<group>"; };
1C7360B6D0757D4FB6433E7B /* AsyncImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncImage.swift; sourceTree = "<group>"; };
1C7360F9649E40B7C2EAB581 /* kplayer.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = kplayer.txt; sourceTree = "<group>"; };
1C7360F9835128FC0A198ED0 /* SVideoLoopPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVideoLoopPlayer.swift; sourceTree = "<group>"; };
1C73611D226B48C24DB37535 /* MasterViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterViewController.swift; sourceTree = "<group>"; };
1C73615FFA2AA98BD1C56CD4 /* links.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = links.html; sourceTree = "<group>"; };
@ -291,7 +287,6 @@
1C73620D01687FB4F1811C5C /* NetworkHelper.swift */,
1C736260E748CF136FF37EA7 /* UploadOperation.swift */,
1C7369EC16B19B32B515169E /* NetData.swift */,
1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */,
1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */,
1C736F9338CE36708244D42A /* DataLoadOperation.swift */,
1C736B794396F2E50387B8F2 /* stringutil.swift */,
@ -394,7 +389,6 @@
8052F5B3AAC2535E5C08A529 /* Pods */,
1C73685B4BBFDAFBF08C032C /* readme.md */,
1C7369BED02028D8564E82D5 /* pathfinder.scpt */,
1C7360F9649E40B7C2EAB581 /* kplayer.txt */,
1C736BF48D5CE855B6E75BE6 /* hints.md */,
);
sourceTree = "<group>";
@ -550,7 +544,6 @@
1C736A5FA5BA53B2597F2ED7 /* Kirschkeks-256x256.png in Resources */,
1C73696E4C0353053BF98031 /* links.html in Resources */,
1C736FAE5D3E5D3BA3C1FAE5 /* links.html in Resources */,
1C736C9821DA743C2E3F3B07 /* kplayer.txt in Resources */,
1C736FB6920008B2603D3415 /* openapi.json in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -574,14 +567,12 @@
"${PODS_ROOT}/Target Support Files/Pods-kplayer/Pods-kplayer-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
"${BUILT_PRODUCTS_DIR}/FileBrowser/FileBrowser.framework",
"${BUILT_PRODUCTS_DIR}/HanekeSwift/Haneke.framework",
"${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FileBrowser.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Haneke.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZIPFoundation.framework",
);
runOnlyForDeploymentPostprocessing = 0;
@ -627,7 +618,6 @@
1C73640D928DE56D35175D39 /* UploadOperation.swift in Sources */,
1C73646F87B495A47D7943C7 /* NetData.swift in Sources */,
1C73693A1334A7792856FC58 /* MasterViewController.swift in Sources */,
1C736D16E81BA1FB325200E0 /* HanekeFetchOperation.swift in Sources */,
1C736D24891597F2728230EE /* ImageLoadOperation.swift in Sources */,
1C7367AF39961D2BA72480ED /* DataLoadOperation.swift in Sources */,
C91E058C2795AC5C0003AB79 /* KItem+CoreDataProperties.swift in Sources */,

42
kplayer/core/FaceManager.swift

@ -57,6 +57,18 @@ struct ProcessFolderRequest: Codable {
}
}
struct ProcessOneImageRequest: Codable {
let inputImage: String
let sourceFace: String
let outputFormat: String?
enum CodingKeys: String, CodingKey {
case inputImage = "input_image"
case sourceFace = "source_face"
case outputFormat = "output_format"
}
}
struct ProcessVideoRequest: Codable {
let inputVideoPath: String
let sourceFacePath: String
@ -176,7 +188,7 @@ struct AnyCodable: Codable {
class FaceManager {
static let sharedInstance = FaceManager()
let faceUrl = "http://win11marco:8000"
let faceUrl = "http://win11marco:5013"
private let session: URLSession
private init() {
@ -290,6 +302,34 @@ class FaceManager {
return try await executeRequest(request)
}
/// Process a single image and return the result as raw image data
/// - Parameters:
/// - inputImage: Base64-encoded image string OR filesystem path to the input image
/// - sourceFace: Base64-encoded face image string OR filesystem path to the source face
/// - outputFormat: 'jpg' or 'png' (default: jpg)
func processOneImage(inputImage: String, sourceFace: String, outputFormat: String? = nil) async throws -> Data {
let body = ProcessOneImageRequest(
inputImage: inputImage,
sourceFace: sourceFace,
outputFormat: outputFormat
)
let request = try createRequest(endpoint: "/process-oneimage", method: "POST", body: body)
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw FaceAPIError.invalidResponse
}
guard (200...299).contains(httpResponse.statusCode) else {
if let errorMessage = try? JSONDecoder().decode([String: String].self, from: data),
let detail = errorMessage["detail"] {
throw FaceAPIError.serverError(detail)
}
throw FaceAPIError.serverError("HTTP \(httpResponse.statusCode)")
}
return data
}
/// Clear the model cache
func clearCache() async throws -> [String: Any] {
let request = try createRequest(endpoint: "/cache/clear", method: "POST")

13
kplayer/core/MediaItem.swift

@ -260,12 +260,13 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject, Identifiable, H
Absolute URL, unter der das Image des Items abgerufen werden kann.
*/
var imageUrlAbsolute: String {
if thumbUrl!.starts(with: "file:") {
return thumbUrl!
if let turl = thumbUrl {
if turl.starts(with: "file:") {
return turl
}
var imageUrl = thumbUrl!.replacingOccurrences(of: "_thumb.", with: ".")
imageUrl = thumbUrl!.replacingOccurrences(of: "/ren/thumbs/", with: "/ren/")
var imageUrl = turl.replacingOccurrences(of: "_thumb.", with: ".")
imageUrl = turl.replacingOccurrences(of: "/ren/thumbs/", with: "/ren/")
if imageUrl.starts(with: "http") {
return imageUrl
@ -276,6 +277,10 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject, Identifiable, H
return NetworkManager.sharedInstance.vidurl + imageUrl.substringStartingFrom(10)
}
else {
return " "
}
}
var playerURL: URL? {
if let ext = externalURL {

75
kplayer/core/openapi.json

@ -162,6 +162,43 @@
}
}
},
"/process-oneimage": {
"post": {
"summary": "Process Oneimage",
"description": "Process a single image with face swapping and return the result as an image stream.\n\n- **input_image**: Base64-encoded image string OR filesystem path to the input image\n- **source_face**: Base64-encoded face image string OR filesystem path to the source face\n- **output_format**: 'jpg' or 'png' (default: jpg)\n\nReturns the processed image as a binary stream (image/jpeg or image/png).",
"operationId": "process_oneimage_process_oneimage_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProcessOneImageRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/cache/clear": {
"post": {
"summary": "Clear Cache",
@ -358,6 +395,44 @@
"source_face_path": "./faces/source.png"
}
},
"ProcessOneImageRequest": {
"properties": {
"input_image": {
"type": "string",
"title": "Input Image",
"description": "Input image as base64-encoded string OR filesystem path"
},
"source_face": {
"type": "string",
"title": "Source Face",
"description": "Source face image as base64-encoded string OR filesystem path"
},
"output_format": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Output Format",
"description": "Output image format: 'jpg' or 'png' (default: jpg)",
"default": "jpg"
}
},
"type": "object",
"required": [
"input_image",
"source_face"
],
"title": "ProcessOneImageRequest",
"example": {
"input_image": "<base64-encoded image data or /path/to/image.jpg>",
"output_format": "jpg",
"source_face": "<base64-encoded face data or /path/to/face.png>"
}
},
"ProcessResponse": {
"properties": {
"success": {

203
kplayer/detail/EditItemView.swift

@ -70,31 +70,7 @@ struct EditItemView: View {
var snap = true
@State
var processedImageURL: String?
@State
var isProcessing = false
@State
var showProcessedImage = false
@State
var loadedImage: UIImage?
@State
var imageLoadError: String?
@State
var imageScale: CGFloat = 1.0
@State
var imageOffset: CGSize = .zero
@State
var lastScale: CGFloat = 1.0
@State
var lastOffset: CGSize = .zero
var faceModel: SPhotoModel? = nil
var len: Double
@ -181,6 +157,8 @@ struct EditItemView: View {
Button(action: { faceSelectedItem("birgit"); }, label: { Text("birgit") }).buttonStyle(BorderlessButtonStyle())
Button(action: { faceSelectedItem("barbara"); }, label: { Text("barbara") }).buttonStyle(BorderlessButtonStyle())
Button(action: { faceSelectedItem("nina"); }, label: { Text("nina") }).buttonStyle(BorderlessButtonStyle())
Button(action: { faceSelectedItem("nicki"); }, label: { Text("nicki") }).buttonStyle(BorderlessButtonStyle())
Button(action: { faceSelectedItem("mitra"); }, label: { Text("mitra") }).buttonStyle(BorderlessButtonStyle())
Button(action: { faceSelectedItem("amruta"); }, label: { Text("amruta") }).buttonStyle(BorderlessButtonStyle())
}
@ -198,136 +176,14 @@ struct EditItemView: View {
UITableView.appearance().backgroundColor = .systemBackground
}
.frame(height: 800, alignment: .top)
.sheet(isPresented: $showProcessedImage) {
NavigationView {
VStack {
if let image = loadedImage {
GeometryReader { geometry in
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: geometry.size.width, height: geometry.size.height)
.scaleEffect(imageScale)
.offset(imageOffset)
.gesture(
SimultaneousGesture(
MagnificationGesture()
.onChanged { value in
let delta = value / lastScale
lastScale = value
let newScale = imageScale * delta
imageScale = min(max(newScale, 1.0), 5.0)
}
.onEnded { value in
lastScale = 1.0
},
DragGesture(minimumDistance: 0)
.onChanged { value in
imageOffset = CGSize(
width: lastOffset.width + value.translation.width,
height: lastOffset.height + value.translation.height
)
}
.onEnded { value in
lastOffset = imageOffset
}
)
)
.onTapGesture(count: 2) {
// Double tap to reset zoom
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
imageScale = 1.0
lastScale = 1.0
imageOffset = .zero
lastOffset = .zero
}
}
}
} else if let error = imageLoadError {
VStack(spacing: 20) {
Image(systemName: "exclamationmark.triangle")
.font(.system(size: 60))
.foregroundColor(.orange)
Text("Failed to load image")
.font(.headline)
Text(error)
.font(.caption)
.foregroundColor(.secondary)
if let imageURL = processedImageURL {
Text("URL: \(imageURL)")
.font(.caption2)
.foregroundColor(.secondary)
}
}
.padding()
} else {
ProgressView("Loading image...")
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
.navigationTitle("Processed Image")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
if loadedImage != nil && imageScale != 1.0 {
Button("Reset") {
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
imageScale = 1.0
lastScale = 1.0
imageOffset = .zero
lastOffset = .zero
}
}
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Done") {
showProcessedImage = false
loadedImage = nil
imageLoadError = nil
// Reset zoom state
imageScale = 1.0
lastScale = 1.0
imageOffset = .zero
lastOffset = .zero
}
}
}
.task {
if let imageURL = processedImageURL {
await loadImageFromURL(imageURL)
}
}
.sheet(isPresented: Binding(get: { faceModel != nil }, set: { if !$0 { faceModel = nil } })) {
if let fm = faceModel {
SPhotoView(model: fm)
}
}
//Spacer()
}
func loadImageFromURL(_ urlString: String) async {
guard let url = URL(string: urlString) else {
imageLoadError = "Invalid URL"
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let image = UIImage(data: data) {
await MainActor.run {
loadedImage = image
imageLoadError = nil
}
} else {
await MainActor.run {
imageLoadError = "Failed to create image from data"
}
}
} catch {
await MainActor.run {
imageLoadError = "Failed to load image: \(error.localizedDescription)"
}
}
}
func faceSelectedItem(_ name: String) {
let path = item.fullPath.replacing("/srv/samba/ren", with: "z:")
let outpath1 = path.replacing("/", with: "")
@ -340,42 +196,37 @@ struct EditItemView: View {
Task {
isProcessing = true
processedImageURL = nil
if (snap) {
let imagePath = item.imageUrlAbsolute.replacing("http://linkstation:8089/ren", with: "z:").replacing("_thumb", with: "")
print(imagePath)
print(item.thumbUrlAbsolute)
let outimagepath = "z:/cut/snapshots/" + name + "/" + imagePath.substringStartingFrom(2).replacing("/", with: "")
if snap {
let imageURL = URL(string: item.imageUrlAbsolute.replacing("_thumb", with: ""))!
do {
let response = try await FaceManager.sharedInstance.processImage(inputImagePath: imagePath, sourceFacePath: "benchmark/" + name + ".jpg", outputPath: outimagepath)
//if response.success, let outputPath = response.outputPath {
// Convert Windows path back to HTTP URL
let httpURL = outimagepath.replacing("z:", with: "http://linkstation:8089/ren").replacing("\\", with: "/")
processedImageURL = httpURL
print("Processed image URL: \(httpURL)")
// Show the sheet with the processed image
showProcessedImage = true
isProcessing = false
let imageData: Data
if let pasteboardImage = UIPasteboard.general.image,
let jpegData = pasteboardImage.jpegData(compressionQuality: 0.9) {
imageData = jpegData
} else {
(imageData, _) = try await URLSession.shared.data(from: imageURL)
}
let base64Image = imageData.base64EncodedString()
let data = try await FaceManager.sharedInstance.processOneImage(inputImage: base64Image, sourceFace: "benchmark/" + name + ".jpg")
if let img = UIImage(data: data) {
let fm = SPhotoModel(allItems: [item])
fm.timer?.invalidate()
fm.image = img
// delegate.cancelEdit()
await MainActor.run { faceModel = fm }
}
else {
delegate.cancelEdit()
}
} catch {
print("Error processing image: \(error)")
isProcessing = false
}
} else {
do {
try await FaceManager.sharedInstance.processVideo(inputVideoPath: path, sourceFacePath: "benchmark/" + name + ".jpg", outputPath: outpath)
delegate.cancelEdit()
isProcessing = false
} catch {
print("Error processing video: \(error)")
isProcessing = false
}
}
}

18
kplayer/detail/ItemCell.swift

@ -5,13 +5,14 @@
import Foundation
import UIKit
import Haneke
import Combine
var defaultImage = UIImage(named: "Kirschkeks-256x256.png")
var blackImage = UIImage(systemName: "0.circle")
class ItemCell: UICollectionViewCell {
var item: MediaItem?
private var cancellable: AnyCancellable?
var image: UIImageView!
var fav: UIImageView!
@ -78,13 +79,19 @@ class ItemCell: UICollectionViewCell {
}
if let _ = item.thumbUrl, let nsurl = URL(string: item.thumbUrlAbsolute) {
image.hnk_setImageFromURL(nsurl, placeholder: defaultImage)
image.image = defaultImage
cancellable = ImageLoader.shared.loadImage(from: nsurl).sink { [weak self] img in
self?.image.image = img ?? defaultImage
}
} else {
if let i = item.image {
image.image = i.scaleToSize(15 * 16, height: 15 * 9)
} else if item.children.count > 0 {
if let _ = item.children[0].thumbUrl, let nsurl = URL(string: item.children[0].thumbUrlAbsolute) {
image.hnk_setImageFromURL(nsurl, placeholder: defaultImage)
image.image = defaultImage
cancellable = ImageLoader.shared.loadImage(from: nsurl).sink { [weak self] img in
self?.image.image = img ?? defaultImage
}
}
}
else
@ -96,7 +103,8 @@ class ItemCell: UICollectionViewCell {
}
override func prepareForReuse() {
image.hnk_cancelSetImage()
image.image = nil;
cancellable?.cancel()
cancellable = nil
image.image = nil
}
}

28
kplayer/photo/SPhotoAlbumView.swift

@ -11,10 +11,12 @@ struct SPhotoAlbumView: View {
@ObservedObject
var model: SPhotoModel
@State var single = true
@State var more = false
@State var edit = false
@State var embedded = false
@State var embDown = false
@State var faceModel: SPhotoModel? = nil
init(completionHandler: ((Bool) -> ())?, model: SPhotoModel) {
self.completionHandler = completionHandler
@ -66,10 +68,16 @@ struct SPhotoAlbumView: View {
}.task() {
model.preload()
}
.sheet(isPresented: Binding(get: { faceModel != nil }, set: { if !$0 { faceModel = nil } })) {
if let fm = faceModel {
SPhotoView(model: fm)
}
}
if embedded && !more {
v.overlay(SEmbeddedVideo(embedded: $embedded, down: $embDown).offset(y: embDown ? 0: 70), alignment: embDown ? .bottomLeading : .topLeading)
} else if more {
v.overlay(VStack (spacing: 15 ) {
KToggleButton(text: "single", binding: $single).frame(height: 30)
KToggleButton(text: "spring", binding: $model.spring).frame(height: 30)
Button(action: {
saveSelectedItem()
@ -96,6 +104,9 @@ struct SPhotoAlbumView: View {
Button(action: {faceSelectedItem("birgit"); more=false; }, label: {Text("birgit")}).buttonStyle(BorderlessButtonStyle())
Button(action: {faceSelectedItem("barbara"); more=false; }, label: {Text("barbara")}).buttonStyle(BorderlessButtonStyle())
Button(action: {faceSelectedItem("nina"); more=false; }, label: {Text("nina")}).buttonStyle(BorderlessButtonStyle())
Button(action: {faceSelectedItem("nicki"); more=false; }, label: {Text("nicki")}).buttonStyle(BorderlessButtonStyle())
Button(action: {faceSelectedItem("mitra"); more=false; }, label: {Text("mitra")}).buttonStyle(BorderlessButtonStyle())
Button(action: {faceSelectedItem("koch"); more=false; }, label: {Text("koch")}).buttonStyle(BorderlessButtonStyle())
Button(action: {faceSelectedItem("amruta"); more=false; }, label: {Text("amruta")}).buttonStyle(BorderlessButtonStyle())
}
.frame(width: 80, alignment: .top).offset(x: 0, y: 70), alignment: .topLeading)
@ -117,16 +128,27 @@ struct SPhotoAlbumView: View {
func faceSelectedItem(_ name: String) {
let item = model.selectedItem
let path = item.fullPath.replacing("/srv/samba/ren", with: "z:")
let dirname = (path as NSString).lastPathComponent
let outpath1 = path.replacing("/", with: "")
let outpath = outpath1.replacing("z:", with: "z:/cut/images/"+name+"/")
let outpath = outpath1.replacing("z:", with: "z:/cut/images/"+name+"/"+dirname + "/")
print(path)
print(outpath1)
print(outpath)
Task {
try await FaceManager.sharedInstance.processFolder(inputFolderPath: path, sourceFacePath: "benchmark/"+name+".jpg", outputFolderPath: outpath)
//try! await FaceManager.sharedInstance.processImage(inputImagePath: "input", sourceFacePath: "benchmark/Renate.jpg")
if !single {
try await FaceManager.sharedInstance.processFolder(inputFolderPath: path, sourceFacePath: "benchmark/" + name + ".jpg", outputFolderPath: outpath)
} else {
let filename = (item.thumbUrl! as NSString).lastPathComponent
let data = try await FaceManager.sharedInstance.processOneImage(inputImage: path+"/"+filename, sourceFace: "benchmark/" + name + ".jpg")
if let img = UIImage(data: data) {
let fm = SPhotoModel(allItems: [item])
fm.timer?.invalidate()
fm.image = img
await MainActor.run { faceModel = fm }
}
}
}
}

4
kplayer/photo/SPhotoModel.swift

@ -190,6 +190,10 @@ class SPhotoModel : ObservableObject {
}
}
if folderIndex > folderItems.count - 1 {
return
}
var selectedItem = compilation ? selectedItem : folderItems[folderIndex]
NetworkManager.sharedInstance.loadPicDetails(items: selectedItem, result: { (im: [MediaItem]) in

1
kplayer/photo/SPhotoScrubber.swift

@ -5,7 +5,6 @@
import Foundation
import SwiftUI
import Haneke
struct SPhotoScrubber: View {
@ObservedObject var model: SPhotoModel

1
kplayer/util/AsyncImage.swift

@ -1,5 +1,4 @@
import SwiftUI
import Haneke
struct AsyncImage<Placeholder: View>: View {
@StateObject private var item: MediaItem

32
kplayer/util/HanekeFetchOperation.swift

@ -1,32 +0,0 @@
//
// Created by Marco Schmickler on 21.03.15.
// Copyright (c) 2015 Marco Schmickler. All rights reserved.
//
import Foundation
import Haneke
class HanekeFetchOperation: Operation {
let baseUrl: URL
var succeeder: (UIImage) -> ()
let index: Int
init(baseUrl: URL, succeeder: @escaping (UIImage) -> (), index: Int) {
self.baseUrl = baseUrl
self.succeeder = succeeder
self.index = index
}
override func main() {
if self.isCancelled {
return
}
Shared.imageCache.fetch(URL: baseUrl).onSuccess { i in
DispatchQueue.main.async(execute: { self.succeeder(i) })
}
}
}
Loading…
Cancel
Save