From 300d9847cbdc3f8fcc5a16e15c10080258fedcf5 Mon Sep 17 00:00:00 2001 From: marcoschmickler Date: Wed, 18 Mar 2026 23:36:17 +0100 Subject: [PATCH] haneke weg --- .idea/kplayer.iml | 8 ++- Podfile | 3 +- kplayer.xcodeproj/project.pbxproj | 10 ---- kplayer/core/FaceManager.swift | 42 +++++++++++++- kplayer/core/openapi.json | 75 +++++++++++++++++++++++++ kplayer/detail/ItemCell.swift | 18 ++++-- kplayer/photo/SPhotoAlbumView.swift | 17 +++++- kplayer/photo/SPhotoScrubber.swift | 1 - kplayer/util/AsyncImage.swift | 1 - kplayer/util/HanekeFetchOperation.swift | 32 ----------- 10 files changed, 151 insertions(+), 56 deletions(-) delete mode 100644 kplayer/util/HanekeFetchOperation.swift diff --git a/.idea/kplayer.iml b/.idea/kplayer.iml index 74121dc..8388dbc 100644 --- a/.idea/kplayer.iml +++ b/.idea/kplayer.iml @@ -1,2 +1,8 @@ - \ No newline at end of file + + + + + + + \ No newline at end of file diff --git a/Podfile b/Podfile index 367e738..5590c5f 100644 --- a/Podfile +++ b/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 diff --git a/kplayer.xcodeproj/project.pbxproj b/kplayer.xcodeproj/project.pbxproj index b70382b..deddc10 100644 --- a/kplayer.xcodeproj/project.pbxproj +++ b/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 = ""; }; 1C736059262A57AADE6AB761 /* Kirschkeks-256x256.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Kirschkeks-256x256.png"; path = "kplayer/Kirschkeks-256x256.png"; sourceTree = ""; }; 1C736069C214E9522BB1BD97 /* ItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemCell.swift; sourceTree = ""; }; - 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HanekeFetchOperation.swift; sourceTree = ""; }; 1C7360A759DFDE11F631E0B6 /* SPhotoAlbumView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPhotoAlbumView.swift; sourceTree = ""; }; 1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageLoadOperation.swift; sourceTree = ""; }; 1C7360B6D0757D4FB6433E7B /* AsyncImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncImage.swift; sourceTree = ""; }; - 1C7360F9649E40B7C2EAB581 /* kplayer.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = kplayer.txt; sourceTree = ""; }; 1C7360F9835128FC0A198ED0 /* SVideoLoopPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVideoLoopPlayer.swift; sourceTree = ""; }; 1C73611D226B48C24DB37535 /* MasterViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterViewController.swift; sourceTree = ""; }; 1C73615FFA2AA98BD1C56CD4 /* links.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = links.html; sourceTree = ""; }; @@ -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 = ""; @@ -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 */, diff --git a/kplayer/core/FaceManager.swift b/kplayer/core/FaceManager.swift index 560ba2d..19485c3 100644 --- a/kplayer/core/FaceManager.swift +++ b/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") diff --git a/kplayer/core/openapi.json b/kplayer/core/openapi.json index 7777fd5..064f965 100644 --- a/kplayer/core/openapi.json +++ b/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": "", + "output_format": "jpg", + "source_face": "" + } + }, "ProcessResponse": { "properties": { "success": { diff --git a/kplayer/detail/ItemCell.swift b/kplayer/detail/ItemCell.swift index 7375c6f..3bf7586 100644 --- a/kplayer/detail/ItemCell.swift +++ b/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 } } diff --git a/kplayer/photo/SPhotoAlbumView.swift b/kplayer/photo/SPhotoAlbumView.swift index 05079f0..5a6f779 100644 --- a/kplayer/photo/SPhotoAlbumView.swift +++ b/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 faceResultImage: UIImage? = nil init(completionHandler: ((Bool) -> ())?, model: SPhotoModel) { self.completionHandler = completionHandler @@ -70,6 +72,7 @@ struct SPhotoAlbumView: View { 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 +99,8 @@ 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("nina")}).buttonStyle(BorderlessButtonStyle()) + Button(action: {faceSelectedItem("mitra"); more=false; }, label: {Text("nina")}).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,15 +122,21 @@ 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) + if !single { + try await FaceManager.sharedInstance.processFolder(inputFolderPath: path, sourceFacePath: "benchmark/" + name + ".jpg", outputFolderPath: outpath) + } + else { + try! await FaceManager.sharedInstance.processImage(inputImagePath: "input", sourceFacePath: "benchmark/" + name + ".jpg") + } //try! await FaceManager.sharedInstance.processImage(inputImagePath: "input", sourceFacePath: "benchmark/Renate.jpg") } } diff --git a/kplayer/photo/SPhotoScrubber.swift b/kplayer/photo/SPhotoScrubber.swift index f5166ae..3565530 100644 --- a/kplayer/photo/SPhotoScrubber.swift +++ b/kplayer/photo/SPhotoScrubber.swift @@ -5,7 +5,6 @@ import Foundation import SwiftUI -import Haneke struct SPhotoScrubber: View { @ObservedObject var model: SPhotoModel diff --git a/kplayer/util/AsyncImage.swift b/kplayer/util/AsyncImage.swift index f15bf39..15bea28 100644 --- a/kplayer/util/AsyncImage.swift +++ b/kplayer/util/AsyncImage.swift @@ -1,5 +1,4 @@ import SwiftUI -import Haneke struct AsyncImage: View { @StateObject private var item: MediaItem diff --git a/kplayer/util/HanekeFetchOperation.swift b/kplayer/util/HanekeFetchOperation.swift deleted file mode 100644 index f59184a..0000000 --- a/kplayer/util/HanekeFetchOperation.swift +++ /dev/null @@ -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) }) - - } - } - -}