feat: initialize gomdown-helper with yt-dlp transfer flow
This commit is contained in:
142
src/content/index.ts
Normal file
142
src/content/index.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import browser from 'webextension-polyfill'
|
||||
import { isLikelyDownloadUrl, normalizeUrl } from '../lib/downloadIntent'
|
||||
|
||||
const CAPTURE_TTL_MS = 8000
|
||||
const captureInFlight = new Map<string, number>()
|
||||
|
||||
function pruneCaptureInFlight(): void {
|
||||
const now = Date.now()
|
||||
for (const [url, expiresAt] of captureInFlight.entries()) {
|
||||
if (expiresAt <= now) captureInFlight.delete(url)
|
||||
}
|
||||
}
|
||||
|
||||
async function sendCapture(url: string, referer: string): Promise<boolean> {
|
||||
const normalized = normalizeUrl(url, window.location.href)
|
||||
if (!normalized) return false
|
||||
|
||||
pruneCaptureInFlight()
|
||||
if (captureInFlight.has(normalized)) return true
|
||||
captureInFlight.set(normalized, Date.now() + CAPTURE_TTL_MS)
|
||||
|
||||
try {
|
||||
const result = (await browser.runtime.sendMessage({
|
||||
type: 'capture-link-download',
|
||||
url: normalized,
|
||||
referer: referer || document.referrer || window.location.href,
|
||||
})) as { ok?: boolean }
|
||||
if (result?.ok) return true
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
|
||||
captureInFlight.delete(normalized)
|
||||
return false
|
||||
}
|
||||
|
||||
function findAnchor(target: EventTarget | null): HTMLAnchorElement | null {
|
||||
if (!target) return null
|
||||
if (target instanceof HTMLAnchorElement) return target
|
||||
if (target instanceof Element) return target.closest('a[href]') as HTMLAnchorElement | null
|
||||
return null
|
||||
}
|
||||
|
||||
function shouldIgnoreHotkey(event: MouseEvent | KeyboardEvent): boolean {
|
||||
return !!(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
|
||||
}
|
||||
|
||||
async function interceptAnchorEvent(event: MouseEvent): Promise<void> {
|
||||
if (event.defaultPrevented) return
|
||||
if (shouldIgnoreHotkey(event)) return
|
||||
|
||||
const anchor = findAnchor(event.target)
|
||||
if (!anchor) return
|
||||
const href = anchor.href || ''
|
||||
if (!href || !isLikelyDownloadUrl(href, window.location.href)) return
|
||||
|
||||
event.preventDefault()
|
||||
event.stopImmediatePropagation()
|
||||
event.stopPropagation()
|
||||
await sendCapture(href, document.referrer || window.location.href)
|
||||
}
|
||||
|
||||
function interceptMouseLike(event: MouseEvent): void {
|
||||
const anchor = findAnchor(event.target)
|
||||
if (!anchor) return
|
||||
const href = anchor.href || ''
|
||||
if (!href || !isLikelyDownloadUrl(href, window.location.href)) return
|
||||
if (shouldIgnoreHotkey(event)) return
|
||||
|
||||
event.preventDefault()
|
||||
event.stopImmediatePropagation()
|
||||
event.stopPropagation()
|
||||
void sendCapture(href, document.referrer || window.location.href)
|
||||
}
|
||||
|
||||
document.addEventListener('pointerdown', (event: PointerEvent) => {
|
||||
if (event.button !== 0) return
|
||||
interceptMouseLike(event)
|
||||
}, true)
|
||||
|
||||
document.addEventListener('mousedown', (event: MouseEvent) => {
|
||||
if (event.button !== 0) return
|
||||
interceptMouseLike(event)
|
||||
}, true)
|
||||
|
||||
document.addEventListener('click', (event: MouseEvent) => {
|
||||
if (event.button !== 0) return
|
||||
void interceptAnchorEvent(event)
|
||||
}, true)
|
||||
|
||||
document.addEventListener('keydown', (event: KeyboardEvent) => {
|
||||
if (event.key !== 'Enter') return
|
||||
if (event.defaultPrevented) return
|
||||
if (shouldIgnoreHotkey(event)) return
|
||||
|
||||
const anchor = findAnchor(event.target)
|
||||
if (!anchor) return
|
||||
const href = anchor.href || ''
|
||||
if (!href || !isLikelyDownloadUrl(href, window.location.href)) return
|
||||
|
||||
event.preventDefault()
|
||||
event.stopImmediatePropagation()
|
||||
event.stopPropagation()
|
||||
void sendCapture(href, document.referrer || window.location.href)
|
||||
}, true)
|
||||
|
||||
document.addEventListener('auxclick', (event: MouseEvent) => {
|
||||
if (event.button !== 1) return
|
||||
void interceptAnchorEvent(event)
|
||||
}, true)
|
||||
|
||||
function installProgrammaticInterceptors(): void {
|
||||
try {
|
||||
const originalOpen = window.open.bind(window)
|
||||
window.open = function gomdownInterceptOpen(url?: string | URL, target?: string, features?: string): Window | null {
|
||||
const raw = String(url || '').trim()
|
||||
if (raw && isLikelyDownloadUrl(raw, window.location.href)) {
|
||||
void sendCapture(raw, window.location.href)
|
||||
return null
|
||||
}
|
||||
return originalOpen(url as string, target, features)
|
||||
}
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
|
||||
try {
|
||||
const originalAnchorClick = HTMLAnchorElement.prototype.click
|
||||
HTMLAnchorElement.prototype.click = function gomdownInterceptAnchorClick(): void {
|
||||
const href = this.href || this.getAttribute('href') || ''
|
||||
if (href && isLikelyDownloadUrl(href, window.location.href)) {
|
||||
void sendCapture(href, document.referrer || window.location.href)
|
||||
return
|
||||
}
|
||||
originalAnchorClick.call(this)
|
||||
}
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
installProgrammaticInterceptors()
|
||||
Reference in New Issue
Block a user