feat: improve media capture routing and refresh pomeranian icon set
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "gomdown-helper",
|
"name": "gomdown-helper",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.11",
|
"version": "0.0.16",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"../../../../../@crx/manifest": {
|
"../../../../../@crx/manifest": {
|
||||||
"file": "assets/crx-manifest.js-B0cyAIB1.js",
|
"file": "assets/crx-manifest.js-mSo6-ym3.js",
|
||||||
"name": "crx-manifest.js",
|
"name": "crx-manifest.js",
|
||||||
"src": "../../../../../@crx/manifest",
|
"src": "../../../../../@crx/manifest",
|
||||||
"isEntry": true
|
"isEntry": true
|
||||||
@@ -20,9 +20,9 @@
|
|||||||
"file": "assets/downloadIntent-Dv31jC2S.js",
|
"file": "assets/downloadIntent-Dv31jC2S.js",
|
||||||
"name": "downloadIntent"
|
"name": "downloadIntent"
|
||||||
},
|
},
|
||||||
"_index.ts-loader-BHtfStLc.js": {
|
"_index.ts-loader-Bju9eGS_.js": {
|
||||||
"file": "assets/index.ts-loader-BHtfStLc.js",
|
"file": "assets/index.ts-loader-Bju9eGS_.js",
|
||||||
"src": "_index.ts-loader-BHtfStLc.js"
|
"src": "_index.ts-loader-Bju9eGS_.js"
|
||||||
},
|
},
|
||||||
"_settings-Bo6W9Drl.js": {
|
"_settings-Bo6W9Drl.js": {
|
||||||
"file": "assets/settings-Bo6W9Drl.js",
|
"file": "assets/settings-Bo6W9Drl.js",
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/background/index.ts": {
|
"src/background/index.ts": {
|
||||||
"file": "assets/index.ts-BAxKsZ8F.js",
|
"file": "assets/index.ts-BljhweV3.js",
|
||||||
"name": "index.ts",
|
"name": "index.ts",
|
||||||
"src": "src/background/index.ts",
|
"src": "src/background/index.ts",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/content/index.ts": {
|
"src/content/index.ts": {
|
||||||
"file": "assets/index.ts-CMnPQ13j.js",
|
"file": "assets/index.ts-C6ePCen1.js",
|
||||||
"name": "index.ts",
|
"name": "index.ts",
|
||||||
"src": "src/content/index.ts",
|
"src": "src/content/index.ts",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/popup/index.html": {
|
"src/popup/index.html": {
|
||||||
"file": "assets/index.html-Tb8yZAds.js",
|
"file": "assets/index.html-BLzIyLM-.js",
|
||||||
"name": "index.html",
|
"name": "index.html",
|
||||||
"src": "src/popup/index.html",
|
"src": "src/popup/index.html",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
|
|||||||
1
packages/chrome/assets/index.html-BLzIyLM-.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{c as k,j as t,R as r}from"./client-CBvt1tWS.js";import{b as i}from"./browser-polyfill-CZ_dLIqp.js";import{g as N,s as m}from"./settings-Bo6W9Drl.js";function b(){const[s,d]=r.useState(null),[x,n]=r.useState(""),[l,u]=r.useState([]);r.useEffect(()=>{N().then(d)},[]),r.useEffect(()=>{let e=null;const a=async()=>{const o=await i.runtime.sendMessage({type:"media:list"});o?.ok&&Array.isArray(o.items)&&u(o.items.slice(0,10))};return a(),e=window.setInterval(()=>{a()},2e3),()=>{e!==null&&window.clearInterval(e)}},[]);const c=(e,a)=>{d(o=>o&&{...o,[e]:a})},h=async e=>{if(!s)return;const a={...s,extensionStatus:e};d(a),await m({extensionStatus:e}),n(e?"Extension ON":"Extension OFF"),window.setTimeout(()=>n(""),1200)},g=async()=>{s&&(await m(s),n("Saved"),window.setTimeout(()=>n(""),1200))},p=async()=>{const e=i.runtime.getURL("src/config/index.html");await i.tabs.create({url:e})},j=async()=>{const e=i.runtime.getURL("src/history/index.html");await i.tabs.create({url:e})},v=async()=>{const e=await i.runtime.sendMessage({type:"page:enqueue-ytdlp"});n(e?.ok?"Active tab sent to gdown (yt-dlp)":`Send failed: ${e?.error||"unknown error"}`),window.setTimeout(()=>n(""),1800)},w=async e=>{const a=await i.runtime.sendMessage({type:"media:enqueue",url:e.url,referer:e.referer||"",kind:e.kind,suggestedOut:e.suggestedOut||"",cookie:e.cookie||"",userAgent:e.userAgent||""});n(a?.ok?"Media sent to gdown":`Send failed: ${a?.error||"unknown error"}`),window.setTimeout(()=>n(""),1600)},y=async()=>{await i.runtime.sendMessage({type:"media:clear"}),u([]),n("Captured media cleared"),window.setTimeout(()=>n(""),1200)};return s?t.jsxs("div",{className:"container",children:[t.jsx("h1",{children:"Gomdown Helper"}),t.jsxs("div",{className:"field",children:[t.jsx("label",{children:"RPC Secret"}),t.jsx("input",{type:"text",value:s.motrixAPIkey,onChange:e=>c("motrixAPIkey",e.target.value),placeholder:"aria2 rpc secret"})]}),t.jsxs("div",{className:"field",children:[t.jsx("label",{children:"RPC Port"}),t.jsx("input",{type:"number",value:s.motrixPort,onChange:e=>c("motrixPort",Number(e.target.value||16800))})]}),t.jsxs("label",{className:"toggle",children:["Extension Enabled",t.jsx("input",{type:"checkbox",checked:s.extensionStatus,onChange:e=>{h(e.target.checked)}})]}),t.jsxs("label",{className:"toggle",children:["Use Native Host",t.jsx("input",{type:"checkbox",checked:s.useNativeHost,onChange:e=>c("useNativeHost",e.target.checked)})]}),t.jsxs("label",{className:"toggle",children:["Activate gdown App",t.jsx("input",{type:"checkbox",checked:s.activateAppOnDownload,onChange:e=>c("activateAppOnDownload",e.target.checked)})]}),t.jsx("button",{onClick:g,children:"Save"}),t.jsx("button",{onClick:p,children:"Settings"}),t.jsx("button",{onClick:j,children:"History"}),t.jsx("button",{onClick:()=>{v()},children:"Send Active Tab (yt-dlp)"}),t.jsxs("div",{className:"media-panel",children:[t.jsxs("div",{className:"media-head",children:[t.jsx("strong",{children:"Captured Media"}),t.jsx("button",{className:"mini ghost",onClick:y,children:"Clear"})]}),l.length===0?t.jsx("div",{className:"empty",children:"No media captured yet"}):t.jsx("div",{className:"media-list",children:l.map(e=>t.jsxs("div",{className:"media-item",children:[t.jsxs("div",{className:"media-meta",children:[t.jsx("span",{className:"kind",children:e.kind.toUpperCase()}),e.pageTitle?t.jsx("span",{className:"url",children:e.pageTitle}):null,t.jsx("span",{className:"url",children:e.url})]}),t.jsx("button",{className:"mini",onClick:()=>{w(e)},children:"Add"})]},e.id))})]}),t.jsx("div",{className:"status",children:x})]}):t.jsx("div",{className:"container",children:"Loading..."})}k.createRoot(document.getElementById("root")).render(t.jsx(r.StrictMode,{children:t.jsx(b,{})}));
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{c as k,j as t,R as r}from"./client-CBvt1tWS.js";import{b as i}from"./browser-polyfill-CZ_dLIqp.js";import{g as N,s as m}from"./settings-Bo6W9Drl.js";function b(){const[s,d]=r.useState(null),[x,n]=r.useState(""),[l,u]=r.useState([]);r.useEffect(()=>{N().then(d)},[]),r.useEffect(()=>{let e=null;const a=async()=>{const o=await i.runtime.sendMessage({type:"media:list"});o?.ok&&Array.isArray(o.items)&&u(o.items.slice(0,10))};return a(),e=window.setInterval(()=>{a()},2e3),()=>{e!==null&&window.clearInterval(e)}},[]);const c=(e,a)=>{d(o=>o&&{...o,[e]:a})},h=async e=>{if(!s)return;const a={...s,extensionStatus:e};d(a),await m({extensionStatus:e}),n(e?"Extension ON":"Extension OFF"),window.setTimeout(()=>n(""),1200)},p=async()=>{s&&(await m(s),n("Saved"),window.setTimeout(()=>n(""),1200))},g=async()=>{const e=i.runtime.getURL("src/config/index.html");await i.tabs.create({url:e})},j=async()=>{const e=i.runtime.getURL("src/history/index.html");await i.tabs.create({url:e})},v=async()=>{const e=await i.runtime.sendMessage({type:"page:enqueue-ytdlp"});n(e?.ok?"Active tab sent to gdown (yt-dlp)":`Send failed: ${e?.error||"unknown error"}`),window.setTimeout(()=>n(""),1800)},w=async e=>{const a=await i.runtime.sendMessage({type:"media:enqueue",url:e.url,referer:e.referer||""});n(a?.ok?"Media sent to gdown":`Send failed: ${a?.error||"unknown error"}`),window.setTimeout(()=>n(""),1600)},y=async()=>{await i.runtime.sendMessage({type:"media:clear"}),u([]),n("Captured media cleared"),window.setTimeout(()=>n(""),1200)};return s?t.jsxs("div",{className:"container",children:[t.jsx("h1",{children:"Gomdown Helper"}),t.jsxs("div",{className:"field",children:[t.jsx("label",{children:"RPC Secret"}),t.jsx("input",{type:"text",value:s.motrixAPIkey,onChange:e=>c("motrixAPIkey",e.target.value),placeholder:"aria2 rpc secret"})]}),t.jsxs("div",{className:"field",children:[t.jsx("label",{children:"RPC Port"}),t.jsx("input",{type:"number",value:s.motrixPort,onChange:e=>c("motrixPort",Number(e.target.value||16800))})]}),t.jsxs("label",{className:"toggle",children:["Extension Enabled",t.jsx("input",{type:"checkbox",checked:s.extensionStatus,onChange:e=>{h(e.target.checked)}})]}),t.jsxs("label",{className:"toggle",children:["Use Native Host",t.jsx("input",{type:"checkbox",checked:s.useNativeHost,onChange:e=>c("useNativeHost",e.target.checked)})]}),t.jsxs("label",{className:"toggle",children:["Activate gdown App",t.jsx("input",{type:"checkbox",checked:s.activateAppOnDownload,onChange:e=>c("activateAppOnDownload",e.target.checked)})]}),t.jsx("button",{onClick:p,children:"Save"}),t.jsx("button",{onClick:g,children:"Settings"}),t.jsx("button",{onClick:j,children:"History"}),t.jsx("button",{onClick:()=>{v()},children:"Send Active Tab (yt-dlp)"}),t.jsxs("div",{className:"media-panel",children:[t.jsxs("div",{className:"media-head",children:[t.jsx("strong",{children:"Captured Media"}),t.jsx("button",{className:"mini ghost",onClick:y,children:"Clear"})]}),l.length===0?t.jsx("div",{className:"empty",children:"No media captured yet"}):t.jsx("div",{className:"media-list",children:l.map(e=>t.jsxs("div",{className:"media-item",children:[t.jsxs("div",{className:"media-meta",children:[t.jsx("span",{className:"kind",children:e.kind.toUpperCase()}),t.jsx("span",{className:"url",children:e.url})]}),t.jsx("button",{className:"mini",onClick:()=>{w(e)},children:"Add"})]},e.id))})]}),t.jsx("div",{className:"status",children:x})]}):t.jsx("div",{className:"container",children:"Loading..."})}k.createRoot(document.getElementById("root")).render(t.jsx(r.StrictMode,{children:t.jsx(b,{})}));
|
|
||||||
1
packages/chrome/assets/index.ts-BljhweV3.js
Normal file
1
packages/chrome/assets/index.ts-C6ePCen1.js
Normal file
@@ -1 +0,0 @@
|
|||||||
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();
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
(async () => {
|
(async () => {
|
||||||
const { onExecute } = await import(
|
const { onExecute } = await import(
|
||||||
/* @vite-ignore */
|
/* @vite-ignore */
|
||||||
chrome.runtime.getURL("assets/index.ts-CMnPQ13j.js")
|
chrome.runtime.getURL("assets/index.ts-C6ePCen1.js")
|
||||||
);
|
);
|
||||||
onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });
|
onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });
|
||||||
})().catch(console.error);
|
})().catch(console.error);
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 597 B After Width: | Height: | Size: 613 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 553 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 15 KiB |
17
packages/chrome/images/pomeranian-bw.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<svg xmlns='http://www.w3.org/2000/svg' width='512' height='512' viewBox='0 0 512 512'>
|
||||||
|
<rect width='512' height='512' rx='92' fill='#ffffff'/>
|
||||||
|
<g transform='translate(0 6) scale(1.08) translate(-19 -20)' fill='none' stroke='#101010' stroke-linecap='round' stroke-linejoin='round'>
|
||||||
|
<path d='M150 204 L102 136 L200 164 Z' stroke-width='22' fill='#fff'/>
|
||||||
|
<path d='M362 204 L410 136 L312 164 Z' stroke-width='22' fill='#fff'/>
|
||||||
|
<circle cx='256' cy='282' r='146' stroke-width='24' fill='#fff'/>
|
||||||
|
<path d='M176 246 C171 220,188 202,210 205 C230 184,256 182,277 204 C299 201,318 220,314 245 C336 257,342 279,336 299 C341 324,327 344,303 352 C286 374,228 374,209 352 C185 344,171 324,176 299 C170 280,173 258,176 246 Z' stroke-width='16' fill='#fff'/>
|
||||||
|
<circle cx='214' cy='272' r='16' fill='#101010' stroke='none'/>
|
||||||
|
<circle cx='298' cy='272' r='16' fill='#101010' stroke='none'/>
|
||||||
|
<circle cx='209' cy='266' r='5' fill='#fff' stroke='none'/>
|
||||||
|
<circle cx='293' cy='266' r='5' fill='#fff' stroke='none'/>
|
||||||
|
<ellipse cx='256' cy='314' rx='24' ry='18' fill='#101010' stroke='none'/>
|
||||||
|
<path d='M220 340 C236 356,276 356,292 340' stroke-width='12'/>
|
||||||
|
<path d='M244 331 L232 344' stroke-width='8'/>
|
||||||
|
<path d='M268 331 L280 344' stroke-width='8'/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Gomdown Helper",
|
"name": "Gomdown Helper",
|
||||||
"description": "Send browser downloads to gdown",
|
"description": "Send browser downloads to gdown",
|
||||||
"version": "0.0.11",
|
"version": "0.0.16",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"icons": {
|
"icons": {
|
||||||
"16": "images/16.png",
|
"16": "images/16.png",
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"js": [
|
"js": [
|
||||||
"assets/index.ts-loader-BHtfStLc.js"
|
"assets/index.ts-loader-Bju9eGS_.js"
|
||||||
],
|
],
|
||||||
"matches": [
|
"matches": [
|
||||||
"<all_urls>"
|
"<all_urls>"
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"images/*",
|
"images/*",
|
||||||
"assets/browser-polyfill-CZ_dLIqp.js",
|
"assets/browser-polyfill-CZ_dLIqp.js",
|
||||||
"assets/downloadIntent-Dv31jC2S.js",
|
"assets/downloadIntent-Dv31jC2S.js",
|
||||||
"assets/index.ts-CMnPQ13j.js"
|
"assets/index.ts-C6ePCen1.js"
|
||||||
],
|
],
|
||||||
"use_dynamic_url": false
|
"use_dynamic_url": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
import './assets/index.ts-BAxKsZ8F.js';
|
import './assets/index.ts-BljhweV3.js';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Gomdown Helper</title>
|
<title>Gomdown Helper</title>
|
||||||
<script type="module" crossorigin src="/assets/index.html-Tb8yZAds.js"></script>
|
<script type="module" crossorigin src="/assets/index.html-BLzIyLM-.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/browser-polyfill-CZ_dLIqp.js">
|
<link rel="modulepreload" crossorigin href="/assets/browser-polyfill-CZ_dLIqp.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/client-CBvt1tWS.js">
|
<link rel="modulepreload" crossorigin href="/assets/client-CBvt1tWS.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/settings-Bo6W9Drl.js">
|
<link rel="modulepreload" crossorigin href="/assets/settings-Bo6W9Drl.js">
|
||||||
|
|||||||
BIN
packages/gomdown-helper.v0.0.12.chrome.zip
Normal file
BIN
packages/gomdown-helper.v0.0.13.chrome.zip
Normal file
BIN
packages/gomdown-helper.v0.0.14.chrome.zip
Normal file
BIN
packages/gomdown-helper.v0.0.15.chrome.zip
Normal file
BIN
packages/gomdown-helper.v0.0.16.chrome.zip
Normal file
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 597 B After Width: | Height: | Size: 613 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 553 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 15 KiB |
17
public/images/pomeranian-bw.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<svg xmlns='http://www.w3.org/2000/svg' width='512' height='512' viewBox='0 0 512 512'>
|
||||||
|
<rect width='512' height='512' rx='92' fill='#ffffff'/>
|
||||||
|
<g transform='translate(0 6) scale(1.08) translate(-19 -20)' fill='none' stroke='#101010' stroke-linecap='round' stroke-linejoin='round'>
|
||||||
|
<path d='M150 204 L102 136 L200 164 Z' stroke-width='22' fill='#fff'/>
|
||||||
|
<path d='M362 204 L410 136 L312 164 Z' stroke-width='22' fill='#fff'/>
|
||||||
|
<circle cx='256' cy='282' r='146' stroke-width='24' fill='#fff'/>
|
||||||
|
<path d='M176 246 C171 220,188 202,210 205 C230 184,256 182,277 204 C299 201,318 220,314 245 C336 257,342 279,336 299 C341 324,327 344,303 352 C286 374,228 374,209 352 C185 344,171 324,176 299 C170 280,173 258,176 246 Z' stroke-width='16' fill='#fff'/>
|
||||||
|
<circle cx='214' cy='272' r='16' fill='#101010' stroke='none'/>
|
||||||
|
<circle cx='298' cy='272' r='16' fill='#101010' stroke='none'/>
|
||||||
|
<circle cx='209' cy='266' r='5' fill='#fff' stroke='none'/>
|
||||||
|
<circle cx='293' cy='266' r='5' fill='#fff' stroke='none'/>
|
||||||
|
<ellipse cx='256' cy='314' rx='24' ry='18' fill='#101010' stroke='none'/>
|
||||||
|
<path d='M220 340 C236 356,276 356,292 340' stroke-width='12'/>
|
||||||
|
<path d='M244 331 L232 344' stroke-width='8'/>
|
||||||
|
<path d='M268 331 L280 344' stroke-width='8'/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -22,6 +22,19 @@ let downloadHooked = false
|
|||||||
let contextMenuHooked = false
|
let contextMenuHooked = false
|
||||||
let contextMenuUpdateInFlight: Promise<void> | null = null
|
let contextMenuUpdateInFlight: Promise<void> | null = null
|
||||||
|
|
||||||
|
const MP4_PREFERRED_FORMAT = 'bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]/best'
|
||||||
|
const SITE_STRATEGIES: Array<{
|
||||||
|
hosts: string[]
|
||||||
|
extractor: 'yt-dlp' | 'aria2'
|
||||||
|
format?: string
|
||||||
|
}> = [
|
||||||
|
{
|
||||||
|
hosts: ['youtube.com', 'www.youtube.com', 'm.youtube.com', 'youtu.be'],
|
||||||
|
extractor: 'yt-dlp',
|
||||||
|
format: MP4_PREFERRED_FORMAT,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
function urlFingerprint(raw: string): string {
|
function urlFingerprint(raw: string): string {
|
||||||
try {
|
try {
|
||||||
const u = new URL(raw)
|
const u = new URL(raw)
|
||||||
@@ -115,7 +128,9 @@ async function transferUrlToGdown(
|
|||||||
url: string,
|
url: string,
|
||||||
referer = '',
|
referer = '',
|
||||||
extractor?: 'yt-dlp' | 'aria2',
|
extractor?: 'yt-dlp' | 'aria2',
|
||||||
format?: string
|
format?: string,
|
||||||
|
out?: string,
|
||||||
|
extra?: { cookie?: string; userAgent?: string; authorization?: string; proxy?: string }
|
||||||
): Promise<{ ok: boolean; error?: string }> {
|
): Promise<{ ok: boolean; error?: string }> {
|
||||||
if (shouldSuppressDuplicateTransfer(url)) {
|
if (shouldSuppressDuplicateTransfer(url)) {
|
||||||
return { ok: false, error: 'duplicate transfer suppressed' }
|
return { ok: false, error: 'duplicate transfer suppressed' }
|
||||||
@@ -132,8 +147,13 @@ async function transferUrlToGdown(
|
|||||||
rpcSecret: settings.motrixAPIkey,
|
rpcSecret: settings.motrixAPIkey,
|
||||||
referer,
|
referer,
|
||||||
split: 64,
|
split: 64,
|
||||||
|
out: out?.trim() || undefined,
|
||||||
|
cookie: extra?.cookie?.trim() || undefined,
|
||||||
|
userAgent: extra?.userAgent?.trim() || undefined,
|
||||||
|
authorization: extra?.authorization?.trim() || undefined,
|
||||||
|
proxy: extra?.proxy?.trim() || undefined,
|
||||||
extractor: extractor === 'yt-dlp' ? 'yt-dlp' : undefined,
|
extractor: extractor === 'yt-dlp' ? 'yt-dlp' : undefined,
|
||||||
format: extractor === 'yt-dlp' ? format || 'bestvideo*+bestaudio/best' : undefined,
|
format: extractor === 'yt-dlp' ? format || MP4_PREFERRED_FORMAT : undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!nativeResult?.ok) {
|
if (!nativeResult?.ok) {
|
||||||
@@ -180,6 +200,42 @@ async function transferUrlToGdown(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hostOf(raw: string): string {
|
||||||
|
try {
|
||||||
|
return new URL(raw).hostname.toLowerCase()
|
||||||
|
} catch {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveStrategy(
|
||||||
|
url: string,
|
||||||
|
referer = '',
|
||||||
|
kind = ''
|
||||||
|
): { extractor: 'yt-dlp' | 'aria2'; format?: string } {
|
||||||
|
const candidates = [hostOf(url), hostOf(referer)].filter(Boolean)
|
||||||
|
for (const rule of SITE_STRATEGIES) {
|
||||||
|
if (candidates.some((host) => rule.hosts.includes(host))) {
|
||||||
|
return { extractor: rule.extractor, format: rule.format }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mediaKind = kind.toLowerCase()
|
||||||
|
if (mediaKind === 'm3u8' || mediaKind === 'm3u' || mediaKind === 'hls') {
|
||||||
|
return { extractor: 'yt-dlp', format: 'best' }
|
||||||
|
}
|
||||||
|
if (mediaKind === 'mp4') {
|
||||||
|
return { extractor: 'aria2' }
|
||||||
|
}
|
||||||
|
return { extractor: 'aria2' }
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHeaderFromRequestHeaders(headers: any[], name: string): string {
|
||||||
|
const lower = name.toLowerCase()
|
||||||
|
const found = headers.find((item: any) => String(item?.name || '').toLowerCase() === lower)
|
||||||
|
return String(found?.value || '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
async function shouldCaptureRequest(details: any): Promise<boolean> {
|
async function shouldCaptureRequest(details: any): Promise<boolean> {
|
||||||
if (details.type !== 'main_frame') return false
|
if (details.type !== 'main_frame') return false
|
||||||
if ((details.method || '').toUpperCase() !== 'GET') return false
|
if ((details.method || '').toUpperCase() !== 'GET') return false
|
||||||
@@ -223,9 +279,66 @@ async function interceptMediaByWebRequest(details: any): Promise<void> {
|
|||||||
|
|
||||||
const req = pendingRequests.get(details.requestId)
|
const req = pendingRequests.get(details.requestId)
|
||||||
const referer = String(req?.documentUrl || req?.originUrl || req?.initiator || req?.url || '')
|
const referer = String(req?.documentUrl || req?.originUrl || req?.initiator || req?.url || '')
|
||||||
|
const requestHeaders = Array.isArray(req?.requestHeaders) ? req.requestHeaders : []
|
||||||
|
const cookie = getHeaderFromRequestHeaders(requestHeaders, 'cookie')
|
||||||
|
const userAgent = getHeaderFromRequestHeaders(requestHeaders, 'user-agent')
|
||||||
const candidate = makeMediaCandidate(details, referer)
|
const candidate = makeMediaCandidate(details, referer)
|
||||||
await upsertMediaCandidate(candidate, mediaFingerprint(candidate.url))
|
let pageTitle = ''
|
||||||
rememberMediaFingerprint(candidate.url)
|
if (candidate.tabId >= 0) {
|
||||||
|
const tab = await browser.tabs.get(candidate.tabId).catch(() => null)
|
||||||
|
pageTitle = String(tab?.title || '').trim()
|
||||||
|
}
|
||||||
|
const suggestedOut = suggestMediaOut(candidate.url, candidate.kind, pageTitle)
|
||||||
|
const enriched = {
|
||||||
|
...candidate,
|
||||||
|
pageTitle: pageTitle || undefined,
|
||||||
|
cookie: cookie || undefined,
|
||||||
|
userAgent: userAgent || undefined,
|
||||||
|
suggestedOut: suggestedOut || undefined,
|
||||||
|
}
|
||||||
|
await upsertMediaCandidate(enriched, mediaFingerprint(enriched.url))
|
||||||
|
rememberMediaFingerprint(enriched.url)
|
||||||
|
if (enriched.tabId >= 0) {
|
||||||
|
await browser.tabs
|
||||||
|
.sendMessage(enriched.tabId, {
|
||||||
|
type: 'media:captured',
|
||||||
|
kind: enriched.kind,
|
||||||
|
url: enriched.url,
|
||||||
|
suggestedOut: enriched.suggestedOut || '',
|
||||||
|
})
|
||||||
|
.catch(() => null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeOut(value: string): string {
|
||||||
|
let next = value
|
||||||
|
.trim()
|
||||||
|
.replace(/[\\/:*?"<>|]/g, '_')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.replace(/^\.+/, '')
|
||||||
|
.replace(/\.+$/, '')
|
||||||
|
if (next.length > 180) next = next.slice(0, 180).trim()
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
function extFromUrl(url: string): string {
|
||||||
|
try {
|
||||||
|
const path = new URL(url).pathname.toLowerCase()
|
||||||
|
const match = path.match(/\.([a-z0-9]{2,6})(?:$|[?#])/)
|
||||||
|
return match?.[1] || ''
|
||||||
|
} catch {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function suggestMediaOut(url: string, kind: string, pageTitle: string): string {
|
||||||
|
const title = sanitizeOut(pageTitle || '')
|
||||||
|
const ext = extFromUrl(url)
|
||||||
|
const baseExt = ext || (kind === 'mp4' ? 'mp4' : kind === 'm3u8' || kind === 'm3u' || kind === 'hls' ? 'mp4' : '')
|
||||||
|
if (!title) return ''
|
||||||
|
if (!baseExt) return title
|
||||||
|
if (title.toLowerCase().endsWith(`.${baseExt}`)) return title
|
||||||
|
return `${title}.${baseExt}`
|
||||||
}
|
}
|
||||||
|
|
||||||
async function applyShelfVisibility(): Promise<void> {
|
async function applyShelfVisibility(): Promise<void> {
|
||||||
@@ -311,17 +424,18 @@ async function handleContextMenuClick(data: any, tab?: any): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const useYtDlp = !linkUrl && !!pageUrl
|
const strategy = resolveStrategy(url, String(data?.pageUrl || tab?.url || ''), '')
|
||||||
const result = await transferUrlToGdown(
|
const result = await transferUrlToGdown(
|
||||||
url,
|
url,
|
||||||
String(data?.pageUrl || tab?.url || ''),
|
String(data?.pageUrl || tab?.url || ''),
|
||||||
useYtDlp ? 'yt-dlp' : 'aria2'
|
strategy.extractor,
|
||||||
|
strategy.format
|
||||||
)
|
)
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
await notify(`전송 실패: ${result.error || 'unknown error'}`)
|
await notify(`전송 실패: ${result.error || 'unknown error'}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await notify(useYtDlp ? '페이지 URL을 yt-dlp로 gdown에 전송했습니다.' : 'gdown으로 전송했습니다.')
|
await notify(strategy.extractor === 'yt-dlp' ? '페이지 URL을 yt-dlp로 gdown에 전송했습니다.' : 'gdown으로 전송했습니다.')
|
||||||
}
|
}
|
||||||
|
|
||||||
function createContextMenuSafe(): void {
|
function createContextMenuSafe(): void {
|
||||||
@@ -404,8 +518,17 @@ browser.runtime.onMessage.addListener((message: any, sender: any) => {
|
|||||||
|
|
||||||
if (message?.type === 'media:enqueue') {
|
if (message?.type === 'media:enqueue') {
|
||||||
const url = String(message?.url || '').trim()
|
const url = String(message?.url || '').trim()
|
||||||
|
const kind = String(message?.kind || '').trim()
|
||||||
|
const suggestedOut = String(message?.suggestedOut || '').trim()
|
||||||
|
const referer = String(message?.referer || '').trim()
|
||||||
|
const cookie = String(message?.cookie || '').trim()
|
||||||
|
const userAgent = String(message?.userAgent || '').trim()
|
||||||
if (!url) return Promise.resolve({ ok: false, error: 'url is empty' })
|
if (!url) return Promise.resolve({ ok: false, error: 'url is empty' })
|
||||||
return transferUrlToGdown(url, String(message?.referer || '')).then((result) => result)
|
const strategy = resolveStrategy(url, referer, kind)
|
||||||
|
return transferUrlToGdown(url, referer, strategy.extractor, strategy.format, suggestedOut, {
|
||||||
|
cookie,
|
||||||
|
userAgent,
|
||||||
|
}).then((result) => result)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message?.type === 'page:enqueue-ytdlp') {
|
if (message?.type === 'page:enqueue-ytdlp') {
|
||||||
|
|||||||
@@ -279,3 +279,56 @@ function watchYoutubeRouteChanges(): void {
|
|||||||
|
|
||||||
ensureYoutubeOverlay()
|
ensureYoutubeOverlay()
|
||||||
watchYoutubeRouteChanges()
|
watchYoutubeRouteChanges()
|
||||||
|
|
||||||
|
let mediaToastRoot: HTMLDivElement | null = null
|
||||||
|
let mediaToastTimer: number | null = null
|
||||||
|
|
||||||
|
function ensureMediaToastRoot(): HTMLDivElement {
|
||||||
|
if (mediaToastRoot) return mediaToastRoot
|
||||||
|
const root = document.createElement('div')
|
||||||
|
root.id = 'gomdown-media-toast'
|
||||||
|
root.style.position = 'fixed'
|
||||||
|
root.style.left = '18px'
|
||||||
|
root.style.bottom = '18px'
|
||||||
|
root.style.zIndex = '2147483647'
|
||||||
|
root.style.maxWidth = '360px'
|
||||||
|
root.style.padding = '10px 12px'
|
||||||
|
root.style.borderRadius = '10px'
|
||||||
|
root.style.border = '1px solid rgba(128, 140, 180, 0.42)'
|
||||||
|
root.style.background = 'rgba(18, 21, 31, 0.95)'
|
||||||
|
root.style.color = '#dce4fa'
|
||||||
|
root.style.fontSize = '12px'
|
||||||
|
root.style.lineHeight = '1.35'
|
||||||
|
root.style.fontFamily = 'ui-sans-serif, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif'
|
||||||
|
root.style.boxShadow = '0 10px 24px rgba(0, 0, 0, 0.28)'
|
||||||
|
root.style.display = 'none'
|
||||||
|
document.documentElement.appendChild(root)
|
||||||
|
mediaToastRoot = root
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMediaCapturedToast(payload: { kind?: string; url?: string; suggestedOut?: string }): void {
|
||||||
|
const root = ensureMediaToastRoot()
|
||||||
|
const kind = String(payload?.kind || 'media').toUpperCase()
|
||||||
|
const out = String(payload?.suggestedOut || '').trim()
|
||||||
|
const shortUrl = String(payload?.url || '').trim().slice(0, 96)
|
||||||
|
root.textContent = out
|
||||||
|
? `캡처됨 [${kind}] ${out}`
|
||||||
|
: `캡처됨 [${kind}] ${shortUrl}${shortUrl.length >= 96 ? '…' : ''}`
|
||||||
|
root.style.display = 'block'
|
||||||
|
if (mediaToastTimer !== null) window.clearTimeout(mediaToastTimer)
|
||||||
|
mediaToastTimer = window.setTimeout(() => {
|
||||||
|
root.style.display = 'none'
|
||||||
|
mediaToastTimer = null
|
||||||
|
}, 2200)
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.runtime.onMessage.addListener((message: any) => {
|
||||||
|
if (message?.type === 'media:captured') {
|
||||||
|
showMediaCapturedToast({
|
||||||
|
kind: message?.kind,
|
||||||
|
url: message?.url,
|
||||||
|
suggestedOut: message?.suggestedOut,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ export type MediaCandidate = {
|
|||||||
kind: MediaKind
|
kind: MediaKind
|
||||||
tabId: number
|
tabId: number
|
||||||
pageUrl: string
|
pageUrl: string
|
||||||
|
pageTitle?: string
|
||||||
referer: string
|
referer: string
|
||||||
|
cookie?: string
|
||||||
|
userAgent?: string
|
||||||
|
suggestedOut?: string
|
||||||
contentType: string
|
contentType: string
|
||||||
detectedAt: number
|
detectedAt: number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ type MediaCandidate = {
|
|||||||
url: string
|
url: string
|
||||||
kind: 'mp4' | 'm3u8' | 'm3u' | 'hls' | 'unknown'
|
kind: 'mp4' | 'm3u8' | 'm3u' | 'hls' | 'unknown'
|
||||||
referer: string
|
referer: string
|
||||||
|
pageTitle?: string
|
||||||
|
suggestedOut?: string
|
||||||
|
cookie?: string
|
||||||
|
userAgent?: string
|
||||||
detectedAt: number
|
detectedAt: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +89,10 @@ function App(): JSX.Element {
|
|||||||
type: 'media:enqueue',
|
type: 'media:enqueue',
|
||||||
url: item.url,
|
url: item.url,
|
||||||
referer: item.referer || '',
|
referer: item.referer || '',
|
||||||
|
kind: item.kind,
|
||||||
|
suggestedOut: item.suggestedOut || '',
|
||||||
|
cookie: item.cookie || '',
|
||||||
|
userAgent: item.userAgent || '',
|
||||||
})) as { ok?: boolean; error?: string }
|
})) as { ok?: boolean; error?: string }
|
||||||
setStatus(result?.ok ? 'Media sent to gdown' : `Send failed: ${result?.error || 'unknown error'}`)
|
setStatus(result?.ok ? 'Media sent to gdown' : `Send failed: ${result?.error || 'unknown error'}`)
|
||||||
window.setTimeout(() => setStatus(''), 1600)
|
window.setTimeout(() => setStatus(''), 1600)
|
||||||
@@ -171,6 +179,7 @@ function App(): JSX.Element {
|
|||||||
<div key={item.id} className="media-item">
|
<div key={item.id} className="media-item">
|
||||||
<div className="media-meta">
|
<div className="media-meta">
|
||||||
<span className="kind">{item.kind.toUpperCase()}</span>
|
<span className="kind">{item.kind.toUpperCase()}</span>
|
||||||
|
{item.pageTitle ? <span className="url">{item.pageTitle}</span> : null}
|
||||||
<span className="url">{item.url}</span>
|
<span className="url">{item.url}</span>
|
||||||
</div>
|
</div>
|
||||||
<button className="mini" onClick={() => void onSendMedia(item)}>Add</button>
|
<button className="mini" onClick={() => void onSendMedia(item)}>Add</button>
|
||||||
|
|||||||