Export cookies in Netscape, JSON, or header format for curl, wget, yt-dlp — replaces Get cookies.txt LOCALLY
// ==UserScript==
// @name Cookie Exporter | Netscape, JSON, Header
// @namespace https://greasyfork.org/en/users/1462137-piknockyou
// @version 3.0.9
// @author Piknockyou
// @license AGPL-3.0
// @description Export cookies in Netscape, JSON, or header format for curl, wget, yt-dlp — replaces Get cookies.txt LOCALLY
// @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
// @grant GM_cookie
// @grant GM.cookie
// @grant GM_registerMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// @match *://*/*
// @noframes
// @run-at document-idle
// ==/UserScript==
(() => {
// ═══════════════════════════════════════════════════════════════
// COOKIE API DETECTION
// ═══════════════════════════════════════════════════════════════
function hasCookieApi() {
return (
(typeof GM_cookie === "object" && GM_cookie?.list) ||
typeof GM_cookie === "function"
);
}
// ═══════════════════════════════════════════════════════════════
// FETCH COOKIES
// ═══════════════════════════════════════════════════════════════
function gmCookieList(details) {
return new Promise((resolve, reject) => {
if (typeof GM_cookie === "object" && GM_cookie?.list) {
GM_cookie.list(details, (cookies, err) => {
if (err) reject(err);
else resolve(cookies || []);
});
} else if (typeof GM_cookie === "function") {
GM_cookie("list", details, (cookies, err) => {
if (err) reject(err);
else resolve(cookies || []);
});
} else {
reject(new Error("No callback-style GM_cookie API"));
}
});
}
async function fetchCookies(hostname) {
for (const details of [
{ url: `https://${hostname}/` },
{ domain: hostname },
{},
]) {
try {
const cookies = await gmCookieList(details);
console.log(
"[Cookie Exporter] OK:",
cookies.length,
JSON.stringify(details),
);
return cookies;
} catch (err) {
console.error(
"[Cookie Exporter] attempt",
JSON.stringify(details),
err,
);
}
}
throw new Error("All cookie queries failed");
}
async function fetchAllCookies() {
try {
const cookies = await gmCookieList({});
console.log("[Cookie Exporter] All cookies:", cookies.length);
return cookies;
} catch (err) {
console.error("[Cookie Exporter] All cookies failed:", err);
throw new Error("Failed to fetch all cookies");
}
}
// ═══════════════════════════════════════════════════════════════
// FORMATS
// ═══════════════════════════════════════════════════════════════
const FORMATS = {
netscape: {
ext: "txt",
mime: "text/plain",
serialize(cookies) {
const lines = cookies.map((c) => {
const domain = c.domain || "";
const flag = domain.startsWith(".") ? "TRUE" : "FALSE";
const path = c.path || "/";
const secure = c.secure ? "TRUE" : "FALSE";
const expiry = c.expirationDate ? Math.floor(c.expirationDate) : 0;
return `${domain}\t${flag}\t${path}\t${secure}\t${expiry}\t${c.name}\t${c.value}`;
});
return [
"# Netscape HTTP Cookie File",
"# https://curl.haxx.se/rfc/cookie_spec.html",
"# This is a generated file! Do not edit.",
"",
...lines,
"",
].join("\n");
},
},
json: {
ext: "json",
mime: "application/json",
serialize(cookies) {
return JSON.stringify(cookies, null, 2);
},
},
header: {
ext: "txt",
mime: "text/plain",
serialize(cookies) {
return cookies.map(({ name, value }) => `${name}=${value}`).join(";\n");
},
},
};
const FORMAT_KEYS = Object.keys(FORMATS);
// ═══════════════════════════════════════════════════════════════
// FORMAT PERSISTENCE
// ═══════════════════════════════════════════════════════════════
function getSelectedFormat() {
return (
(typeof GM_getValue === "function" && GM_getValue("exportFormat")) ||
"netscape"
);
}
function setSelectedFormat(key) {
if (typeof GM_setValue === "function") {
GM_setValue("exportFormat", key);
}
}
// ═══════════════════════════════════════════════════════════════
// DOWNLOAD
// ═══════════════════════════════════════════════════════════════
function domDownload(content, filename, mime) {
const blob = new Blob([content], { type: mime });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.style.display = "none";
a.rel = "noopener";
(document.body || document.documentElement).appendChild(a);
a.addEventListener(
"click",
(e) => {
e.stopPropagation();
if (typeof e.stopImmediatePropagation === "function") {
e.stopImmediatePropagation();
}
},
{ capture: true, once: true },
);
a.click();
a.remove();
setTimeout(() => URL.revokeObjectURL(url), 10000);
}
function downloadFile(content, filename, mime = "text/plain") {
return new Promise((resolve) => {
domDownload(content, filename, mime);
resolve();
});
}
// ═══════════════════════════════════════════════════════════════
// NOTIFICATION
// ═══════════════════════════════════════════════════════════════
function notify(msg, color = "#4CAF50") {
const el = document.createElement("div");
Object.assign(el.style, {
position: "fixed",
bottom: "20px",
right: "20px",
padding: "10px 18px",
background: color,
color: "#fff",
borderRadius: "6px",
fontSize: "13px",
fontWeight: "500",
fontFamily: "sans-serif",
zIndex: "2147483647",
boxShadow: "0 3px 12px rgba(0,0,0,0.3)",
transition: "opacity 0.3s",
});
el.textContent = msg;
document.body.appendChild(el);
setTimeout(() => {
el.style.opacity = "0";
setTimeout(() => el.remove(), 400);
}, 2500);
}
// ═══════════════════════════════════════════════════════════════
// BANNER (when API unavailable)
// ═══════════════════════════════════════════════════════════════
function showBanner() {
const host = document.createElement("div");
Object.assign(host.style, {
position: "fixed",
top: "0",
left: "0",
width: "100vw",
height: "100vh",
zIndex: "2147483647",
});
const shadow = host.attachShadow({ mode: "closed" });
const style = document.createElement("style");
style.textContent = `
:host { all: initial; }
.overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); display: flex; align-items: center; justify-content: center; padding: 20px; font-family: sans-serif; }
.box { background: #1e1e1e; border: 1px solid #444; border-radius: 12px; max-width: 520px; width: 100%; padding: 28px 32px; color: #e0e0e0; }
.title { font-size: 18px; font-weight: 700; text-align: center; margin-bottom: 14px; color: #f44336; }
.desc { font-size: 13px; line-height: 1.6; color: #ccc; margin-bottom: 14px; }
.sec { background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 8px; padding: 12px 14px; margin: 10px 0; }
.sec-title { font-size: 13px; font-weight: 600; color: #4CAF50; margin-bottom: 6px; }
.steps { margin: 0; padding-left: 18px; font-size: 12px; line-height: 1.7; color: #ccc; }
.btn { display: block; width: 100%; padding: 12px; background: #f44336; color: #fff; border: none; border-radius: 6px; font-size: 14px; font-weight: 600; cursor: pointer; margin-top: 14px; }
`;
shadow.appendChild(style);
const overlay = document.createElement("div");
overlay.className = "overlay";
const box = document.createElement("div");
box.className = "box";
box.innerHTML = `
<div class="title">Cookie API Not Available</div>
<div class="desc">Your userscript manager doesn't expose the GM_cookie API.</div>
<div class="sec"><div class="sec-title">Violentmonkey</div>
<ol class="steps"><li>Violentmonkey → Settings → Advanced → Script settings</li><li>Enable "Allow GM_cookie to access HTTP-only cookies"</li><li>Also check it in this script's Settings tab</li></ol></div>
<div class="sec"><div class="sec-title">Tampermonkey</div>
<ol class="steps"><li>Tampermonkey → Settings → Config mode: Advanced</li><li>Security → "Allow scripts to access cookies" → All</li></ol></div>
<button class="btn">Got it</button>
`;
box.querySelector(".btn").onclick = () => host.remove();
overlay.onclick = (e) => {
if (e.target === overlay) host.remove();
};
overlay.appendChild(box);
shadow.appendChild(overlay);
document.body.appendChild(host);
}
// ═══════════════════════════════════════════════════════════════
// MAIN EXPORT
// ═══════════════════════════════════════════════════════════════
async function exportCookies(scope = "current") {
if (!hasCookieApi()) {
console.log("[Cookie Exporter] no API");
showBanner();
return;
}
const formatKey = getSelectedFormat();
const fmt = FORMATS[formatKey];
if (!fmt) {
notify(`Unknown format: ${formatKey}`, "#f44336");
return;
}
try {
const cookies =
scope === "all"
? await fetchAllCookies()
: await fetchCookies(window.location.hostname);
if (!cookies || cookies.length === 0) {
notify(
`No cookies${scope === "all" ? "" : ` for ${window.location.hostname}`}`,
"#FF9800",
);
return;
}
const text = fmt.serialize(cookies);
const name =
scope === "all" ? "cookies" : `${window.location.hostname}_cookies`;
await downloadFile(text, `${name}.${fmt.ext}`, fmt.mime);
notify(`Exported ${cookies.length} cookies as ${formatKey}`);
} catch (e) {
console.error("[Cookie Exporter] FAILED:", e);
notify(`${e.message}`, "#f44336");
}
}
// ═══════════════════════════════════════════════════════════════
// MENU COMMANDS
// ═══════════════════════════════════════════════════════════════
if (typeof GM_registerMenuCommand === "function") {
GM_registerMenuCommand("Export Cookies", () => exportCookies("current"));
GM_registerMenuCommand("Export All Cookies", () => exportCookies("all"));
let formatMenuId = null;
const updateFormatMenu = () => {
const key = getSelectedFormat();
formatMenuId = GM_registerMenuCommand(
`Format: ${key} (click to change)`,
() => {
const next =
FORMAT_KEYS[(FORMAT_KEYS.indexOf(key) + 1) % FORMAT_KEYS.length];
setSelectedFormat(next);
notify(`Format: ${next}`);
updateFormatMenu();
},
{ id: formatMenuId, autoClose: false },
);
};
updateFormatMenu();
}
})();