比较提交
3
次代码提交
| 作者 | SHA1 | 提交日期 | |
|---|---|---|---|
|
|
8a70d38b7c | ||
|
|
767bba8ac1 | ||
|
|
47f6e55ecd |
没有与搜索条件相匹配的文件
+2
@@ -42,3 +42,5 @@ reference-binaries/
|
||||
!releases/1.2.0/*.pkg
|
||||
|
||||
!releases/1.3.1/*.pkg
|
||||
|
||||
!releases/1.3.2/*.pkg
|
||||
+48
-48
@@ -8,7 +8,7 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
6945FC8F3088B7220013E108 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6945FC8E3088B7220013E108 /* Cocoa.framework */; };
|
||||
6945FC973088B7220013E108 /* beatygril XPC Service.pluginkit in Embed PlugIns */ = {isa = PBXBuildFile; fileRef = 6945FC963088B7220013E108 /* beatygril XPC Service.pluginkit */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
6945FC973088B7220013E108 /* FCP-BeautyGirl XPC Service.pluginkit in Embed PlugIns */ = {isa = PBXBuildFile; fileRef = 6945FC963088B7220013E108 /* FCP-BeautyGirl XPC Service.pluginkit */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
6945FC9B3088B7220013E108 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6945FC9A3088B7220013E108 /* Foundation.framework */; };
|
||||
6945FC9D3088B7220013E108 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6945FC9C3088B7220013E108 /* AppKit.framework */; };
|
||||
6945FC9F3088B7220013E108 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6945FC9E3088B7220013E108 /* Accelerate.framework */; };
|
||||
@@ -22,14 +22,14 @@
|
||||
containerPortal = 6945FC833088B7220013E108 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 6945FC953088B7220013E108;
|
||||
remoteInfo = "XPC Service";
|
||||
remoteInfo = "FCP-BeautyGirl XPC Service";
|
||||
};
|
||||
6945FCA43088B7220013E108 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 6945FC833088B7220013E108 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 6945FC953088B7220013E108;
|
||||
remoteInfo = "XPC Service";
|
||||
remoteInfo = "FCP-BeautyGirl XPC Service";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
@@ -39,16 +39,16 @@
|
||||
dstPath = "";
|
||||
dstSubfolder = PlugIns;
|
||||
files = (
|
||||
6945FC973088B7220013E108 /* beatygril XPC Service.pluginkit in Embed PlugIns */,
|
||||
6945FC973088B7220013E108 /* FCP-BeautyGirl XPC Service.pluginkit in Embed PlugIns */,
|
||||
);
|
||||
name = "Embed PlugIns";
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
6945FC8B3088B7220013E108 /* beatygril.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = beatygril.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6945FC8B3088B7220013E108 /* FCP-BeautyGirl.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FCP-BeautyGirl.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6945FC8E3088B7220013E108 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
6945FC963088B7220013E108 /* beatygril XPC Service.pluginkit */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "beatygril XPC Service.pluginkit"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6945FC963088B7220013E108 /* FCP-BeautyGirl XPC Service.pluginkit */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "FCP-BeautyGirl XPC Service.pluginkit"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6945FC9A3088B7220013E108 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
6945FC9C3088B7220013E108 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||
6945FC9E3088B7220013E108 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
|
||||
@@ -57,12 +57,12 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
6945FCCB3088B7220013E108 /* Exceptions for "beatygril" folder in "Wrapper Application" target */ = {
|
||||
6945FCCB3088B7220013E108 /* Exceptions for "FCP-BeautyGirl" folder in "Wrapper Application" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
"/Localized: Plugin/InfoPlist.strings",
|
||||
"/Localized: Plugin/Localizable.strings",
|
||||
Plugin/beatygrilPlugIn.m,
|
||||
Plugin/FCPBeautyGirlPlugIn.m,
|
||||
Plugin/Info.plist,
|
||||
Plugin/main.m,
|
||||
Plugin/PGBeautyRenderer.m,
|
||||
@@ -74,7 +74,7 @@
|
||||
);
|
||||
target = 6945FC8A3088B7220013E108 /* Wrapper Application */;
|
||||
};
|
||||
6945FCD33088B7220013E108 /* Exceptions for "beatygril" folder in "XPC Service" target */ = {
|
||||
6945FCD33088B7220013E108 /* Exceptions for "FCP-BeautyGirl" folder in "XPC Service" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
"/Localized: Wrapper Application/Credits.rtf",
|
||||
@@ -91,13 +91,13 @@
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
6945FCA63088B7220013E108 /* beatygril */ = {
|
||||
6945FCA63088B7220013E108 /* FCP-BeautyGirl */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
6945FCCB3088B7220013E108 /* Exceptions for "beatygril" folder in "Wrapper Application" target */,
|
||||
6945FCD33088B7220013E108 /* Exceptions for "beatygril" folder in "XPC Service" target */,
|
||||
6945FCCB3088B7220013E108 /* Exceptions for "FCP-BeautyGirl" folder in "Wrapper Application" target */,
|
||||
6945FCD33088B7220013E108 /* Exceptions for "FCP-BeautyGirl" folder in "XPC Service" target */,
|
||||
);
|
||||
path = beatygril;
|
||||
path = FCP-BeautyGirl;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
@@ -125,7 +125,7 @@
|
||||
6945FC823088B7220013E108 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6945FCA63088B7220013E108 /* beatygril */,
|
||||
6945FCA63088B7220013E108 /* FCP-BeautyGirl */,
|
||||
6945FC8D3088B7220013E108 /* Frameworks */,
|
||||
6945FC8C3088B7220013E108 /* Products */,
|
||||
);
|
||||
@@ -134,8 +134,8 @@
|
||||
6945FC8C3088B7220013E108 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6945FC8B3088B7220013E108 /* beatygril.app */,
|
||||
6945FC963088B7220013E108 /* beatygril XPC Service.pluginkit */,
|
||||
6945FC8B3088B7220013E108 /* FCP-BeautyGirl.app */,
|
||||
6945FC963088B7220013E108 /* FCP-BeautyGirl XPC Service.pluginkit */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -172,11 +172,11 @@
|
||||
6945FCA53088B7220013E108 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
6945FCA63088B7220013E108 /* beatygril */,
|
||||
6945FCA63088B7220013E108 /* FCP-BeautyGirl */,
|
||||
);
|
||||
name = "Wrapper Application";
|
||||
productName = "Wrapper Application";
|
||||
productReference = 6945FC8B3088B7220013E108 /* beatygril.app */;
|
||||
name = "FCP-BeautyGirl";
|
||||
productName = "FCP-BeautyGirl";
|
||||
productReference = 6945FC8B3088B7220013E108 /* FCP-BeautyGirl.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
6945FC953088B7220013E108 /* XPC Service */ = {
|
||||
@@ -192,11 +192,11 @@
|
||||
buildRules = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
6945FCA63088B7220013E108 /* beatygril */,
|
||||
6945FCA63088B7220013E108 /* FCP-BeautyGirl */,
|
||||
);
|
||||
name = "XPC Service";
|
||||
productName = "XPC Service";
|
||||
productReference = 6945FC963088B7220013E108 /* beatygril XPC Service.pluginkit */;
|
||||
name = "FCP-BeautyGirl XPC Service";
|
||||
productName = "FCP-BeautyGirl XPC Service";
|
||||
productReference = 6945FC963088B7220013E108 /* FCP-BeautyGirl XPC Service.pluginkit */;
|
||||
productType = "com.apple.product-type.bundle";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
@@ -216,7 +216,7 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 6945FC863088B7220013E108 /* Build configuration list for PBXProject "beatygril" */;
|
||||
buildConfigurationList = 6945FC863088B7220013E108 /* Build configuration list for PBXProject "FCP-BeautyGirl" */;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
@@ -316,15 +316,15 @@
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
INFOPLIST_FILE = beatygril/Plugin/Info.plist;
|
||||
INFOPLIST_FILE = FCP-BeautyGirl/Plugin/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
MACH_O_TYPE = mh_execute;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dayu.beatygril.fxplug;
|
||||
PRODUCT_NAME = "beatygril $(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "beatygril/Plugin/XPC Service-Bridging-Header.h";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dayu.fcpbeautygirl.fxplug;
|
||||
PRODUCT_NAME = "FCP-BeautyGirl XPC Service";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "FCP-BeautyGirl/Plugin/XPC Service-Bridging-Header.h";
|
||||
WRAPPER_EXTENSION = pluginkit;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -339,15 +339,15 @@
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
INFOPLIST_FILE = beatygril/Plugin/Info.plist;
|
||||
INFOPLIST_FILE = FCP-BeautyGirl/Plugin/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
MACH_O_TYPE = mh_execute;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dayu.beatygril.fxplug;
|
||||
PRODUCT_NAME = "beatygril $(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "beatygril/Plugin/XPC Service-Bridging-Header.h";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dayu.fcpbeautygirl.fxplug;
|
||||
PRODUCT_NAME = "FCP-BeautyGirl XPC Service";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "FCP-BeautyGirl/Plugin/XPC Service-Bridging-Header.h";
|
||||
WRAPPER_EXTENSION = pluginkit;
|
||||
};
|
||||
name = Release;
|
||||
@@ -355,7 +355,7 @@
|
||||
6945FCD13088B7220013E108 /* Debug configuration for PBXNativeTarget "Wrapper Application" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = "beatygril/Wrapper Application/SandboxEntitlements.entitlements";
|
||||
CODE_SIGN_ENTITLEMENTS = "FCP-BeautyGirl/Wrapper Application/SandboxEntitlements.entitlements";
|
||||
OTHER_LDFLAGS = ("$(inherited)", "-framework", CoreServices, );
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
@@ -363,17 +363,17 @@
|
||||
DEVELOPMENT_TEAM = B3W25FX5H5;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
INFOPLIST_FILE = "beatygril/Wrapper Application/Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dayu.beatygril;
|
||||
PRODUCT_NAME = beatygril;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "beatygril/Wrapper Application/Wrapper Application-Bridging-Header.h";
|
||||
INFOPLIST_FILE = "FCP-BeautyGirl/Wrapper Application/Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dayu.fcpbeautygirl;
|
||||
PRODUCT_NAME = FCP-BeautyGirl;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "FCP-BeautyGirl/Wrapper Application/Wrapper Application-Bridging-Header.h";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
6945FCD23088B7220013E108 /* Release configuration for PBXNativeTarget "Wrapper Application" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = "beatygril/Wrapper Application/SandboxEntitlements.entitlements";
|
||||
CODE_SIGN_ENTITLEMENTS = "FCP-BeautyGirl/Wrapper Application/SandboxEntitlements.entitlements";
|
||||
OTHER_LDFLAGS = ("$(inherited)", "-framework", CoreServices, );
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
@@ -381,14 +381,14 @@
|
||||
DEVELOPMENT_TEAM = B3W25FX5H5;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
INFOPLIST_FILE = "beatygril/Wrapper Application/Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dayu.beatygril;
|
||||
PRODUCT_NAME = beatygril;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "beatygril/Wrapper Application/Wrapper Application-Bridging-Header.h";
|
||||
INFOPLIST_FILE = "FCP-BeautyGirl/Wrapper Application/Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dayu.fcpbeautygirl;
|
||||
PRODUCT_NAME = FCP-BeautyGirl;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "FCP-BeautyGirl/Wrapper Application/Wrapper Application-Bridging-Header.h";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
6945FCD43088B7220013E108 /* Debug configuration for PBXProject "beatygril" */ = {
|
||||
6945FCD43088B7220013E108 /* Debug configuration for PBXProject "FCP-BeautyGirl" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ADDITIONAL_SDKS = /Library/Developer/SDKs/FxPlug.sdk;
|
||||
@@ -461,7 +461,7 @@
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
6945FCD53088B7220013E108 /* Release configuration for PBXProject "beatygril" */ = {
|
||||
6945FCD53088B7220013E108 /* Release configuration for PBXProject "FCP-BeautyGirl" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ADDITIONAL_SDKS = /Library/Developer/SDKs/FxPlug.sdk;
|
||||
@@ -531,11 +531,11 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
6945FC863088B7220013E108 /* Build configuration list for PBXProject "beatygril" */ = {
|
||||
6945FC863088B7220013E108 /* Build configuration list for PBXProject "FCP-BeautyGirl" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
6945FCD43088B7220013E108 /* Debug configuration for PBXProject "beatygril" */,
|
||||
6945FCD53088B7220013E108 /* Release configuration for PBXProject "beatygril" */,
|
||||
6945FCD43088B7220013E108 /* Debug configuration for PBXProject "FCP-BeautyGirl" */,
|
||||
6945FCD53088B7220013E108 /* Release configuration for PBXProject "FCP-BeautyGirl" */,
|
||||
);
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
文件已重命名,内容未更改。
+1
-1
@@ -2,7 +2,7 @@
|
||||
#import <FxPlug/FxPlugSDK.h>
|
||||
#import "PGBeautyTypes.h"
|
||||
|
||||
@interface beatygrilPlugIn : NSObject <FxTileableEffect, FxAnalyzer, FxCustomParameterViewHost_v2>
|
||||
@interface FCPBeautyGirlPlugIn : NSObject <FxTileableEffect, FxAnalyzer, FxCustomParameterViewHost_v2>
|
||||
@property (nonatomic, assign) id<PROAPIAccessing> apiManager;
|
||||
- (PGBeautySettings)settingsAtTime:(CMTime)time;
|
||||
- (void)analyzeForward;
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
#import "beatygrilPlugIn.h"
|
||||
#import "FCPBeautyGirlPlugIn.h"
|
||||
#import "PGParameterIDs.h"
|
||||
#import "PGBeautyRenderer.h"
|
||||
#import "PGFaceProcessor.h"
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
static os_log_t PGRenderLog(void) {
|
||||
static os_log_t log; static dispatch_once_t once;
|
||||
dispatch_once(&once, ^{ log=os_log_create("com.dayu.beatygril", "render"); });
|
||||
dispatch_once(&once, ^{ log=os_log_create("com.dayu.fcpbeautygirl", "render"); });
|
||||
return log;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
return [image imageByApplyingTransform:CGAffineTransformMakeTranslation(b.left,b.bottom)];
|
||||
}
|
||||
|
||||
@interface beatygrilPlugIn ()
|
||||
@interface FCPBeautyGirlPlugIn ()
|
||||
@property (strong) PGBeautyRenderer *beautyRenderer;
|
||||
@property (strong) PGFaceProcessor *faceProcessor;
|
||||
@property (strong) PGFaceProcessor *analysisFaceProcessor;
|
||||
@@ -99,7 +99,7 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
@property (strong) NSHashTable<PGInspectorView *> *inspectorViews;
|
||||
@end
|
||||
|
||||
@implementation beatygrilPlugIn
|
||||
@implementation FCPBeautyGirlPlugIn
|
||||
- (instancetype)initWithAPIManager:(id<PROAPIAccessing>)manager {
|
||||
if ((self=[super init])) {
|
||||
_apiManager=manager;
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.3.1</string>
|
||||
<string>1.3.2</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>16</string>
|
||||
<string>17</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string></string>
|
||||
<key>PlugInKit</key>
|
||||
@@ -57,7 +57,7 @@
|
||||
<array>
|
||||
<dict>
|
||||
<key>className</key>
|
||||
<string>beatygrilPlugIn</string>
|
||||
<string>FCPBeautyGirlPlugIn</string>
|
||||
<key>displayName</key>
|
||||
<string>大鱼人像美化</string>
|
||||
<key>group</key>
|
||||
@@ -72,7 +72,7 @@
|
||||
<key>uuid</key>
|
||||
<string>FB14B0E6-59B2-4836-916F-CD0C559E9241</string>
|
||||
<key>version</key>
|
||||
<string>1.3.1</string>
|
||||
<string>1.3.2</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>className</key>
|
||||
@@ -84,7 +84,7 @@
|
||||
<key>uuid</key>
|
||||
<string>E0E432C8-3D71-4ABA-B006-E7F519E06C87</string>
|
||||
<key>version</key>
|
||||
<string>1.3.1</string>
|
||||
<string>1.3.2</string>
|
||||
<key>protocolNames</key>
|
||||
<array>
|
||||
<string>FxOnScreenControl</string>
|
||||
文件已重命名,内容未更改。
文件已重命名,内容未更改。
文件已重命名,内容未更改。
文件已重命名,内容未更改。
+1
-1
@@ -5,7 +5,7 @@
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
static NSString * const PGFaceErrorDomain = @"media.dayu.PaintFCP.Face";
|
||||
static NSString * const PGFaceErrorDomain = @"com.dayu.fcpbeautygirl.face";
|
||||
// Contour origin / internal anchor, in the reference's recovered pin order.
|
||||
// The unused middle entry of each reference triplet is intentionally omitted.
|
||||
static const uint8_t PGThinPins[17][2] = {
|
||||
文件已重命名,内容未更改。
文件已重命名,内容未更改。
文件已重命名,内容未更改。
文件已重命名,内容未更改。
文件已重命名,内容未更改。
文件已重命名,内容未更改。
@@ -0,0 +1 @@
|
||||
"FCP-BeautyGirl::FxPlug Name" = "大鱼人像美化";
|
||||
@@ -0,0 +1 @@
|
||||
"FCP-BeautyGirl::FxPlug Description" = "Portrait smoothing, skin tone, face shaping and automatic face tracking.";
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// AppDelegate.h
|
||||
// beatygril
|
||||
// FCP-BeautyGirl
|
||||
//
|
||||
// Created by dayu on 2026/10/21.
|
||||
//
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
|
||||
@implementation AppDelegate
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
|
||||
self.window.title = @"大鱼人像美化 · 免费开源";
|
||||
self.window.title = @"FCP-BeautyGirl · 大鱼人像美化";
|
||||
[self.window setContentSize:NSMakeSize(800, 450)];
|
||||
self.window.styleMask &= ~NSWindowStyleMaskResizable;
|
||||
self.window.backgroundColor = [NSColor colorWithRed:0.94 green:0.92 blue:0.85 alpha:1];
|
||||
+7
-7
@@ -20,11 +20,11 @@
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||
<items>
|
||||
<menuItem title="beatygril" id="1Xt-HY-uBw">
|
||||
<menuItem title="FCP-BeautyGirl" id="1Xt-HY-uBw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="beatygril" systemMenu="apple" id="uQy-DD-JDr">
|
||||
<menu key="submenu" title="FCP-BeautyGirl" systemMenu="apple" id="uQy-DD-JDr">
|
||||
<items>
|
||||
<menuItem title="About beatygril" id="5kV-Vb-QxS">
|
||||
<menuItem title="About FCP-BeautyGirl" id="5kV-Vb-QxS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
|
||||
@@ -38,7 +38,7 @@
|
||||
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
||||
<menuItem title="Hide beatygril" keyEquivalent="h" id="Olw-nP-bQN">
|
||||
<menuItem title="Hide FCP-BeautyGirl" keyEquivalent="h" id="Olw-nP-bQN">
|
||||
<connections>
|
||||
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
|
||||
</connections>
|
||||
@@ -56,7 +56,7 @@
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
|
||||
<menuItem title="Quit beatygril" keyEquivalent="q" id="4sb-4s-VLi">
|
||||
<menuItem title="Quit FCP-BeautyGirl" keyEquivalent="q" id="4sb-4s-VLi">
|
||||
<connections>
|
||||
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
|
||||
</connections>
|
||||
@@ -669,7 +669,7 @@
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
|
||||
<items>
|
||||
<menuItem title="beatygril Help" keyEquivalent="?" id="FKE-Sm-Kum">
|
||||
<menuItem title="FCP-BeautyGirl Help" keyEquivalent="?" id="FKE-Sm-Kum">
|
||||
<connections>
|
||||
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
|
||||
</connections>
|
||||
@@ -679,7 +679,7 @@
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
<window title="beatygril" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
|
||||
<window title="FCP-BeautyGirl" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
|
||||
+4
-2
@@ -4,6 +4,8 @@
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>FCP-BeautyGirl · 大鱼人像美化</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
@@ -17,9 +19,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.3.1</string>
|
||||
<string>1.3.2</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>16</string>
|
||||
<string>17</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
文件已重命名,内容未更改。
文件已重命名,内容未更改。
文件已重命名,内容未更改。
文件已重命名,内容未更改。
文件已重命名,内容未更改。
@@ -1,4 +1,4 @@
|
||||
# 大鱼人像美化 · beatygril
|
||||
# 大鱼人像美化 · FCP-BeautyGirl
|
||||
|
||||
基于本仓库的 FxPlug4 初始工程制作的人像美化滤镜,支持 Motion 和 Final Cut Pro。源码使用 Objective-C、Vision、Core Image 与 Metal。
|
||||
|
||||
@@ -6,15 +6,17 @@
|
||||
|
||||
功能包括磨皮、肤色、细节、牙齿美白、眼睛提亮、九类脸型调整、自动人脸跟随和多人处理。原版 MSE18V2 的静态分析依据、近似实现与尚未对齐的部分见 [分析说明](docs/analysis/REA-分析与复刻说明.md)。本版尚未完成原版逐像素与复杂运动跟踪对照。
|
||||
|
||||
1.3.2(17)已统一工程、APP、插件类名和应用ID,正式安装为 `FCP-BeautyGirl.app` / `com.dayu.fcpbeautygirl`,嵌套插件ID为 `com.dayu.fcpbeautygirl.fxplug`。旧APP已迁移清理,滤镜UUID、参数编号和归档格式保持兼容。1901项自动回归及身份迁移检查通过;APP和含配套模板的PKG均已签名、公证、粘附票据和验证。更名后的旧工程重开仍由用户实测。
|
||||
|
||||
## 使用
|
||||
|
||||
正式安装路径为 `/Library/Plug-Ins/FxPlug/beatygril.app`。Motion 滤镜分类为「大鱼」,滤镜名「大鱼人像美化」。Final Cut Pro 在「效果 → 大鱼磨皮瘦脸 → 大鱼人像美化」中使用配套 Motion 效果,模板目录必须同时保留 `.moef` 与 `Drop Zone.tiff`。美化会随当前帧的人脸自动更新,不需要点击分析。界面以磨皮、美白、瘦脸三张卡片呈现,默认跟随主要人脸;多人模式对多脸变形。单项眼睛可暂时停用并保留数值,右侧箭头恢复默认;磨皮半径、饱和度、锐化和整体混合等收在「更多设置」。旧手动分析与调试参数仅保留隐藏兼容,实际输出统一使用实时自动跟随。
|
||||
正式安装路径为 `/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app`。Motion 滤镜分类为「大鱼」,滤镜名「大鱼人像美化」。Final Cut Pro 在「效果 → 大鱼磨皮瘦脸 → 大鱼人像美化」中使用配套 Motion 效果,模板目录必须同时保留 `.moef` 与 `Drop Zone.tiff`。美化会随当前帧的人脸自动更新,不需要点击分析。界面以磨皮、美白、瘦脸三张卡片呈现,默认跟随主要人脸;多人模式对多脸变形。单项眼睛可暂时停用并保留数值,右侧箭头恢复默认;磨皮半径、饱和度、锐化和整体混合等收在「更多设置」。旧手动分析与调试参数仅保留隐藏兼容,实际输出统一使用实时自动跟随。
|
||||
|
||||
1.3.0 的磨皮卡片依次提供「磨皮程度、微细锐化、五官保护、非肤色区保护」。两种保护默认 100%;降低五官保护让五官区域参与更多磨皮,降低非肤色区保护则扩大磨皮范围,均不扩大肤色提亮范围。微细锐化在局部支持区域恢复纹理;旧全局磨皮和全局锐化仍保留在更多设置,旧工程的参数与归档继续兼容。迁移证据、实现边界与验证见[新版磨皮参数迁移](docs/analysis/新版磨皮参数迁移.md)。1.3.0 的新增参数已随 1.3.1 一并提供。
|
||||
|
||||
1.3.1(16)修复 V 脸影响眼区的问题,改为四组下颌轮廓到外唇的局部采样,并对齐新目标的 0.065 强度映射。1901 项自动检查通过,APP 与本机 PKG 工坊制作的安装包均已签名、公证和验证。正式 APP 已安装;新效果的原生宿主视觉仍由用户实测。原理与边界见 [V 脸下颌区域修复](docs/analysis/V脸下颌区域修复.md)。
|
||||
|
||||
当前 [1.3.1 正式下载](https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.1) 与 [大鱼文章](https://dayu.media/15593/) 已更新,安装包包含「大鱼磨皮瘦脸」分类的配套效果及资源,源码快照也已同步。
|
||||
当前 [1.3.2 正式下载](https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.2) 与 [大鱼文章](https://dayu.media/15593/) 是本版发布入口,安装包包含「大鱼磨皮瘦脸」分类的配套效果及资源,源码快照也已同步。
|
||||
|
||||
## 开发和恢复
|
||||
|
||||
@@ -22,9 +24,9 @@
|
||||
|
||||
```sh
|
||||
./scripts/test.sh
|
||||
PAINTFCP_HOST_ASAN=1 ./scripts/test-host.sh
|
||||
PAINTFCP_OSC_ASAN=1 ./scripts/test-osc.sh
|
||||
xcodebuild -project beatygril/beatygril.xcodeproj -scheme 'Wrapper Application' -configuration Release -derivedDataPath /tmp/paintfcp-build -destination 'generic/platform=macOS' build
|
||||
FCP_BEAUTYGIRL_HOST_ASAN=1 ./scripts/test-host.sh
|
||||
FCP_BEAUTYGIRL_OSC_ASAN=1 ./scripts/test-osc.sh
|
||||
xcodebuild -project FCP-BeautyGirl/FCP-BeautyGirl.xcodeproj -scheme 'FCP-BeautyGirl' -configuration Release -derivedDataPath /tmp/fcp-beautygirl-build -destination 'generic/platform=macOS' build
|
||||
```
|
||||
|
||||
工程从 `/Library/Developer/SDKs/FxPlug.sdk` 读取头文件与链接信息,从 `/Library/Developer/Frameworks` 嵌入可运行的 Apple 框架。构建默认使用团队 B3W25FX5H5 的 Developer ID Application 身份;正式交付使用 `python3 scripts/deliver.py` 完成公证、粘附和安装;安装前先关闭 Motion 与 FCP,脚本在检测到宿主仍运行时保留原安装版并停止。原生验收完成后继续脚本的清理阶段,清除临时注册与 APP 副本。
|
||||
|
||||
+10
@@ -1,5 +1,8 @@
|
||||
# 恢复说明
|
||||
|
||||
当前产品身份自1.3.2(17)统一为FCP-BeautyGirl:工程 `FCP-BeautyGirl/FCP-BeautyGirl.xcodeproj`、scheme `FCP-BeautyGirl`、APP `/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app`、主ID `com.dayu.fcpbeautygirl`、插件ID `com.dayu.fcpbeautygirl.fxplug`。签名源码 `767bba8ac11e9bf9268a1e02d1a06d3c120c46aa`,APP公证Accepted `559eedf0-048f-4d0e-b013-6a187695f2da`,已安装并迁移清理旧APP;程序与滤镜参数、UUID保持兼容。中央身份定义见product.json,旧名称只用于经过ID、签名团队和滤镜UUID验证的迁移。下文历史版本保留各自当时的名称与路径。
|
||||
|
||||
|
||||
- 大鱼备份远程(origin):`https://dayu.media/git/dayu/paintfcp-fxplug.git`
|
||||
- GitHub 公开远程(github):`https://github.com/dayumeida/FCP-BeautyGirl.git`
|
||||
- 本地项目根:`/Users/dayu/Documents/PaintFCP`
|
||||
@@ -130,3 +133,10 @@ GitHub于2026-10-22更正仓库名为 `FCP-BeautyGirl`,远端为 `https://gith
|
||||
|
||||
|
||||
GitHub用户名现为 `dayumeida`,账号ID117798680保持一致。当前公开远端 `https://github.com/dayumeida/FCP-BeautyGirl.git` 的恢复检查点 `2faf7de77929cf7707645ebb38095b03d5729647` 已通过匿名独立克隆、241文件字节/执行位和strict/full fsck核验,产品源码与账号更名前一致。两个仓库的全部分支/标签及Release附件均保留;当前文章、本地github远端和仓库目录表已同步。回执见 `docs/verification/github/username-recovery-20261022.json`。
|
||||
|
||||
|
||||
## 1.3.2 统一产品身份
|
||||
|
||||
发行入口为 `v1.3.2`;正式 APP 源码为 `767bba8ac11e9bf9268a1e02d1a06d3c120c46aa`。新机器按 `product.json` 的工程、scheme 与路径恢复,运行 `python3 scripts/deliver.py --wait-for-hosts --user-tests`;旧产品先验证 Bundle ID、签名团队和滤镜 UUID,再迁移并支持失败回滚。最终包为 `releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg`,SHA-256 `20a88dacf68cc8812f5af86cfe94c1c0614414a68a019eaeac7ca9d078165ae1`,签名、公证 Accepted `4a831775-2317-4372-a4cc-102415657ea3`、票据和 Gatekeeper 均通过。正式 APP 已安装并清理旧身份。1901 项自动回归、8 项 APP 迁移、8 项最终 PKG 身份脚本迁移与模板隔离测试通过;未运行真实 installer,原生旧工程重开仍待用户检查。
|
||||
|
||||
源码包须从发行标签导出,排除 PKG 二进制并加入 SOURCE-SNAPSHOT.json。发布与独立克隆恢复回执位于 `docs/verification/v1.3.2/`,不包含用户人脸素材、签名私钥或公证凭据。历史标签和旧版本安装包保留,不覆盖。
|
||||
@@ -1,3 +0,0 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
"beatygril::FxPlug Name" = "beatygril";
|
||||
@@ -1,4 +0,0 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
"beatygril::FxPlug Description" = "A helpful description of\nwhat this plugin does.";
|
||||
|
||||
+7
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
公开源码:https://github.com/dayumeida/FCP-BeautyGirl
|
||||
|
||||
正式安装包:https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.1
|
||||
正式安装包:https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.2
|
||||
|
||||
本地 `github` 远端指向上述 GitHub 仓库,`origin` 继续指向大鱼仓库,`main` 仍跟随 `origin/main`。当前项目的完整提交历史、main 和全部现有版本标签已推送,两端引用与本地一致;未改写历史或覆盖标签。后续更新需要分别推送到 origin 和 github,不会自动定时同步。
|
||||
|
||||
@@ -23,7 +23,7 @@ git switch --detach 356683b41af702d6c0143ad7fa5f540d752d6e14
|
||||
源代码编写署名:代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。
|
||||
|
||||
|
||||
## 1.3.1 当前发行与恢复
|
||||
## 1.3.1 发行与恢复
|
||||
|
||||
main已快进纳入1.3.0磨皮参数和1.3.1的V脸下颌修复;origin和github的main及v1.3.1引用已回读核验。v1.3.1固定完整SHA为 `b35b4a5d1ca252d2f83c798613560a768274de3c`,签名APP源码为 `7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f`。独立匿名GitHub克隆232文件字节与执行位一致、strict/full fsck和恢复PKG签名/票据通过。无LFS或子模块。后续main仅补充发布记录,不改写标签。
|
||||
|
||||
@@ -40,3 +40,8 @@ GitHub仓库从 `FAMEDaYu/FCP-BeatyGril` 更名为 `FAMEDaYu/FCP-BeautyGirl`,
|
||||
## 2026-10-22 用户名更改
|
||||
|
||||
用户将GitHub登录名从 `FAMEDaYu` 改为 `dayumeida`,账号ID117798680保持不变。两个公开仓库迁移到dayumeida名下:FCP-BeautyGirl(ID1428817365)和FCPX-(ID904215594)。更名前后全部分支/标签SHA及Release/附件ID、名称、大小一致。当前项目github远端为 `https://github.com/dayumeida/FCP-BeautyGirl.git`,origin及main上游保持原大鱼仓库;文章15593、活动文档和Release说明更新为新地址。正式版本标签、公证APP/PKG和源码快照保留原始字节,不为账号更名重新构建。个人主页新地址为 `https://github.com/dayumeida`,旧个人主页不提供重定向;仓库旧地址由GitHub重定向。历史验证回执保留当时用户名。账号更名核验见verification/github/username-20261022.json。
|
||||
|
||||
|
||||
## 1.3.2 产品身份统一
|
||||
|
||||
发行标签 `v1.3.2`,正式 APP 签名源码 `767bba8ac11e9bf9268a1e02d1a06d3c120c46aa`。新工程和 APP 名为 FCP-BeautyGirl,应用标识及安装路径见 product.json;旧身份仅保留经过验证的迁移入口。GitHub、大鱼 Release 和文章 15593 使用新名称安装包及源码快照,历史发行资料不改写。当前交付与发布核验保存在 verification/v1.3.2。
|
||||
@@ -0,0 +1,5 @@
|
||||
Beauty renderer: 57 checks, 0 failures (Metal).
|
||||
2026-10-22 18:30:09.786 face-tests[35177:8446323] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
Face processing: 116 checks passed
|
||||
2026-10-22 18:30:23.734 slim-tests[35267:8446747] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
PASS: 1033 slim alignment checks; maximum UV oracle error 1.09828022e-05
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"status": "installed_user_qa_pending",
|
||||
"bundleIdentifier": "com.dayu.fcpbeautygirl",
|
||||
"installationPath": "/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app",
|
||||
"options": {
|
||||
"waitForHosts": true,
|
||||
"userTests": true
|
||||
},
|
||||
"nativeEffectQA": {
|
||||
"status": "pending_user",
|
||||
"performedByScript": false
|
||||
},
|
||||
"source": {
|
||||
"commit": "767bba8ac11e9bf9268a1e02d1a06d3c120c46aa",
|
||||
"trackedWorktreeChanges": []
|
||||
},
|
||||
"updatedAt": "2026-10-22T10:38:29.383549+00:00",
|
||||
"version": "1.3.2",
|
||||
"build": "17",
|
||||
"stagingSignature": "Executable=/private/tmp/fcp-beautygirl-release/Build/Products/Release/FCP-BeautyGirl.app/Contents/MacOS/FCP-BeautyGirl\nIdentifier=com.dayu.fcpbeautygirl\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=482 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=28f0e84981124d854690d9be80e8ecb7a855ed02\nCandidateCDHashFull sha256=28f0e84981124d854690d9be80e8ecb7a855ed02e3de84009119fc616e22aecd\nHash choices=sha256\nCMSDigest=28f0e84981124d854690d9be80e8ecb7a855ed02e3de84009119fc616e22aecd\nCMSDigestType=2\nCDHash=28f0e84981124d854690d9be80e8ecb7a855ed02\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 18:38:05\nInfo.plist entries=23\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=5\nInternal requirements count=1 size=184\nTotal signatures=1\nChosen signature=1\n",
|
||||
"notarization": {
|
||||
"id": "559eedf0-048f-4d0e-b013-6a187695f2da",
|
||||
"message": "Processing complete",
|
||||
"status": "Accepted"
|
||||
},
|
||||
"signingVerified": true,
|
||||
"stapleValidated": true,
|
||||
"stagingPluginUnregistered": true,
|
||||
"runningHosts": [],
|
||||
"installedSignature": "Executable=/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app/Contents/MacOS/FCP-BeautyGirl\nIdentifier=com.dayu.fcpbeautygirl\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=482 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=28f0e84981124d854690d9be80e8ecb7a855ed02\nCandidateCDHashFull sha256=28f0e84981124d854690d9be80e8ecb7a855ed02e3de84009119fc616e22aecd\nHash choices=sha256\nCMSDigest=28f0e84981124d854690d9be80e8ecb7a855ed02e3de84009119fc616e22aecd\nCMSDigestType=2\nCDHash=28f0e84981124d854690d9be80e8ecb7a855ed02\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 18:38:05\nNotarization Ticket=stapled\nInfo.plist entries=23\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=5\nInternal requirements count=1 size=184\nTotal signatures=1\nChosen signature=1\n",
|
||||
"installedSignatureVerified": true,
|
||||
"installedStapleValidated": true,
|
||||
"gatekeeperAccepted": true,
|
||||
"identityMigration": {
|
||||
"legacyInstallationsRemoved": [
|
||||
"/Library/Plug-Ins/FxPlug/beatygril.app"
|
||||
],
|
||||
"filterUUIDPreserved": "FB14B0E6-59B2-4836-916F-CD0C559E9241"
|
||||
},
|
||||
"pluginDiscovery": {
|
||||
"bundleIdentifier": "com.dayu.fcpbeautygirl.fxplug",
|
||||
"formalPath": "/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app/Contents/PlugIns/FCP-BeautyGirl XPC Service.pluginkit",
|
||||
"output": " com.dayu.fcpbeautygirl.fxplug(1.1)\t6C990AD2-B58E-4C2F-91A4-927849E7AA10\t2026-10-22 10:38:29 +0000\t/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app/Contents/PlugIns/FCP-BeautyGirl XPC Service.pluginkit\n (1 plug-in)\n"
|
||||
},
|
||||
"removedGeneratedPaths": [
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/legacy-beatygril.app",
|
||||
"/tmp/fcp-beautygirl-release",
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/notarization.zip"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
2026-10-22 18:30:11.816 host-tests[35227:8446515] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
Default IOSurface maximum byte difference: 0
|
||||
Alternate destination tag maximum byte difference: 0
|
||||
Anatomy default 0/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 0/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 1/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 1/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy big-eye 0/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 0/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 1/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 1/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy chin-positive 0/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 0/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 1/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 1/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-negative 0/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 0/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 1/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 1/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy short-positive 0/0 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0
|
||||
Anatomy short-positive 0/1 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0
|
||||
Anatomy short-positive 1/0 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0
|
||||
Anatomy short-positive 1/1 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0
|
||||
Anatomy short-negative 0/0 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0
|
||||
Anatomy short-negative 0/1 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0
|
||||
Anatomy short-negative 1/0 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0
|
||||
Anatomy short-negative 1/1 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0
|
||||
Anatomy jaw 0/0 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0
|
||||
Anatomy jaw 0/1 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0
|
||||
Anatomy jaw 1/0 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0
|
||||
Anatomy jaw 1/1 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0
|
||||
Anatomy v-face 0/0 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0
|
||||
Anatomy v-face 0/1 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0
|
||||
Anatomy v-face 1/0 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0
|
||||
Anatomy v-face 1/1 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0
|
||||
Legacy fallback upright comparison: max=2 mean=0.0010357
|
||||
Host adapter: 370 checks, 0 failures
|
||||
@@ -0,0 +1 @@
|
||||
Inspector: 227 checks, 0 failures
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"status": "verified",
|
||||
"installedProduct": {
|
||||
"path": "/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app",
|
||||
"bundleId": "com.dayu.fcpbeautygirl",
|
||||
"version": "1.3.2"
|
||||
},
|
||||
"signatureAllArchitecturesVerified": true,
|
||||
"scopedInventory": {
|
||||
"roots": [
|
||||
"/Applications",
|
||||
"/Library/Plug-Ins/FxPlug",
|
||||
"/Users/dayu/Library/Plug-Ins/FxPlug",
|
||||
"/Users/dayu/Documents/PaintFCP",
|
||||
"/Users/dayu/Desktop",
|
||||
"/Users/dayu/Downloads",
|
||||
"/Users/dayu/Library/Developer/Xcode/DerivedData",
|
||||
"/tmp/fcp-beautygirl-release",
|
||||
"/tmp/paintfcp-build-20261021"
|
||||
],
|
||||
"products": [
|
||||
{
|
||||
"path": "/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app",
|
||||
"bundleId": "com.dayu.fcpbeautygirl",
|
||||
"version": "1.3.2"
|
||||
}
|
||||
],
|
||||
"errors": [],
|
||||
"scope": "Listed roots only; not all mounted volumes"
|
||||
},
|
||||
"legacyPluginOriginalPathAbsent": true,
|
||||
"filterUUIDPreserved": "FB14B0E6-59B2-4836-916F-CD0C559E9241",
|
||||
"templateBytesUnchanged": true,
|
||||
"nativeOldProjectReopen": "pending_user"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
........
|
||||
----------------------------------------------------------------------
|
||||
Ran 8 tests in 2.805s
|
||||
|
||||
OK
|
||||
@@ -0,0 +1,5 @@
|
||||
........
|
||||
----------------------------------------------------------------------
|
||||
Ran 8 tests in 0.029s
|
||||
|
||||
OK
|
||||
@@ -0,0 +1,21 @@
|
||||
2026-10-22 18:30:08.818 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
|
||||
2026-10-22 18:30:08.826 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,180) surface=320x240 origin=0
|
||||
2026-10-22 18:30:09.311 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.8,0.3) setter=(1152,-432) readback=(0.8,0.3) mode=centered-pixels verified=1
|
||||
2026-10-22 18:30:09.311 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.7,0.4) objectBounds=(-1920,-1080,3840,2160) canvas=(152,78) surface=320x240 origin=0
|
||||
2026-10-22 18:30:09.359 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.8,0.3) setter=(0.8,0.3) readback=(0.12345,0.87654) mode=normalized verified=0
|
||||
2026-10-22 18:30:09.359 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.75,0.25) setter=(0.75,0.25) readback=(0.75,0.25) mode=normalized verified=1
|
||||
2026-10-22 18:30:09.360 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.75,0.25) objectBounds=(-100,-50,200,100) canvas=(160,60) surface=320x240 origin=0
|
||||
2026-10-22 18:30:09.409 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.625,0.375) setter=(0.625,0.375) readback=(0.625,0.375) mode=normalized verified=1
|
||||
2026-10-22 18:30:09.409 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.625,0.375) objectBounds=(-100,-50,200,100) canvas=(240,60) surface=320x240 origin=0
|
||||
2026-10-22 18:30:09.457 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
|
||||
2026-10-22 18:30:09.457 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(144,180) surface=320x240 origin=0
|
||||
2026-10-22 18:30:09.506 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-22 18:30:09.506 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0,1) objectBounds=(-100,-50,200,100) canvas=(100,140) surface=320x240 origin=0
|
||||
2026-10-22 18:30:09.574 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(nan,nan) surface=320x240 origin=0
|
||||
2026-10-22 18:30:09.628 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-22 18:30:09.629 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(inf,nan) surface=320x240 origin=0
|
||||
2026-10-22 18:30:09.689 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-22 18:30:09.690 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,nan) surface=320x240 origin=0
|
||||
2026-10-22 18:30:09.742 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-22 18:30:09.742 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
OSC adapter: 98 checks, 0 failures
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"status": "verified",
|
||||
"version": "1.3.2",
|
||||
"build": "17",
|
||||
"package": "releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg",
|
||||
"bytes": 4889706,
|
||||
"sha256": "20a88dacf68cc8812f5af86cfe94c1c0614414a68a019eaeac7ca9d078165ae1",
|
||||
"sourceApp": "/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app",
|
||||
"sourceCommit": "767bba8ac11e9bf9268a1e02d1a06d3c120c46aa",
|
||||
"motionSource": "/Users/dayu/Movies/Motion Templates.localized/Effects.localized/大鱼磨皮瘦脸",
|
||||
"motionFiles": [
|
||||
{
|
||||
"file": "大鱼人像美化/Drop Zone.tiff",
|
||||
"sha256": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e",
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"file": "大鱼人像美化/large.png",
|
||||
"sha256": "a02ddb255b1ae255c749bb837a759e2dee1c3b8ffb9bdc1167957761bd5a1b97",
|
||||
"locked": true
|
||||
},
|
||||
{
|
||||
"file": "大鱼人像美化/small.png",
|
||||
"sha256": "1552b838dd88f60dee0795e20aaca652f9c558ecefc93500e895fbff77c2a4d5",
|
||||
"locked": true
|
||||
},
|
||||
{
|
||||
"file": "大鱼人像美化/大鱼人像美化.moef",
|
||||
"sha256": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15",
|
||||
"locked": false
|
||||
}
|
||||
],
|
||||
"packager": "/Applications/PKG工坊.app/Contents/MacOS/PKG工坊",
|
||||
"productMigration": "PKG Forge base plus identity-checked backup/rollback block; final package freshly signed, notarized and stapled",
|
||||
"notarization": {
|
||||
"message": "Processing complete",
|
||||
"id": "4a831775-2317-4372-a4cc-102415657ea3",
|
||||
"status": "Accepted"
|
||||
},
|
||||
"realInstallerExecuted": false
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
/Users/dayu/Documents/PaintFCP/releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg: accepted
|
||||
source=Notarized Developer ID
|
||||
origin=Developer ID Installer: zhong luo (B3W25FX5H5)
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"message": "Processing complete",
|
||||
"id": "4a831775-2317-4372-a4cc-102415657ea3",
|
||||
"status": "Accepted"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
Package "FCP-BeautyGirl-1.3.2.pkg":
|
||||
Status: signed by a developer certificate issued by Apple for distribution
|
||||
Notarization: trusted by the Apple notary service
|
||||
Signed with a trusted timestamp on: 2026-10-22 10:40:28 +0000
|
||||
Certificate Chain:
|
||||
1. Developer ID Installer: zhong luo (B3W25FX5H5)
|
||||
Expires: 2027-02-01 22:12:15 +0000
|
||||
SHA256 Fingerprint:
|
||||
4D AC CD 04 A5 4C 7E F8 13 13 A2 ED D1 C2 C0 D0 29 EF FA BF 7B 53
|
||||
5A 43 C8 72 4D C4 0A A3 94 34
|
||||
------------------------------------------------------------------------
|
||||
2. Developer ID Certification Authority
|
||||
Expires: 2027-02-01 22:12:15 +0000
|
||||
SHA256 Fingerprint:
|
||||
7A FC 9D 01 A6 2F 03 A2 DE 96 37 93 6D 4A FE 68 09 0D 2D E1 8D 03
|
||||
F2 9C 88 CF B0 B1 BA 63 58 7F
|
||||
------------------------------------------------------------------------
|
||||
3. Apple Root CA
|
||||
Expires: 2035-02-09 21:40:36 +0000
|
||||
SHA256 Fingerprint:
|
||||
B0 B1 73 0E CB C7 FF 45 05 14 2C 49 F1 29 5E 6E DA 6B CA ED 7E 2C
|
||||
68 C5 BE 91 B5 A1 10 01 F0 24
|
||||
@@ -0,0 +1,5 @@
|
||||
productsign: using timestamp authority for signature
|
||||
productsign: signing product with identity "Developer ID Installer: zhong luo (B3W25FX5H5)" from keychain /Users/dayu/Library/Keychains/login.keychain-db
|
||||
productsign: adding certificate "Developer ID Certification Authority"
|
||||
productsign: adding certificate "Apple Root CA"
|
||||
productsign: Wrote signed product archive to /Users/dayu/Documents/PaintFCP/releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg
|
||||
@@ -0,0 +1,3 @@
|
||||
Processing: /Users/dayu/Documents/PaintFCP/releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg
|
||||
Processing: /Users/dayu/Documents/PaintFCP/releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg
|
||||
The staple and validate action worked!
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"package": "releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg",
|
||||
"bytes": 4889706,
|
||||
"sha256": "20a88dacf68cc8812f5af86cfe94c1c0614414a68a019eaeac7ca9d078165ae1",
|
||||
"version": "1.3.2",
|
||||
"build": "17",
|
||||
"app_files_compared": 34,
|
||||
"app_byte_and_symlink_equality": true,
|
||||
"template_files": {
|
||||
"大鱼人像美化.moef": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15",
|
||||
"Drop Zone.tiff": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e",
|
||||
"large.png": "a02ddb255b1ae255c749bb837a759e2dee1c3b8ffb9bdc1167957761bd5a1b97",
|
||||
"small.png": "1552b838dd88f60dee0795e20aaca652f9c558ecefc93500e895fbff77c2a4d5"
|
||||
},
|
||||
"locked_relative_paths": [
|
||||
"大鱼人像美化/large.png",
|
||||
"大鱼人像美化/small.png"
|
||||
],
|
||||
"script_sha256": {
|
||||
"preinstall": "b0562f7705c84a2f34e31df67a4f0d429e60083bab122dad99f56a4b266c0174",
|
||||
"postinstall": "d147ccab1c428659fd2b0ed60e77399d1eb5910d1049dcdcbdbfc88fd3824300"
|
||||
},
|
||||
"isolated_script_tests": [
|
||||
"fresh install",
|
||||
"replace old locked thumbnail",
|
||||
"partial copy failure restores old bytes and locks"
|
||||
],
|
||||
"isolation_changes": [
|
||||
"Console identity/home redirected to task fixture",
|
||||
"chown replaced with true",
|
||||
"FxPlug verification skipped; formal app signature and discovery verified separately"
|
||||
],
|
||||
"real_installer_execution": false,
|
||||
"app_and_template_installed": "APP installed through signed delivery workflow; formal template files matched the specified installed category, not changed by running this PKG",
|
||||
"fonts": "No editable third-party font dependency; promotional lettering baked into PNG. No optional font ZIP needed."
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"package": "releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg",
|
||||
"bytes": 4889706,
|
||||
"sha256": "20a88dacf68cc8812f5af86cfe94c1c0614414a68a019eaeac7ca9d078165ae1",
|
||||
"version": "1.3.2",
|
||||
"build": "17",
|
||||
"app_files_compared": 34,
|
||||
"app_byte_and_symlink_equality": true,
|
||||
"template_files": {
|
||||
"大鱼人像美化.moef": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15",
|
||||
"Drop Zone.tiff": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e",
|
||||
"large.png": "a02ddb255b1ae255c749bb837a759e2dee1c3b8ffb9bdc1167957761bd5a1b97",
|
||||
"small.png": "1552b838dd88f60dee0795e20aaca652f9c558ecefc93500e895fbff77c2a4d5"
|
||||
},
|
||||
"locked_relative_paths": [
|
||||
"大鱼人像美化/large.png",
|
||||
"大鱼人像美化/small.png"
|
||||
],
|
||||
"script_sha256": {
|
||||
"preinstall": "b0562f7705c84a2f34e31df67a4f0d429e60083bab122dad99f56a4b266c0174",
|
||||
"postinstall": "d147ccab1c428659fd2b0ed60e77399d1eb5910d1049dcdcbdbfc88fd3824300"
|
||||
},
|
||||
"isolated_script_tests": [
|
||||
"fresh install",
|
||||
"replace old locked thumbnail",
|
||||
"partial copy failure restores old bytes and locks"
|
||||
],
|
||||
"isolation_changes": [
|
||||
"Console identity/home redirected to task fixture",
|
||||
"chown replaced with true",
|
||||
"FxPlug verification skipped; formal app signature and discovery verified separately"
|
||||
],
|
||||
"real_installer_execution": false,
|
||||
"app_and_template_installed": "APP installed through signed delivery workflow; formal template files matched the specified installed category, not changed by running this PKG",
|
||||
"fonts": "No editable third-party font dependency; promotional lettering baked into PNG. No optional font ZIP needed."
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
识别到 2 个安装内容:
|
||||
- FCP-BeautyGirl.app
|
||||
类型:FxPlug 插件
|
||||
目录:/Library/Plug-Ins/FxPlug
|
||||
状态:已签名并公证,保留隔离属性,自动验证并修复注册;失败时打开 APP 所在目录
|
||||
- 大鱼磨皮瘦脸
|
||||
类型:FCP 效果
|
||||
目录:~/Movies/Motion Templates.localized/Effects.localized
|
||||
状态:非 APP,保留隔离属性
|
||||
|
||||
识别完成,未生成安装包。
|
||||
@@ -0,0 +1,27 @@
|
||||
识别到 2 个安装内容:
|
||||
- FCP-BeautyGirl.app
|
||||
类型:FxPlug 插件
|
||||
目录:/Library/Plug-Ins/FxPlug
|
||||
状态:已签名并公证,保留隔离属性,自动验证并修复注册;失败时打开 APP 所在目录
|
||||
- 大鱼磨皮瘦脸
|
||||
类型:FCP 效果
|
||||
目录:~/Movies/Motion Templates.localized/Effects.localized
|
||||
状态:非 APP,保留隔离属性
|
||||
|
||||
完成:/Users/dayu/Documents/PaintFCP/build/package-1.3.2/base/FCP-BeautyGirl-1.3.2.pkg
|
||||
|
||||
本次唯一标识:com.dayu.fcppackage.b20261022104001942.tt2yn7vc
|
||||
安装包版本:1.3.2(继承 FCP-BeautyGirl.app)
|
||||
[正在准备内容]
|
||||
正在整理 2 个内容项
|
||||
[正在生成安装包]
|
||||
正在构建组件包
|
||||
[正在签名]
|
||||
正在使用 Developer ID Installer 签名
|
||||
[正在等待 Apple 公证]
|
||||
已提交 Apple,公证通常需要几分钟
|
||||
[正在粘附公证票据]
|
||||
公证已通过,正在粘附票据
|
||||
[正在验证结果]
|
||||
正在检查签名和安装包结构
|
||||
签名、包结构与公证票据验证通过
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"baseline": "d1137edd836cc360c53d21c104a06f2435382441",
|
||||
"signedSource": "767bba8ac11e9bf9268a1e02d1a06d3c120c46aa",
|
||||
"renderAndStateFilesCompared": [
|
||||
"PGFaceSelectOSC.m",
|
||||
"PGParameterIDs.h",
|
||||
"PGInspectorState.m",
|
||||
"PGBeautyTypes.h",
|
||||
"PGBeautyRenderer.m",
|
||||
"PGFaceProcessor.h",
|
||||
"FCPBeautyGirlPlugIn.h",
|
||||
"PGInspectorView.m",
|
||||
"main.m",
|
||||
"PGFaceProcessor.m",
|
||||
"PGBeautyRenderer.h",
|
||||
"PGInspectorState.h",
|
||||
"FCPBeautyGirlPlugIn.m",
|
||||
"PGInspectorView.h"
|
||||
],
|
||||
"normalization": "Only entry class/header names, log subsystem and NSError domain differ; pixel and archived-state logic bytes unchanged.",
|
||||
"regressionChecks": 1901,
|
||||
"migrationUnitScenarios": 8,
|
||||
"installerMigrationScenarios": "7 runtime scenarios passed; final package-block check runs after build."
|
||||
}
|
||||
+16
-16
@@ -2,7 +2,7 @@
|
||||
|
||||
更新时间:2026-10-22,Asia/Shanghai。项目根目录:`/Users/dayu/Documents/PaintFCP`。
|
||||
|
||||
当前正式 APP 为 **1.3.1(16)**,通用 Release、正式签名、Apple公证、票据、Gatekeeper和正式路径注册均已核验,临时APP副本已清理。1901项自动回归及1561项重叠真实帧几何ASan通过,V脸和新磨皮参数的最终宿主视觉仍由用户实测。签名源码 `7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f`;本次PKG由本机PKG工坊CLI打包,包含用户指定已安装的「大鱼磨皮瘦脸」分类及4项模板资源。文章15593、GitHub和大鱼Release均已更新1.3.1,main与标签已同步;线上下载和恢复的具体核验边界见本版本回执。
|
||||
当前正式 APP 为 **1.3.2(17)**,工程、APP、插件服务及应用标识统一为 FCP-BeautyGirl。通用 Release、正式签名、Apple 公证、票据、Gatekeeper 和正式路径注册均已核验,旧 APP 与临时副本已清理。签名源码 `767bba8ac11e9bf9268a1e02d1a06d3c120c46aa`,发行标签 `v1.3.2`。1901 项自动回归、8 项 APP 身份迁移、8 项最终安装脚本身份迁移及模板隔离测试通过。滤镜 UUID、参数编号和归档保持原格式;新身份下的旧工程重开与最终原生视觉仍由用户实测。安装包基础由本机 PKG 工坊 CLI 生成,加入身份迁移后重新签名公证,包含指定已安装的「大鱼磨皮瘦脸」分类四项资源。文章 15593、GitHub 与大鱼 Release 沿用原入口,发布同步结果见本版回执。
|
||||
|
||||
代码由 **OpenAI + Codex 全程编写,站长仅参与调试和测试**。文章和仓库须保留这个表述,不使用“站长原创代码”等说法。免费版本无需账号、激活码或付费授权;“自动注册”指系统插件注册。
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
| 入口 | 用途 |
|
||||
| --- | --- |
|
||||
| [1.3.1 交付回执](verification/v1.3.1/delivery-receipt.json) | 当前安装版、签名源码、公证、清理及原生视觉待测范围 |
|
||||
| [1.3.2 交付回执](verification/v1.3.2/delivery-receipt.json) | 当前安装版、签名源码、公证、清理及原生视觉待测范围 |
|
||||
| [README](../README.md) | 产品介绍、使用与版本变化 |
|
||||
| [恢复说明](../RECOVERY.md) | 精确源码节点、依赖、远端恢复记录 |
|
||||
| [GitHub 同步说明](GitHub同步说明.md) | 两个远端、公开克隆及 Release 验证 |
|
||||
@@ -30,12 +30,12 @@
|
||||
| 项目 | 当前值 |
|
||||
| --- | --- |
|
||||
| 显示名称 | 大鱼人像美化 |
|
||||
| Xcode 产品名 | `beatygril`,保留现有拼写 |
|
||||
| APP 版本 | `1.3.1` / `16`,arm64 与 x86_64 通用版本 |
|
||||
| 唯一正式 APP | `/Library/Plug-Ins/FxPlug/beatygril.app` |
|
||||
| 主 Bundle ID | `com.dayu.beatygril` |
|
||||
| 嵌套插件 | `Contents/PlugIns/beatygril XPC Service.pluginkit` |
|
||||
| 插件 Bundle ID | `com.dayu.beatygril.fxplug` |
|
||||
| Xcode 产品名 | `FCP-BeautyGirl` |
|
||||
| APP 版本 | `1.3.2` / `17`,arm64 与 x86_64 通用版本 |
|
||||
| 唯一正式 APP | `/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app` |
|
||||
| 主 Bundle ID | `com.dayu.fcpbeautygirl` |
|
||||
| 嵌套插件 | `Contents/PlugIns/FCP-BeautyGirl XPC Service.pluginkit` |
|
||||
| 插件 Bundle ID | `com.dayu.fcpbeautygirl.fxplug` |
|
||||
| 滤镜 UUID | `FB14B0E6-59B2-4836-916F-CD0C559E9241` |
|
||||
| 自定义面板参数 ID | `210` |
|
||||
| FCP 效果分类 | 大鱼磨皮瘦脸 → 大鱼人像美化 |
|
||||
@@ -54,13 +54,13 @@
|
||||
|
||||
## 源码结构
|
||||
|
||||
Xcode 工程:`beatygril/beatygril.xcodeproj`。外层 scheme 为 `Wrapper Application`,插件 target 为 `XPC Service`。
|
||||
Xcode 工程:`FCP-BeautyGirl/FCP-BeautyGirl.xcodeproj`。外层 scheme 为 `FCP-BeautyGirl`,插件 target 为 `FCP-BeautyGirl XPC Service`。
|
||||
|
||||
下表插件文件均位于 `beatygril/beatygril/Plugin/`。
|
||||
下表插件文件均位于 `FCP-BeautyGirl/FCP-BeautyGirl/Plugin/`。
|
||||
|
||||
| 文件或目录 | 职责与修改入口 |
|
||||
| --- | --- |
|
||||
| `beatygrilPlugIn.m` | FxPlug 参数、宿主 action、图像适配、渲染状态与整体混合 |
|
||||
| `FCPBeautyGirlPlugIn.m` | FxPlug 参数、宿主 action、图像适配、渲染状态与整体混合 |
|
||||
| `PGFaceProcessor.m` | Vision 检测、76 点、自动跟随、几何稳定、局部变形和几何蒙版 |
|
||||
| `PGBeautyRenderer.m` | 磨皮、肤色、细节、牙齿和眼睛等外观处理 |
|
||||
| `PGInspectorView.m` | AppKit 卡片、滑杆、数值框、开关、重置和连续提交 |
|
||||
@@ -120,9 +120,9 @@ Xcode 工程:`beatygril/beatygril.xcodeproj`。外层 scheme 为 `Wrapper Appl
|
||||
|
||||
```sh
|
||||
./scripts/test.sh
|
||||
PAINTFCP_HOST_ASAN=1 ./scripts/test-host.sh
|
||||
PAINTFCP_OSC_ASAN=1 ./scripts/test-osc.sh
|
||||
PAINTFCP_INSPECTOR_ASAN=1 ./scripts/test-inspector.sh
|
||||
FCP_BEAUTYGIRL_HOST_ASAN=1 ./scripts/test-host.sh
|
||||
FCP_BEAUTYGIRL_OSC_ASAN=1 ./scripts/test-osc.sh
|
||||
FCP_BEAUTYGIRL_INSPECTOR_ASAN=1 ./scripts/test-inspector.sh
|
||||
python3 scripts/inspector-template.py audit 'Templates/大鱼人像美化/大鱼人像美化.moef'
|
||||
```
|
||||
|
||||
@@ -157,7 +157,7 @@ python3 scripts/inspector-template.py audit 'Templates/大鱼人像美化/大鱼
|
||||
| 资源 | 文件 |
|
||||
| --- | --- |
|
||||
| 项目封面 | `Assets/Artwork/portrait-cover.png` |
|
||||
| APP 说明图 | `beatygril/beatygril/Wrapper Application/Resources/LauncherInfo.png` |
|
||||
| APP 说明图 | `FCP-BeautyGirl/FCP-BeautyGirl/Wrapper Application/Resources/LauncherInfo.png` |
|
||||
| 大缩略图 | `Templates/大鱼人像美化/large.png`,640×360 |
|
||||
| 小缩略图 | `Templates/大鱼人像美化/small.png`,192×108 |
|
||||
| 生成记录 | `docs/design/artwork-manifest.json`、`artwork-prompt.json` |
|
||||
@@ -183,7 +183,7 @@ ls -lO "$HOME/Movies/Motion Templates.localized/Effects.localized/大鱼磨皮
|
||||
python3 scripts/deliver.py
|
||||
```
|
||||
|
||||
脚本获取 `/Users/dayu/.codex/app-delivery-locks/com.dayu.beatygril.lock`,使用固定暂存 `/tmp/paintfcp-build-20261021`,记录到 `build/delivery/`。它构建通用 Release,由内到外签名 Framework、嵌套插件、外层 APP,验证严格签名,提交公证并要求 `Accepted`,粘附并验证票据,等待宿主退出后替换唯一正式路径,检查 Gatekeeper 与 PluginKit。默认停在 `HOST_VALIDATION_PENDING`,完成实际宿主验收及副本盘点后才回车进入清理。
|
||||
脚本同时获取新ID与product.json所列旧ID的共享交付锁,主锁为 `/Users/dayu/.codex/app-delivery-locks/com.dayu.fcpbeautygirl.lock`,使用固定暂存 `/tmp/fcp-beautygirl-release`,记录到 `build/delivery/`。它构建通用 Release,由内到外签名 Framework、嵌套插件、外层 APP,验证严格签名,提交公证并要求 `Accepted`,粘附并验证票据,等待宿主退出后替换唯一正式路径,检查 Gatekeeper 与 PluginKit。默认停在 `HOST_VALIDATION_PENDING`,完成实际宿主验收及副本盘点后才回车进入清理。
|
||||
|
||||
| 选项 | 用途 |
|
||||
| --- | --- |
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "FCP-BeautyGirl",
|
||||
"version": "1.3.2",
|
||||
"build": "17",
|
||||
"bundleIdentifier": "com.dayu.fcpbeautygirl",
|
||||
"pluginName": "FCP-BeautyGirl XPC Service.pluginkit",
|
||||
"pluginBundleIdentifier": "com.dayu.fcpbeautygirl.fxplug",
|
||||
"pluginClass": "FCPBeautyGirlPlugIn",
|
||||
"sourceRoot": "FCP-BeautyGirl",
|
||||
"sourceDirectory": "FCP-BeautyGirl/FCP-BeautyGirl",
|
||||
"xcodeProject": "FCP-BeautyGirl/FCP-BeautyGirl.xcodeproj",
|
||||
"scheme": "FCP-BeautyGirl",
|
||||
"installationPath": "/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app",
|
||||
"filterUUID": "FB14B0E6-59B2-4836-916F-CD0C559E9241",
|
||||
"teamIdentifier": "B3W25FX5H5",
|
||||
"legacyProducts": [
|
||||
{
|
||||
"name": "beatygril",
|
||||
"bundleIdentifier": "com.dayu.beatygril",
|
||||
"pluginName": "beatygril XPC Service.pluginkit",
|
||||
"pluginBundleIdentifier": "com.dayu.beatygril.fxplug",
|
||||
"installationPath": "/Library/Plug-Ins/FxPlug/beatygril.app"
|
||||
}
|
||||
]
|
||||
}
|
||||
二进制文件未显示。
@@ -0,0 +1,19 @@
|
||||
# FCP-BeautyGirl · 大鱼人像美化 1.3.2(17)
|
||||
|
||||
统一工程、APP、插件服务、源码类名和应用标识的拼写。主 Bundle ID 为 `com.dayu.fcpbeautygirl`,插件 ID 为 `com.dayu.fcpbeautygirl.fxplug`,唯一正式安装路径为 `/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app`。
|
||||
|
||||
安装器核对旧版的 Bundle ID、签名团队和滤镜 UUID 后迁移旧 APP;失败时恢复旧版,避免新旧服务并存。滤镜 UUID、参数编号和面板归档保留,渲染逻辑沿用 1.3.1。1901 项自动回归、8 项 APP 迁移测试、8 项安装器身份迁移测试,以及模板初装、更新与失败回滚隔离检查通过。更名后的旧工程重开与原生画面仍需用户实测。
|
||||
|
||||
- 文章:https://dayu.media/15593/
|
||||
- GitHub:https://github.com/dayumeida/FCP-BeautyGirl
|
||||
- 大鱼备份:https://dayu.media/git/dayu/paintfcp-fxplug
|
||||
- 发布标签:v1.3.2
|
||||
- 正式 APP 签名源码:767bba8ac11e9bf9268a1e02d1a06d3c120c46aa
|
||||
- APP 公证:Accepted 559eedf0-048f-4d0e-b013-6a187695f2da
|
||||
- PKG 公证:Accepted 4a831775-2317-4372-a4cc-102415657ea3
|
||||
- PKG:FCP-BeautyGirl-1.3.2.pkg,4,889,706 bytes
|
||||
- SHA-256:20a88dacf68cc8812f5af86cfe94c1c0614414a68a019eaeac7ca9d078165ae1
|
||||
|
||||
本机 PKG 工坊 CLI 生成基础包,加入本产品的旧身份迁移与回滚段后,对最终包重新签名、公证和粘附票据。包含用户指定的「大鱼磨皮瘦脸」分类:大鱼人像美化.moef、Drop Zone.tiff、large.png 和 small.png。有效载荷与正式 APP、四个模板文件一致;恢复两张缩略图的锁定。签名、公证票据、Gatekeeper 和隔离安装脚本核验通过,未实际运行本 PKG 安装器;本机 APP 已通过正式交付流程安装。
|
||||
|
||||
安装前保存并退出 Final Cut Pro 和 Motion。Final Cut Pro 在「效果 → 大鱼磨皮瘦脸 → 大鱼人像美化」中使用。免费开源,无需激活码。代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。Apple SDK 及相关资源遵循各自许可。
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"title": "大鱼人像美化 1.3.2:FCP / Motion 免费开源自动美颜插件",
|
||||
"status": "publish",
|
||||
"content": "<!-- wp:paragraph -->\n<p>大鱼人像美化是一款适用于 Final Cut Pro 与 Motion 的免费开源人像插件,把磨皮、美白和脸型调整放进同一块原生卡片面板。插件会识别当前画面中的人脸并自动跟随,无需先点击分析;你可以按素材需要调整强度,拖动滑杆时持续查看效果。</p>\n<!-- /wp:paragraph -->\n<!-- wp:paragraph -->\n<p><strong>代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。</strong></p>\n<!-- /wp:paragraph -->\n\n<!-- wp:image {\"sizeSlug\":\"full\"} -->\n<figure class=\"wp-block-image size-full\"><img src=\"https://dayu.media/wp-content/uploads/2026/10/1792604750-357ca780597c39f.png\" alt=\"大鱼人像美化古风宣传封面,展示自动瘦脸、自动跟随与自动美颜\" /><figcaption class=\"wp-element-caption\">古风宣传插画;面部线条用于示意瘦脸与跟随功能。</figcaption></figure>\n<!-- /wp:image -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">可以做什么</h2>\n<!-- /wp:heading -->\n\n<!-- wp:list {\"className\":\"wp-block-list\"} -->\n<ul class=\"wp-block-list\"><!-- wp:list-item -->\n<li>磨皮:磨皮程度、微细锐化、五官保护和非肤色区保护分别调节;全局磨皮与锐化保留在更多设置。</li>\n<!-- /wp:list-item -->\n\n<!-- wp:list-item -->\n<li>美白:肤色预设、肤色亮度、皮肤色相和牙齿美白。</li>\n<!-- /wp:list-item -->\n\n<!-- wp:list-item -->\n<li>脸型:瘦脸、大眼、短脸、窄脸、下巴、V 脸、下颌骨、颧骨与小头调整。</li>\n<!-- /wp:list-item -->\n\n<!-- wp:list-item -->\n<li>自动跟随:默认跟随主要人脸,也提供多人模式、防跳脸范围与检测框预览。</li>\n<!-- /wp:list-item -->\n\n<!-- wp:list-item -->\n<li>实时预览:滑杆、数值、单项开关和重置集中在同一面板,便于反复对比。</li>\n<!-- /wp:list-item --></ul>\n<!-- /wp:list -->\n\n<!-- wp:paragraph -->\n<p>自动识别负责定位处理区域,效果强度由你控制。建议先从较小的数值开始;多人交叉、严重遮挡和极端侧脸的结果需要结合具体素材检查。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">版本与下载</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>当前版本 <strong>1.3.2(17)</strong>,支持 macOS 13 或更高版本,包含 Apple Silicon 与 Intel 两种架构。下载包包含 FxPlug4 程序和一个配套 FCP 效果模板。APP 与 PKG 均已完成 Developer ID 正式签名、Apple 公证和票据粘附;安装包为所有用户免费资源。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>1.3.2 将工程、APP 与应用标识统一为 FCP-BeautyGirl;安装器会核对身份并迁移旧版程序,保留原滤镜 UUID 和参数存储格式。此版本沿用 1.3.1 的 V 脸下颌修复、微细锐化、连续五官保护和非肤色区保护。1901 项自动回归及安装迁移检查通过;本次更名后的旧工程重开与原生画面仍需结合实际素材检查。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">安装</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>安装前先保存工作并退出 Final Cut Pro 和 Motion,安装期间保持关闭。运行本页提供的 PKG,按安装器提示完成安装;它会把程序安装到 <code>/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app</code>,把配套效果安装到当前登录用户的 Motion Templates 目录,并自动验证和修复系统插件注册。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>本插件免费开源,<strong>无需激活码、付费注册或账号授权</strong>。主 APP 打开时显示说明图,窗口显示三秒后自动退出;宿主中的滤镜由独立服务运行。安装器还会重新锁定两张缩略图,避免被宿主自动替换。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:image {\"sizeSlug\":\"full\"} -->\n<figure class=\"wp-block-image size-full\"><img src=\"https://dayu.media/wp-content/uploads/2026/10/1792604743-1c99c2ba53c7614.png\" alt=\"主程序说明页:免费开源,自动注册,无需激活码,启动三秒后自动退出\" /></figure>\n<!-- /wp:image -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">使用</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Final Cut Pro:打开<strong>效果</strong>浏览器,找到<strong>大鱼磨皮瘦脸</strong>分类,将<strong>大鱼人像美化</strong>添加到人像片段。Motion:在原生滤镜资源库的<strong>大鱼</strong>分类中选择<strong>大鱼人像美化</strong>。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>在检查器中按需调节磨皮、美白和脸型。每项左侧的眼睛可暂时停用该项,数值会保留;右侧回转箭头用于重置。人脸会随画面自动更新,开启检测框预览可以检查跟随位置。多人模式建议先用短片段观Line truncated
|
||||
"excerpt": "1.3.2 将工程、APP 与应用 ID 统一为 FCP-BeautyGirl,安装自动迁移旧版。保留 V 脸下颌修复和磨皮参数,提供免费开源源码及签名公证安装包。",
|
||||
"seo": {
|
||||
"title": {
|
||||
"enabled": true,
|
||||
"value": "大鱼人像美化 1.3.2|FCP / Motion 免费开源自动美颜插件"
|
||||
},
|
||||
"description": {
|
||||
"enabled": true,
|
||||
"value": "大鱼人像美化是适用于 Final Cut Pro 和 Motion 的免费开源 FxPlug4 插件。1.3.2 将工程、APP 和应用 ID 统一为 FCP-BeautyGirl,自动迁移旧版;保留 V 脸下颌修复及磨皮保护参数。提供 Apple 签名公证安装包和源码,无需激活码,附安装使用说明。"
|
||||
},
|
||||
"keywords": {
|
||||
"enabled": true,
|
||||
"value": "大鱼人像美化,FCP美颜插件,Motion磨皮,自动瘦脸,自动跟随,FxPlug4,免费开源"
|
||||
}
|
||||
},
|
||||
"resource": {
|
||||
"cao_info": [
|
||||
{
|
||||
"title": "版本",
|
||||
"desc": "1.3.2(17)"
|
||||
},
|
||||
{
|
||||
"title": "宿主",
|
||||
"desc": "Final Cut Pro / Motion"
|
||||
},
|
||||
{
|
||||
"title": "系统",
|
||||
"desc": "macOS 13 或更高版本"
|
||||
},
|
||||
{
|
||||
"title": "架构",
|
||||
"desc": "Apple Silicon / Intel"
|
||||
},
|
||||
{
|
||||
"title": "授权",
|
||||
"desc": "免费开源,无需激活码"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<!-- wp:paragraph -->
|
||||
<p>大鱼人像美化是一款适用于 Final Cut Pro 与 Motion 的免费开源人像插件,把磨皮、美白和脸型调整放进同一块原生卡片面板。插件会识别当前画面中的人脸并自动跟随,无需先点击分析;你可以按素材需要调整强度,拖动滑杆时持续查看效果。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:paragraph -->
|
||||
<p><strong>代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。</strong></p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:image {"sizeSlug":"full"} -->
|
||||
<figure class="wp-block-image size-full"><img src="https://dayu.media/wp-content/uploads/2026/10/1792604750-357ca780597c39f.png" alt="大鱼人像美化古风宣传封面,展示自动瘦脸、自动跟随与自动美颜" /><figcaption class="wp-element-caption">古风宣传插画;面部线条用于示意瘦脸与跟随功能。</figcaption></figure>
|
||||
<!-- /wp:image -->
|
||||
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">可以做什么</h2>
|
||||
<!-- /wp:heading -->
|
||||
|
||||
<!-- wp:list {"className":"wp-block-list"} -->
|
||||
<ul class="wp-block-list"><!-- wp:list-item -->
|
||||
<li>磨皮:磨皮程度、微细锐化、五官保护和非肤色区保护分别调节;全局磨皮与锐化保留在更多设置。</li>
|
||||
<!-- /wp:list-item -->
|
||||
|
||||
<!-- wp:list-item -->
|
||||
<li>美白:肤色预设、肤色亮度、皮肤色相和牙齿美白。</li>
|
||||
<!-- /wp:list-item -->
|
||||
|
||||
<!-- wp:list-item -->
|
||||
<li>脸型:瘦脸、大眼、短脸、窄脸、下巴、V 脸、下颌骨、颧骨与小头调整。</li>
|
||||
<!-- /wp:list-item -->
|
||||
|
||||
<!-- wp:list-item -->
|
||||
<li>自动跟随:默认跟随主要人脸,也提供多人模式、防跳脸范围与检测框预览。</li>
|
||||
<!-- /wp:list-item -->
|
||||
|
||||
<!-- wp:list-item -->
|
||||
<li>实时预览:滑杆、数值、单项开关和重置集中在同一面板,便于反复对比。</li>
|
||||
<!-- /wp:list-item --></ul>
|
||||
<!-- /wp:list -->
|
||||
|
||||
<!-- wp:paragraph -->
|
||||
<p>自动识别负责定位处理区域,效果强度由你控制。建议先从较小的数值开始;多人交叉、严重遮挡和极端侧脸的结果需要结合具体素材检查。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">版本与下载</h2>
|
||||
<!-- /wp:heading -->
|
||||
|
||||
<!-- wp:paragraph -->
|
||||
<p>当前版本 <strong>1.3.2(17)</strong>,支持 macOS 13 或更高版本,包含 Apple Silicon 与 Intel 两种架构。下载包包含 FxPlug4 程序和一个配套 FCP 效果模板。APP 与 PKG 均已完成 Developer ID 正式签名、Apple 公证和票据粘附;安装包为所有用户免费资源。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:paragraph -->
|
||||
<p>1.3.2 将工程、APP 与应用标识统一为 FCP-BeautyGirl;安装器会核对身份并迁移旧版程序,保留原滤镜 UUID 和参数存储格式。此版本沿用 1.3.1 的 V 脸下颌修复、微细锐化、连续五官保护和非肤色区保护。1901 项自动回归及安装迁移检查通过;本次更名后的旧工程重开与原生画面仍需结合实际素材检查。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">安装</h2>
|
||||
<!-- /wp:heading -->
|
||||
|
||||
<!-- wp:paragraph -->
|
||||
<p>安装前先保存工作并退出 Final Cut Pro 和 Motion,安装期间保持关闭。运行本页提供的 PKG,按安装器提示完成安装;它会把程序安装到 <code>/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app</code>,把配套效果安装到当前登录用户的 Motion Templates 目录,并自动验证和修复系统插件注册。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:paragraph -->
|
||||
<p>本插件免费开源,<strong>无需激活码、付费注册或账号授权</strong>。主 APP 打开时显示说明图,窗口显示三秒后自动退出;宿主中的滤镜由独立服务运行。安装器还会重新锁定两张缩略图,避免被宿主自动替换。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:image {"sizeSlug":"full"} -->
|
||||
<figure class="wp-block-image size-full"><img src="https://dayu.media/wp-content/uploads/2026/10/1792604743-1c99c2ba53c7614.png" alt="主程序说明页:免费开源,自动注册,无需激活码,启动三秒后自动退出" /></figure>
|
||||
<!-- /wp:image -->
|
||||
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">使用</h2>
|
||||
<!-- /wp:heading -->
|
||||
|
||||
<!-- wp:paragraph -->
|
||||
<p>Final Cut Pro:打开<strong>效果</strong>浏览器,找到<strong>大鱼磨皮瘦脸</strong>分类,将<strong>大鱼人像美化</strong>添加到人像片段。Motion:在原生滤镜资源库的<strong>大鱼</strong>分类中选择<strong>大鱼人像美化</strong>。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:paragraph -->
|
||||
<p>在检查器中按需调节磨皮、美白和脸型。每项左侧的眼睛可暂时停用该项,数值会保留;右侧回转箭头用于重置。人脸会随画面自动更新,开启检测框预览可以检查跟随位置。多人模式建议先用短片段观察稳定性。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">常见问题</h2>
|
||||
<!-- /wp:heading -->
|
||||
|
||||
<!-- wp:paragraph -->
|
||||
<p>如果效果没有出现或显示红色警告,先退出 Final Cut Pro 和 Motion,再打开一次主 APP,等待其自动退出,然后重新启动宿主。可在“终端”执行下面这条命令:</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:code -->
|
||||
<pre class="wp-block-code"><code>open '/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app'</code></pre>
|
||||
<!-- /wp:code -->
|
||||
|
||||
<!-- wp:paragraph -->
|
||||
<p>该流程可重新触发系统注册,并不能解决所有报错。仍异常时请核对 macOS、宿主版本及安装包完整性;不要只复制单个 .moef 文件,配套程序与 Drop Zone.tiff 也需要保留。当前仅提供上述一个效果模板。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">源码与开源许可</h2>
|
||||
<!-- /wp:heading -->
|
||||
|
||||
<!-- wp:paragraph -->
|
||||
<p>公开源码仓库:<a href="https://github.com/dayumeida/FCP-BeautyGirl" target="_blank" rel="noreferrer noopener">GitHub · FCP-BeautyGirl</a>,可直接浏览源码、完整提交历史和版本标签。<a href="https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.2" target="_blank" rel="noreferrer noopener">1.3.2 Release</a> 提供正式签名公证安装包。大鱼站仓库作为备份保留:<a href="https://dayu.media/git/dayu/paintfcp-fxplug" target="_blank" rel="noreferrer noopener">paintfcp-fxplug</a>(该入口仍受站点登录与会员网关限制)。也可下载 <a href="{{source_url}}">1.3.2 源码快照 ZIP</a>,源码与安装包均免费提供。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:paragraph -->
|
||||
<p>项目代码按仓库中的 MIT 许可文件提供,源码包含 Xcode 工程、Objective-C / Metal 实现、模板及构建与验证说明。Apple SDK、运行框架及样本按各自许可使用,详见源码中的 LICENSE 和 THIRD_PARTY_NOTICES.md。自行编译需要 Xcode 与 FxPlug SDK;证书和签名私钥不包含在源码中。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<!-- wp:image {"id":15597,"sizeSlug":"large","linkDestination":"none"} -->
|
||||
<figure class="wp-block-image size-large"><img src="https://dayu.media/wp-content/uploads/2026/10/1792605142-78805a221a988e7-359x1024.png" alt="" class="wp-image-15597" /></figure>
|
||||
<!-- /wp:image -->
|
||||
+20
-18
@@ -3,26 +3,24 @@
|
||||
The final prompt keeps the lock held while native-host validation is performed.
|
||||
"""
|
||||
import argparse, datetime, json, os, pathlib, plistlib, re, shutil, subprocess, sys, time
|
||||
from product_identity import PRODUCT, LegacyMigration, delivery_locks, validate_app
|
||||
root=pathlib.Path(__file__).resolve().parent.parent
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--wait-for-hosts',action='store_true',help='After notarization, retain the delivery lock and wait for Motion/FCP to exit naturally before installing; never control or quit either host.')
|
||||
parser.add_argument('--user-tests',action='store_true',help='After non-UI installed-app verification, record native effect QA as pending user testing and clean known generated copies without the interactive validation prompt.')
|
||||
parser.add_argument('--resume-prepared',action='store_true',help='Resume an already Accepted and stapled staged app after checking its recorded signature, version and unchanged product source.')
|
||||
args=parser.parse_args()
|
||||
bundle_id='com.dayu.beatygril'
|
||||
lock=pathlib.Path('/Users/dayu/.codex/app-delivery-locks')/(bundle_id+'.lock')
|
||||
lock.parent.mkdir(parents=True,exist_ok=True)
|
||||
try:lock.mkdir()
|
||||
except FileExistsError:raise SystemExit('Delivery lock occupied; inspect owner.json and the owner process')
|
||||
(lock/'owner.json').write_text(json.dumps({'pid':os.getpid(),'cwd':str(root),'bundle_id':bundle_id}))
|
||||
staging=pathlib.Path('/tmp/paintfcp-build-20261021')
|
||||
bundle_id=PRODUCT['bundleIdentifier']
|
||||
locks=delivery_locks();locks.__enter__()
|
||||
staging=pathlib.Path('/tmp/fcp-beautygirl-release')
|
||||
logs=root/'build'/'delivery';logs.mkdir(parents=True,exist_ok=True)
|
||||
app=staging/'Build/Products/Release/beatygril.app'
|
||||
installed=pathlib.Path('/Library/Plug-Ins/FxPlug/beatygril.app')
|
||||
app=staging/'Build/Products/Release'/(PRODUCT['name']+'.app')
|
||||
installed=pathlib.Path(PRODUCT['installationPath'])
|
||||
archive=logs/'notarization.zip'
|
||||
identity='Developer ID Application: zhong luo (B3W25FX5H5)'
|
||||
keychain='/Users/dayu/Library/Keychains/login.keychain-db'
|
||||
previous=logs/'previous-beatygril.app'
|
||||
previous=logs/'previous-FCP-BeautyGirl.app'
|
||||
migration=LegacyMigration(logs)
|
||||
receipt_path=logs/'delivery-receipt.json'
|
||||
receipt={'status':'preparing','bundleIdentifier':bundle_id,'installationPath':str(installed),
|
||||
'options':{'waitForHosts':args.wait_for_hosts,'userTests':args.user_tests},
|
||||
@@ -80,7 +78,7 @@ def cleanup_known_products():
|
||||
try:
|
||||
delivery_source={'commit':run(['git','rev-parse','HEAD']).strip(),
|
||||
'trackedWorktreeChanges':run(['git','status','--porcelain','--untracked-files=no']).splitlines()}
|
||||
plugin=app/'Contents/PlugIns/beatygril XPC Service.pluginkit'
|
||||
plugin=app/'Contents/PlugIns'/PRODUCT['pluginName']
|
||||
if args.resume_prepared:
|
||||
old_receipt=json.loads(receipt_path.read_text())
|
||||
result=old_receipt.get('notarization',{})
|
||||
@@ -94,7 +92,7 @@ try:
|
||||
if not expected or not actual or expected.group(1)!=actual.group(1):raise RuntimeError('Prepared signature hash changed')
|
||||
if info.get('CFBundleShortVersionString')!=old_receipt.get('version') or info.get('CFBundleVersion')!=old_receipt.get('build'):raise RuntimeError('Prepared version changed')
|
||||
source_commit=old_receipt.get('source',{}).get('commit')
|
||||
if not source_commit or run(['git','diff',source_commit,'--','beatygril']).strip():raise RuntimeError('Product source changed since prepared build')
|
||||
if not source_commit or run(['git','diff',source_commit,'--',PRODUCT['sourceRoot'],'product.json']).strip():raise RuntimeError('Product source changed since prepared build')
|
||||
receipt.update(old_receipt);receipt.pop('failure',None)
|
||||
receipt['resumedBy']=delivery_source
|
||||
receipt['options']={'waitForHosts':args.wait_for_hosts,'userTests':args.user_tests,'resumePrepared':True}
|
||||
@@ -107,14 +105,14 @@ try:
|
||||
identities=run(['security','find-identity','-v','-p','codesigning',keychain])
|
||||
if identity not in identities:raise RuntimeError('Valid Developer ID Application identity is unavailable')
|
||||
print('Building universal Release…',flush=True)
|
||||
run(['xcodebuild','-project','beatygril/beatygril.xcodeproj','-scheme','Wrapper Application','-configuration','Release','-derivedDataPath',str(staging),'-destination','generic/platform=macOS','build'],logs/'build.log')
|
||||
run(['xcodebuild','-project',PRODUCT['xcodeProject'],'-scheme',PRODUCT['scheme'],'-configuration','Release','-derivedDataPath',str(staging),'-destination','generic/platform=macOS','build'],logs/'build.log')
|
||||
# Xcode can register a fresh build. Remove this known generated path before
|
||||
# lengthy signing/notarization while a user may still be testing the old app.
|
||||
unregister_generated_plugin(plugin)
|
||||
for framework in sorted((plugin/'Contents/Frameworks').glob('*.framework')):
|
||||
run(['codesign','--force','--timestamp','--options','runtime','--sign',identity,str(framework)])
|
||||
run(['codesign','--force','--timestamp','--options','runtime','--sign',identity,str(plugin)])
|
||||
run(['codesign','--force','--timestamp','--options','runtime','--entitlements',str(root/'beatygril/beatygril/Wrapper Application/SandboxEntitlements.entitlements'),'--sign',identity,str(app)])
|
||||
run(['codesign','--force','--timestamp','--options','runtime','--entitlements',str(root/PRODUCT['sourceDirectory']/'Wrapper Application/SandboxEntitlements.entitlements'),'--sign',identity,str(app)])
|
||||
run(['codesign','--verify','--deep','--strict',str(app)])
|
||||
info=require_product(app)
|
||||
receipt['version']=info.get('CFBundleShortVersionString');receipt['build']=info.get('CFBundleVersion')
|
||||
@@ -148,6 +146,8 @@ try:
|
||||
if installed.exists():
|
||||
shutil.move(str(installed),str(previous))
|
||||
try:
|
||||
validate_app(app,bundle_id)
|
||||
migration.relocate(unregister_generated_plugin)
|
||||
shutil.copytree(app,installed,symlinks=True)
|
||||
run(['codesign','--verify','--deep','--strict',str(installed)])
|
||||
run(['xcrun','stapler','validate',str(installed)])
|
||||
@@ -156,21 +156,23 @@ try:
|
||||
signature=subprocess.run(['codesign','-dvvv',str(installed)],capture_output=True,text=True,check=True).stderr
|
||||
if info.get('CFBundleIdentifier')!=bundle_id or 'TeamIdentifier=B3W25FX5H5' not in signature:raise RuntimeError('Installed app identity mismatch')
|
||||
(logs/'installed-signature.txt').write_text(signature)
|
||||
run(['pluginkit','-a',str(installed/'Contents/PlugIns/beatygril XPC Service.pluginkit')])
|
||||
run(['pluginkit','-a',str(installed/'Contents/PlugIns'/PRODUCT['pluginName'])])
|
||||
# Xcode may register its build product. Only the installed plugin should
|
||||
# be discoverable while native host validation is in progress.
|
||||
unregister_generated_plugin(plugin)
|
||||
installed_plugin=installed/'Contents/PlugIns/beatygril XPC Service.pluginkit'
|
||||
installed_plugin=installed/'Contents/PlugIns'/PRODUCT['pluginName']
|
||||
plugin_info=plistlib.loads((installed_plugin/'Contents/Info.plist').read_bytes())
|
||||
discovered=run(['pluginkit','-m','-A','-D','-v','-i',plugin_info['CFBundleIdentifier']])
|
||||
if str(installed_plugin) not in discovered:raise RuntimeError('PluginKit did not report the formal installed plug-in path')
|
||||
(logs/'installed-discovery.txt').write_text(discovered)
|
||||
save_receipt('installed',installedSignature=signature,installedSignatureVerified=True,
|
||||
installedStapleValidated=True,gatekeeperAccepted=True,
|
||||
identityMigration={'legacyInstallationsRemoved':[str(source) for legacy,source,backup in migration.moved], 'filterUUIDPreserved':PRODUCT['filterUUID']},
|
||||
pluginDiscovery={'bundleIdentifier':plugin_info['CFBundleIdentifier'],'formalPath':str(installed_plugin),'output':discovered})
|
||||
except Exception:
|
||||
if installed.exists():shutil.rmtree(installed)
|
||||
if previous.exists():shutil.move(str(previous),str(installed))
|
||||
migration.rollback(lambda path:run(['pluginkit','-a',str(path)]))
|
||||
raise
|
||||
print(json.dumps({'status':'installed','path':str(installed),'notarization':result}),flush=True)
|
||||
if args.user_tests:
|
||||
@@ -179,11 +181,11 @@ try:
|
||||
input('HOST_VALIDATION_PENDING: press Return only after host verification and duplicate inventory.\n')
|
||||
receipt['nativeEffectQA']={'status':'operator_confirmed','performedByScript':False}
|
||||
# Only this task’s known generated product copies are removed.
|
||||
removed=cleanup_known_products()
|
||||
removed=migration.cleanup()+cleanup_known_products()
|
||||
save_receipt('installed_user_qa_pending' if args.user_tests else 'complete',removedGeneratedPaths=removed)
|
||||
print('Delivery staging and notarization archive cleaned.',flush=True)
|
||||
except BaseException as error:
|
||||
save_receipt('failed',failure={'type':type(error).__name__,'message':str(error)})
|
||||
raise
|
||||
finally:
|
||||
(lock/'owner.json').unlink();lock.rmdir()
|
||||
locks.__exit__(None,None,None)
|
||||
@@ -0,0 +1,83 @@
|
||||
"""Add this product's reversible identity migration to PKG Forge's postinstall."""
|
||||
from product_identity import PRODUCT
|
||||
|
||||
def inject_migration(post, product=PRODUCT):
|
||||
legacy, = product['legacyProducts']
|
||||
block = r'''
|
||||
# FCP-BeautyGirl identity migration. Payload and template work are already complete.
|
||||
BG_NEW_APP="${TARGET_VOLUME%/}@@NEW_PATH@@"
|
||||
BG_OLD_APP="${TARGET_VOLUME%/}@@OLD_PATH@@"
|
||||
BG_BACKUP_DIR=""
|
||||
BG_OLD_MOVED=0
|
||||
bg_validate_identity() {
|
||||
local app="$1" expected="$2" plugin="$3" actual signature uuid
|
||||
[[ -d "$app" && ! -L "$app" ]] || return 1
|
||||
actual="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$app/Contents/Info.plist")" || return 1
|
||||
[[ "$actual" == "$expected" ]] || return 1
|
||||
/usr/bin/codesign --verify --all-architectures --deep --strict "$app" || return 1
|
||||
signature="$(/usr/bin/codesign -dvvv "$app" 2>&1)" || return 1
|
||||
[[ "$signature" == *'TeamIdentifier=@@TEAM@@'* ]] || return 1
|
||||
uuid="$(/usr/libexec/PlistBuddy -c 'Print :ProPlugPlugInList:0:uuid' "$app/Contents/PlugIns/$plugin/Contents/Info.plist")" || return 1
|
||||
[[ "$uuid" == '@@UUID@@' ]]
|
||||
}
|
||||
bg_unregister_identity() {
|
||||
# Both bootstrap session and Unix identity belong to the desktop user.
|
||||
/bin/launchctl asuser "$CONSOLE_UID" /usr/bin/sudo -u "$CONSOLE_USER" /usr/bin/pluginkit -r "$1" >/dev/null 2>&1 || true
|
||||
}
|
||||
bg_rollback_identity() {
|
||||
local result=$?
|
||||
trap - EXIT
|
||||
if (( result != 0 && BG_OLD_MOVED )); then
|
||||
bg_validate_identity "$BG_NEW_APP" '@@NEW_ID@@' '@@NEW_PLUGIN@@' || {
|
||||
/bin/echo "新 APP 身份已变化,旧版保留在 $BG_BACKUP_DIR" >&2
|
||||
exit "$result"
|
||||
}
|
||||
bg_unregister_identity "$BG_NEW_APP/Contents/PlugIns/@@NEW_PLUGIN@@"
|
||||
/bin/rm -rf "$BG_NEW_APP"
|
||||
/bin/mv "$BG_BACKUP_DIR/@@OLD_NAME@@.app" "$BG_OLD_APP"
|
||||
/bin/rmdir "$BG_BACKUP_DIR"
|
||||
fx_verify_app "$BG_OLD_APP" 0 'Contents/PlugIns/@@OLD_PLUGIN@@' '@@OLD_PLUGIN_ID@@' '1.1' || true
|
||||
fi
|
||||
exit "$result"
|
||||
}
|
||||
bg_finish_identity() {
|
||||
if (( BG_OLD_MOVED )); then
|
||||
bg_validate_identity "$BG_BACKUP_DIR/@@OLD_NAME@@.app" '@@OLD_ID@@' '@@OLD_PLUGIN@@'
|
||||
/bin/rm -rf "$BG_BACKUP_DIR/@@OLD_NAME@@.app"
|
||||
/bin/rmdir "$BG_BACKUP_DIR"
|
||||
fi
|
||||
trap - EXIT
|
||||
}
|
||||
bg_validate_identity "$BG_NEW_APP" '@@NEW_ID@@' '@@NEW_PLUGIN@@' || {
|
||||
/bin/echo '新版 APP 签名或身份验证失败,未迁移旧版。' >&2
|
||||
exit 1
|
||||
}
|
||||
if [[ -e "$BG_OLD_APP" || -L "$BG_OLD_APP" ]]; then
|
||||
bg_validate_identity "$BG_OLD_APP" '@@OLD_ID@@' '@@OLD_PLUGIN@@' || {
|
||||
/bin/echo '旧路径的 APP 身份不属于本产品,未移动或删除。' >&2
|
||||
exit 1
|
||||
}
|
||||
BG_RECOVERY_ROOT="${TARGET_VOLUME%/}/Library/Application Support/@@NAME@@/Migration"
|
||||
/bin/mkdir -p "$BG_RECOVERY_ROOT"
|
||||
BG_BACKUP_DIR="$(/usr/bin/mktemp -d "$BG_RECOVERY_ROOT/.upgrade-XXXXXXXX")"
|
||||
bg_unregister_identity "$BG_OLD_APP/Contents/PlugIns/@@OLD_PLUGIN@@"
|
||||
/bin/mv "$BG_OLD_APP" "$BG_BACKUP_DIR/@@OLD_NAME@@.app"
|
||||
BG_OLD_MOVED=1
|
||||
fi
|
||||
trap bg_rollback_identity EXIT
|
||||
'''
|
||||
values = {'NEW_PATH': product['installationPath'], 'OLD_PATH': legacy['installationPath'],
|
||||
'NEW_ID': product['bundleIdentifier'], 'OLD_ID': legacy['bundleIdentifier'],
|
||||
'NEW_PLUGIN': product['pluginName'], 'OLD_PLUGIN': legacy['pluginName'],
|
||||
'OLD_PLUGIN_ID': legacy['pluginBundleIdentifier'], 'OLD_NAME': legacy['name'],
|
||||
'NAME': product['name'], 'TEAM': product['teamIdentifier'], 'UUID': product['filterUUID']}
|
||||
for key, value in values.items():
|
||||
if any(c in value for c in ['"', "'", '\n', '$', '`']):
|
||||
raise ValueError('Unsafe installer identity value: ' + key)
|
||||
block = block.replace('@@' + key + '@@', value)
|
||||
marker = '\ntrap - EXIT\nfx_verify_app '
|
||||
if post.count(marker) != 1 or not post.rstrip().endswith('exit 0'):
|
||||
raise ValueError('Unexpected PKG Forge postinstall structure')
|
||||
post = post.replace(marker, '\ntrap - EXIT\n' + block + '\nfx_verify_app ', 1)
|
||||
end = post.rfind('exit 0')
|
||||
return post[:end] + 'bg_finish_identity\n' + post[end:]
|
||||
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Package the installed product with local PKG Forge, then sign/notarize its migration update."""
|
||||
import argparse, datetime, hashlib, json, os, pathlib, plistlib, shutil, subprocess, sys
|
||||
from installer_identity import inject_migration
|
||||
from product_identity import PRODUCT, ROOT, delivery_locks, validate_app
|
||||
|
||||
def run(args, log=None, env=None):
|
||||
result = subprocess.run(args, cwd=ROOT, text=True, capture_output=True, env=env)
|
||||
if log:
|
||||
pathlib.Path(log).write_text(result.stdout + result.stderr)
|
||||
result.check_returncode()
|
||||
return result.stdout
|
||||
|
||||
version = PRODUCT['version']
|
||||
work = ROOT / 'build' / ('package-' + version)
|
||||
release = ROOT / 'releases' / version
|
||||
proof = ROOT / 'docs/verification' / ('v' + version)
|
||||
package_name = PRODUCT['name'] + '-' + version + '.pkg'
|
||||
final = release / package_name
|
||||
keychain = '/Users/dayu/Library/Keychains/login.keychain-db'
|
||||
installer_identity = 'Developer ID Installer: zhong luo (B3W25FX5H5)'
|
||||
cli = '/Applications/PKG工坊.app/Contents/MacOS/PKG工坊'
|
||||
source_template = pathlib.Path('/Users/dayu/Movies/Motion Templates.localized/Effects.localized/大鱼磨皮瘦脸')
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--verify-existing', action='store_true', help='Resume verification of the already Accepted, stapled final package without rebuilding or resubmitting it.')
|
||||
args = parser.parse_args()
|
||||
|
||||
with delivery_locks():
|
||||
if not args.verify_existing:
|
||||
if work.exists() or final.exists():
|
||||
raise SystemExit('Packaging work or final package already exists; inspect before retrying')
|
||||
work.mkdir(parents=True)
|
||||
release.mkdir(parents=True, exist_ok=True)
|
||||
proof.mkdir(parents=True, exist_ok=True)
|
||||
formal = pathlib.Path(PRODUCT['installationPath'])
|
||||
info = validate_app(formal, PRODUCT['bundleIdentifier'])
|
||||
assert info['CFBundleShortVersionString'] == version and info['CFBundleVersion'] == PRODUCT['build']
|
||||
assert installer_identity in run(['security', 'find-identity', '-v', '-p', 'basic', keychain])
|
||||
template = work / 'input/大鱼磨皮瘦脸'
|
||||
manifest = []
|
||||
for source in sorted(source_template.rglob('*')):
|
||||
if source.is_file() and source.name != '.DS_Store':
|
||||
target = template / source.relative_to(source_template)
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(source, target)
|
||||
if source.stat().st_flags & 2:
|
||||
run(['chflags', 'uchg', str(target)])
|
||||
manifest.append({'file': str(source.relative_to(source_template)), 'sha256': hashlib.sha256(source.read_bytes()).hexdigest(), 'locked': bool(source.stat().st_flags & 2)})
|
||||
assert len(manifest) == 4 and len(list(template.rglob('*.moef'))) == 1
|
||||
base = work / 'base' / package_name
|
||||
base.parent.mkdir()
|
||||
print('PKG Forge: generating signed/notarized base installer from the formal APP and installed Motion category.', flush=True)
|
||||
run([cli, 'inspect', str(formal), str(template)], proof / 'pkgforge-inspect.txt')
|
||||
run([cli, 'package', '--name', PRODUCT['name'], '--version', version, '--identifier-prefix', 'com.dayu.fcppackage', '--signing-identity', installer_identity, '--notary-profile', 'codex-dayu-notary-B3W25FX5H5', '--keychain', keychain, '--output', str(base), str(formal), str(template)], proof / 'pkgforge.txt')
|
||||
run(['pkgutil', '--check-signature', str(base)])
|
||||
run(['xcrun', 'stapler', 'validate', str(base)])
|
||||
expanded = work / 'expanded'
|
||||
run(['pkgutil', '--expand', str(base), str(expanded)])
|
||||
script = expanded / 'Payload.pkg/Scripts/postinstall'
|
||||
script.write_text(inject_migration(script.read_text()))
|
||||
run(['/bin/zsh', '-n', str(script)])
|
||||
# PKG Forge owns the payload/template/update scripts. Only the product's
|
||||
# identity migration is added, then the complete result is signed afresh.
|
||||
unsigned = work / 'migration-unsigned.pkg'
|
||||
run(['pkgutil', '--flatten', str(expanded), str(unsigned)])
|
||||
run(['productsign', '--sign', installer_identity, '--keychain', keychain, '--timestamp', str(unsigned), str(final)], proof / 'pkg-signing.txt')
|
||||
print('Submitting the final migration installer to Apple notarization.', flush=True)
|
||||
result = json.loads(run(['xcrun', 'notarytool', 'submit', str(final), '--keychain-profile', 'codex-dayu-notary-B3W25FX5H5', '--keychain', keychain, '--wait', '--output-format', 'json']))
|
||||
(proof / 'pkg-notarization.json').write_text(json.dumps(result, indent=2) + '\n')
|
||||
if result.get('status') != 'Accepted':
|
||||
raise RuntimeError('Final migration package notarization was not Accepted')
|
||||
run(['xcrun', 'stapler', 'staple', str(final)], proof / 'pkg-staple.txt')
|
||||
run(['xcrun', 'stapler', 'validate', str(final)])
|
||||
run(['pkgutil', '--check-signature', str(final)], proof / 'pkg-signature.txt')
|
||||
run(['spctl', '-a', '-vv', '-t', 'install', str(final)], proof / 'pkg-gatekeeper.txt')
|
||||
else:
|
||||
if not work.exists() or not final.exists():
|
||||
raise RuntimeError('No prepared package to verify')
|
||||
formal = pathlib.Path(PRODUCT['installationPath'])
|
||||
info = validate_app(formal, PRODUCT['bundleIdentifier'])
|
||||
assert info['CFBundleShortVersionString'] == version and info['CFBundleVersion'] == PRODUCT['build']
|
||||
result = json.loads((proof / 'pkg-notarization.json').read_text())
|
||||
assert result['status'] == 'Accepted'
|
||||
run(['xcrun', 'stapler', 'validate', str(final)])
|
||||
run(['pkgutil', '--check-signature', str(final)])
|
||||
run(['spctl', '-a', '-vv', '-t', 'install', str(final)])
|
||||
template = work / 'input/大鱼磨皮瘦脸'
|
||||
manifest = [{'file': str(p.relative_to(template)), 'sha256': hashlib.sha256(p.read_bytes()).hexdigest(), 'locked': bool(p.stat().st_flags & 2)} for p in sorted(template.rglob('*')) if p.is_file()]
|
||||
assert len(manifest) == 4
|
||||
full = work / 'full'
|
||||
try:
|
||||
run(['pkgutil', '--expand-full', str(final), str(full)])
|
||||
run([sys.executable, 'scripts/verify-release-package.py', '--version', version, '--build', PRODUCT['build'], '--expanded', str(full), '--fixture', 'build/pkg-isolation-' + version], proof / 'pkg-verification-output.txt')
|
||||
env = dict(os.environ, FCP_BEAUTYGIRL_POSTINSTALL=str(full / 'Payload.pkg/Scripts/postinstall'))
|
||||
run([sys.executable, 'tests/installer_identity_tests.py'], proof / 'installer-tests.txt', env=env)
|
||||
finally:
|
||||
if full.exists():
|
||||
run(['chflags', '-R', 'nouchg', str(full)])
|
||||
shutil.rmtree(full)
|
||||
receipt = {'status': 'verified', 'version': version, 'build': PRODUCT['build'], 'package': str(final.relative_to(ROOT)), 'bytes': final.stat().st_size, 'sha256': hashlib.sha256(final.read_bytes()).hexdigest(), 'sourceApp': str(formal), 'sourceCommit': json.loads((ROOT / 'build/delivery/delivery-receipt.json').read_text())['source']['commit'], 'motionSource': str(source_template), 'motionFiles': manifest, 'packager': cli, 'productMigration': 'PKG Forge base plus identity-checked backup/rollback block; final package freshly signed, notarized and stapled', 'notarization': result, 'realInstallerExecuted': False}
|
||||
(proof / 'pkg-delivery.json').write_text(json.dumps(receipt, ensure_ascii=False, indent=2) + '\n')
|
||||
run(['chflags', '-R', 'nouchg', str(work)])
|
||||
shutil.rmtree(work)
|
||||
print(json.dumps(receipt, ensure_ascii=False), flush=True)
|
||||
@@ -0,0 +1,81 @@
|
||||
"""Shared product identity and reversible, identity-checked legacy APP migration."""
|
||||
import contextlib, json, os, pathlib, plistlib, shutil, subprocess
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
PRODUCT = json.loads((ROOT / 'product.json').read_text())
|
||||
|
||||
def validate_app(path, bundle_id, team=PRODUCT['teamIdentifier']):
|
||||
path = pathlib.Path(path)
|
||||
info = plistlib.loads((path / 'Contents/Info.plist').read_bytes())
|
||||
if info.get('CFBundleIdentifier') != bundle_id:
|
||||
raise RuntimeError('APP identity mismatch: ' + str(path))
|
||||
subprocess.run(['codesign', '--verify', '--all-architectures', '--deep', '--strict', str(path)], check=True, capture_output=True)
|
||||
signature = subprocess.run(['codesign', '-dvvv', str(path)], check=True, text=True, capture_output=True).stderr
|
||||
if ('TeamIdentifier=' + team) not in signature:
|
||||
raise RuntimeError('APP signing team mismatch: ' + str(path))
|
||||
plugins = list((path / 'Contents/PlugIns').glob('*.pluginkit'))
|
||||
if not any(PRODUCT['filterUUID'] in [p.get('uuid') for p in plistlib.loads((plugin / 'Contents/Info.plist').read_bytes()).get('ProPlugPlugInList', [])] for plugin in plugins):
|
||||
raise RuntimeError('APP does not contain this product filter UUID: ' + str(path))
|
||||
return info
|
||||
|
||||
@contextlib.contextmanager
|
||||
def delivery_locks(product=PRODUCT):
|
||||
"""Hold both old and new identity locks throughout an identity migration."""
|
||||
base = pathlib.Path('/Users/dayu/.codex/app-delivery-locks')
|
||||
base.mkdir(parents=True, exist_ok=True)
|
||||
held = []
|
||||
try:
|
||||
for bundle_id in sorted({product['bundleIdentifier']} | {p['bundleIdentifier'] for p in product.get('legacyProducts', [])}):
|
||||
lock = base / (bundle_id + '.lock')
|
||||
lock.mkdir()
|
||||
held.append(lock)
|
||||
(lock / 'owner.json').write_text(json.dumps({'pid': os.getpid(), 'cwd': str(ROOT), 'bundle_id': bundle_id, 'product': product['name']}))
|
||||
yield
|
||||
finally:
|
||||
for lock in reversed(held):
|
||||
(lock / 'owner.json').unlink(missing_ok=True)
|
||||
lock.rmdir()
|
||||
|
||||
class LegacyMigration:
|
||||
def __init__(self, backup_root, *, product=PRODUCT, volume=pathlib.Path('/'), verifier=validate_app):
|
||||
self.product, self.backup_root, self.verifier = product, pathlib.Path(backup_root), verifier
|
||||
self.volume, self.moved = pathlib.Path(volume), []
|
||||
|
||||
def candidates(self):
|
||||
result = []
|
||||
for legacy in self.product.get('legacyProducts', []):
|
||||
source = self.volume / pathlib.Path(legacy['installationPath']).relative_to('/')
|
||||
if source.exists():
|
||||
self.verifier(source, legacy['bundleIdentifier'])
|
||||
backup = self.backup_root / ('legacy-' + legacy['name'] + '.app')
|
||||
if backup.exists():
|
||||
raise RuntimeError('Legacy recovery copy already exists: ' + str(backup))
|
||||
result.append((legacy, source, backup))
|
||||
return result
|
||||
|
||||
def relocate(self, unregister):
|
||||
# Validate every candidate before moving any of them.
|
||||
for legacy, source, backup in self.candidates():
|
||||
self.verifier(source, legacy['bundleIdentifier'])
|
||||
unregister(source / 'Contents/PlugIns' / legacy['pluginName'])
|
||||
backup.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.move(str(source), str(backup))
|
||||
self.moved.append((legacy, source, backup))
|
||||
|
||||
def rollback(self, register):
|
||||
for legacy, source, backup in reversed(self.moved):
|
||||
if source.exists():
|
||||
raise RuntimeError('Refusing to overwrite a new APP during rollback: ' + str(source))
|
||||
self.verifier(backup, legacy['bundleIdentifier'])
|
||||
shutil.move(str(backup), str(source))
|
||||
register(source / 'Contents/PlugIns' / legacy['pluginName'])
|
||||
self.moved.clear()
|
||||
|
||||
def cleanup(self):
|
||||
removed = []
|
||||
for legacy, source, backup in self.moved:
|
||||
self.verifier(backup, legacy['bundleIdentifier'])
|
||||
shutil.rmtree(backup)
|
||||
removed.append(str(backup))
|
||||
self.moved.clear()
|
||||
return removed
|
||||
@@ -38,7 +38,7 @@ with zipfile.ZipFile(source_zip) as z:
|
||||
assert z.testzip() is None and any(n.endswith('/Wrapper Application/AppDelegate.m') for n in z.namelist())
|
||||
status=request('/dayu-publisher/v1/status');assert status['can_publish'] and status['can_upload'] and 'seo' in status['fields']
|
||||
source=upload('source',ROOT/'build/Dayu-Portrait-1.2.0-source.zip','application/zip')
|
||||
card=upload('card',ROOT/'beatygril/beatygril/Wrapper Application/Resources/LauncherInfo.png','image/png')
|
||||
card=upload('card',ROOT/'FCP-BeautyGirl/FCP-BeautyGirl/Wrapper Application/Resources/LauncherInfo.png','image/png')
|
||||
content=(ROOT/'releases/1.2.0/article.html').read_text().replace('{{source_url}}',html.escape(source['url'],quote=True)).replace('{{card_url}}',html.escape(card['url'],quote=True))
|
||||
seo={
|
||||
'title':{'enabled':True,'value':'大鱼人像美化 1.2.0|FCP / Motion 免费开源自动美颜插件'},
|
||||
|
||||
+4
-4
@@ -1,13 +1,13 @@
|
||||
#!/bin/zsh
|
||||
set -euo pipefail
|
||||
cd "${0:A:h:h}"
|
||||
output_dir=$(mktemp -d /tmp/paintfcp-host-tests.XXXXXX)
|
||||
output_dir=$(mktemp -d /tmp/fcp-beautygirl-host-tests.XXXXXX)
|
||||
trap 'rm -rf "$output_dir"' EXIT
|
||||
plugin_dir="$PWD/beatygril/beatygril/Plugin"
|
||||
plugin_dir="$PWD/FCP-BeautyGirl/FCP-BeautyGirl/Plugin"
|
||||
sdk_frameworks=/Library/Developer/SDKs/FxPlug.sdk/Library/Frameworks
|
||||
runtime_frameworks=/Library/Developer/Frameworks
|
||||
compiler_extra=()
|
||||
if [[ "${PAINTFCP_HOST_ASAN:-0}" == 1 ]]; then
|
||||
if [[ "${FCP_BEAUTYGIRL_HOST_ASAN:-0}" == 1 ]]; then
|
||||
compiler_extra=(-fsanitize=address -g)
|
||||
fi
|
||||
xcrun clang "${compiler_extra[@]}" -fobjc-arc -fmodules -Wall -Wextra -Wno-unused-parameter \
|
||||
@@ -16,7 +16,7 @@ xcrun clang "${compiler_extra[@]}" -fobjc-arc -fmodules -Wall -Wextra -Wno-unuse
|
||||
-Wl,-rpath,"$runtime_frameworks" \
|
||||
"$plugin_dir/PGBeautyRenderer.m" "$plugin_dir/PGFaceProcessor.m" \
|
||||
"$plugin_dir/PGInspectorState.m" "$plugin_dir/PGInspectorView.m" \
|
||||
"$plugin_dir/beatygrilPlugIn.m" tests/host_adapter_tests.m \
|
||||
"$plugin_dir/FCPBeautyGirlPlugIn.m" tests/host_adapter_tests.m \
|
||||
-framework Foundation -framework AppKit -framework CoreImage -framework CoreGraphics \
|
||||
-framework CoreMedia -framework CoreVideo -framework IOSurface -framework Metal \
|
||||
-framework Vision -framework ImageIO -framework FxPlug -framework PluginManager \
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#!/bin/zsh
|
||||
set -euo pipefail
|
||||
cd "${0:A:h:h}"
|
||||
output_dir=$(mktemp -d /tmp/paintfcp-inspector-tests.XXXXXX)
|
||||
output_dir=$(mktemp -d /tmp/fcp-beautygirl-inspector-tests.XXXXXX)
|
||||
trap 'rm -rf "$output_dir"' EXIT
|
||||
plugin_dir="$PWD/beatygril/beatygril/Plugin"
|
||||
plugin_dir="$PWD/FCP-BeautyGirl/FCP-BeautyGirl/Plugin"
|
||||
compiler_extra=()
|
||||
if [[ "${PAINTFCP_INSPECTOR_ASAN:-0}" == 1 ]]; then
|
||||
if [[ "${FCP_BEAUTYGIRL_INSPECTOR_ASAN:-0}" == 1 ]]; then
|
||||
compiler_extra=(-fsanitize=address -g)
|
||||
fi
|
||||
python3 scripts/inspector-template.py audit 'Templates/大鱼人像美化/大鱼人像美化.moef' \
|
||||
@@ -16,7 +16,7 @@ xcrun clang "${compiler_extra[@]}" -fobjc-arc -fmodules -Wall -Wextra -Wno-unuse
|
||||
-Wl,-rpath,/Library/Developer/Frameworks \
|
||||
"$plugin_dir/PGBeautyRenderer.m" "$plugin_dir/PGFaceProcessor.m" \
|
||||
"$plugin_dir/PGInspectorState.m" "$plugin_dir/PGInspectorView.m" \
|
||||
"$plugin_dir/beatygrilPlugIn.m" tests/inspector_tests.m \
|
||||
"$plugin_dir/FCPBeautyGirlPlugIn.m" tests/inspector_tests.m \
|
||||
-framework Foundation -framework AppKit -framework CoreImage -framework CoreGraphics \
|
||||
-framework CoreMedia -framework CoreVideo -framework IOSurface -framework Metal \
|
||||
-framework Vision -framework ImageIO -framework FxPlug -framework PluginManager \
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
#!/bin/zsh
|
||||
set -euo pipefail
|
||||
cd "${0:A:h:h}"
|
||||
output_dir=$(mktemp -d /tmp/paintfcp-osc-tests.XXXXXX)
|
||||
output_dir=$(mktemp -d /tmp/fcp-beautygirl-osc-tests.XXXXXX)
|
||||
trap 'rm -rf "$output_dir"' EXIT
|
||||
plugin_dir="$PWD/beatygril/beatygril/Plugin"
|
||||
plugin_dir="$PWD/FCP-BeautyGirl/FCP-BeautyGirl/Plugin"
|
||||
sdk_frameworks=/Library/Developer/SDKs/FxPlug.sdk/Library/Frameworks
|
||||
runtime_frameworks=/Library/Developer/Frameworks
|
||||
compiler_extra=()
|
||||
if [[ "${PAINTFCP_OSC_ASAN:-0}" == 1 ]]; then
|
||||
if [[ "${FCP_BEAUTYGIRL_OSC_ASAN:-0}" == 1 ]]; then
|
||||
compiler_extra=(-fsanitize=address -g)
|
||||
fi
|
||||
xcrun clang "${compiler_extra[@]}" -fobjc-arc -fmodules -Wall -Wextra -Wno-unused-parameter \
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
#!/bin/zsh
|
||||
set -euo pipefail
|
||||
cd "${0:A:h:h}"
|
||||
output_dir=$(mktemp -d /tmp/paintfcp-tests.XXXXXX)
|
||||
output_dir=$(mktemp -d /tmp/fcp-beautygirl-tests.XXXXXX)
|
||||
trap 'rm -rf "$output_dir"' EXIT
|
||||
plugin_dir="$PWD/beatygril/beatygril/Plugin"
|
||||
plugin_dir="$PWD/FCP-BeautyGirl/FCP-BeautyGirl/Plugin"
|
||||
common=(-fobjc-arc -fmodules -Wall -Wextra -Wno-unused-parameter -DCI_SILENCE_GL_DEPRECATION -mmacosx-version-min=13.0 -I "$plugin_dir" -framework Foundation -framework CoreImage -framework CoreGraphics -framework CoreMedia -framework Metal -framework Vision -framework ImageIO)
|
||||
xcrun clang "${common[@]}" "$plugin_dir/PGBeautyRenderer.m" tests/beauty_render_tests.m -o "$output_dir/beauty-tests"
|
||||
"$output_dir/beauty-tests"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
Only --publish performs writes. Credentials stay in memory and go only to Dayu.
|
||||
"""
|
||||
import argparse, base64, hashlib, html, json, pathlib, ssl, subprocess, urllib.parse, urllib.request, uuid, zipfile
|
||||
from product_identity import PRODUCT
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--version', required=True)
|
||||
@@ -49,8 +50,8 @@ if not args.publish:
|
||||
raise SystemExit(0)
|
||||
baseline = json.loads((work / 'article-before.json').read_text())['article']
|
||||
payload = json.loads((release / 'article-payload.json').read_text())
|
||||
package = release / ('大鱼人像美化-' + args.version + '.pkg')
|
||||
source = root / 'build' / ('Dayu-Portrait-' + args.version + '-source.zip')
|
||||
package = release / (PRODUCT['name'] + '-' + args.version + '.pkg')
|
||||
source = root / 'build' / (PRODUCT['name'] + '-' + args.version + '-source.zip')
|
||||
with zipfile.ZipFile(source) as z:
|
||||
assert z.testzip() is None and any(n.endswith('/Wrapper Application/AppDelegate.m') for n in z.namelist())
|
||||
assert not any(n.endswith(('.app', '.pkg', '.p12', '.key', '.env')) for n in z.namelist())
|
||||
@@ -77,7 +78,7 @@ assert '{{' not in payload['content']
|
||||
if not state.get('response'):
|
||||
boundary = 'DayuRelease' + state['idempotencyKey'].replace('-', '')
|
||||
parts = []
|
||||
for name, data, filename, mime in [('payload', json.dumps(payload, ensure_ascii=False).encode(), None, 'application/json'), ('package', package.read_bytes(), 'Dayu-Portrait-' + args.version + '.pkg', 'application/octet-stream')]:
|
||||
for name, data, filename, mime in [('payload', json.dumps(payload, ensure_ascii=False).encode(), None, 'application/json'), ('package', package.read_bytes(), package.name, 'application/octet-stream')]:
|
||||
head = '--' + boundary + '\r\nContent-Disposition: form-data; name="' + name + '"'
|
||||
if filename:
|
||||
head += '; filename="' + filename + '"'
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
"""Verify an expanded release payload and run template-only update/rollback in isolation."""
|
||||
from pathlib import Path
|
||||
import argparse,hashlib,json,os,plistlib,shutil,subprocess,sys
|
||||
from product_identity import PRODUCT
|
||||
root=Path(__file__).resolve().parent.parent
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--version',default='1.2.0')
|
||||
parser.add_argument('--build',default='14')
|
||||
parser.add_argument('--version',default=PRODUCT['version'])
|
||||
parser.add_argument('--build',default=PRODUCT['build'])
|
||||
parser.add_argument('--expanded',default='build/pkg-expanded')
|
||||
parser.add_argument('--fixture',default='build/pkg-isolation')
|
||||
args=parser.parse_args()
|
||||
expanded=root/args.expanded/'Payload.pkg';payload=expanded/'Payload'
|
||||
formal=Path('/Library/Plug-Ins/FxPlug/beatygril.app');packed=payload/'Library/Plug-Ins/FxPlug/beatygril.app'
|
||||
formal=Path('/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app');packed=payload/'Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app'
|
||||
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
def files(p):
|
||||
return {str(x.relative_to(p)):('symlink:'+os.readlink(x) if x.is_symlink() else sha(x)) for x in p.rglob('*') if x.is_file() or x.is_symlink()}
|
||||
@@ -33,7 +34,10 @@ base=root/args.fixture;base.mkdir(exist_ok=False)
|
||||
volume=base/'volume';home=base/'user';home.mkdir()
|
||||
start=post.index('CURRENT_ACTIVE=0')
|
||||
isolated='#!/bin/zsh\nset -eu\nTARGET_VOLUME="'+str(volume)+'"\nCONSOLE_USER="'+os.getlogin()+'"\nCONSOLE_UID="'+str(os.getuid())+'"\nCONSOLE_GROUP="staff"\nUSER_HOME="'+str(home)+'"\n'+post[start:]
|
||||
isolated=isolated[:isolated.index('fx_verify_app "${TARGET_VOLUME%/}/Library/Plug-Ins/FxPlug/beatygril.app"')]+'exit 0\n'
|
||||
stop = isolated.find('# FCP-BeautyGirl identity migration.')
|
||||
if stop < 0:
|
||||
stop = isolated.index('fx_verify_app "${TARGET_VOLUME%/}/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app"')
|
||||
isolated=isolated[:stop]+'exit 0\n'
|
||||
isolated=isolated.replace('/usr/sbin/chown -R','/usr/bin/true')
|
||||
new_copy=next(l for l in isolated.splitlines() if l.startswith('/usr/bin/ditto "${TARGET_VOLUME%/}'))
|
||||
def refill():
|
||||
@@ -66,7 +70,7 @@ assert files(final)==original
|
||||
assert all((final/n).stat().st_flags & 2 for n in ['large.png','small.png'])
|
||||
backups=list((home/'Library/Application Support/PKGForge/Template Backups').rglob('.locked-paths'))
|
||||
assert backups and all('/Movies/' not in str(x) for x in backups)
|
||||
package=root/('releases/'+args.version+'/大鱼人像美化-'+args.version+'.pkg')
|
||||
package=root/('releases/'+args.version+'/'+PRODUCT['name']+'-'+args.version+'.pkg')
|
||||
info=plistlib.loads((packed/'Contents/Info.plist').read_bytes())
|
||||
assert info['CFBundleShortVersionString']==args.version and info['CFBundleVersion']==args.build
|
||||
report={'package':str(package.relative_to(root)),'bytes':package.stat().st_size,'sha256':sha(package),'version':args.version,'build':args.build,'app_files_compared':len(files(formal)),'app_byte_and_symlink_equality':True,'template_files':{n:sha(template/n) for n in runtime},'locked_relative_paths':['大鱼人像美化/large.png','大鱼人像美化/small.png'],'script_sha256':{'preinstall':sha(expanded/'Scripts/preinstall'),'postinstall':sha(expanded/'Scripts/postinstall')},'isolated_script_tests':['fresh install','replace old locked thumbnail','partial copy failure restores old bytes and locks'],'isolation_changes':['Console identity/home redirected to task fixture','chown replaced with true','FxPlug verification skipped; formal app signature and discovery verified separately'],'real_installer_execution':False,'app_and_template_installed':'APP installed through signed delivery workflow; formal template files matched the specified installed category, not changed by running this PKG','fonts':'No editable third-party font dependency; promotional lettering baked into PNG. No optional font ZIP needed.'}
|
||||
|
||||
+11
-11
@@ -5,7 +5,7 @@
|
||||
#import <Metal/Metal.h>
|
||||
#import <CoreVideo/CoreVideo.h>
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#import "beatygrilPlugIn.h"
|
||||
#import "FCPBeautyGirlPlugIn.h"
|
||||
#import "PGParameterIDs.h"
|
||||
#import "PGFaceProcessor.h"
|
||||
#import "PGBeautyRenderer.h"
|
||||
@@ -209,19 +209,19 @@ static NSData *ExpectedCrop(PGHostTile *source, NSUInteger x, NSUInteger y, NSUI
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static NSData *Snapshot(beatygrilPlugIn *plugin, CMTime time) {
|
||||
static NSData *Snapshot(FCPBeautyGirlPlugIn *plugin, CMTime time) {
|
||||
NSData *state = nil; NSError *error = nil;
|
||||
Check([plugin pluginState:&state atTime:time quality:kFxQuality_HIGH error:&error] && state.length > 0 && !error, @"plugin snapshot serializes through host parameter API");
|
||||
return state;
|
||||
}
|
||||
static BOOL Render(beatygrilPlugIn *plugin, PGHostTile *destination, NSArray *sources, NSData *state, CMTime time, NSError **error, BOOL *escaped) {
|
||||
static BOOL Render(FCPBeautyGirlPlugIn *plugin, PGHostTile *destination, NSArray *sources, NSData *state, CMTime time, NSError **error, BOOL *escaped) {
|
||||
@try { return [plugin renderDestinationImage:destination sourceImages:sources pluginState:state atTime:time error:error]; }
|
||||
@catch (NSException *exception) {
|
||||
if (escaped) *escaped = YES;
|
||||
fprintf(stderr, "EXCEPTION: %s\n", exception.reason.UTF8String); return NO;
|
||||
}
|
||||
}
|
||||
static void RejectState(beatygrilPlugIn *plugin, PGHostTile *destination, PGHostTile *source, NSData *state, NSString *description) {
|
||||
static void RejectState(FCPBeautyGirlPlugIn *plugin, PGHostTile *destination, PGHostTile *source, NSData *state, NSString *description) {
|
||||
NSError *error = nil; BOOL escaped = NO;
|
||||
BOOL rendered = Render(plugin, destination, @[source], state, kCMTimeZero, &error, &escaped);
|
||||
Check(!rendered && !escaped && error != nil, description);
|
||||
@@ -353,7 +353,7 @@ static void PortraitAnatomyTests(NSString *fixture, CIImage *portrait) {
|
||||
NSData *originBaseline = nil;
|
||||
for (NSUInteger sourceTop = 0; sourceTop < 2; sourceTop++) for (NSUInteger destinationTop = 0; destinationTop < 2; destinationTop++) {
|
||||
@autoreleasepool {
|
||||
PGHostAPI *api = [PGHostAPI new]; beatygrilPlugIn *plugin = [[beatygrilPlugIn alloc] initWithAPIManager:api];
|
||||
PGHostAPI *api = [PGHostAPI new]; FCPBeautyGirlPlugIn *plugin = [[FCPBeautyGirlPlugIn alloc] initWithAPIManager:api];
|
||||
Check([plugin addParametersWithError:nil], @"anatomy case creates the real plugin parameters");
|
||||
if (effect) api.values[@(controls[effect])] = @(values[effect]);
|
||||
PGHostTile *input = Tile(width, height, 37, -21, sourceTop ? kFxImageOrigin_TOP_LEFT : kFxImageOrigin_BOTTOM_LEFT);
|
||||
@@ -406,7 +406,7 @@ static void TrackingCoordinateTests(NSDictionary *archive, NSDictionary *hostVal
|
||||
values[@(PGTarget)] = @2; values[@(PGTrackEnabled)] = @YES; values[@(PGBigEye)] = @1; values[@(PGChin)] = @1;
|
||||
values[@(PGTrackData3)] = legacyString;
|
||||
PGHostAPI *legacyAPI = [PGHostAPI new]; legacyAPI.hostTime = time;
|
||||
beatygrilPlugIn *legacy = [[beatygrilPlugIn alloc] initWithAPIManager:legacyAPI]; [legacy addParametersWithError:nil]; legacyAPI.values = [values mutableCopy];
|
||||
FCPBeautyGirlPlugIn *legacy = [[FCPBeautyGirlPlugIn alloc] initWithAPIManager:legacyAPI]; [legacy addParametersWithError:nil]; legacyAPI.values = [values mutableCopy];
|
||||
PGHostTile *output = Tile(512,512,0,0,kFxImageOrigin_TOP_LEFT);
|
||||
Check(Render(legacy, output, @[source], Snapshot(legacy,time), time, nil, NULL), @"unmarked old coordinate archive renders through the real adapter");
|
||||
NSData *legacyBytes = SurfaceBytes(output);
|
||||
@@ -420,7 +420,7 @@ static void TrackingCoordinateTests(NSDictionary *archive, NSDictionary *hostVal
|
||||
forged[@"hostCoordinateSpace"] = @"iosurface-ci-y-up-v1";
|
||||
NSData *trustedTrack = PlistState(forged);
|
||||
PGHostAPI *trustedAPI = [PGHostAPI new]; trustedAPI.hostTime = time;
|
||||
beatygrilPlugIn *trusted = [[beatygrilPlugIn alloc] initWithAPIManager:trustedAPI]; [trusted addParametersWithError:nil];
|
||||
FCPBeautyGirlPlugIn *trusted = [[FCPBeautyGirlPlugIn alloc] initWithAPIManager:trustedAPI]; [trusted addParametersWithError:nil];
|
||||
trustedAPI.values = [values mutableCopy]; trustedAPI.values[@(PGTrackData3)] = [trustedTrack base64EncodedStringWithOptions:0];
|
||||
Check(Render(trusted, output, @[source], Snapshot(trusted,time), time, nil, NULL), @"fresh plugin accepts a snapshot with marked legacy analysis data");
|
||||
NSData *trustedBytes = SurfaceBytes(output);
|
||||
@@ -465,7 +465,7 @@ static BOOL Preview(PGHostTile *before, PGHostTile *after, NSString *path) {
|
||||
int main(int argc, const char *argv[]) {
|
||||
setbuf(stdout, NULL);
|
||||
@autoreleasepool {
|
||||
PGHostAPI *api = [PGHostAPI new]; beatygrilPlugIn *plugin = [[beatygrilPlugIn alloc] initWithAPIManager:api];
|
||||
PGHostAPI *api = [PGHostAPI new]; FCPBeautyGirlPlugIn *plugin = [[FCPBeautyGirlPlugIn alloc] initWithAPIManager:api];
|
||||
NSError *error = nil;
|
||||
Check([plugin addParametersWithError:&error] && !error, @"host creates complete parameter set");
|
||||
UInt32 identifiers[] = {PGAmount,PGBrightness,PGRadius,PGSharpness,PGMix,PGThreshold,PGSoftness,PGEdgeProtect,PGShowMask,PGMidtone,PGHue,PGSaturation,PGHighPass,PGThin,PGFaceBoxes,PGSingleFace,PGSelection,PGShort,PGNarrow,PGChin,PGVFace,PGJaw,PGCheekbone,PGSmallHead,PGSmallHeadRange,PGFeatureProtect,PGTeeth,PGEyeBright,PGBigEye,PGTrackBoxes,PGFastMulti,PGJumpGuard,PGEvenTone,PGTonePreset,PGInvertMask,PGDebugChannel,PGTrackEnabled,PGTarget,PGAnalyzeForward,PGAnalyzeBackward,PGClearTrack,PGStatus,PGTrackData1,PGTrackData2,PGTrackData3,PGSmoothingEnabled,PGColorEnabled,PGShapeEnabled,PGDetailEnabled,PGInspectorPanel};
|
||||
@@ -589,7 +589,7 @@ int main(int argc, const char *argv[]) {
|
||||
TrackingCoordinateTests(trackingArchive,api.values,portraitSource,portrait,api.hostTime);
|
||||
Check([api.values[@(PGTrackEnabled)] boolValue] && CMTimeCompare(api.lastSetBoolTime, api.hostTime) == 0, @"analysis enables saved target at host seed time");
|
||||
NSData *trackedState = Snapshot(plugin, api.hostTime);
|
||||
beatygrilPlugIn *restoredPlugin = [[beatygrilPlugIn alloc] initWithAPIManager:api];
|
||||
FCPBeautyGirlPlugIn *restoredPlugin = [[FCPBeautyGirlPlugIn alloc] initWithAPIManager:api];
|
||||
Check(Render(restoredPlugin, portraitOutput, @[portraitSource], trackedState, api.hostTime, &error, NULL), @"fresh plugin instance uses automatic follow despite serialized legacy analysis settings");
|
||||
[plugin analyzeBackward];
|
||||
Check(api.backwardRequests == 1, @"backward analyze button calls host API");
|
||||
@@ -598,7 +598,7 @@ int main(int argc, const char *argv[]) {
|
||||
[plugin setupAnalysisForTimeRange:desired frameDuration:CMTimeMake(1, 24) error:&error]; [plugin cleanupAnalysis:&error];
|
||||
[plugin clearTracking]; Check([api.values[@(PGTrackData3)] length] == 0, @"clear button uses current host target");
|
||||
PGHostAPI *writeFailureAPI = [PGHostAPI new];
|
||||
beatygrilPlugIn *writeFailurePlugin = [[beatygrilPlugIn alloc] initWithAPIManager:writeFailureAPI];
|
||||
FCPBeautyGirlPlugIn *writeFailurePlugin = [[FCPBeautyGirlPlugIn alloc] initWithAPIManager:writeFailureAPI];
|
||||
[writeFailurePlugin addParametersWithError:&error]; [writeFailurePlugin analyzeForward];
|
||||
[writeFailurePlugin setupAnalysisForTimeRange:clip frameDuration:CMTimeMake(1, 24) error:&error];
|
||||
[writeFailurePlugin analyzeFrame:portraitSource atTime:writeFailureAPI.hostTime error:&error];
|
||||
@@ -608,7 +608,7 @@ int main(int argc, const char *argv[]) {
|
||||
[writeFailurePlugin analyzeForward];
|
||||
Check(writeFailureAPI.forwardRequests == 2, @"persistence failure releases analysis busy state for retry");
|
||||
PGHostAPI *unavailable = [PGHostAPI new]; [unavailable.disabledAPIs addObject:@"FxParameterCreationAPI_v5"];
|
||||
beatygrilPlugIn *missingPlugin = [[beatygrilPlugIn alloc] initWithAPIManager:unavailable];
|
||||
FCPBeautyGirlPlugIn *missingPlugin = [[FCPBeautyGirlPlugIn alloc] initWithAPIManager:unavailable];
|
||||
error = nil; Check(![missingPlugin addParametersWithError:&error] && error != nil, @"missing host creation API reports error");
|
||||
[unavailable.disabledAPIs removeAllObjects]; unavailable.rejectedParameter = PGThin;
|
||||
error = nil; Check(![missingPlugin addParametersWithError:&error] && error != nil, @"host rejecting one control fails complete parameter creation");
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import importlib.util, json, pathlib, plistlib, subprocess, tempfile, unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
spec = importlib.util.spec_from_file_location('product_identity', ROOT / 'scripts/product_identity.py')
|
||||
identity = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(identity)
|
||||
|
||||
class IdentityMigrationTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory(prefix='beautygirl-identity-test.')
|
||||
self.volume = pathlib.Path(self.tmp.name)
|
||||
self.legacy = identity.PRODUCT['legacyProducts'][0]
|
||||
self.old = self.volume / pathlib.Path(self.legacy['installationPath']).relative_to('/')
|
||||
(self.old / 'Contents/PlugIns' / self.legacy['pluginName'] / 'Contents').mkdir(parents=True)
|
||||
(self.old / 'Contents/Info.plist').write_bytes(plistlib.dumps({'CFBundleIdentifier': self.legacy['bundleIdentifier']}))
|
||||
(self.old / 'Contents/PlugIns' / self.legacy['pluginName'] / 'Contents/Info.plist').write_bytes(plistlib.dumps({'ProPlugPlugInList': [{'uuid': identity.PRODUCT['filterUUID']}]}))
|
||||
self.signature = patch.object(identity.subprocess, 'run', return_value=subprocess.CompletedProcess([], 0, '', 'TeamIdentifier=B3W25FX5H5\n'))
|
||||
self.signature.start()
|
||||
self.migration = identity.LegacyMigration(self.volume / 'backups', volume=self.volume)
|
||||
|
||||
def tearDown(self):
|
||||
self.signature.stop()
|
||||
self.tmp.cleanup()
|
||||
|
||||
def test_success_removes_only_validated_old_product(self):
|
||||
original = (self.old / 'Contents/Info.plist').read_bytes()
|
||||
unrelated = self.old.parent / 'Unrelated.app'
|
||||
unrelated.mkdir()
|
||||
calls = []
|
||||
self.migration.relocate(calls.append)
|
||||
self.assertFalse(self.old.exists())
|
||||
self.assertEqual(len(calls), 1)
|
||||
self.assertEqual((self.migration.moved[0][2] / 'Contents/Info.plist').read_bytes(), original)
|
||||
self.assertEqual(len(self.migration.cleanup()), 1)
|
||||
self.assertTrue(unrelated.exists())
|
||||
|
||||
def test_failed_new_install_restores_old_bytes_and_registration(self):
|
||||
original = (self.old / 'Contents/Info.plist').read_bytes()
|
||||
self.migration.relocate(lambda p: None)
|
||||
calls = []
|
||||
self.migration.rollback(calls.append)
|
||||
self.assertEqual((self.old / 'Contents/Info.plist').read_bytes(), original)
|
||||
self.assertEqual(calls, [self.old / 'Contents/PlugIns' / self.legacy['pluginName']])
|
||||
|
||||
def test_wrong_bundle_id_is_never_moved(self):
|
||||
(self.old / 'Contents/Info.plist').write_bytes(plistlib.dumps({'CFBundleIdentifier': 'com.unrelated.product'}))
|
||||
with self.assertRaisesRegex(RuntimeError, 'identity mismatch'):
|
||||
self.migration.relocate(lambda p: self.fail('Must not deregister another product'))
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_wrong_filter_uuid_is_never_moved(self):
|
||||
(self.old / 'Contents/PlugIns' / self.legacy['pluginName'] / 'Contents/Info.plist').write_bytes(plistlib.dumps({'ProPlugPlugInList': []}))
|
||||
with self.assertRaisesRegex(RuntimeError, 'filter UUID'):
|
||||
self.migration.relocate(lambda p: self.fail('Must not deregister another filter'))
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_invalid_signature_keeps_original(self):
|
||||
with patch.object(identity.subprocess, 'run', side_effect=subprocess.CalledProcessError(1, 'codesign')):
|
||||
with self.assertRaises(subprocess.CalledProcessError):
|
||||
self.migration.relocate(lambda p: self.fail('Unverified APP must remain'))
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_existing_backup_cannot_be_overwritten(self):
|
||||
backup = self.volume / 'backups' / ('legacy-' + self.legacy['name'] + '.app')
|
||||
backup.mkdir(parents=True)
|
||||
with self.assertRaisesRegex(RuntimeError, 'already exists'):
|
||||
self.migration.relocate(lambda p: self.fail('Recovery copy must be preserved'))
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_failed_deregistration_keeps_old_installation(self):
|
||||
def unavailable(path):
|
||||
raise RuntimeError('registry unavailable')
|
||||
with self.assertRaisesRegex(RuntimeError, 'registry unavailable'):
|
||||
self.migration.relocate(unavailable)
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_project_and_legacy_template_share_stable_filter_identity(self):
|
||||
p = identity.PRODUCT
|
||||
pbx = (ROOT / p['xcodeProject'] / 'project.pbxproj').read_text()
|
||||
self.assertEqual(pbx.count('PRODUCT_BUNDLE_IDENTIFIER = ' + p['bundleIdentifier'] + ';'), 2)
|
||||
self.assertEqual(pbx.count('PRODUCT_BUNDLE_IDENTIFIER = ' + p['pluginBundleIdentifier'] + ';'), 2)
|
||||
self.assertNotIn('beatygril', pbx)
|
||||
plugin = plistlib.loads((ROOT / p['sourceDirectory'] / 'Plugin/Info.plist').read_bytes())
|
||||
self.assertEqual(plugin['ProPlugPlugInList'][0]['className'], p['pluginClass'])
|
||||
self.assertEqual(plugin['ProPlugPlugInList'][0]['uuid'], p['filterUUID'])
|
||||
self.assertEqual(plugin['CFBundleShortVersionString'], p['version'])
|
||||
self.assertIn('pluginUUID="' + p['filterUUID'] + '"', (ROOT / 'Templates/大鱼人像美化/大鱼人像美化.moef').read_text())
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
+8
-8
@@ -56,7 +56,7 @@ static PGInspectorState *State(NSDictionary *values,NSSet *disabled) {
|
||||
return [PGInspectorState stateWithValues:values disabledParameters:disabled?:[NSSet set] hasEdits:YES];
|
||||
}
|
||||
static void RegistrationTests(void) {
|
||||
PGHostAPI *api=[PGHostAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
PGHostAPI *api=[PGHostAPI new];FCPBeautyGirlPlugIn *plugin=[[FCPBeautyGirlPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
NSUInteger visible=0;for(NSNumber *key in api.definitions){NSDictionary *d=api.definitions[key];if(!([d[@"flags"]unsignedIntValue]&kFxParameterFlag_HIDDEN)){visible++;Check(key.unsignedIntValue==PGInspectorPanel,@"only the custom panel is visible in the native parameter list");}}
|
||||
Check(visible==1,@"all duplicate standard controls and manual-analysis groups are hidden");
|
||||
FxParameterFlags flags=[api.definitions[@(PGInspectorPanel)][@"flags"]unsignedIntValue];
|
||||
@@ -136,7 +136,7 @@ static void ModelTests(void) {
|
||||
Check(s.faceChin==0 && s.faceBigEye==0 && s.skinBrightness==0 && s.skinTonePreset==0 && s.midtoneDetail==0 && s.highPassDetail==0 && s.showTrackBox==0,@"card eye switches bypass the complete associated effect group");
|
||||
}
|
||||
static void ControllerTests(void) {
|
||||
PGInspectorTestAPI *api=[PGInspectorTestAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
PGInspectorTestAPI *api=[PGInspectorTestAPI new];FCPBeautyGirlPlugIn *plugin=[[FCPBeautyGirlPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
api.values[@(PGMidtone)]=@.5;
|
||||
PGInspectorController *c=[[PGInspectorController alloc]initWithAPIManager:api];[c refresh];
|
||||
Check(c.available && Near([c.state valueForParameter:PGMidtone],.5) && !c.state.hasEdits,@"initial UI reads the current legacy/template values instead of replacing them");
|
||||
@@ -169,7 +169,7 @@ static void ControllerTests(void) {
|
||||
[c setValue:.1 forParameter:PGBrightness];Check(Near([c.state valueForParameter:PGChin],-.8),@"new-time edits preserve the other controls from that time instead of a stale prior frame");
|
||||
api.values=[api.undoValues mutableCopy];[c documentDidChange];
|
||||
Check(Near([c.state valueForParameter:PGBrightness],[seekValues[@(PGBrightness)]doubleValue]),@"a host undo notification refreshes same-time state immediately without a timed wait");
|
||||
PGHostAPI *legacy=[PGHostAPI new];beatygrilPlugIn *legacyPlugin=[[beatygrilPlugIn alloc]initWithAPIManager:legacy];[legacyPlugin addParametersWithError:nil];
|
||||
PGHostAPI *legacy=[PGHostAPI new];FCPBeautyGirlPlugIn *legacyPlugin=[[FCPBeautyGirlPlugIn alloc]initWithAPIManager:legacy];[legacyPlugin addParametersWithError:nil];
|
||||
legacy.values[@(PGDetailEnabled)]=@NO;legacy.values[@(PGHighPass)]=@.6;legacy.values[@(PGSharpness)]=@.4;
|
||||
PGInspectorController *imported=[[PGInspectorController alloc]initWithAPIManager:legacy];[imported refresh];[imported setValue:.3 forParameter:PGBrightness];
|
||||
PGBeautySettings migrated=PGSettingsWithInspectorState(PGBeautyDefaults(),imported.state);
|
||||
@@ -185,7 +185,7 @@ static void ControllerTests(void) {
|
||||
Check(!offline.available && ![offline setValue:.2 forParameter:PGChin],@"missing host actions disable editing instead of unsafe parameter access");
|
||||
}
|
||||
static void ViewInteractionTests(void) {
|
||||
PGHostAPI *api=[PGHostAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
PGHostAPI *api=[PGHostAPI new];FCPBeautyGirlPlugIn *plugin=[[FCPBeautyGirlPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
PGInspectorView *view=(PGInspectorView *)[plugin createViewForParameterID:PGInspectorPanel];[view refreshFromHost];
|
||||
NSSlider *chin=(NSSlider *)FindControl(view,NSSlider.class,@"下巴");Check(chin!=nil && chin.enabled,@"custom view exposes an accessible native chin slider");
|
||||
chin.doubleValue=-.75;Fire(chin);PGInspectorState *s=api.values[@(PGInspectorPanel)];
|
||||
@@ -238,7 +238,7 @@ static void ViewInteractionTests(void) {
|
||||
}
|
||||
}
|
||||
static void BeautyControlInteractionTests(void) {
|
||||
PGInspectorTestAPI *api=[PGInspectorTestAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
PGInspectorTestAPI *api=[PGInspectorTestAPI new];FCPBeautyGirlPlugIn *plugin=[[FCPBeautyGirlPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
api.values[@(PGFeatureProtectionAmount)]=@.37;api.values[@(PGHighPass)]=@.63;api.values[@(PGHue)]=@12;api.values[@(PGBrightness)]=@.3;
|
||||
PGInspectorView *view=(PGInspectorView *)[plugin createViewForParameterID:PGInspectorPanel];[view refreshFromHost];
|
||||
Check(Near([view.controller.state valueForParameter:PGFeatureProtectionAmount],.37) && !view.controller.state.hasEdits,@"an untouched inspector preserves native fractional protection when the legacy boolean is enabled");
|
||||
@@ -276,7 +276,7 @@ static void BeautyControlInteractionTests(void) {
|
||||
Check(api.actionDepth==0 && api.actionStarts==api.actionEnds && api.accessViolations==0,@"all new numeric, bypass, reset and master interactions use balanced host actions");
|
||||
}
|
||||
static void HostHandshakeTests(void) {
|
||||
PGInspectorTestAPI *api=[PGInspectorTestAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
PGInspectorTestAPI *api=[PGInspectorTestAPI new];FCPBeautyGirlPlugIn *plugin=[[FCPBeautyGirlPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
api.values[@(PGMidtone)]=@.5;
|
||||
api.parameterAPIsRequireAction=YES;api.actionRequiresView=YES;
|
||||
PGInspectorView *view=(PGInspectorView *)[plugin createViewForParameterID:PGInspectorPanel];[view refreshFromHost];
|
||||
@@ -293,7 +293,7 @@ static void HostHandshakeTests(void) {
|
||||
Check(api.accessViolations==0 && api.actionDepth==0 && api.actionStarts==api.actionEnds,@"action-scoped connection, edits, failure and reconnection remain balanced");
|
||||
}
|
||||
static void ContinuousPreviewTests(void) {
|
||||
PGInspectorTestAPI *api=[PGInspectorTestAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
PGInspectorTestAPI *api=[PGInspectorTestAPI new];FCPBeautyGirlPlugIn *plugin=[[FCPBeautyGirlPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
api.parameterAPIsRequireAction=YES;api.actionRequiresView=YES;
|
||||
PGInspectorView *view=(PGInspectorView *)[plugin createViewForParameterID:PGInspectorPanel];[view refreshFromHost];
|
||||
NSArray *names=@[@"短脸",@"五官保护",@"非肤色区保护",@"微细锐化"],*ids=@[@(PGShort),@(PGFeatureProtectionAmount),@(PGNonSkinProtection),@(PGMicroDetail)];
|
||||
@@ -319,7 +319,7 @@ static void ContinuousPreviewTests(void) {
|
||||
static void OptionalInspectorPreview(void) {
|
||||
NSString *path=NSProcessInfo.processInfo.environment[@"PAINTFCP_INSPECTOR_PNG"];
|
||||
if(!path.length)return;
|
||||
PGHostAPI *api=[PGHostAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
PGHostAPI *api=[PGHostAPI new];FCPBeautyGirlPlugIn *plugin=[[FCPBeautyGirlPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||
api.values[@(PGMidtone)]=@.4;
|
||||
PGInspectorView *view=(PGInspectorView *)[plugin createViewForParameterID:PGInspectorPanel];[view refreshFromHost];
|
||||
[view setFrameSize:NSMakeSize(380,20)];
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
"""Run the generated installer migration in isolated volumes, never live paths."""
|
||||
import os, pathlib, plistlib, shlex, subprocess, sys, tempfile, unittest
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT / 'scripts'))
|
||||
from installer_identity import inject_migration, PRODUCT
|
||||
|
||||
class InstallerIdentityTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory(prefix='beautygirl-installer-test.')
|
||||
self.root = pathlib.Path(self.tmp.name)
|
||||
self.old = self.root / pathlib.Path(PRODUCT['legacyProducts'][0]['installationPath']).relative_to('/')
|
||||
self.new = self.root / pathlib.Path(PRODUCT['installationPath']).relative_to('/')
|
||||
for app, info in [(self.old, PRODUCT['legacyProducts'][0]), (self.new, PRODUCT)]:
|
||||
(app / 'Contents/PlugIns' / info['pluginName'] / 'Contents').mkdir(parents=True)
|
||||
(app / 'Contents/Info.plist').write_bytes(plistlib.dumps({'CFBundleIdentifier': info['bundleIdentifier']}))
|
||||
(app / 'Contents/PlugIns' / info['pluginName'] / 'Contents/Info.plist').write_bytes(plistlib.dumps({'ProPlugPlugInList': [{'uuid': PRODUCT['filterUUID']}]}))
|
||||
(app / 'payload-byte').write_bytes(b'original signed payload stand-in')
|
||||
self.signer = self.root / 'codesign-fixture'
|
||||
self.signer.write_text('#!/bin/zsh\napp="${argv[-1]}"\nif [[ -f "$app/.invalid" ]]; then exit 1; fi\n/bin/echo TeamIdentifier=B3W25FX5H5 >&2\n')
|
||||
self.signer.chmod(0o755)
|
||||
|
||||
def tearDown(self):
|
||||
self.tmp.cleanup()
|
||||
|
||||
def run_installer(self, fail_registration=False):
|
||||
base = '''#!/bin/zsh
|
||||
set -eu
|
||||
TARGET_VOLUME=@@VOLUME@@
|
||||
CONSOLE_UID=501
|
||||
CONSOLE_USER=fixture
|
||||
FX_FAILURE=0
|
||||
fx_verify_app() {
|
||||
/bin/echo "$1" >> @@LOG@@
|
||||
if [[ "$1" == "$BG_NEW_APP" && @@FAIL@@ == 1 ]]; then return 1; fi
|
||||
return 0
|
||||
}
|
||||
trap - EXIT
|
||||
fx_verify_app "${TARGET_VOLUME}/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app" || FX_FAILURE=1
|
||||
if (( FX_FAILURE )); then exit 1; fi
|
||||
exit 0
|
||||
'''
|
||||
base = base.replace('@@VOLUME@@', shlex.quote(str(self.root))).replace('@@LOG@@', shlex.quote(str(self.root / 'registration-calls'))).replace('@@FAIL@@', '1' if fail_registration else '0')
|
||||
code = inject_migration(base)
|
||||
# Only signature and user-session registration are isolated. Identity
|
||||
# parsing, file moves, deletes, traps and rollback run unchanged.
|
||||
code = code.replace('/usr/bin/codesign', shlex.quote(str(self.signer)))
|
||||
code = code.replace('/bin/launchctl asuser', '/usr/bin/true asuser')
|
||||
script = self.root / 'postinstall'
|
||||
script.write_text(code)
|
||||
subprocess.run(['/bin/zsh', '-n', str(script)], check=True, capture_output=True)
|
||||
return subprocess.run(['/bin/zsh', str(script)], text=True, capture_output=True)
|
||||
|
||||
def test_upgrade_keeps_new_and_removes_old_and_recovery_copy(self):
|
||||
self.assertEqual(self.run_installer().returncode, 0)
|
||||
self.assertTrue(self.new.exists())
|
||||
self.assertFalse(self.old.exists())
|
||||
self.assertFalse(list((self.root / 'Library/Application Support').rglob('*.app')))
|
||||
|
||||
def test_registration_failure_restores_old_and_removes_new(self):
|
||||
original = (self.old / 'payload-byte').read_bytes()
|
||||
result = self.run_installer(True)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertFalse(self.new.exists())
|
||||
self.assertEqual((self.old / 'payload-byte').read_bytes(), original)
|
||||
self.assertIn(str(self.old), (self.root / 'registration-calls').read_text())
|
||||
|
||||
def test_invalid_new_signature_does_not_touch_old(self):
|
||||
(self.new / '.invalid').touch()
|
||||
self.assertNotEqual(self.run_installer().returncode, 0)
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_foreign_old_bundle_id_is_preserved(self):
|
||||
(self.old / 'Contents/Info.plist').write_bytes(plistlib.dumps({'CFBundleIdentifier': 'com.unrelated.product'}))
|
||||
self.assertNotEqual(self.run_installer().returncode, 0)
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_invalid_old_signature_is_preserved(self):
|
||||
(self.old / '.invalid').touch()
|
||||
self.assertNotEqual(self.run_installer().returncode, 0)
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_fresh_install_and_repeat_are_idempotent(self):
|
||||
import shutil
|
||||
shutil.rmtree(self.old)
|
||||
self.assertEqual(self.run_installer().returncode, 0)
|
||||
self.assertEqual(self.run_installer().returncode, 0)
|
||||
self.assertTrue(self.new.exists())
|
||||
|
||||
def test_unrelated_neighbor_is_preserved(self):
|
||||
unrelated = self.old.parent / 'OtherProduct.app'
|
||||
unrelated.mkdir()
|
||||
self.assertEqual(self.run_installer().returncode, 0)
|
||||
self.assertTrue(unrelated.exists())
|
||||
|
||||
@unittest.skipUnless(os.environ.get('FCP_BEAUTYGIRL_POSTINSTALL'), 'Final PKG is not built yet')
|
||||
def test_final_package_contains_the_tested_migration_block(self):
|
||||
actual = pathlib.Path(os.environ['FCP_BEAUTYGIRL_POSTINSTALL']).read_text()
|
||||
generated = inject_migration('\ntrap - EXIT\nfx_verify_app candidate\nexit 0\n')
|
||||
start = generated.index('# FCP-BeautyGirl identity migration.')
|
||||
end = generated.rindex('\nfx_verify_app ')
|
||||
self.assertIn(generated[start:end], actual)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
在新工单中引用
屏蔽一个用户