Merge pull request #120 from DeepDoge/master

💩 Upgraded to manifest v3
This commit is contained in:
kodxana 2022-05-02 17:11:36 +02:00 committed by GitHub
commit 63a251c0ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 167 additions and 80 deletions

5
global.d.ts vendored
View file

@ -1,4 +1,9 @@
declare module '*.md' {
var _: string
export default _
}
declare namespace chrome
{
export const action = chrome.browserAction
}

56
manifest.v2.json Normal file
View file

@ -0,0 +1,56 @@
{
"manifest_version": 2,
"name": "Watch on LBRY",
"version": "2.0.0",
"icons": {
"16": "assets/icons/wol/icon16.png",
"48": "assets/icons/wol/icon48.png",
"128": "assets/icons/wol/icon128.png"
},
"permissions": [
"https://www.youtube.com/",
"https://yewtu.be/",
"https://vid.puffyan.us/",
"https://invidio.xamh.de/",
"https://invidious.kavin.rocks/",
"https://api.odysee.com/",
"https://lbry.tv/",
"https://odysee.com/",
"https://madiator.com/",
"https://finder.madiator.com/",
"tabs",
"storage"
],
"web_accessible_resources": [
"pages/popup/index.html",
"pages/YTtoLBRY/index.html",
"pages/import/index.html",
"assets/icons/lbry/lbry-logo.svg",
"assets/icons/lbry/odysee-logo.svg",
"assets/icons/lbry/madiator-logo.svg"
],
"browser_action": {
"default_title": "Watch on LBRY",
"default_popup": "pages/popup/index.html"
},
"content_scripts": [
{
"matches": [
"https://www.youtube.com/*",
"https://yewtu.be/*",
"https://vid.puffyan.us/*",
"https://invidio.xamh.de/*",
"https://invidious.kavin.rocks/*"
],
"js": [
"scripts/ytContent.js"
]
}
],
"background": {
"scripts": [
"service-worker-entry-point.js"
],
"persistent": true
}
}

View file

@ -1,7 +1,17 @@
{
"manifest_version": 3,
"name": "Watch on LBRY",
"version": "2.0.0",
"icons": {
"16": "assets/icons/wol/icon16.png",
"48": "assets/icons/wol/icon48.png",
"128": "assets/icons/wol/icon128.png"
},
"permissions": [
"tabs",
"storage"
],
"host_permissions": [
"https://www.youtube.com/",
"https://yewtu.be/",
"https://vid.puffyan.us/",
@ -11,10 +21,24 @@
"https://lbry.tv/",
"https://odysee.com/",
"https://madiator.com/",
"https://finder.madiator.com/",
"tabs",
"storage"
"https://finder.madiator.com/"
],
"web_accessible_resources": [{
"resources": [
"pages/popup/index.html",
"pages/YTtoLBRY/index.html",
"pages/import/index.html",
"assets/icons/lbry/lbry-logo.svg",
"assets/icons/lbry/odysee-logo.svg",
"assets/icons/lbry/madiator-logo.svg"
],
"matches": ["<all_urls>"],
"extension_ids": []
}],
"action": {
"default_title": "Watch on LBRY",
"default_popup": "pages/popup/index.html"
},
"content_scripts": [
{
"matches": [
@ -30,28 +54,6 @@
}
],
"background": {
"scripts": [
"settings/background.js",
"scripts/background.js"
],
"persistent": true
},
"browser_action": {
"default_title": "Watch on LBRY",
"default_popup": "pages/popup/index.html"
},
"web_accessible_resources": [
"pages/popup/index.html",
"pages/YTtoLBRY/index.html",
"pages/import/index.html",
"assets/icons/lbry/lbry-logo.svg",
"assets/icons/lbry/odysee-logo.svg",
"assets/icons/lbry/madiator-logo.svg"
],
"icons": {
"16": "assets/icons/wol/icon16.png",
"48": "assets/icons/wol/icon48.png",
"128": "assets/icons/wol/icon128.png"
},
"manifest_version": 2
"service_worker": "service-worker-entry-point.js"
}
}

View file

@ -5,11 +5,29 @@
"scripts": {
"build:assets": "cpx \"src/**/*.{json,png,svg}\" dist/",
"watch:assets": "cpx --watch -v \"src/**/*.{json,png,svg}\" dist/",
"build:parcel": "cross-env NODE_ENV=production parcel build --no-source-maps --no-minify \"src/**/*.{js,jsx,ts,tsx}\" \"src/**/*.html\" \"src/**/*.md\"",
"watch:parcel": "parcel watch --no-hmr --no-source-maps \"src/**/*.{js,jsx,ts,tsx}\" \"src/**/*.html\" \"src/**/*.md\"",
"build:webext": "web-ext build --source-dir ./dist --overwrite-dest",
"build": "npm-run-all -l -p build:parcel build:assets",
"watch": "npm-run-all -l -p watch:parcel watch:assets",
"clear:dist": "rm -r ./dist ; mkdir ./dist",
"build:base": "npm-run-all -l -p build:parcel build:assets",
"pick:manifest:v2": "cp -b ./manifest.v2.json ./dist/manifest.json",
"pick:manifest:v3": "cp -b ./manifest.v3.json ./dist/manifest.json",
"build:v2": "npm run clear:dist ; npm run build:base && npm run pick:manifest:v2",
"build:v3": "npm run clear:dist ; npm run build:base && npm run pick:manifest:v3",
"build": "npm run build:v3",
"watch:v2": "npm run clear:dist ; npm run pick:manifest:v2 && npm-run-all -l -p watch:parcel watch:assets",
"watch:v3": "npm run clear:dist ; npm run pick:manifest:v3 && npm-run-all -l -p watch:parcel watch:assets",
"watch": "npm run watch:v3",
"start:chrome": "web-ext run -t chromium --source-dir ./dist",
"start:firefox": "web-ext run --source-dir ./dist",
"test": "jest"

View file

@ -1,10 +1,10 @@
import path from 'path'
import { DialogManager } from '../../components/dialogs'
import type { DialogManager } from '../../components/dialogs'
import { getExtensionSettingsAsync, setExtensionSetting, ytUrlResolversSettings } from "../../settings"
import { getFileContent } from '../file'
async function generateKeys() {
const keys = await window.crypto.subtle.generateKey(
const keys = await crypto.subtle.generateKey(
{
name: "RSASSA-PKCS1-v1_5",
// Consider using a 4096-bit key for systems that require long-term security
@ -23,7 +23,7 @@ async function generateKeys() {
}
async function exportPrivateKey(key: CryptoKey) {
const exported = await window.crypto.subtle.exportKey(
const exported = await crypto.subtle.exportKey(
"pkcs8",
key
)
@ -34,7 +34,7 @@ const publicKeyPrefix = `MEwwDQYJKoZIhvcNAQEBBQADOwAwOAIxA`
const publicKeySuffix = `IDAQAB` //`wIDAQAB` `WIDAQAB`
const publicKeyLength = 65
async function exportPublicKey(key: CryptoKey) {
const exported = await window.crypto.subtle.exportKey(
const exported = await crypto.subtle.exportKey(
"spki",
key
)
@ -43,7 +43,7 @@ async function exportPublicKey(key: CryptoKey) {
}
function importPrivateKey(base64: string) {
return window.crypto.subtle.importKey(
return crypto.subtle.importKey(
"pkcs8",
Buffer.from(base64, 'base64'),
{
@ -56,7 +56,7 @@ function importPrivateKey(base64: string) {
}
export async function sign(data: string, privateKey: string) {
return Buffer.from(await window.crypto.subtle.sign(
return Buffer.from(await crypto.subtle.sign(
{ name: "RSASSA-PKCS1-v1_5" },
await importPrivateKey(privateKey),
await crypto.subtle.digest({ name: 'SHA-1' }, Buffer.from(data))

View file

@ -1,4 +1,3 @@
interface YtExportedJsonSubscription {
id: string
etag: string

View file

@ -2,6 +2,8 @@
let db: IDBDatabase | null = null
if (typeof chrome.extension === 'undefined') throw new Error("YT urlCache can only be accessed from extension windows and service-workers.")
if (typeof self.indexedDB !== 'undefined') {
const openRequest = indexedDB.open("yt-url-resolver-cache")
openRequest.addEventListener('upgradeneeded', () => openRequest.result.createObjectStore("store").createIndex("expireAt", "expireAt"))
@ -81,5 +83,5 @@ async function get(id: string): Promise<string | null | undefined> {
return response.value
}
export const LbryPathnameCache = { put, get, clearAll }
export const lbryUrlCache = { put, get, clearAll }

View file

@ -2,7 +2,7 @@ import { chunk } from "lodash"
import path from "path"
import { getExtensionSettingsAsync, ytUrlResolversSettings } from "../../settings"
import { sign } from "../crypto"
import { LbryPathnameCache } from "./urlCache"
import { lbryUrlCache } from "./urlCache"
const QUERY_CHUNK_SIZE = 100
@ -27,7 +27,7 @@ export async function resolveById(params: Paramaters, progressCallback?: (progre
// Check for cache first, add them to the results if there are any cache
// And remove them from the params, so we dont request for them
params = (await Promise.all(params.map(async (item) => {
const cachedLbryUrl = await LbryPathnameCache.get(item.id)
const cachedLbryUrl = await lbryUrlCache.get(item.id)
// Cache can be null, if there is no lbry url yet
if (cachedLbryUrl !== undefined) {
@ -58,7 +58,7 @@ export async function resolveById(params: Paramaters, progressCallback?: (progre
for (const item of params) {
const lbryUrl = (item.type === 'channel' ? response.data.channels : response.data.videos)?.[item.id]?.replaceAll('#', ':') ?? null
// we cache it no matter if its null or not
await LbryPathnameCache.put(lbryUrl, item.id)
await lbryUrlCache.put(lbryUrl, item.id)
if (lbryUrl) results[item.id] = { id: lbryUrl, type: item.type }
}

View file

@ -3,6 +3,21 @@ import { useState } from 'preact/hooks'
import { createDialogManager, Dialogs } from '../../components/dialogs'
import { importProfileKeysFromFile, inputKeyFile } from '../../modules/crypto'
export async function openImportPopup() {
const importPopupWindow = open(
'/pages/import/index.html',
'Import Profile',
[
`height=${Math.max(document.body.clientHeight, screen.height * .5)}`,
`width=${document.body.clientWidth}`,
`toolbar=0,menubar=0,location=0`,
`top=${screenY}`,
`left=${screenX}`
].join(','))
close()
importPopupWindow?.focus()
}
function ImportPage() {
const [loading, updateLoading] = useState(() => false)

View file

@ -2,8 +2,9 @@ import { h, render } from 'preact'
import { useState } from 'preact/hooks'
import { createDialogManager, Dialogs } from '../../components/dialogs'
import { exportProfileKeysAsFile, friendlyPublicKey, generateProfileAndSetNickname, getProfile, purgeProfile, resetProfileSettings } from '../../modules/crypto'
import { LbryPathnameCache } from '../../modules/yt/urlCache'
import { lbryUrlCache } from '../../modules/yt/urlCache'
import { getTargetPlatfromSettingsEntiries, getYtUrlResolversSettingsEntiries, setExtensionSetting, useExtensionSettings } from '../../settings'
import { openImportPopup } from '../import/main'
/** Gets all the options for redirect destinations as selection options */
@ -31,21 +32,6 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
}
}
async function importButtonClick() {
const importPopupWindow = open(
'/pages/import/index.html',
'Import Profile',
[
`height=${Math.max(document.body.clientHeight, screen.height * .5)}`,
`width=${document.body.clientWidth}`,
`toolbar=0,menubar=0,location=0`,
`top=${screenY}`,
`left=${screenX}`
].join(','))
close()
importPopupWindow?.focus()
}
return <div id='popup'>
<Dialogs manager={dialogManager} />
{
@ -100,7 +86,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
<a onClick={() => exportProfileKeysAsFile()} className={`button active`}>
Export
</a>
<a onClick={() => importButtonClick()}
<a onClick={() => openImportPopup()}
className={`button`}
>
Import
@ -139,7 +125,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
<label>You don't have a profile.</label>
<p>You can either import keypair for an existing profile or generate a new profile keypair.</p>
<div className='options'>
<a onClick={() => importButtonClick()} className={`button`}>
<a onClick={() => openImportPopup()} className={`button`}>
Import
</a>
<a onClick={() => loads(generateProfileAndSetNickname(dialogManager)).then(() => renderPopup())} className={`button active`}>
@ -180,7 +166,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
</a>
)}
</div>
<a onClick={() => loads(LbryPathnameCache.clearAll().then(() => dialogManager.alert("Cleared Cache!")))} className={`button active`}>
<a onClick={() => loads(lbryUrlCache.clearAll().then(() => dialogManager.alert("Cleared Cache!")))} className={`button active`}>
Clear Resolver Cache
</a>
</section>

View file

@ -1,6 +1,6 @@
import { h, render } from 'preact'
import { parseYouTubeURLTimeString } from '../modules/yt'
import { resolveById } from '../modules/yt/urlResolve'
import type { resolveById } from '../modules/yt/urlResolve'
import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, TargetPlatform, targetPlatformSettings } from '../settings'
const sleep = (t: number) => new Promise(resolve => setTimeout(resolve, t))

View file

@ -0,0 +1,2 @@
import './settings/background'
import './scripts/background'

View file

@ -1,31 +1,33 @@
import { DEFAULT_SETTINGS, ExtensionSettings, getExtensionSettingsAsync, setExtensionSetting, targetPlatformSettings, ytUrlResolversSettings } from '.'
import { DEFAULT_SETTINGS, ExtensionSettings, getExtensionSettingsAsync, setExtensionSetting, targetPlatformSettings, ytUrlResolversSettings } from '../settings'
// This is for manifest v2 and v3
const chromeAction = chrome.action ?? chrome.browserAction
/** Reset settings to default value and update the browser badge text */
async function initSettings() {
let settings = await getExtensionSettingsAsync()
let settings = await getExtensionSettingsAsync()
// get all the values that aren't set and use them as a change set
const invalidEntries = (Object.entries(DEFAULT_SETTINGS) as Array<[keyof ExtensionSettings, ExtensionSettings[keyof ExtensionSettings]]>)
.filter(([k]) => settings[k] === undefined || settings[k] === null)
// get all the values that aren't set and use them as a change set
const invalidEntries = (Object.entries(DEFAULT_SETTINGS) as Array<[keyof ExtensionSettings, ExtensionSettings[keyof ExtensionSettings]]>)
.filter(([k]) => settings[k] === undefined || settings[k] === null)
// fix our local var and set it in storage for later
if (invalidEntries.length > 0) {
const changeSet = Object.fromEntries(invalidEntries)
chrome.storage.local.set(changeSet)
settings = await getExtensionSettingsAsync()
}
// fix our local var and set it in storage for later
if (invalidEntries.length > 0) {
const changeSet = Object.fromEntries(invalidEntries)
chrome.storage.local.set(changeSet)
settings = await getExtensionSettingsAsync()
}
if (!Object.keys(targetPlatformSettings).includes(settings.targetPlatform)) setExtensionSetting('targetPlatform', DEFAULT_SETTINGS.targetPlatform)
if (!Object.keys(ytUrlResolversSettings).includes(settings.urlResolver)) setExtensionSetting('urlResolver', DEFAULT_SETTINGS.urlResolver)
if (!Object.keys(targetPlatformSettings).includes(settings.targetPlatform)) setExtensionSetting('targetPlatform', DEFAULT_SETTINGS.targetPlatform)
if (!Object.keys(ytUrlResolversSettings).includes(settings.urlResolver)) setExtensionSetting('urlResolver', DEFAULT_SETTINGS.urlResolver)
chrome.browserAction.setBadgeText({ text: settings.redirect ? 'ON' : 'OFF' })
chromeAction.setBadgeText({ text: settings.redirect ? 'ON' : 'OFF' })
}
chrome.storage.onChanged.addListener((changes, areaName) => {
if (areaName !== 'local' || !changes.redirect) return
chrome.browserAction.setBadgeText({ text: changes.redirect.newValue ? 'ON' : 'OFF' })
if (areaName !== 'local' || !changes.redirect) return
chromeAction.setBadgeText({ text: changes.redirect.newValue ? 'ON' : 'OFF' })
})
chrome.runtime.onStartup.addListener(initSettings)
chrome.runtime.onInstalled.addListener(initSettings)
chrome.runtime.onInstalled.addListener(initSettings)

View file

@ -1,4 +1,4 @@
import { JSX } from "preact"
import type { JSX } from "preact"
import { useEffect, useReducer } from "preact/hooks"
export interface ExtensionSettings {

View file

@ -41,6 +41,7 @@
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
"importsNotUsedAsValues": "error", /* Import types always with `import type` */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
@ -53,7 +54,6 @@
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */