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) })
-
- }
- }
-
-}