diff options
Diffstat (limited to 'background.js')
-rw-r--r-- | background.js | 512 |
1 files changed, 331 insertions, 181 deletions
diff --git a/background.js b/background.js index 5c847024..3f5145cd 100644 --- a/background.js +++ b/background.js @@ -1,25 +1,47 @@ -'use strict'; +"use strict"; -const invidiousDefault = 'https://invidio.us'; const youtubeDomains = [ - 'm.youtube.com', - 'youtube.com', - 'img.youtube.com', - 'www.youtube.com', - 'youtube-nocookie.com', - 'www.youtube-nocookie.com', - 'youtu.be', - 's.ytimg.com', + "m.youtube.com", + "youtube.com", + "img.youtube.com", + "www.youtube.com", + "youtube-nocookie.com", + "www.youtube-nocookie.com", + "youtu.be", + "s.ytimg.com", + "music.youtube.com", +]; +const invidiousInstances = [ + "https://invidious.snopyta.org", + "https://yewtu.be", + "https://invidious.13ad.de", + "https://invidious.xyz", + "https://invidious.site", + "https://invidiou.site", + "https://invidious.fdn.fr", + "https://invidious.toot.koeln", ]; -const nitterDefault = 'https://nitter.net'; const twitterDomains = [ - 'twitter.com', - 'www.twitter.com', - 'mobile.twitter.com', - 'pbs.twimg.com', - 'video.twimg.com', + "twitter.com", + "www.twitter.com", + "mobile.twitter.com", + "pbs.twimg.com", + "video.twimg.com", +]; +const nitterInstances = [ + "https://nitter.net", + "https://nitter.snopyta.org", + "https://nitter.42l.fr", + "https://nitter.nixnet.services", + "https://nitter.13ad.de", + "https://nitter.pussthecat.org", + "https://nitter.mastodont.cat", + "https://nitter.tedomum.net", + "https://nitter.cattube.org", + "https://nitter.fdn.fr", + "https://nitter.1d4.us", + "https://nitter.kavin.rocks", ]; -const bibliogramDefault = 'https://bibliogram.art'; const instagramDomains = [ "instagram.com", "www.instagram.com", @@ -27,50 +49,57 @@ const instagramDomains = [ "about.instagram.com", ]; const instagramReservedPaths = [ - 'about', - 'explore', - 'support', - 'press', - 'api', - 'privacy', - 'safety', - 'admin', - 'graphql', - 'accounts', - 'help', - 'terms', - 'contact', - 'blog', - 'igtv', - 'u', - 'p', - 'fragment', - 'imageproxy', - 'videoproxy', - '.well-known' + "about", + "explore", + "support", + "press", + "api", + "privacy", + "safety", + "admin", + "graphql", + "accounts", + "help", + "terms", + "contact", + "blog", + "igtv", + "u", + "p", + "fragment", + "imageproxy", + "videoproxy", + ".well-known", ]; const bibliogramBypassPaths = /\/(accounts\/|embeds?.js)/; const bibliogramInstances = [ - 'https://bibliogram.art', - 'https://bibliogram.snopyta.org' + "https://bibliogram.art", + "https://bibliogram.snopyta.org", + "https://bibliogram.pussthecat.org", + "https://bibliogram.nixnet.services", + "https://bg.endl.site", + "https://bibliogram.13ad.de ", + "https://bibliogram.stemy.me ", + "https://bibliogram.hamster.dance", + "https://bibliogram.ggc-project.de", ]; -const osmDefault = 'https://openstreetmap.org'; -const googleMapsRegex = /https?:\/\/(((www|maps)\.)?(google).*(\/maps)|maps\.(google).*)/; +const osmDefault = "https://openstreetmap.org"; +const googleMapsRegex = /https?:\/\/(((www|maps)\.)?(google\.).*(\/maps)|maps\.(google\.).*)/; const mapCentreRegex = /@(-?\d[0-9.]*),(-?\d[0-9.]*),(\d{1,2})[.z]/; const dataLatLngRegex = /(!3d|!4d)(-?[0-9]{1,10}.[0-9]{1,10})/g; const placeRegex = /\/place\/(.*)\//; const travelModes = { - 'driving': 'fossgis_osrm_car', - 'walking': 'fossgis_osrm_foot', - 'bicycling': 'fossgis_osrm_bike', - 'transit': 'fossgis_osrm_car' // not implemented on OSM, default to car. + driving: "fossgis_osrm_car", + walking: "fossgis_osrm_foot", + bicycling: "fossgis_osrm_bike", + transit: "fossgis_osrm_car", // not implemented on OSM, default to car. }; const layers = { - 'none': 'S', - 'transit': 'T', - 'traffic': 'S', // not implemented on OSM, default to standard. - 'bicycling': 'C' -} + none: "S", + transit: "T", + traffic: "S", // not implemented on OSM, default to standard. + bicycling: "C", +}; let disableNitter; let disableInvidious; @@ -84,85 +113,119 @@ let alwaysProxy; let onlyEmbeddedVideo; let videoQuality; let invidiousDarkMode; -let whitelist; +let invidiousVolume; +let invidiousPlayerStyle; +let invidiousSubtitles; +let invidiousAutoplay; +let exceptions; window.browser = window.browser || window.chrome; browser.storage.sync.get( [ - 'nitterInstance', - 'invidiousInstance', - 'bibliogramInstance', - 'osmInstance', - 'disableNitter', - 'disableInvidious', - 'disableBibliogram', - 'disableOsm', - 'alwaysProxy', - 'onlyEmbeddedVideo', - 'videoQuality', - 'invidiousDarkMode', - 'whitelist' + "nitterInstance", + "invidiousInstance", + "bibliogramInstance", + "osmInstance", + "disableNitter", + "disableInvidious", + "disableBibliogram", + "disableOsm", + "alwaysProxy", + "onlyEmbeddedVideo", + "videoQuality", + "invidiousDarkMode", + "invidiousVolume", + "invidiousPlayerStyle", + "invidiousSubtitles", + "invidiousAutoplay", + "exceptions", ], - result => { + (result) => { disableNitter = result.disableNitter; disableInvidious = result.disableInvidious; disableBibliogram = result.disableBibliogram; disableOsm = result.disableOsm; - nitterInstance = result.nitterInstance || nitterDefault; - invidiousInstance = result.invidiousInstance || invidiousDefault; - bibliogramInstance = result.bibliogramInstance || bibliogramDefault; + nitterInstance = result.nitterInstance; + invidiousInstance = result.invidiousInstance; + bibliogramInstance = result.bibliogramInstance; osmInstance = result.osmInstance || osmDefault; alwaysProxy = result.alwaysProxy; onlyEmbeddedVideo = result.onlyEmbeddedVideo; videoQuality = result.videoQuality; invidiousDarkMode = result.invidiousDarkMode; - whitelist = result.whitelist ? result.whitelist.map(e => new RegExp(e)) : []; + exceptions = result.exceptions + ? result.exceptions.map((e) => { + return new RegExp(e); + }) + : []; + invidiousVolume = result.invidiousVolume; + invidiousPlayerStyle = result.invidiousPlayerStyle; + invidiousSubtitles = result.invidiousSubtitles || ""; + invidiousAutoplay = result.invidiousAutoplay; } ); -browser.storage.onChanged.addListener(changes => { - if ('nitterInstance' in changes) { - nitterInstance = changes.nitterInstance.newValue || nitterDefault; +browser.storage.onChanged.addListener((changes) => { + if ("nitterInstance" in changes) { + nitterInstance = changes.nitterInstance.newValue; } - if ('invidiousInstance' in changes) { - invidiousInstance = changes.invidiousInstance.newValue || invidiousDefault; + if ("invidiousInstance" in changes) { + invidiousInstance = changes.invidiousInstance.newValue; } - if ('bibliogramInstance' in changes) { - bibliogramInstance = changes.bibliogramInstance.newValue || bibliogramDefault; + if ("bibliogramInstance" in changes) { + bibliogramInstance = changes.bibliogramInstance.newValue; } - if ('osmInstance' in changes) { + if ("osmInstance" in changes) { osmInstance = changes.osmInstance.newValue || osmDefault; } - if ('disableNitter' in changes) { + if ("disableNitter" in changes) { disableNitter = changes.disableNitter.newValue; } - if ('disableInvidious' in changes) { + if ("disableInvidious" in changes) { disableInvidious = changes.disableInvidious.newValue; } - if ('disableBibliogram' in changes) { + if ("disableBibliogram" in changes) { disableBibliogram = changes.disableBibliogram.newValue; } - if ('disableOsm' in changes) { + if ("disableOsm" in changes) { disableOsm = changes.disableOsm.newValue; } - if ('alwaysProxy' in changes) { + if ("alwaysProxy" in changes) { alwaysProxy = changes.alwaysProxy.newValue; } - if ('onlyEmbeddedVideo' in changes) { + if ("onlyEmbeddedVideo" in changes) { onlyEmbeddedVideo = changes.onlyEmbeddedVideo.newValue; } - if ('videoQuality' in changes) { + if ("videoQuality" in changes) { videoQuality = changes.videoQuality.newValue; } - if ('invidiousDarkMode' in changes) { + if ("invidiousDarkMode" in changes) { invidiousDarkMode = changes.invidiousDarkMode.newValue; } - if ('whitelist' in changes) { - whitelist = changes.whitelist.newValue.map(e => new RegExp(e)); + if ("invidiousVolume" in changes) { + invidiousVolume = changes.invidiousVolume.newValue; + } + if ("invidiousPlayerStyle" in changes) { + invidiousPlayerStyle = changes.invidiousPlayerStyle.newValue; + } + if ("invidiousSubtitles" in changes) { + invidiousSubtitles = changes.invidiousSubtitles.newValue; + } + if ("invidiousAutoplay" in changes) { + invidiousAutoplay = changes.invidiousAutoplay.newValue; + } + if ("exceptions" in changes) { + exceptions = changes.exceptions.newValue.map((e) => { + return new RegExp(e); + }); } }); +function getRandomInstance(instanceList) { + return instanceList[~~(instanceList.length * Math.random())]; +} + function addressToLatLng(address, callback) { const xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = () => { @@ -175,120 +238,181 @@ function addressToLatLng(address, callback) { `${json.boundingbox[2]},${json.boundingbox[1]},${json.boundingbox[3]},${json.boundingbox[0]}` ); } - } - else { + } else { console.info("Error: Status is " + xmlhttp.status); } } }; xmlhttp.open( - 'GET', + "GET", `https://nominatim.openstreetmap.org/search/${address}?format=json&limit=1`, false ); xmlhttp.send(); } -function isWhitelisted(initiator) { - return initiator && whitelist.some(regex => (regex.test(initiator.href))); +function isException(url, initiator) { + return ( + exceptions.some((regex) => regex.test(url.href)) || + (initiator && exceptions.some((regex) => regex.test(initiator.href))) + ); +} + +function isFirefox() { + return typeof InstallTrigger !== "undefined"; } function redirectYouTube(url, initiator, type) { - if (disableInvidious || isWhitelisted(initiator)) { + if (disableInvidious || isException(url, initiator)) { return null; } - if (initiator && (initiator.origin === invidiousInstance || youtubeDomains.includes(initiator.host))) { + if ( + initiator && + (initiator.origin === invidiousInstance || + invidiousInstances.includes(initiator.origin) || + youtubeDomains.includes(initiator.host)) + ) { return null; } - if (url.pathname.match(/iframe_api/)) { - // Redirect requests for YouTube Player API to local files instead - return browser.runtime.getURL('assets/iframe_api.js'); - } else if (url.pathname.match(/www-widgetapi/)) { - // Redirect requests for YouTube Player API to local files instead - return browser.runtime.getURL('assets/www-widgetapi.js'); - } else { - // Proxy video through the server if enabled by user - if (alwaysProxy) { - url.searchParams.append('local', true); - } - if (videoQuality) { - url.searchParams.append('quality', videoQuality); - } - if (onlyEmbeddedVideo && type !== 'sub_frame') { - return null; - } - if (invidiousDarkMode) { - url.searchParams.append('dark_mode', invidiousDarkMode); - } - return `${invidiousInstance}${url.pathname}${url.search}`; + if (url.pathname.match(/iframe_api/) || url.pathname.match(/www-widgetapi/)) { + // Don't redirect YouTube Player API. + return null; + } + if (url.host.split(".")[0] === "studio") { + // Avoid redirecting `studio.youtube.com` + return null; } + if (onlyEmbeddedVideo && type !== "sub_frame") { + return null; + } + // Apply settings + if (alwaysProxy) { + url.searchParams.append("local", true); + } + if (videoQuality) { + url.searchParams.append("quality", videoQuality); + } + if (invidiousDarkMode) { + url.searchParams.append("dark_mode", invidiousDarkMode); + } + if (invidiousVolume) { + url.searchParams.append("volume", invidiousVolume); + } + if (invidiousPlayerStyle) { + url.searchParams.append("player_style", invidiousPlayerStyle); + } + if (invidiousSubtitles) { + url.searchParams.append("subtitles", invidiousSubtitles); + } + url.searchParams.append("autoplay", invidiousAutoplay ? 1 : 0); + + return `${invidiousInstance || getRandomInstance(invidiousInstances)}${ + url.pathname + }${url.search}`; } function redirectTwitter(url, initiator) { - if (disableNitter || isWhitelisted(initiator)) { + if (disableNitter || isException(url, initiator)) { return null; } - if (url.host.split('.')[0] === 'pbs') { - return `${nitterInstance}/pic/${encodeURIComponent(url.href)}`; - } else if (url.host.split('.')[0] === 'video') { - return `${nitterInstance}/gif/${encodeURIComponent(url.href)}`; - } else if (url.pathname.includes('tweets')) { - return `${nitterInstance}${url.pathname.replace('/tweets', '')}${url.search}`; + if (url.pathname.includes("/home")) { + return null; + } + if ( + isFirefox() && + initiator && + (initiator.origin === nitterInstance || + nitterInstances.includes(initiator.origin) || + twitterDomains.includes(initiator.host)) + ) { + browser.storage.sync.set({ + redirectBypassFlag: true, + }); + return null; + } + if (url.host.split(".")[0] === "pbs") { + return `${ + nitterInstance || getRandomInstance(nitterInstances) + }/pic/${encodeURIComponent(url.href)}`; + } else if (url.host.split(".")[0] === "video") { + return `${ + nitterInstance || getRandomInstance(nitterInstances) + }/gif/${encodeURIComponent(url.href)}`; + } else if (url.pathname.includes("tweets")) { + return `${ + nitterInstance || getRandomInstance(nitterInstances) + }${url.pathname.replace("/tweets", "")}${url.search}`; } else { - return `${nitterInstance}${url.pathname}${url.search}`; + return `${nitterInstance || getRandomInstance(nitterInstances)}${ + url.pathname + }${url.search}`; } } function redirectInstagram(url, initiator, type) { - if (disableBibliogram || isWhitelisted(initiator)) { + if (disableBibliogram || isException(url, initiator)) { return null; } // Do not redirect Bibliogram view on Instagram links - if (initiator && (initiator.origin === bibliogramInstance || instagramDomains.includes(initiator.host))) { + if ( + initiator && + (initiator.origin === bibliogramInstance || + bibliogramInstances.includes(initiator.origin) || + instagramDomains.includes(initiator.host)) + ) { return null; } // Do not redirect /accounts, /embeds.js, or anything other than main_frame - if (type !== 'main_frame' || url.pathname.match(bibliogramBypassPaths)) { + if (type !== "main_frame" || url.pathname.match(bibliogramBypassPaths)) { return null; } - if (url.pathname === '/' || instagramReservedPaths.includes(url.pathname.split('/')[1])) { - return `${bibliogramInstance}${url.pathname}${url.search}`; + if ( + url.pathname === "/" || + instagramReservedPaths.includes(url.pathname.split("/")[1]) + ) { + return `${bibliogramInstance || getRandomInstance(bibliogramInstances)}${ + url.pathname + }${url.search}`; } else { // Likely a user profile, redirect to '/u/...' - return `${bibliogramInstance}/u${url.pathname}${url.search}`; + return `${bibliogramInstance || getRandomInstance(bibliogramInstances)}/u${ + url.pathname + }${url.search}`; } } function redirectGoogleMaps(url, initiator) { - if (disableOsm || isWhitelisted(initiator)) { + if (disableOsm || isException(url, initiator)) { return null; } let redirect; - let mapCentre = ''; - let params = ''; + let mapCentre = ""; + let params = ""; // Set map centre if present if (url.pathname.match(mapCentreRegex)) { const [, lat, lon, zoom] = url.pathname.match(mapCentreRegex); mapCentre = `#map=${zoom}/${lat}/${lon}`; - } else if (url.search.includes('center=')) { - const [lat, lon] = url.searchParams.get('center').split(','); - mapCentre = `#map=${url.searchParams.get('zoom') || '17'}/${lat}/${lon}`; + } else if (url.search.includes("center=")) { + const [lat, lon] = url.searchParams.get("center").split(","); + mapCentre = `#map=${url.searchParams.get("zoom") || "17"}/${lat}/${lon}`; // Set default zoom if mapCentre not present } else { - params = '&zoom=17'; + params = "&zoom=17"; } // Set map layer - params = `${params}&layers=${layers[url.searchParams.get('layer')] || layers['none']}`; + params = `${params}&layers=${ + layers[url.searchParams.get("layer")] || layers["none"] + }`; // Handle Google Maps Embed API - if (url.pathname.includes('/embed')) { - let query = ''; - if (url.searchParams.has('q')) { - query = url.searchParams.get('q'); - } else if (url.searchParams.has('query')) { - query = url.searchParams.has('query'); - } else if (url.searchParams.has('pb')) { + if (url.pathname.includes("/embed")) { + let query = ""; + if (url.searchParams.has("q")) { + query = url.searchParams.get("q"); + } else if (url.searchParams.has("query")) { + query = url.searchParams.has("query"); + } else if (url.searchParams.has("pb")) { try { - query = url.searchParams.get('pb').split(/!2s(.*?)!/)[1]; + query = url.searchParams.get("pb").split(/!2s(.*?)!/)[1]; } catch (error) { console.error(error); // Unable to find map marker in URL. @@ -301,92 +425,118 @@ function redirectGoogleMaps(url, initiator) { }); redirect = `${osmInstance}/export/embed.html?bbox=${bbox}&layer=mapnik&marker=${marker}`; // Handle Google Maps Directions - } else if (url.pathname.includes('/dir')) { - const travelMode = travelModes[url.searchParams.get('travelmode')] || travelModes['driving']; + } else if (url.pathname.includes("/dir")) { + const travelMode = + travelModes[url.searchParams.get("travelmode")] || travelModes["driving"]; let origin; - addressToLatLng(url.searchParams.get('origin'), coords => { + addressToLatLng(url.searchParams.get("origin"), (coords) => { origin = coords; }); let destination; - addressToLatLng(url.searchParams.get('destination'), coords => { + addressToLatLng(url.searchParams.get("destination"), (coords) => { destination = coords; }); redirect = `${osmInstance}/directions?engine=${travelMode}&route=${origin}%3B${destination}${mapCentre}${params}`; // Get marker from data attribute - } else if (url.pathname.includes('data=') && url.pathname.match(dataLatLngRegex)) { + } else if ( + url.pathname.includes("data=") && + url.pathname.match(dataLatLngRegex) + ) { const [mlat, mlon] = url.pathname.match(dataLatLngRegex); - redirect = `${osmInstance}/?mlat=${mlat.replace('!3d', '')}&mlon=${mlon.replace('!4d', '')}${mapCentre}${params}`; + redirect = `${osmInstance}/?mlat=${mlat.replace( + "!3d", + "" + )}&mlon=${mlon.replace("!4d", "")}${mapCentre}${params}`; // Get marker from ll param - } else if (url.searchParams.has('ll')) { - const [mlat, mlon] = url.searchParams.get('ll').split(','); + } else if (url.searchParams.has("ll")) { + const [mlat, mlon] = url.searchParams.get("ll").split(","); redirect = `${osmInstance}/?mlat=${mlat}&mlon=${mlon}${mapCentre}${params}`; // Get marker from viewpoint param. - } else if (url.searchParams.has('viewpoint')) { - const [mlat, mlon] = url.searchParams.get('viewpoint').split(','); + } else if (url.searchParams.has("viewpoint")) { + const [mlat, mlon] = url.searchParams.get("viewpoint").split(","); redirect = `${osmInstance}/?mlat=${mlat}&mlon=${mlon}${mapCentre}${params}`; // Use query as search if present. } else { let query; - if (url.searchParams.has('q')) { - query = url.searchParams.get('q'); - } else if (url.searchParams.has('query')) { - query = url.searchParams.get('query'); + if (url.searchParams.has("q")) { + query = url.searchParams.get("q"); + } else if (url.searchParams.has("query")) { + query = url.searchParams.get("query"); } else if (url.pathname.match(placeRegex)) { query = url.pathname.match(placeRegex)[1]; } - redirect = `${osmInstance}/${query ? 'search?query=' + query : ''}${mapCentre || '#'}${params}`; + redirect = `${osmInstance}/${query ? "search?query=" + query : ""}${ + mapCentre || "#" + }${params}`; } return redirect; } browser.webRequest.onBeforeRequest.addListener( - details => { + (details) => { const url = new URL(details.url); let initiator; - if (details.initiator) { - initiator = new URL(details.initiator); - } else if (details.originUrl) { + if (details.originUrl) { initiator = new URL(details.originUrl); + } else if (details.initiator) { + initiator = new URL(details.initiator); } let redirect; if (youtubeDomains.includes(url.host)) { redirect = { - redirectUrl: redirectYouTube(url, initiator, details.type) + redirectUrl: redirectYouTube(url, initiator, details.type), }; } else if (twitterDomains.includes(url.host)) { redirect = { - redirectUrl: redirectTwitter(url, initiator) + redirectUrl: redirectTwitter(url, initiator), }; } else if (instagramDomains.includes(url.host)) { redirect = { - redirectUrl: redirectInstagram(url, initiator, details.type) + redirectUrl: redirectInstagram(url, initiator, details.type), }; } else if (url.href.match(googleMapsRegex)) { redirect = { - redirectUrl: redirectGoogleMaps(url, initiator) + redirectUrl: redirectGoogleMaps(url, initiator), }; } if (redirect && redirect.redirectUrl) { console.info( - 'Redirecting', `"${url.href}"`, '=>', `"${redirect.redirectUrl}"` + "Redirecting", + `"${url.href}"`, + "=>", + `"${redirect.redirectUrl}"` ); - console.info('Details', details); + console.info("Details", details); } return redirect; }, { - urls: ["<all_urls>"] + urls: ["<all_urls>"], }, - ['blocking'] + ["blocking"] ); -browser.runtime.onInstalled.addListener( - details => { - if (details.reason === 'install') { - browser.storage.sync.set({ - bibliogramInstance: bibliogramInstances[~~(bibliogramInstances.length * Math.random())] - }); - } +browser.runtime.onInstalled.addListener((details) => { + if (details.reason === "update") { + browser.storage.sync.get( + ["whitelist", "exceptions", "invidiousInstance"], + (result) => { + if (result.whitelist) { + let whitelist = result.whitelist.map((e) => + e.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + ); + browser.storage.sync.set({ + exceptions: result.exceptions.concat(whitelist), + whitelist: null, + }); + } + if (result.invidiousInstance === "https://invidio.us") { + browser.storage.sync.set({ + invidiousInstance: null, + }); + } + } + ); } -); +}); |