feat: add youtube floating quick-download action
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "gomdown-helper",
|
||||
"private": true,
|
||||
"version": "0.0.10",
|
||||
"version": "0.0.11",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"../../../../../@crx/manifest": {
|
||||
"file": "assets/crx-manifest.js--9ZsUvq0.js",
|
||||
"file": "assets/crx-manifest.js-B0cyAIB1.js",
|
||||
"name": "crx-manifest.js",
|
||||
"src": "../../../../../@crx/manifest",
|
||||
"isEntry": true
|
||||
@@ -20,9 +20,9 @@
|
||||
"file": "assets/downloadIntent-Dv31jC2S.js",
|
||||
"name": "downloadIntent"
|
||||
},
|
||||
"_index.ts-loader-DMyyuf2n.js": {
|
||||
"file": "assets/index.ts-loader-DMyyuf2n.js",
|
||||
"src": "_index.ts-loader-DMyyuf2n.js"
|
||||
"_index.ts-loader-BHtfStLc.js": {
|
||||
"file": "assets/index.ts-loader-BHtfStLc.js",
|
||||
"src": "_index.ts-loader-BHtfStLc.js"
|
||||
},
|
||||
"_settings-Bo6W9Drl.js": {
|
||||
"file": "assets/settings-Bo6W9Drl.js",
|
||||
@@ -32,7 +32,7 @@
|
||||
]
|
||||
},
|
||||
"src/background/index.ts": {
|
||||
"file": "assets/index.ts-U2ACoZ75.js",
|
||||
"file": "assets/index.ts-BAxKsZ8F.js",
|
||||
"name": "index.ts",
|
||||
"src": "src/background/index.ts",
|
||||
"isEntry": true,
|
||||
@@ -57,7 +57,7 @@
|
||||
]
|
||||
},
|
||||
"src/content/index.ts": {
|
||||
"file": "assets/index.ts-BGLNJwsP.js",
|
||||
"file": "assets/index.ts-CMnPQ13j.js",
|
||||
"name": "index.ts",
|
||||
"src": "src/content/index.ts",
|
||||
"isEntry": true,
|
||||
|
||||
1
packages/chrome/assets/index.ts-BAxKsZ8F.js
Normal file
1
packages/chrome/assets/index.ts-BAxKsZ8F.js
Normal file
File diff suppressed because one or more lines are too long
1
packages/chrome/assets/index.ts-CMnPQ13j.js
Normal file
1
packages/chrome/assets/index.ts-CMnPQ13j.js
Normal file
@@ -0,0 +1 @@
|
||||
import{b as m}from"./browser-polyfill-CZ_dLIqp.js";import{i as a,n as k}from"./downloadIntent-Dv31jC2S.js";const E=8e3,i=new Map;function v(){const e=Date.now();for(const[r,t]of i.entries())t<=e&&i.delete(r)}async function u(e,r){const t=k(e,window.location.href);if(!t)return!1;if(v(),i.has(t))return!0;i.set(t,Date.now()+E);try{if((await m.runtime.sendMessage({type:"capture-link-download",url:t,referer:r||document.referrer||window.location.href}))?.ok)return!0}catch{}return i.delete(t),!1}function p(e){return e?e instanceof HTMLAnchorElement?e:e instanceof Element?e.closest("a[href]"):null:null}function h(e){return!!(e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)}async function g(e){if(e.defaultPrevented||h(e))return;const r=p(e.target);if(!r)return;const t=r.href||"";!t||!a(t,window.location.href)||(e.preventDefault(),e.stopImmediatePropagation(),e.stopPropagation(),await u(t,document.referrer||window.location.href))}function b(e){const r=p(e.target);if(!r)return;const t=r.href||"";!t||!a(t,window.location.href)||h(e)||(e.preventDefault(),e.stopImmediatePropagation(),e.stopPropagation(),u(t,document.referrer||window.location.href))}document.addEventListener("pointerdown",e=>{e.button===0&&b(e)},!0);document.addEventListener("mousedown",e=>{e.button===0&&b(e)},!0);document.addEventListener("click",e=>{e.button===0&&g(e)},!0);document.addEventListener("keydown",e=>{if(e.key!=="Enter"||e.defaultPrevented||h(e))return;const r=p(e.target);if(!r)return;const t=r.href||"";!t||!a(t,window.location.href)||(e.preventDefault(),e.stopImmediatePropagation(),e.stopPropagation(),u(t,document.referrer||window.location.href))},!0);document.addEventListener("auxclick",e=>{e.button===1&&g(e)},!0);function C(){try{const e=window.open.bind(window);window.open=function(t,n,x){const d=String(t||"").trim();return d&&a(d,window.location.href)?(u(d,window.location.href),null):e(t,n,x)}}catch{}try{const e=HTMLAnchorElement.prototype.click;HTMLAnchorElement.prototype.click=function(){const t=this.href||this.getAttribute("href")||"";if(t&&a(t,window.location.href)){u(t,document.referrer||window.location.href);return}e.call(this)}}catch{}}C();let l=null,o=null,w=!1,y=null,s=window.location.href;function L(e){try{const r=new URL(e);return r.hostname!=="www.youtube.com"&&r.hostname!=="youtube.com"?!1:r.pathname==="/watch"&&r.searchParams.has("v")}catch{return!1}}function c(e,r="idle"){o&&(o.textContent=e,r==="ok"?o.style.color="#8ff0a4":r==="error"?o.style.color="#ff9b9b":o.style.color="#aeb7d8")}async function P(){if(!w){w=!0,c("gdown으로 전송 중...");try{const e=await m.runtime.sendMessage({type:"page:enqueue-ytdlp-url",url:window.location.href,referer:window.location.href});e?.ok?c("다운로드 모달로 전송됨","ok"):c(`전송 실패: ${e?.error||"unknown error"}`,"error")}catch(e){c(`전송 실패: ${String(e)}`,"error")}finally{w=!1}}}function I(){l&&(l.remove(),l=null,o=null)}function f(){if(window.top!==window.self)return;if(!L(window.location.href)){I();return}if(l)return;const e=document.createElement("div");e.id="gomdown-youtube-overlay",e.style.position="fixed",e.style.right="20px",e.style.bottom="24px",e.style.zIndex="2147483647",e.style.background="rgba(17, 21, 32, 0.94)",e.style.border="1px solid rgba(133, 148, 195, 0.35)",e.style.borderRadius="12px",e.style.padding="10px",e.style.boxShadow="0 8px 24px rgba(0, 0, 0, 0.28)",e.style.backdropFilter="blur(6px)",e.style.width="220px",e.style.fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif",e.style.color="#e8edff";const r=document.createElement("div");r.textContent="Gdown Helper",r.style.fontSize="12px",r.style.fontWeight="700",r.style.marginBottom="8px";const t=document.createElement("button");t.type="button",t.textContent="이 영상 다운로드",t.style.width="100%",t.style.height="34px",t.style.border="1px solid #5a69f0",t.style.borderRadius="8px",t.style.background="#5a69f0",t.style.color="#ffffff",t.style.fontSize="12px",t.style.fontWeight="700",t.style.cursor="pointer",t.addEventListener("click",()=>{P()});const n=document.createElement("div");n.textContent="클릭 시 gdown 다운로드 모달로 연결",n.style.fontSize="11px",n.style.marginTop="8px",n.style.lineHeight="1.35",n.style.color="#aeb7d8",e.appendChild(r),e.appendChild(t),e.appendChild(n),document.documentElement.appendChild(e),l=e,o=n}function S(){y===null&&(y=window.setInterval(()=>{const e=window.location.href;e!==s&&(s=e,f())},800),window.addEventListener("popstate",()=>{s=window.location.href,f()}),document.addEventListener("yt-navigate-finish",()=>{s=window.location.href,f()}))}f();S();
|
||||
13
packages/chrome/assets/index.ts-loader-BHtfStLc.js
Normal file
13
packages/chrome/assets/index.ts-loader-BHtfStLc.js
Normal file
@@ -0,0 +1,13 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const injectTime = performance.now();
|
||||
(async () => {
|
||||
const { onExecute } = await import(
|
||||
/* @vite-ignore */
|
||||
chrome.runtime.getURL("assets/index.ts-CMnPQ13j.js")
|
||||
);
|
||||
onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });
|
||||
})().catch(console.error);
|
||||
|
||||
})();
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 3,
|
||||
"name": "Gomdown Helper",
|
||||
"description": "Send browser downloads to gdown",
|
||||
"version": "0.0.10",
|
||||
"version": "0.0.11",
|
||||
"default_locale": "en",
|
||||
"icons": {
|
||||
"16": "images/16.png",
|
||||
@@ -28,7 +28,7 @@
|
||||
"content_scripts": [
|
||||
{
|
||||
"js": [
|
||||
"assets/index.ts-loader-DMyyuf2n.js"
|
||||
"assets/index.ts-loader-BHtfStLc.js"
|
||||
],
|
||||
"matches": [
|
||||
"<all_urls>"
|
||||
@@ -59,7 +59,7 @@
|
||||
"images/*",
|
||||
"assets/browser-polyfill-CZ_dLIqp.js",
|
||||
"assets/downloadIntent-Dv31jC2S.js",
|
||||
"assets/index.ts-BGLNJwsP.js"
|
||||
"assets/index.ts-CMnPQ13j.js"
|
||||
],
|
||||
"use_dynamic_url": false
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
import './assets/index.ts-U2ACoZ75.js';
|
||||
import './assets/index.ts-BAxKsZ8F.js';
|
||||
|
||||
BIN
packages/gomdown-helper.v0.0.11.chrome.zip
Normal file
BIN
packages/gomdown-helper.v0.0.11.chrome.zip
Normal file
Binary file not shown.
@@ -419,6 +419,13 @@ browser.runtime.onMessage.addListener((message: any, sender: any) => {
|
||||
})
|
||||
}
|
||||
|
||||
if (message?.type === 'page:enqueue-ytdlp-url') {
|
||||
const url = String(message?.url || '').trim()
|
||||
const referer = String(message?.referer || url).trim()
|
||||
if (!url) return Promise.resolve({ ok: false, error: 'url is empty' })
|
||||
return transferUrlToGdown(url, referer || url, 'yt-dlp')
|
||||
}
|
||||
|
||||
return undefined
|
||||
})
|
||||
|
||||
|
||||
@@ -140,3 +140,142 @@ function installProgrammaticInterceptors(): void {
|
||||
}
|
||||
|
||||
installProgrammaticInterceptors()
|
||||
|
||||
let ytOverlayRoot: HTMLDivElement | null = null
|
||||
let ytOverlayStatus: HTMLDivElement | null = null
|
||||
let ytOverlayBusy = false
|
||||
let ytUrlWatcherTimer: number | null = null
|
||||
let lastObservedUrl = window.location.href
|
||||
|
||||
function isYoutubeWatchPage(url: string): boolean {
|
||||
try {
|
||||
const parsed = new URL(url)
|
||||
if (parsed.hostname !== 'www.youtube.com' && parsed.hostname !== 'youtube.com') return false
|
||||
return parsed.pathname === '/watch' && parsed.searchParams.has('v')
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function setYtOverlayStatus(message: string, tone: 'ok' | 'error' | 'idle' = 'idle'): void {
|
||||
if (!ytOverlayStatus) return
|
||||
ytOverlayStatus.textContent = message
|
||||
if (tone === 'ok') ytOverlayStatus.style.color = '#8ff0a4'
|
||||
else if (tone === 'error') ytOverlayStatus.style.color = '#ff9b9b'
|
||||
else ytOverlayStatus.style.color = '#aeb7d8'
|
||||
}
|
||||
|
||||
async function enqueueCurrentYoutubePage(): Promise<void> {
|
||||
if (ytOverlayBusy) return
|
||||
ytOverlayBusy = true
|
||||
setYtOverlayStatus('gdown으로 전송 중...')
|
||||
try {
|
||||
const result = (await browser.runtime.sendMessage({
|
||||
type: 'page:enqueue-ytdlp-url',
|
||||
url: window.location.href,
|
||||
referer: window.location.href,
|
||||
})) as { ok?: boolean; error?: string }
|
||||
if (result?.ok) {
|
||||
setYtOverlayStatus('다운로드 모달로 전송됨', 'ok')
|
||||
} else {
|
||||
setYtOverlayStatus(`전송 실패: ${result?.error || 'unknown error'}`, 'error')
|
||||
}
|
||||
} catch (error) {
|
||||
setYtOverlayStatus(`전송 실패: ${String(error)}`, 'error')
|
||||
} finally {
|
||||
ytOverlayBusy = false
|
||||
}
|
||||
}
|
||||
|
||||
function removeYoutubeOverlay(): void {
|
||||
if (ytOverlayRoot) {
|
||||
ytOverlayRoot.remove()
|
||||
ytOverlayRoot = null
|
||||
ytOverlayStatus = null
|
||||
}
|
||||
}
|
||||
|
||||
function ensureYoutubeOverlay(): void {
|
||||
if (window.top !== window.self) return
|
||||
if (!isYoutubeWatchPage(window.location.href)) {
|
||||
removeYoutubeOverlay()
|
||||
return
|
||||
}
|
||||
if (ytOverlayRoot) return
|
||||
|
||||
const root = document.createElement('div')
|
||||
root.id = 'gomdown-youtube-overlay'
|
||||
root.style.position = 'fixed'
|
||||
root.style.right = '20px'
|
||||
root.style.bottom = '24px'
|
||||
root.style.zIndex = '2147483647'
|
||||
root.style.background = 'rgba(17, 21, 32, 0.94)'
|
||||
root.style.border = '1px solid rgba(133, 148, 195, 0.35)'
|
||||
root.style.borderRadius = '12px'
|
||||
root.style.padding = '10px'
|
||||
root.style.boxShadow = '0 8px 24px rgba(0, 0, 0, 0.28)'
|
||||
root.style.backdropFilter = 'blur(6px)'
|
||||
root.style.width = '220px'
|
||||
root.style.fontFamily = 'ui-sans-serif, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif'
|
||||
root.style.color = '#e8edff'
|
||||
|
||||
const title = document.createElement('div')
|
||||
title.textContent = 'Gdown Helper'
|
||||
title.style.fontSize = '12px'
|
||||
title.style.fontWeight = '700'
|
||||
title.style.marginBottom = '8px'
|
||||
|
||||
const action = document.createElement('button')
|
||||
action.type = 'button'
|
||||
action.textContent = '이 영상 다운로드'
|
||||
action.style.width = '100%'
|
||||
action.style.height = '34px'
|
||||
action.style.border = '1px solid #5a69f0'
|
||||
action.style.borderRadius = '8px'
|
||||
action.style.background = '#5a69f0'
|
||||
action.style.color = '#ffffff'
|
||||
action.style.fontSize = '12px'
|
||||
action.style.fontWeight = '700'
|
||||
action.style.cursor = 'pointer'
|
||||
action.addEventListener('click', () => {
|
||||
void enqueueCurrentYoutubePage()
|
||||
})
|
||||
|
||||
const status = document.createElement('div')
|
||||
status.textContent = '클릭 시 gdown 다운로드 모달로 연결'
|
||||
status.style.fontSize = '11px'
|
||||
status.style.marginTop = '8px'
|
||||
status.style.lineHeight = '1.35'
|
||||
status.style.color = '#aeb7d8'
|
||||
|
||||
root.appendChild(title)
|
||||
root.appendChild(action)
|
||||
root.appendChild(status)
|
||||
document.documentElement.appendChild(root)
|
||||
|
||||
ytOverlayRoot = root
|
||||
ytOverlayStatus = status
|
||||
}
|
||||
|
||||
function watchYoutubeRouteChanges(): void {
|
||||
if (ytUrlWatcherTimer !== null) return
|
||||
ytUrlWatcherTimer = window.setInterval(() => {
|
||||
const current = window.location.href
|
||||
if (current === lastObservedUrl) return
|
||||
lastObservedUrl = current
|
||||
ensureYoutubeOverlay()
|
||||
}, 800)
|
||||
|
||||
window.addEventListener('popstate', () => {
|
||||
lastObservedUrl = window.location.href
|
||||
ensureYoutubeOverlay()
|
||||
})
|
||||
|
||||
document.addEventListener('yt-navigate-finish', () => {
|
||||
lastObservedUrl = window.location.href
|
||||
ensureYoutubeOverlay()
|
||||
})
|
||||
}
|
||||
|
||||
ensureYoutubeOverlay()
|
||||
watchYoutubeRouteChanges()
|
||||
|
||||
Reference in New Issue
Block a user