diff --git a/blog.html b/blog.html
index f7c562af..d569d71e 100644
--- a/blog.html
+++ b/blog.html
@@ -40,7 +40,6 @@
bitvid Blog
-
diff --git a/js/analytics.js b/js/analytics.js
deleted file mode 100644
index c4038f95..00000000
--- a/js/analytics.js
+++ /dev/null
@@ -1,195 +0,0 @@
-// js/analytics.js
-import { ANALYTICS_CONFIG } from "./analyticsConfig.js";
-import { userLogger } from "./utils/logger.js";
-
-const SCRIPT_ATTR = "data-bitvid-analytics";
-const SCRIPT_IDENTIFIER = "umami";
-const pendingCalls = [];
-let flushTimerId = null;
-let scriptLoadedOnce = false;
-
-function isBrowserEnvironment() {
- return typeof window !== "undefined" && typeof document !== "undefined";
-}
-
-function flushPendingCalls() {
- if (!isBrowserEnvironment()) {
- return;
- }
-
- const umami = window.umami;
- if (!umami) {
- return;
- }
-
- while (pendingCalls.length > 0) {
- const { method, args } = pendingCalls.shift();
- const fn = typeof umami[method] === "function" ? umami[method] : null;
- if (!fn) {
- continue;
- }
- try {
- fn.apply(umami, args);
- } catch (err) {
- userLogger.warn("[analytics] Failed to call", method, err);
- }
- }
-}
-
-function scheduleFlush() {
- if (!isBrowserEnvironment()) {
- return;
- }
-
- if (flushTimerId !== null) {
- return;
- }
-
- // 🔧 merged conflicting changes from codex/add-tracking-script-to-all-pages vs unstable
- flushTimerId = window.setInterval(() => {
- if (window.umami && typeof window.umami.track === "function") {
- window.clearInterval(flushTimerId);
- flushTimerId = null;
- flushPendingCalls();
- }
- }, 500);
-}
-
-export function ensureAnalyticsLoaded(doc = typeof document !== "undefined" ? document : null) {
- if (!doc) {
- return null;
- }
-
- let script = doc.querySelector(`script[${SCRIPT_ATTR}]`);
- if (script) {
- if (!scriptLoadedOnce) {
- // If a server rendered script already exists, make sure we flush asap.
- scriptLoadedOnce = true;
- flushPendingCalls();
- }
- return script;
- }
-
- script = doc.createElement("script");
- script.defer = true;
- script.src = ANALYTICS_CONFIG.scriptSrc;
- script.setAttribute(SCRIPT_ATTR, SCRIPT_IDENTIFIER);
- script.dataset.websiteId = ANALYTICS_CONFIG.websiteId;
- // Allow Umami to handle automatic pageview tracking by default.
- script.addEventListener("load", () => {
- scriptLoadedOnce = true;
- flushPendingCalls();
- });
- doc.head.appendChild(script);
-
- scheduleFlush();
-
- return script;
-}
-
-function queueCall(method, args) {
- pendingCalls.push({ method, args });
- scheduleFlush();
-}
-
-function invokeUmami(method, args) {
- if (!isBrowserEnvironment()) {
- return;
- }
-
- ensureAnalyticsLoaded();
-
- const umami = window.umami;
- if (umami && typeof umami[method] === "function") {
- try {
- umami[method].apply(umami, args);
- } catch (err) {
- userLogger.warn("[analytics] Failed to call", method, err);
- }
- return;
- }
-
- queueCall(method, args);
-}
-
-export function trackPageView(path, referrer) {
- if (!isBrowserEnvironment()) {
- return;
- }
-
- const resolvedPath =
- typeof path === "string" && path.length > 0
- ? path
- : `${window.location.pathname}${window.location.hash || ""}`;
-
- const resolvedReferrer =
- typeof referrer === "string" ? referrer : document.referrer || "";
-
- // 🔧 merged conflicting changes from codex/add-tracking-script-to-all-pages vs unstable
- let absoluteUrl = resolvedPath;
- try {
- absoluteUrl = new URL(resolvedPath, window.location.origin).toString();
- } catch (err) {
- // Fall back to the provided path if it cannot be resolved.
- }
-
- let absoluteReferrer = resolvedReferrer;
- if (resolvedReferrer) {
- try {
- absoluteReferrer = new URL(resolvedReferrer, window.location.origin).toString();
- } catch (err) {
- // Keep original referrer if it cannot be normalized.
- }
- }
-
- invokeUmami("track", [
- (basePayload = {}) => ({
- ...basePayload,
- url: absoluteUrl || basePayload.url,
- referrer: absoluteReferrer || basePayload.referrer,
- }),
- "pageview",
- ]);
-}
-
-export function trackVideoView({
- videoId,
- title,
- source,
- hasMagnet,
- hasUrl,
-} = {}) {
- if (!isBrowserEnvironment()) {
- return;
- }
-
- const payload = {};
-
- if (videoId) {
- payload.videoId = String(videoId);
- }
-
- if (title) {
- payload.title = String(title);
- }
-
- if (source) {
- payload.source = String(source);
- }
-
- if (typeof hasMagnet === "boolean") {
- payload.hasMagnet = hasMagnet;
- }
-
- if (typeof hasUrl === "boolean") {
- payload.hasUrl = hasUrl;
- }
-
- // 🔧 merged conflicting changes from codex/add-tracking-script-to-all-pages vs unstable
- invokeUmami("track", [ANALYTICS_CONFIG.videoViewEventName, payload]);
-}
-
-// Immediately queue the analytics script so page views are captured early.
-if (isBrowserEnvironment()) {
- ensureAnalyticsLoaded();
-}
diff --git a/js/analyticsConfig.js b/js/analyticsConfig.js
deleted file mode 100644
index a4bf0749..00000000
--- a/js/analyticsConfig.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// js/analyticsConfig.js
-// Central configuration for site-wide analytics tracking.
-
-export const ANALYTICS_CONFIG = Object.freeze({
- /**
- * Hosted Umami script that powers analytics.
- * Update this value if the tracker is relocated.
- */
- scriptSrc: "https://umami.malin.onl/script.js",
- /**
- * The Umami website identifier for this deployment.
- */
- websiteId: "1f8eead2-79f0-4dba-8c3b-ed9b08b6e877",
- /**
- * Event name used when recording individual video views.
- */
- videoViewEventName: "video_view",
-});
diff --git a/js/app.js b/js/app.js
index cd0ab0c1..6008e37b 100644
--- a/js/app.js
+++ b/js/app.js
@@ -15,7 +15,6 @@ import { safeDecodeMagnet } from "./magnetUtils.js";
import { extractMagnetHints, normalizeAndAugmentMagnet } from "./magnet.js";
import { deriveTorrentPlaybackConfig } from "./playbackUtils.js";
import { URL_FIRST_ENABLED } from "./constants.js";
-import { trackVideoView } from "./analytics.js";
import { attachHealthBadges } from "./gridHealth.js";
import { attachUrlHealthBadges } from "./urlHealthObserver.js";
import { updateVideoCardSourceVisibility } from "./utils/cardSourceVisibility.js";
@@ -6592,14 +6591,6 @@ class Application {
const sanitizedMagnet = magnetSupported ? usableMagnetCandidate : "";
- trackVideoView({
- videoId: video.id || eventId,
- title: video.title || "Untitled",
- source: "event",
- hasMagnet: !!sanitizedMagnet,
- hasUrl: !!trimmedUrl,
- });
-
const knownPostedAt = this.getKnownVideoPostedAt(video);
const normalizedEditedAt = Number.isFinite(video.created_at)
? Math.floor(video.created_at)
@@ -6963,17 +6954,6 @@ class Application {
this.zapController?.setVisibility(false);
this.zapController?.resetState();
- trackVideoView({
- videoId:
- typeof title === "string" && title.trim().length > 0
- ? `direct:${title.trim()}`
- : "direct-playback",
- title,
- source: "direct",
- hasMagnet: !!sanitizedMagnet,
- hasUrl: !!sanitizedUrl,
- });
-
if (!sanitizedUrl && !sanitizedMagnet) {
const message = trimmedMagnet && !magnetSupported
? UNSUPPORTED_BTITH_MESSAGE
diff --git a/js/index.js b/js/index.js
index 13d0d68e..17c7f8e2 100644
--- a/js/index.js
+++ b/js/index.js
@@ -137,14 +137,9 @@ const fetchPartial = async (url) => {
async function loadModal(url) {
try {
const html = await fetchPartial(url);
- // Remove analytics loader tags from modal partials to avoid duplicate pageview events.
- const sanitizedHtml = html.replace(
- /