UNPKG

read-browser-cookies

Version:

Node.js version of `--cookies-from-browser` from yt-dlp

197 lines (196 loc) 7.68 kB
"use strict"; /** * https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/cookies.py */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.processChromeTimestamp = exports.readChromium = exports.chromiumBasedBrowsers = void 0; const fs_1 = __importDefault(require("fs")); const globby_1 = __importDefault(require("globby")); const lodash_1 = require("lodash"); const os_1 = __importDefault(require("os")); const path_1 = __importDefault(require("path")); const psl_1 = __importDefault(require("psl")); const db_1 = require("../db"); const helper_1 = require("../helper"); const decrypt_1 = require("./decrypt"); const debug = helper_1.baseDebug.extend('chromium:index'); exports.chromiumBasedBrowsers = [ 'brave', 'chrome', 'chromium', 'edge', 'opera', 'vivaldi', ]; // cookiesfrombrowser: A tuple containing // - the name of the browser, the profile // - name/path from where cookies are loaded // - the name of the keyring, // - and the container name, // e.g. // ('chrome', ) // or ('vivaldi', 'default', 'BASICTEXT') // or ('firefox', 'default', None, 'Meta') async function readChromium(browserName, { profile, keyring, site } = {}) { const config = getChromiumBasedBrowserSettings(browserName); let searchRoot; // no profile if (!profile) { searchRoot = config.browserDir; } // profile is path else if (isPath(profile)) { searchRoot = profile; config.browserDir = config.supportsProfiles ? path_1.default.dirname(profile) : profile; } // profile is normal string else { if (config.supportsProfiles) { searchRoot = path_1.default.join(config.browserDir, profile); } else { console.error('%s does not support profiles'); searchRoot = config.browserDir; } } // check search_root if (!fs_1.default.existsSync(searchRoot)) { debug('search root: %s not exists', searchRoot); return; } const cookieDatabasePath = findMostRecentlyUsedFile(searchRoot, 'Cookies'); if (!cookieDatabasePath) { debug('cookies database not found'); return; } // site, use domain for query db // e.g if site = space.bilibili.com, should include cookies under .bilibili.com // postprocess after db query let tlDomain; if (site) { tlDomain = psl_1.default.get(site) || undefined; debug('site = %s, tlDomain = %s', site, tlDomain); } const allRows = await (0, db_1.query)(cookieDatabasePath, tlDomain); const decryptor = new decrypt_1.ChromeCookieDecryptor(config.browserDir, config.keyringName, keyring); await decryptor.init(); let decryptedRows = allRows.map((row) => { const newrow = { ...(0, lodash_1.omit)(row, ['encryptedValue']), // decrypt value: decryptor.decrypt(row.encryptedValue) || row.value, // expiresUtc = 0, means session cookie expiresUtc: row.expiresUtc ? processChromeTimestamp(row.expiresUtc) : row.expiresUtc, }; return newrow; }); if (site && tlDomain) { const allowedHostKey = []; const parts = site.split('.'); let startIndex = 0; const cur = () => parts.slice(startIndex).join('.'); while (cur().length >= tlDomain.length) { allowedHostKey.push('.' + cur()); startIndex++; } debug('site = %s, allowedHostKey = %s', site, allowedHostKey); decryptedRows = decryptedRows.filter((row) => { return allowedHostKey.includes(row.hostKey); }); } return decryptedRows; } exports.readChromium = readChromium; function isPath(value) { return value?.includes(path_1.default.sep); } function findMostRecentlyUsedFile(root, filename) { const files = globby_1.default.sync(`./**/${filename}`, { cwd: root, absolute: true, }); const withStat = files.map((f) => ({ filepath: f, stat: fs_1.default.statSync(f) })); const sorted = withStat.sort((a, b) => -(a.stat.mtimeMs - b.stat.mtimeMs)); // desc const first = sorted[0]?.filepath; return first; } /** * def _get_chromium_based_browser_settings(browser_name): */ function getChromiumBasedBrowserSettings(browserName) { // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/user_data_dir.md let browserDir = ''; (0, helper_1.execCrossPlatform)({ mac() { const appdata = path_1.default.posix.join(os_1.default.homedir(), 'Library/Application Support'); const join = path_1.default.posix.join; browserDir = { brave: join(appdata, 'BraveSoftware/Brave-Browser'), chrome: join(appdata, 'Google/Chrome'), chromium: join(appdata, 'Chromium'), edge: join(appdata, 'Microsoft Edge'), opera: join(appdata, 'com.operasoftware.Opera'), vivaldi: join(appdata, 'Vivaldi'), }[browserName]; }, win() { const appdata_local = process.env.LOCALAPPDATA; const appdata_roaming = process.env.APPDATA; if (!appdata_local || !appdata_roaming) { throw new Error('expect LOCALAPPDATA & APPDATA environment variable'); } const join = path_1.default.win32.join; browserDir = { brave: join(appdata_local, 'BraveSoftware/Brave-Browser/User Data'), chrome: join(appdata_local, 'Google/Chrome/User Data'), chromium: join(appdata_local, 'Chromium/User Data'), edge: join(appdata_local, 'Microsoft/Edge/User Data'), opera: join(appdata_roaming, 'Opera Software/Opera Stable'), vivaldi: join(appdata_local, 'Vivaldi/User Data'), }[browserName]; }, linux() { const config = xdgConfigHome(); const join = path_1.default.posix.join; browserDir = { brave: join(config, 'BraveSoftware/Brave-Browser'), chrome: join(config, 'google-chrome'), chromium: join(config, 'chromium'), edge: join(config, 'microsoft-edge'), opera: join(config, 'opera'), vivaldi: join(config, 'vivaldi'), }[browserName]; }, }); const keyringName = { brave: 'Brave', chrome: 'Chrome', chromium: 'Chromium', edge: process.platform == 'darwin' ? 'Microsoft Edge' : 'Chromium', opera: process.platform == 'darwin' ? 'Opera' : 'Chromium', vivaldi: process.platform == 'darwin' ? 'Vivaldi' : 'Chrome', }[browserName]; const browsersWithoutProfiles = ['opera']; return { browserDir: browserDir, keyringName: keyringName, supportsProfiles: !browsersWithoutProfiles.includes(browserName), }; } function xdgConfigHome() { return process.env.XDG_CONFIG_HOME || path_1.default.join(os_1.default.homedir(), '.config'); } /** * 17 位时间戳的处理 * https://stackoverflow.com/questions/20458406/what-is-the-format-of-chromes-timestamps * 11644473600 = 1970-1-1 - 1601-1-1, 单位 (s) */ function processChromeTimestamp(chromeTs) { const unixTsInSeconds = chromeTs / 1000000 - 11644473600; // (s) len=10 const unixTsInMillSeconds = Math.round(unixTsInSeconds * 1000); // (ms) len=13 return unixTsInMillSeconds; } exports.processChromeTimestamp = processChromeTimestamp;