Skip to content

fix: prime video patches, log setting and race condition#24

Merged
WINOFFRG merged 2 commits intomasterfrom
fix/2303
Mar 28, 2026
Merged

fix: prime video patches, log setting and race condition#24
WINOFFRG merged 2 commits intomasterfrom
fix/2303

Conversation

@WINOFFRG
Copy link
Copy Markdown
Owner

@WINOFFRG WINOFFRG commented Mar 23, 2026

Summary by CodeRabbit

  • New Features

    • Added a selectable global log level in settings and rules UI.
    • Injects a Prime Video bootstrap to intercept and patch targeted player scripts.
  • Bug Fixes

    • Improved Prime Video blocking regex and fixed related race conditions.
    • Import button label cleaned up.
  • Chores

    • Product-insights now conditionally enabled; provider initialization gated.
    • Persistent storage and cross-tab sync for log level added.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 23, 2026

📝 Walkthrough

Walkthrough

Adds a Prime Video in-page bootstrap to intercept and patch target scripts, introduces global log level management with storage and messaging, conditionally gates PostHog via build/runtime flags with provider shims, and exposes log-level controls in the UI and background/content initialization.

Changes

Cohort / File(s) Summary
Prime Video bootstrap & injector
src/entrypoints/primevideo-bootstrap.ts, src/entrypoints/primevideo-interceptor.content.ts
New per-page bootstrap that detects, fetches, patches, and replaces Prime Video target scripts; installs DOM insertion hooks and mutation observers; content script injects the bootstrap at document_start.
Prime Video shared patching utilities
src/lib/apps/primevideo/script-patch-shared.ts
New shared utilities: URL regex, find/replace snippets, and patchPrimeVideoScriptContent() with change detection.
Prime Video integration cleanup
src/lib/apps/primevideo/script-watcher.ts, src/lib/apps/primevideo/config.ts
Removed startScriptTagInterceptor() and simplified patchScripts middleware to a no-op; config removed its onInit interceptor usage.
Log level system & logger updates
src/lib/logger.ts, src/lib/storage.ts, src/lib/messaging.ts
Introduces DEFAULT_LOG_LEVEL, LOG_LEVEL_OPTIONS, global log-level state and APIs (get/setGlobalLogLevel), storage item for log level, and new messaging types GET_LOG_LEVEL/SET_LOG_LEVEL with STORAGE_CHANGED payload extension.
Store & UI integration
src/hooks/useStore.tsx, src/components/App.tsx, src/components/OTTModal.tsx
Adds logLevel to RootState, actions/hooks (useLogLevel, useSetLogLevel, setLogLevel, updateLogLevelFromStorage), initializes from storage with gated PRODUCT_INSIGHTS_AVAILABLE, and OTTModal renders log-level selector and conditionally shows product-insights toggle.
Background & content entrypoint changes
src/entrypoints/background.ts, src/entrypoints/content/index.tsx, src/entrypoints/script.ts
Background initializes global log level and handles log-level GET/SET messages; product-insights reads/writes gated by PRODUCT_INSIGHTS_AVAILABLE. Content entrypoint swaps to ProductInsightsProvider. script.ts moves fetchApiPolyfill earlier.
PostHog conditionalization & providers
src/lib/posthog-enabled.ts, src/lib/posthog-disabled.ts, src/lib/posthog-impl.ts, src/lib/posthog-provider-enabled.tsx, src/lib/posthog-provider-disabled.tsx, src/lib/posthog-provider-impl.tsx, src/lib/posthog-provider.tsx, src/lib/posthog.ts
Refactors PostHog into enabled/disabled implementations with feature flag PRODUCT_INSIGHTS_AVAILABLE, exposes init/set/get helpers and diagnostics; re-exports and provider wrappers added; original posthog.ts now re-exports implementation.
Build & manifest updates
wxt.config.ts
Adds posthogBuildEnabled flag, Vite aliasing to select enabled/disabled PostHog implementations, and marks primevideo-bootstrap.js as web-accessible resource.
Changelog
.changeset/calm-deserts-deny.md
Adds changeset bump (minor) describing Prime Video blocking regex update and race condition fix.

Sequence Diagram(s)

sequenceDiagram
    participant PV as Prime Video Page
    participant PIC as Interceptor Content Script
    participant BS as Bootstrap Script
    participant Fetch as Fetch API

    PV->>PIC: page loads
    PIC->>PIC: determine isDev
    PIC->>BS: inject bootstrap (document_start)

    BS->>BS: set per-page bootstrapped flag
    BS->>BS: scan existing <script src>
    BS->>BS: install DOM hooks & MutationObserver

    PV->>BS: script element added/loaded
    BS->>BS: test URL via isPrimeVideoTargetScriptUrl
    alt target script
        BS->>Fetch: fetch original script
        Fetch-->>BS: script content
        BS->>BS: patchPrimeVideoScriptContent()
        BS->>BS: create inline patched <script>
        BS->>PV: replace original, preserve handlers, set dataset status
    else non-target
        BS->>PV: leave script unchanged
    end
Loading
sequenceDiagram
    participant User as User
    participant Modal as OTTModal
    participant Store as Zustand Store
    participant BG as Background
    participant Storage as AppStorage

    User->>Modal: select new log level
    Modal->>Store: setLogLevel(newLevel)
    Store->>Store: setGlobalLogLevel(newLevel)
    Store->>BG: sendMessage(SET_LOG_LEVEL, { level: newLevel })

    BG->>Storage: storageManager.setLogLevel(level)
    Storage-->>BG: persisted
    BG->>BG: setGlobalLogLevel(level)
    BG->>BG: broadcast STORAGE_CHANGED { logLevel }

    BG-->>Store: STORAGE_CHANGED { logLevel }
    Store->>Store: updateLogLevelFromStorage(level)
    Store->>Modal: re-render with updated logLevel
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 I hopped in code where Prime Video streams,
Patched sneaky scripts and tended logging beams,
A log-level carrot, a gated PostHog flower,
Tabs now whisper level changes hour by hour,
Hooray — small fixes make the extension bloom!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: it addresses Prime Video script patches, adds log level configuration, and fixes a race condition through the new bootstrap approach.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/2303

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
src/components/OTTModal.tsx (1)

397-421: Consider adding a descriptive title for the log level section.

The RuleSection uses an empty title="" which renders awkwardly. Adding a title like "Developer" or "Settings" would improve UX clarity.

💡 Suggested improvement
-                <RuleSection title="">
+                <RuleSection title="Developer">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/OTTModal.tsx` around lines 397 - 421, The RuleSection
rendering the log level control currently passes an empty title (title=""),
causing an awkward header; update the JSX where RuleSection is used (the
component containing the select for log level) to provide a meaningful title
such as "Developer" or "Settings" — locate the RuleSection instance that wraps
the log level select (references: RuleSection, LOG_LEVEL_OPTIONS,
handleLogLevelChange, logLevel) and change title="" to a descriptive string to
improve UX.
src/entrypoints/primevideo-interceptor.content.ts (1)

6-7: Remove unnecessary type assertion; the string literal naturally matches the expected type.

The as Parameters<typeof injectScript>[0] assertion is redundant. WXT's injectScript expects type ScriptPublicPath which accepts strings matching the pattern ${string}.js. The literal "/primevideo-bootstrap.js" already satisfies this type, as shown by the assertion-free usage of "/script.js" in src/entrypoints/content/index.tsx:54. Assign the string directly without the assertion:

const bootstrapPath = "/primevideo-bootstrap.js";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/entrypoints/primevideo-interceptor.content.ts` around lines 6 - 7, Remove
the redundant type assertion on bootstrapPath: the string literal
"/primevideo-bootstrap.js" already satisfies injectScript's parameter type
(ScriptPublicPath), so change the declaration of bootstrapPath (currently using
as Parameters<typeof injectScript>[0]) to a plain string assignment; update the
variable defined as bootstrapPath and ensure any call sites using injectScript
continue to pass bootstrapPath unchanged.
src/entrypoints/primevideo-bootstrap.ts (1)

