💩 upgraded to manifest v3

This commit is contained in:
Shiba 2022-05-02 12:12:11 +00:00
parent 7076a657fc
commit cebdf480fe
13 changed files with 88 additions and 79 deletions

5
global.d.ts vendored
View file

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

View file

@ -1,7 +1,17 @@
{ {
"manifest_version": 3,
"name": "Watch on LBRY", "name": "Watch on LBRY",
"version": "2.0.0", "version": "2.0.0",
"icons": {
"16": "assets/icons/wol/icon16.png",
"48": "assets/icons/wol/icon48.png",
"128": "assets/icons/wol/icon128.png"
},
"permissions": [ "permissions": [
"tabs",
"storage"
],
"host_permissions": [
"https://www.youtube.com/", "https://www.youtube.com/",
"https://yewtu.be/", "https://yewtu.be/",
"https://vid.puffyan.us/", "https://vid.puffyan.us/",
@ -11,10 +21,24 @@
"https://lbry.tv/", "https://lbry.tv/",
"https://odysee.com/", "https://odysee.com/",
"https://madiator.com/", "https://madiator.com/",
"https://finder.madiator.com/", "https://finder.madiator.com/"
"tabs",
"storage"
], ],
"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": [ "content_scripts": [
{ {
"matches": [ "matches": [
@ -30,28 +54,6 @@
} }
], ],
"background": { "background": {
"scripts": [ "service_worker": "service-worker-entry-point.js"
"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
} }

View file

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

View file

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

View file

@ -2,6 +2,8 @@
let db: IDBDatabase | null = null 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') { if (typeof self.indexedDB !== 'undefined') {
const openRequest = indexedDB.open("yt-url-resolver-cache") const openRequest = indexedDB.open("yt-url-resolver-cache")
openRequest.addEventListener('upgradeneeded', () => openRequest.result.createObjectStore("store").createIndex("expireAt", "expireAt")) 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 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 path from "path"
import { getExtensionSettingsAsync, ytUrlResolversSettings } from "../../settings" import { getExtensionSettingsAsync, ytUrlResolversSettings } from "../../settings"
import { sign } from "../crypto" import { sign } from "../crypto"
import { LbryPathnameCache } from "./urlCache" import { lbryUrlCache } from "./urlCache"
const QUERY_CHUNK_SIZE = 100 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 // 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 // And remove them from the params, so we dont request for them
params = (await Promise.all(params.map(async (item) => { 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 // Cache can be null, if there is no lbry url yet
if (cachedLbryUrl !== undefined) { if (cachedLbryUrl !== undefined) {
@ -58,7 +58,7 @@ export async function resolveById(params: Paramaters, progressCallback?: (progre
for (const item of params) { for (const item of params) {
const lbryUrl = (item.type === 'channel' ? response.data.channels : response.data.videos)?.[item.id]?.replaceAll('#', ':') ?? null 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 // 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 } 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 { createDialogManager, Dialogs } from '../../components/dialogs'
import { importProfileKeysFromFile, inputKeyFile } from '../../modules/crypto' 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() { function ImportPage() {
const [loading, updateLoading] = useState(() => false) const [loading, updateLoading] = useState(() => false)

View file

@ -2,8 +2,9 @@ import { h, render } from 'preact'
import { useState } from 'preact/hooks' import { useState } from 'preact/hooks'
import { createDialogManager, Dialogs } from '../../components/dialogs' import { createDialogManager, Dialogs } from '../../components/dialogs'
import { exportProfileKeysAsFile, friendlyPublicKey, generateProfileAndSetNickname, getProfile, purgeProfile, resetProfileSettings } from '../../modules/crypto' 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 { getTargetPlatfromSettingsEntiries, getYtUrlResolversSettingsEntiries, setExtensionSetting, useExtensionSettings } from '../../settings'
import { openImportPopup } from '../import/main'
/** Gets all the options for redirect destinations as selection options */ /** 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'> return <div id='popup'>
<Dialogs manager={dialogManager} /> <Dialogs manager={dialogManager} />
{ {
@ -100,7 +86,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
<a onClick={() => exportProfileKeysAsFile()} className={`button active`}> <a onClick={() => exportProfileKeysAsFile()} className={`button active`}>
Export Export
</a> </a>
<a onClick={() => importButtonClick()} <a onClick={() => openImportPopup()}
className={`button`} className={`button`}
> >
Import Import
@ -139,7 +125,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
<label>You don't have a profile.</label> <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> <p>You can either import keypair for an existing profile or generate a new profile keypair.</p>
<div className='options'> <div className='options'>
<a onClick={() => importButtonClick()} className={`button`}> <a onClick={() => openImportPopup()} className={`button`}>
Import Import
</a> </a>
<a onClick={() => loads(generateProfileAndSetNickname(dialogManager)).then(() => renderPopup())} className={`button active`}> <a onClick={() => loads(generateProfileAndSetNickname(dialogManager)).then(() => renderPopup())} className={`button active`}>
@ -180,7 +166,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
</a> </a>
)} )}
</div> </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 Clear Resolver Cache
</a> </a>
</section> </section>

View file

@ -1,6 +1,6 @@
import { h, render } from 'preact' import { h, render } from 'preact'
import { parseYouTubeURLTimeString } from '../modules/yt' 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' import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, TargetPlatform, targetPlatformSettings } from '../settings'
const sleep = (t: number) => new Promise(resolve => setTimeout(resolve, t)) 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,5 +1,4 @@
import { DEFAULT_SETTINGS, ExtensionSettings, getExtensionSettingsAsync, setExtensionSetting, targetPlatformSettings, ytUrlResolversSettings } from '.' import { DEFAULT_SETTINGS, ExtensionSettings, getExtensionSettingsAsync, setExtensionSetting, targetPlatformSettings, ytUrlResolversSettings } from '../settings'
/** Reset settings to default value and update the browser badge text */ /** Reset settings to default value and update the browser badge text */
async function initSettings() { async function initSettings() {
let settings = await getExtensionSettingsAsync() let settings = await getExtensionSettingsAsync()
@ -18,14 +17,13 @@ async function initSettings() {
if (!Object.keys(targetPlatformSettings).includes(settings.targetPlatform)) setExtensionSetting('targetPlatform', DEFAULT_SETTINGS.targetPlatform) 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(ytUrlResolversSettings).includes(settings.urlResolver)) setExtensionSetting('urlResolver', DEFAULT_SETTINGS.urlResolver)
chrome.browserAction.setBadgeText({ text: settings.redirect ? 'ON' : 'OFF' }) chrome.action.setBadgeText({ text: settings.redirect ? 'ON' : 'OFF' })
} }
chrome.storage.onChanged.addListener((changes, areaName) => { chrome.storage.onChanged.addListener((changes, areaName) => {
if (areaName !== 'local' || !changes.redirect) return if (areaName !== 'local' || !changes.redirect) return
chrome.browserAction.setBadgeText({ text: changes.redirect.newValue ? 'ON' : 'OFF' }) chrome.action.setBadgeText({ text: changes.redirect.newValue ? 'ON' : 'OFF' })
}) })
chrome.runtime.onStartup.addListener(initSettings) 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" import { useEffect, useReducer } from "preact/hooks"
export interface ExtensionSettings { export interface ExtensionSettings {

View file

@ -41,6 +41,7 @@
// "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
"importsNotUsedAsValues": "error", /* Import types always with `import type` */
/* Module Resolution Options */ /* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ // "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'. */ "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. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */ /* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ // "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. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */