diff --git a/dist/hpl-toolbox-0.1.6.exe b/dist/hpl-toolbox-0.1.6.exe index 1c95734..dc85d6e 100644 Binary files a/dist/hpl-toolbox-0.1.6.exe and b/dist/hpl-toolbox-0.1.6.exe differ diff --git a/dist/hpl-toolbox-0.1.6.vsix b/dist/hpl-toolbox-0.1.6.vsix index e6fadeb..37951c8 100644 Binary files a/dist/hpl-toolbox-0.1.6.vsix and b/dist/hpl-toolbox-0.1.6.vsix differ diff --git a/src/tools/playworksConverter.ts b/src/tools/playworksConverter.ts index d157a56..a347dfd 100644 --- a/src/tools/playworksConverter.ts +++ b/src/tools/playworksConverter.ts @@ -75,6 +75,11 @@ export function openPlayworksConverter(_context: vscode.ExtensionContext) { } const results: NetworkResult[] = []; + try { + results.push({ network: 'un', file: copyUnityInput(opts), ok: true }); + } catch (e: any) { + results.push({ network: 'un', file: '', ok: false, error: e.message }); + } for (const network of opts.networks) { try { const filePath = await convertNetwork(html, opts, network); @@ -94,24 +99,25 @@ export function openPlayworksConverter(_context: vscode.ExtensionContext) { }); } -const ZIPPED_NETWORKS = new Set(['gg', 'vu', 'mtg']); -const SUPPORTED_NETWORKS = new Set(['al', 'is', 'fb', 'gg', 'mo', 'vu', 'mtg', 'un', 'tt']); -const MRAID_NETWORKS = new Set(['al', 'is', 'un']); +const ZIPPED_NETWORKS = new Set(['gg', 'ap', 'vu', 'mtg']); +const SUPPORTED_NETWORKS = new Set(['al', 'is', 'fb', 'gg', 'ap', 'mo', 'vu', 'mtg', 'tt']); +const MRAID_NETWORKS = new Set(['al', 'is']); +const EXITAPI_NETWORKS = new Set(['gg', 'ap']); const NETWORK_OUTPUT_FOLDERS: Record = { al: 'Applovin', is: 'Ironsource', fb: 'Facebook', gg: 'GoogleAds', + ap: 'Appreciate', mo: 'Moloco', vu: 'Vungle', mtg: 'Mintegral', - un: 'Unity', tt: 'TikTok', }; function sourceBaseName(sourcePath: string): string { return path.basename(sourcePath, path.extname(sourcePath)) - .replace(/_(?:un|al|is|fb|gg|mo|vu|mtg|tt)$/, ''); + .replace(/_(?:un|al|is|fb|gg|ap|mo|vu|mtg|tt)$/, ''); } function outputBaseName(opts: ConvertOptions): string { @@ -149,6 +155,18 @@ async function convertNetwork(html: string, opts: ConvertOptions, network: strin return outPath; } +function copyUnityInput(opts: ConvertOptions): string { + const baseName = outputBaseName(opts); + const outputDir = path.join(opts.outputDir, 'Unity'); + const outPath = path.join(outputDir, `${baseName}_un.html`); + fs.mkdirSync(outputDir, { recursive: true }); + if (path.resolve(opts.sourcePath) === path.resolve(outPath)) { + return outPath; + } + fs.copyFileSync(opts.sourcePath, outPath); + return outPath; +} + function transformHtml(html: string, network: string): string { let result = sanitizePlayworksHtml(html); @@ -159,7 +177,7 @@ function transformHtml(html: string, network: string): string { let headInject = buildLifecycleScript(); if (MRAID_NETWORKS.has(network)) { headInject += '' + buildMraidComplianceScript(); - } else if (network === 'gg') { + } else if (EXITAPI_NETWORKS.has(network)) { headInject += ''; } result = injectBeforeHeadClose(result, headInject); @@ -205,7 +223,7 @@ function trimAfterFirstHtmlClose(html: string): string { function finalizeNetworkHtml(html: string, network: string): string { let result = cleanupPlayworksHtml(html); result = dedupeScriptSrc(result, 'mraid.js', MRAID_NETWORKS.has(network)); - result = dedupeScriptSrc(result, 'exitapi.js', network === 'gg'); + result = dedupeScriptSrc(result, 'exitapi.js', EXITAPI_NETWORKS.has(network)); return result; } @@ -349,7 +367,6 @@ function buildCTAScript(network: string): string { switch (network) { case 'al': case 'is': - case 'un': // MRAID — mraid.js injected in ctaLogic = `${closeCall}if(typeof mraid!=="undefined"&&typeof mraid.open==="function"){var s=typeof mraid.getState==="function"?mraid.getState():"default";if(s!=="loading"){mraid.open(o);return;}}window.open(o,"_blank");`; break; @@ -357,6 +374,7 @@ function buildCTAScript(network: string): string { ctaLogic = `${closeCall}if(typeof FbPlayableAd!=="undefined"&&typeof FbPlayableAd.onCTAClick==="function"){FbPlayableAd.onCTAClick();return;}window.open(o,"_blank");`; break; case 'gg': + case 'ap': // Google — exitapi.js injected in ctaLogic = `${closeCall}if(typeof ExitApi!=="undefined"&&typeof ExitApi.exit==="function"){ExitApi.exit();return;}window.open(o,"_blank");`; break; @@ -418,10 +436,10 @@ function getHtml(): string { { tag: 'is', label: 'Ironsource', note: 'HTML + mraid.js in head' }, { tag: 'fb', label: 'Facebook', note: 'HTML' }, { tag: 'gg', label: 'Google Ads', note: 'ZIP + exitapi.js in head' }, + { tag: 'ap', label: 'Appreciate', note: 'ZIP + exitapi.js in head' }, { tag: 'mo', label: 'Moloco', note: 'HTML + FbPlayableAd CTA' }, { tag: 'vu', label: 'Vungle', note: 'ZIP + __VUNGLE__ flag' }, { tag: 'mtg', label: 'Mintegral', note: 'ZIP + onload="gameReady()"' }, - { tag: 'un', label: 'Unity', note: 'HTML + mraid.js in head' }, { tag: 'tt', label: 'TikTok', note: 'HTML + __TIKTOK__ flag' }, ]; diff --git a/standalone/playworks.go b/standalone/playworks.go index ab34320..69d33c4 100644 --- a/standalone/playworks.go +++ b/standalone/playworks.go @@ -79,10 +79,10 @@ func PlayworksPage(w http.ResponseWriter, r *http.Request) { + - @@ -218,6 +218,12 @@ func PlayworksConvert(w http.ResponseWriter, r *http.Request) { } results := []playworksNetworkResult{} + unityPath, err := copyPlayworksUnityInput(data, opts) + if err != nil { + results = append(results, playworksNetworkResult{Network: "un", OK: false, Error: err.Error()}) + } else { + results = append(results, playworksNetworkResult{Network: "un", File: unityPath, OK: true}) + } for _, network := range opts.Networks { filePath, err := convertPlayworksNetwork(string(data), opts, network) if err != nil { @@ -229,7 +235,7 @@ func PlayworksConvert(w http.ResponseWriter, r *http.Request) { writeJSON(w, map[string]any{"results": results}) } -var playworksSuffixRx = regexp.MustCompile(`_(?:un|al|is|fb|gg|mo|vu|mtg|tt)$`) +var playworksSuffixRx = regexp.MustCompile(`_(?:un|al|is|fb|gg|ap|mo|vu|mtg|tt)$`) var playworksMraidScriptRx = regexp.MustCompile(`(?i)]*\bsrc\s*=\s*["']mraid\.js["'][^>]*>\s*\s*`) var playworksExitapiScriptRx = regexp.MustCompile(`(?i)]*\bsrc\s*=\s*["']exitapi\.js["'][^>]*>\s*\s*`) var playworksHeadCloseRx = regexp.MustCompile(`(?i)`) @@ -296,6 +302,19 @@ func convertPlayworksNetwork(htmlSrc string, opts playworksConvertOptions, netwo return outPath, nil } +func copyPlayworksUnityInput(data []byte, opts playworksConvertOptions) (string, error) { + baseName := playworksOutputBaseName(opts) + outputDir := filepath.Join(opts.OutputDir, "Unity") + if err := os.MkdirAll(outputDir, 0755); err != nil { + return "", err + } + outPath := filepath.Join(outputDir, fmt.Sprintf("%s_un.html", baseName)) + if err := os.WriteFile(outPath, data, 0644); err != nil { + return "", err + } + return outPath, nil +} + func playworksNetworkOutputFolder(network string) string { switch network { case "al": @@ -306,14 +325,14 @@ func playworksNetworkOutputFolder(network string) string { return "Facebook" case "gg": return "GoogleAds" + case "ap": + return "Appreciate" case "mo": return "Moloco" case "vu": return "Vungle" case "mtg": return "Mintegral" - case "un": - return "Unity" case "tt": return "TikTok" default: @@ -322,16 +341,20 @@ func playworksNetworkOutputFolder(network string) string { } func playworksNeedsZip(network string) bool { - return network == "gg" || network == "vu" || network == "mtg" + return network == "gg" || network == "ap" || network == "vu" || network == "mtg" } func playworksIsMraidNetwork(network string) bool { - return network == "al" || network == "is" || network == "un" + return network == "al" || network == "is" +} + +func playworksIsExitapiNetwork(network string) bool { + return network == "gg" || network == "ap" } func playworksIsSupportedNetwork(network string) bool { switch network { - case "al", "is", "fb", "gg", "mo", "vu", "mtg", "un", "tt": + case "al", "is", "fb", "gg", "ap", "mo", "vu", "mtg", "tt": return true default: return false @@ -343,7 +366,7 @@ func transformPlayworksHTML(htmlSrc, network string) string { headInject := buildPlayworksLifecycleScript() if playworksIsMraidNetwork(network) { headInject += `` + buildPlayworksMraidComplianceScript() - } else if network == "gg" { + } else if playworksIsExitapiNetwork(network) { headInject += `` } result = injectPlayworksBeforeHeadClose(result, headInject) @@ -385,7 +408,7 @@ func trimPlayworksAfterFirstHTMLClose(htmlSrc string) string { func finalizePlayworksNetworkHTML(htmlSrc, network string) string { result := cleanupPlayworksHTML(htmlSrc) result = dedupePlayworksScriptSrc(result, "mraid.js", playworksIsMraidNetwork(network)) - result = dedupePlayworksScriptSrc(result, "exitapi.js", network == "gg") + result = dedupePlayworksScriptSrc(result, "exitapi.js", playworksIsExitapiNetwork(network)) return result } @@ -565,11 +588,11 @@ func buildPlayworksCTAScript(network string) string { closeCall := `try{window.gameClose();}catch(e){}` ctaLogic := closeCall + `window.open(o,"_blank");` switch network { - case "al", "is", "un": + case "al", "is": ctaLogic = closeCall + `if(typeof mraid!=="undefined"&&typeof mraid.open==="function"){var s=typeof mraid.getState==="function"?mraid.getState():"default";if(s!=="loading"){mraid.open(o);return;}}window.open(o,"_blank");` case "fb": ctaLogic = closeCall + `if(typeof FbPlayableAd!=="undefined"&&typeof FbPlayableAd.onCTAClick==="function"){FbPlayableAd.onCTAClick();return;}window.open(o,"_blank");` - case "gg": + case "gg", "ap": ctaLogic = closeCall + `if(typeof ExitApi!=="undefined"&&typeof ExitApi.exit==="function"){ExitApi.exit();return;}window.open(o,"_blank");` case "mo": ctaLogic = closeCall + `if(typeof FbPlayableAd!=="undefined"&&typeof FbPlayableAd.onCTAClick==="function"){FbPlayableAd.onCTAClick();return;}if(typeof window.clickTag==="string"&&window.clickTag){window.open(window.clickTag,"_blank");return;}window.open(o,"_blank");` diff --git a/standalone/playworks_export_test.go b/standalone/playworks_export_test.go index 8137fa0..476c521 100644 --- a/standalone/playworks_export_test.go +++ b/standalone/playworks_export_test.go @@ -12,7 +12,7 @@ func TestExportPlayworksNetworksForRuntimeCheck(t *testing.T) { t.Skip("PLAYWORKS_EXPORT_DIR not set") } - sourcePath := filepath.Join("..", "aiAssets", "Playworks_UnityNetwork.html") + sourcePath := filepath.Join("..", "aiAssets", "DogCat3_Full.html") data, err := os.ReadFile(sourcePath) if err != nil { t.Fatalf("read source: %v", err) @@ -24,7 +24,11 @@ func TestExportPlayworksNetworksForRuntimeCheck(t *testing.T) { BaseName: "wat_mip_grhpl_dogcat3_05_real_na_noseason_en_full_na", } - for _, network := range []string{"al", "fb", "gg", "is", "mo", "mtg", "un", "vu"} { + if _, err := copyPlayworksUnityInput(data, opts); err != nil { + t.Fatalf("copy unity input: %v", err) + } + + for _, network := range []string{"al", "ap", "fb", "gg", "is", "mo", "mtg", "vu"} { if _, err := convertPlayworksNetwork(string(data), opts, network); err != nil { t.Fatalf("convert %s: %v", network, err) } diff --git a/standalone/playworks_test.go b/standalone/playworks_test.go index 8c62d0f..dcab366 100644 --- a/standalone/playworks_test.go +++ b/standalone/playworks_test.go @@ -8,7 +8,7 @@ import ( ) func TestTransformPlayworksHTMLNetworkRequirements(t *testing.T) { - sourcePath := filepath.Join("..", "aiAssets", "Playworks_UnityNetwork.html") + sourcePath := filepath.Join("..", "aiAssets", "DogCat3_Full.html") data, err := os.ReadFile(sourcePath) if err != nil { t.Fatalf("read source: %v", err) @@ -21,8 +21,8 @@ func TestTransformPlayworksHTMLNetworkRequirements(t *testing.T) { }{ {network: "al", wantMraid: 1}, {network: "is", wantMraid: 1}, - {network: "un", wantMraid: 1}, {network: "gg", wantExitapi: 1}, + {network: "ap", wantExitapi: 1}, {network: "fb"}, {network: "mo"}, {network: "vu"},