199-216: Multiple interception mechanisms may process the same script.

The code has four interception paths: DOM method wrapping (lines 150-152), setAttribute override (lines 154-168), src property setter (lines 178-196), and MutationObserver (lines 199-210). Additionally, existing scripts are scanned at startup (lines 212-216).

The processedScripts WeakSet (line 27) correctly deduplicates, but consider adding a brief comment explaining this intentional defense-in-depth approach for future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/entrypoints/primevideo-bootstrap.ts` around lines 199 - 216, The multiple
interception mechanisms (DOM method wrapping, setAttribute override, src
property setter, MutationObserver and initial script scan) can appear redundant
to future readers; add a short clarifying comment near the processedScripts
WeakSet declaration (processedScripts) that explains this is intentional
defense-in-depth and that deduplication is handled by processedScripts, and also
add brief inline notes next to the functions inspectNode and replaceScript (and
the MutationObserver/initial querySelectorAll usage) describing which
interception each covers so maintainers understand why all four paths are
present and that processedScripts prevents double-processing.
src/lib/logger.ts (1)

23-39: loggerInstances Set holds strong references - potential memory consideration.

The loggerInstances Set keeps references to all created Logger instances (including child loggers from line 464). If loggers are created dynamically at runtime (not just at module initialization), they won't be garbage collected.

For the current usage pattern where loggers are created at module level (e.g., export const logger = createLogger("app")), this is acceptable. However, if dynamic logger creation becomes common, consider using a WeakSet or adding a cleanup method.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/logger.ts` around lines 23 - 39, loggerInstances currently uses a
strong Set which prevents garbage collection of dynamically created Logger
instances (including child loggers created by createLogger and
Logger.createChild); change loggerInstances to a WeakSet<Logger> so references
do not keep loggers alive, and ensure any code that iterates loggerInstances
(e.g., setGlobalLogLevel) still works with WeakSet semantics (iterate by
tracking registrations or update setGlobalLogLevel to store the level and call
Logger.setLevel on new registrations), or alternatively add
registerLogger/unregisterLogger helpers and call unregister from
Logger.dispose()/createChild to remove references when loggers are no longer
needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/entrypoints/primevideo-bootstrap.ts`:
- Around line 41-51: fetchPatchedScript is calling the app-level fetch which is
intercepted by the patchScripts middleware, causing duplicate patching when you
then call patchPrimeVideoScriptContent; modify fetchPatchedScript to bypass
middleware by using the original unwrapped fetch (e.g., ctx.originalFetch) or
add an internal marker header (like X-Internal-Fetch: true) and update the
patchScripts middleware to skip processing when that marker is present; update
references to scriptCache and fetchPatchedScript to use the new bypass so
bootstrap fetches return unmodified responses before
patchPrimeVideoScriptContent is applied.

In `@src/entrypoints/primevideo-interceptor.content.ts`:
- Around line 9-11: The current use of "void injectScript(bootstrapPath, {
keepInDom: isDev })" discards the returned Promise so injection failures are
silently swallowed; update the call in primevideo-interceptor.content.ts to
handle errors by either awaiting injectScript(...) or attaching a .catch(...)
that logs the error (use the same logging mechanism/pattern as
content/index.tsx), referencing injectScript, bootstrapPath and isDev so any
thrown error is caught and reported instead of being ignored.

In `@src/lib/apps/primevideo/script-patch-shared.ts`:
- Around line 20-26: The replacement only uses String.replace(...) which
substitutes only the first match; update the return object in the function that
builds the patch so that all occurrences are replaced by using
content.replaceAll(PRIMEVIDEO_SCRIPT_FIND_PATTERN,
PRIMEVIDEO_SCRIPT_REPLACE_WITH) (or, if PRIMEVIDEO_SCRIPT_FIND_PATTERN is a
RegExp string, convert it to a RegExp with the global flag and call
content.replace(new RegExp(PRIMEVIDEO_SCRIPT_FIND_PATTERN, 'g'),
PRIMEVIDEO_SCRIPT_REPLACE_WITH)) to ensure every instance of
PRIMEVIDEO_SCRIPT_FIND_PATTERN is replaced throughout the script content.

In `@src/lib/posthog-impl.ts`:
- Line 1: Create a new file src/lib/posthog-disabled.ts that mirrors the public
API of src/lib/posthog-enabled.ts but implements all exported
functions/classes/constants as no-ops (e.g., functions that return void or
resolved Promises, methods that do nothing, and objects with stubbed methods),
and re-export any types or interfaces so consumers keep the same types. Ensure
every exported symbol from posthog-enabled (named exports and any default
export) exists in posthog-disabled with the same signature and exported name so
imports of "@/lib/posthog-impl" resolve cleanly in disabled builds.

---

Nitpick comments:
In `@src/components/OTTModal.tsx`:
- Around line 397-421: The RuleSection rendering the log level control currently
passes an empty title (title=""), causing an awkward header; update the JSX
where RuleSection is used (the component containing the select for log level) to
provide a meaningful title such as "Developer" or "Settings" — locate the
RuleSection instance that wraps the log level select (references: RuleSection,
LOG_LEVEL_OPTIONS, handleLogLevelChange, logLevel) and change title="" to a
descriptive string to improve UX.

In `@src/entrypoints/primevideo-bootstrap.ts`:
- Around line 199-216: The multiple interception mechanisms (DOM method
wrapping, setAttribute override, src property setter, MutationObserver and
initial script scan) can appear redundant to future readers; add a short
clarifying comment near the processedScripts WeakSet declaration
(processedScripts) that explains this is intentional defense-in-depth and that
deduplication is handled by processedScripts, and also add brief inline notes
next to the functions inspectNode and replaceScript (and the
MutationObserver/initial querySelectorAll usage) describing which interception
each covers so maintainers understand why all four paths are present and that
processedScripts prevents double-processing.

In `@src/entrypoints/primevideo-interceptor.content.ts`:
- Around line 6-7: Remove the redundant type assertion on bootstrapPath: the
string literal "/primevideo-bootstrap.js" already satisfies injectScript's
parameter type (ScriptPublicPath), so change the declaration of bootstrapPath
(currently using as Parameters<typeof injectScript>[0]) to a plain string
assignment; update the variable defined as bootstrapPath and ensure any call
sites using injectScript continue to pass bootstrapPath unchanged.

In `@src/lib/logger.ts`:
- Around line 23-39: loggerInstances currently uses a strong Set which prevents
garbage collection of dynamically created Logger instances (including child
loggers created by createLogger and Logger.createChild); change loggerInstances
to a WeakSet<Logger> so references do not keep loggers alive, and ensure any
code that iterates loggerInstances (e.g., setGlobalLogLevel) still works with
WeakSet semantics (iterate by tracking registrations or update setGlobalLogLevel
to store the level and call Logger.setLevel on new registrations), or
alternatively add registerLogger/unregisterLogger helpers and call unregister
from Logger.dispose()/createChild to remove references when loggers are no
longer needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e8f6d323-2c23-4d37-a10a-18403cbe266d

📥 Commits

Reviewing files that changed from the base of the PR and between a2d3280 and 5a91352.

📒 Files selected for processing (23)
  • .changeset/calm-deserts-deny.md
  • src/components/App.tsx
  • src/components/OTTModal.tsx
  • src/entrypoints/background.ts
  • src/entrypoints/content/index.tsx
  • src/entrypoints/primevideo-bootstrap.ts
  • src/entrypoints/primevideo-interceptor.content.ts
  • src/entrypoints/script.ts
  • src/hooks/useStore.tsx
  • src/lib/apps/primevideo/config.ts
  • src/lib/apps/primevideo/script-patch-shared.ts
  • src/lib/apps/primevideo/script-watcher.ts
  • src/lib/logger.ts
  • src/lib/messaging.ts
  • src/lib/posthog-enabled.ts
  • src/lib/posthog-impl.ts
  • src/lib/posthog-provider-disabled.tsx
  • src/lib/posthog-provider-enabled.tsx
  • src/lib/posthog-provider-impl.tsx
  • src/lib/posthog-provider.tsx
  • src/lib/posthog.ts
  • src/lib/storage.ts
  • wxt.config.ts

Comment thread src/entrypoints/primevideo-bootstrap.ts
Comment thread src/entrypoints/primevideo-interceptor.content.ts
Comment thread src/lib/apps/primevideo/script-patch-shared.ts
Comment thread src/lib/posthog-impl.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/lib/posthog-disabled.ts (1)

6-14: Minor DRY cleanup for api_host.

Behavior is correct, but Line 7 can reference POSTHOG_API_HOST instead of another string literal to keep defaults centralized.

♻️ Proposed small cleanup
 export const posthogOptions = {
-  api_host: "",
+  api_host: POSTHOG_API_HOST,
   autocapture: false,
   capture_pageview: false,
   persistence: "localStorage",
   disable_external_dependency_loading: true,
   disable_surveys: true,
   disable_session_recording: true,
 } as const;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/posthog-disabled.ts` around lines 6 - 14, Replace the hard-coded
empty string for api_host in posthogOptions with the centralized default
constant POSTHOG_API_HOST: update the posthogOptions object (symbol:
posthogOptions) to reference POSTHOG_API_HOST instead of "" and add the
necessary import or reference so POSTHOG_API_HOST is available in this module.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/apps/primevideo/script-patch-shared.ts`:
- Around line 12-27: The patch function patchPrimeVideoScriptContent returns a
{changed, content} tuple but its consumer currently ignores changed (see the
.then((content) => patchPrimeVideoScriptContent(content).content) usage in
primevideo-bootstrap.ts); update that consumer to inspect the returned changed
flag and handle the false case explicitly — e.g., if changed is false, log a
warning (with context that the PRIMEVIDEO_SCRIPT_FIND_PATTERN was not found) and
either throw an error or fall back to the original external script loading path
so the system does not mark the script as "patched" incorrectly; keep
patchPrimeVideoScriptContent as-is but ensure the caller checks the {changed}
value before proceeding.

In `@src/lib/apps/primevideo/script-watcher.ts`:
- Around line 3-6: The patchScripts middleware is a no-op while
patchPrimeVideoScriptContent is applied unconditionally, making the
bypass-lite-plan toggle ineffective; update the bootstrap to check the rule
state before calling patchPrimeVideoScriptContent (or remove patchScripts from
the config if you prefer to always patch), e.g., locate the call to
patchPrimeVideoScriptContent in the primevideo bootstrap code and wrap it with
the feature toggle check used for rules, or implement logic inside patchScripts
to invoke patchPrimeVideoScriptContent when the rule is enabled (reference
symbols: patchScripts, patchPrimeVideoScriptContent, and the rule identifier for
"bypass-lite-plan") so toggling the rule actually enables/disables the patch.

---

Nitpick comments:
In `@src/lib/posthog-disabled.ts`:
- Around line 6-14: Replace the hard-coded empty string for api_host in
posthogOptions with the centralized default constant POSTHOG_API_HOST: update
the posthogOptions object (symbol: posthogOptions) to reference POSTHOG_API_HOST
instead of "" and add the necessary import or reference so POSTHOG_API_HOST is
available in this module.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1cf2c41c-c24f-4a72-824c-bb37802c1b64

📥 Commits

Reviewing files that changed from the base of the PR and between 5a91352 and 4a42e25.

📒 Files selected for processing (4)
  • src/entrypoints/primevideo-bootstrap.ts
  • src/lib/apps/primevideo/script-patch-shared.ts
  • src/lib/apps/primevideo/script-watcher.ts
  • src/lib/posthog-disabled.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/entrypoints/primevideo-bootstrap.ts

Comment on lines +12 to +27
export function patchPrimeVideoScriptContent(content: string) {
if (!content.includes(PRIMEVIDEO_SCRIPT_FIND_PATTERN)) {
return {
changed: false,
content,
};
}

return {
changed: true,
content: content.replace(
PRIMEVIDEO_SCRIPT_FIND_PATTERN,
PRIMEVIDEO_SCRIPT_REPLACE_WITH,
),
};
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify how the changed flag is used across the codebase
rg -n "patchPrimeVideoScriptContent" --type ts -A 3 -B 1

Repository: WINOFFRG/ottpro

Length of output: 1264


🏁 Script executed:

# Get the full content of script-patch-shared.ts to see the pattern and any documentation
head -50 src/lib/apps/primevideo/script-patch-shared.ts

Repository: WINOFFRG/ottpro

Length of output: 814


🏁 Script executed:

# Check for tests related to this function
fd -e "spec\|test" --type f | xargs rg -l "patchPrimeVideoScriptContent" 2>/dev/null

Repository: WINOFFRG/ottpro

Length of output: 145


🏁 Script executed:

# Get full context of how this is used in primevideo-bootstrap.ts
sed -n '35,55p' src/entrypoints/primevideo-bootstrap.ts

Repository: WINOFFRG/ottpro

Length of output: 680


🏁 Script executed:

# Search for any error handling or logging around script patching
rg -n "scriptCache\|patchPrimeVideo" src/entrypoints/primevideo-bootstrap.ts -A 5 -B 2

Repository: WINOFFRG/ottpro

Length of output: 41


🏁 Script executed:

# Find and read the test file for script-patch-shared.ts
fd -e "spec\|test" --type f -path "*primevideo*" | head -5

Repository: WINOFFRG/ottpro

Length of output: 227


🏁 Script executed:

# Check the actual test content
cat src/lib/apps/primevideo/__tests__/script-patch-shared.spec.ts

Repository: WINOFFRG/ottpro

Length of output: 152


🏁 Script executed:

# Check if there's any error handling in the actual runtime usage
cat src/entrypoints/primevideo-bootstrap.ts

Repository: WINOFFRG/ottpro

Length of output: 6233


The changed flag is silently ignored, masking failed patches.

The function correctly returns a changed flag, but the consumer in primevideo-bootstrap.ts (line 45) ignores it entirely:

.then((content) => patchPrimeVideoScriptContent(content).content);

When the pattern isn't found (e.g., if Amazon updates their script structure), the original unpatched content is still inlined as-is, and the status is set to "patched" regardless. This creates a silent failure: the script executes but without the subtitle detection patch, potentially breaking functionality without any visibility to users or developers.

Recommend adding a check of the changed flag and either:

  1. Logging a warning when patching is skipped, or
  2. Handling the failure case explicitly (e.g., falling back to the original external script or failing with visibility)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/apps/primevideo/script-patch-shared.ts` around lines 12 - 27, The
patch function patchPrimeVideoScriptContent returns a {changed, content} tuple
but its consumer currently ignores changed (see the .then((content) =>
patchPrimeVideoScriptContent(content).content) usage in
primevideo-bootstrap.ts); update that consumer to inspect the returned changed
flag and handle the false case explicitly — e.g., if changed is false, log a
warning (with context that the PRIMEVIDEO_SCRIPT_FIND_PATTERN was not found) and
either throw an error or fall back to the original external script loading path
so the system does not mark the script as "patched" incorrectly; keep
patchPrimeVideoScriptContent as-is but ensure the caller checks the {changed}
value before proceeding.

Comment on lines 3 to +6
/**
* Middleware for fetch/XHR interception - patches response content
*/
export const patchScripts: Middleware = async (ctx, next) => {
if (!TARGET_URL_PATTERN.test(ctx.url)) {
return next();
}

if (handledUrls.has(ctx.url)) {
return next();
}
handledUrls.add(ctx.url);

try {
const response = await ctx.originalFetch(ctx.url, ctx.init);
const content = await response.text();

if (!content.includes(FIND_PATTERN)) {
ctx.setHandled();
ctx.setResponse(
new Response(content, {
status: response.status,
statusText: response.statusText,
headers: response.headers,
}),
);
return;
}

const fileName = ctx.url.split("/").pop()?.split("?")[0];
logger.info(`🔧 Patched script: ${fileName}`);

const patchedContent = content.replace(FIND_PATTERN, REPLACE_WITH);

ctx.setHandled();
ctx.setResponse(
new Response(patchedContent, {
status: response.status,
statusText: response.statusText,
headers: response.headers,
}),
);
} catch (error) {
logger.error("Patch failed", error);
return next();
}
};

/**
* MutationObserver to intercept <script> tags and redirect through fetch
* This ensures scripts loaded via HTML go through our fetch polyfill
*/
export function startScriptTagInterceptor() {
const processedScripts = new Set<HTMLScriptElement>();

const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (
node instanceof HTMLScriptElement &&
node.src &&
TARGET_URL_PATTERN.test(node.src) &&
!processedScripts.has(node)
) {
processedScripts.add(node);
const originalSrc = node.src;
const originalOnload = node.onload;
const originalOnerror = node.onerror;

node.removeAttribute("src");
node.removeAttribute("defer");
node.removeAttribute("async");

fetch(originalSrc)
.then((response) => response.text())
.then((patchedContent) => {
node.textContent = patchedContent;

if (originalOnload) {
originalOnload.call(node, new Event("load"));
}
})
.catch((error) => {
logger.error("Script fetch failed", error);
if (originalOnerror) {
originalOnerror.call(node, new Event("error"));
}
});
}
}
}
});

observer.observe(document.documentElement, {
childList: true,
subtree: true,
});
}
export const patchScripts: Middleware = (_ctx, next) => next();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Rule wiring ==="
rg -n -C3 '"bypass-lite-plan"|middleware:\s*patchScripts|export const patchScripts' src || true

echo
echo "=== Bootstrap patch path ==="
rg -n -C4 'patchPrimeVideoScriptContent|bypass-lite-plan|isRuleEnabled|get.*Rule|enabled' src/entrypoints src/lib || true

BOOTSTRAP_FILE="$(fd 'primevideo-bootstrap.ts' src | head -n1 || true)"
if [ -n "${BOOTSTRAP_FILE:-}" ]; then
  echo
  echo "=== ${BOOTSTRAP_FILE} ==="
  sed -n '1,240p' "$BOOTSTRAP_FILE"
fi

Repository: WINOFFRG/ottpro

Length of output: 45242


The bypass-lite-plan rule toggle is now non-functional.

The middleware patchScripts in src/lib/apps/primevideo/script-watcher.ts is a no-op, but src/entrypoints/primevideo-bootstrap.ts patches Prime Video scripts unconditionally (line 45: patchPrimeVideoScriptContent(content).content) without checking the rule's enabled state. Users toggling this rule in settings will have no effect. Either gate the bootstrap patches with a rule state check, or remove the obsolete middleware from the config.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/apps/primevideo/script-watcher.ts` around lines 3 - 6, The
patchScripts middleware is a no-op while patchPrimeVideoScriptContent is applied
unconditionally, making the bypass-lite-plan toggle ineffective; update the
bootstrap to check the rule state before calling patchPrimeVideoScriptContent
(or remove patchScripts from the config if you prefer to always patch), e.g.,
locate the call to patchPrimeVideoScriptContent in the primevideo bootstrap code
and wrap it with the feature toggle check used for rules, or implement logic
inside patchScripts to invoke patchPrimeVideoScriptContent when the rule is
enabled (reference symbols: patchScripts, patchPrimeVideoScriptContent, and the
rule identifier for "bypass-lite-plan") so toggling the rule actually
enables/disables the patch.

@WINOFFRG WINOFFRG merged commit 3077a6a into master Mar 28, 2026
1 check passed
@github-actions github-actions Bot mentioned this pull request Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant