mirror of
https://github.com/LBRYFoundation/Watch-on-LBRY.git
synced 2025-08-23 17:47:26 +00:00
commit
6b8f193bfd
7 changed files with 85 additions and 74 deletions
|
@ -1,14 +1,15 @@
|
||||||
import { getExtensionSettingsAsync } from "./settings"
|
import { getExtensionSettingsAsync, ytUrlResolversSettings } from "./settings"
|
||||||
import { setSetting } from "./useSettings"
|
import { setSetting } from "./useSettings"
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
async function generateKeys() {
|
async function generateKeys() {
|
||||||
const keys = await window.crypto.subtle.generateKey(
|
const keys = await window.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
|
||||||
modulusLength: 2048,
|
modulusLength: 384,
|
||||||
publicExponent: new Uint8Array([1, 0, 1]),
|
publicExponent: new Uint8Array([1, 0, 1]),
|
||||||
hash: "SHA-256",
|
hash: "SHA-1",
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
["sign", "verify"]
|
["sign", "verify"]
|
||||||
|
@ -28,22 +29,26 @@ async function exportPrivateKey(key: CryptoKey) {
|
||||||
return Buffer.from(exported).toString('base64')
|
return Buffer.from(exported).toString('base64')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const publicKeyPrefix = `MEwwDQYJKoZIhvcNAQEBBQADOwAwOAIxA`
|
||||||
|
const publicKeySuffix = `IDAQAB` //`wIDAQAB` `WIDAQAB`
|
||||||
|
const publicKeyLength = 65
|
||||||
async function exportPublicKey(key: CryptoKey) {
|
async function exportPublicKey(key: CryptoKey) {
|
||||||
const exported = await window.crypto.subtle.exportKey(
|
const exported = await window.crypto.subtle.exportKey(
|
||||||
"spki",
|
"spki",
|
||||||
key
|
key
|
||||||
)
|
)
|
||||||
return Buffer.from(exported).toString('base64')
|
const publicKey = Buffer.from(exported).toString('base64')
|
||||||
|
console.log(publicKey)
|
||||||
|
return publicKey.substring(publicKeyPrefix.length, publicKeyPrefix.length + publicKeyLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
function importPrivateKey(base64: string) {
|
function importPrivateKey(base64: string) {
|
||||||
|
|
||||||
return window.crypto.subtle.importKey(
|
return window.crypto.subtle.importKey(
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
Buffer.from(base64, 'base64'),
|
Buffer.from(base64, 'base64'),
|
||||||
{
|
{
|
||||||
name: "RSASSA-PKCS1-v1_5",
|
name: "RSASSA-PKCS1-v1_5",
|
||||||
hash: "SHA-256",
|
hash: "SHA-1",
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
["sign"]
|
["sign"]
|
||||||
|
@ -54,7 +59,7 @@ export async function sign(data: string, privateKey: string) {
|
||||||
return Buffer.from(await window.crypto.subtle.sign(
|
return Buffer.from(await window.crypto.subtle.sign(
|
||||||
{ name: "RSASSA-PKCS1-v1_5" },
|
{ name: "RSASSA-PKCS1-v1_5" },
|
||||||
await importPrivateKey(privateKey),
|
await importPrivateKey(privateKey),
|
||||||
Buffer.from(data)
|
await crypto.subtle.digest({ name: 'SHA-1' }, Buffer.from(data))
|
||||||
)).toString('base64')
|
)).toString('base64')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +68,33 @@ export function resetProfileSettings() {
|
||||||
setSetting('privateKey', null)
|
setSetting('privateKey', null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function apiRequest<T extends object>(method: 'GET' | 'POST', pathname: string, data: T) {
|
||||||
|
const settings = await getExtensionSettingsAsync()
|
||||||
|
/* const urlResolverSettings = ytUrlResolversSettings[settings.urlResolver]
|
||||||
|
if (!urlResolverSettings.signRequest) throw new Error() */
|
||||||
|
|
||||||
|
console.log(ytUrlResolversSettings)
|
||||||
|
const url = new URL(ytUrlResolversSettings.madiatorFinder.href/* urlResolverSettings.href */)
|
||||||
|
console.log(url)
|
||||||
|
url.pathname = path.join(url.pathname, pathname)
|
||||||
|
url.searchParams.set('data', JSON.stringify(data))
|
||||||
|
|
||||||
|
if (true/* requiresSignature */) {
|
||||||
|
if (!settings.privateKey || !settings.publicKey)
|
||||||
|
throw new Error('There is no profile.')
|
||||||
|
|
||||||
|
url.searchParams.set('keys', JSON.stringify({
|
||||||
|
signature: await sign(url.searchParams.toString(), settings.privateKey!),
|
||||||
|
publicKey: settings.publicKey
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const respond = await fetch(url.href, { method })
|
||||||
|
|
||||||
|
if (respond.ok) return respond.json()
|
||||||
|
throw new Error((await respond.json()).message)
|
||||||
|
}
|
||||||
|
|
||||||
export async function generateProfileAndSetNickname(overwrite = false) {
|
export async function generateProfileAndSetNickname(overwrite = false) {
|
||||||
let { publicKey, privateKey } = await getExtensionSettingsAsync()
|
let { publicKey, privateKey } = await getExtensionSettingsAsync()
|
||||||
|
|
||||||
|
@ -74,80 +106,43 @@ export async function generateProfileAndSetNickname(overwrite = false) {
|
||||||
alert("Invalid nickname")
|
alert("Invalid nickname")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
if (overwrite || !privateKey || !publicKey) {
|
if (overwrite || !privateKey || !publicKey) {
|
||||||
resetProfileSettings()
|
resetProfileSettings()
|
||||||
await generateKeys().then((keys) => {
|
await generateKeys().then((keys) => {
|
||||||
publicKey = keys.publicKey
|
publicKey = keys.publicKey
|
||||||
privateKey = keys.privateKey
|
privateKey = keys.privateKey
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
const url = new URL('https://finder.madiator.com/api/v1/profile')
|
|
||||||
url.searchParams.set('data', JSON.stringify({ nickname }))
|
|
||||||
url.searchParams.set('keys', JSON.stringify({
|
|
||||||
signature: await sign(url.searchParams.toString(), privateKey!),
|
|
||||||
publicKey
|
|
||||||
}))
|
|
||||||
const respond = await fetch(url.href, { method: "POST" })
|
|
||||||
if (respond.ok) {
|
|
||||||
setSetting('publicKey', publicKey)
|
setSetting('publicKey', publicKey)
|
||||||
setSetting('privateKey', privateKey)
|
setSetting('privateKey', privateKey)
|
||||||
alert(`Your nickname has been set to ${nickname}`)
|
|
||||||
}
|
}
|
||||||
else alert((await respond.json()).message)
|
await apiRequest('POST', '/profile', { nickname })
|
||||||
|
alert(`Your nickname has been set to ${nickname}`)
|
||||||
|
} catch (error: any) {
|
||||||
|
resetProfileSettings()
|
||||||
|
alert(error.message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function purgeProfile() {
|
export async function purgeProfile() {
|
||||||
try {
|
try {
|
||||||
if (!confirm("This will purge all of your online and offline profile data.\nStill wanna continue?")) return
|
if (!confirm("This will purge all of your online and offline profile data.\nStill wanna continue?")) return
|
||||||
const settings = await getExtensionSettingsAsync()
|
await apiRequest('POST', '/profile/purge', {})
|
||||||
|
|
||||||
if (!settings.privateKey || !settings.publicKey)
|
|
||||||
throw new Error('There is no profile to be purged.')
|
|
||||||
|
|
||||||
const url = new URL('https://finder.madiator.com/api/v1/profile/purge')
|
|
||||||
url.searchParams.set('keys', JSON.stringify({
|
|
||||||
signature: await sign(url.searchParams.toString(), settings.privateKey!),
|
|
||||||
publicKey: settings.publicKey
|
|
||||||
}))
|
|
||||||
const respond = await fetch(url.href, { method: "POST" })
|
|
||||||
if (respond.ok) {
|
|
||||||
resetProfileSettings()
|
resetProfileSettings()
|
||||||
alert(`Your profile has been purged`)
|
alert(`Your profile has been purged`)
|
||||||
}
|
|
||||||
else throw new Error((await respond.json()).message)
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
alert(error.message)
|
alert(error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProfile() {
|
export async function getProfile() {
|
||||||
try {
|
let { publicKey, privateKey } = await getExtensionSettingsAsync()
|
||||||
const settings = await getExtensionSettingsAsync()
|
return (await apiRequest('GET', '/profile', { publicKey })) as { nickname: string, score: number, publickKey: string }
|
||||||
|
|
||||||
if (!settings.privateKey || !settings.publicKey)
|
|
||||||
throw new Error('There is no profile.')
|
|
||||||
|
|
||||||
const url = new URL('https://finder.madiator.com/api/v1/profile')
|
|
||||||
url.searchParams.set('data', JSON.stringify({ publicKey: settings.publicKey }))
|
|
||||||
url.searchParams.set('keys', JSON.stringify({
|
|
||||||
signature: await sign(url.searchParams.toString(), settings.privateKey!),
|
|
||||||
publicKey: settings.publicKey
|
|
||||||
}))
|
|
||||||
const respond = await fetch(url.href, { method: "GET" })
|
|
||||||
if (respond.ok) {
|
|
||||||
const profile = await respond.json() as { nickname: string, score: number, publickKey: string }
|
|
||||||
return profile
|
|
||||||
}
|
|
||||||
else throw new Error((await respond.json()).message)
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function friendlyPublicKey(publicKey: string | null) {
|
export function friendlyPublicKey(publicKey: string | null) {
|
||||||
// This is copy paste of Madiator Finder's friendly public key
|
// This is copy paste of Madiator Finder's friendly public key
|
||||||
return publicKey?.substring(publicKey.length - 64, publicKey.length - 32)
|
return `${publicKey?.substring(0, 32)}...`
|
||||||
}
|
}
|
||||||
|
|
||||||
function download(data: string, filename: string, type: string) {
|
function download(data: string, filename: string, type: string) {
|
||||||
|
@ -177,7 +172,7 @@ async function readFile() {
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
|
|
||||||
reader.addEventListener('load', () => resolve(reader.result?.toString() ?? null))
|
reader.addEventListener('load', () => resolve(reader.result?.toString() ?? null))
|
||||||
reader.readAsBinaryString(myFile)
|
reader.readAsText(myFile)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,12 +118,17 @@ export const getYtUrlResolversSettingsEntiries = () => Object.entries(ytUrlResol
|
||||||
export const ytUrlResolversSettings = {
|
export const ytUrlResolversSettings = {
|
||||||
odyseeApi: ytUrlResolver({
|
odyseeApi: ytUrlResolver({
|
||||||
name: "Odysee",
|
name: "Odysee",
|
||||||
href: "https://api.odysee.com/yt/resolve",
|
href: "https://api.odysee.com/yt",
|
||||||
signRequest: false
|
signRequest: false
|
||||||
}),
|
}),
|
||||||
madiatorFinder: ytUrlResolver({
|
madiatorFinder: ytUrlResolver({
|
||||||
name: "Madiator Finder",
|
name: "Madiator Finder",
|
||||||
href: "https://finder.madiator.com/api/v1/resolve",
|
href: "https://finder.madiator.com/api/v1",
|
||||||
signRequest: true
|
signRequest: true
|
||||||
})
|
}),
|
||||||
|
/* madiatorFinderLocal: ytUrlResolver({
|
||||||
|
name: "Madiator Finder Local",
|
||||||
|
href: "http://127.0.0.1:3001/api/v1",
|
||||||
|
signRequest: true
|
||||||
|
}) */
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ import { chunk } from "lodash"
|
||||||
import { sign } from "../crypto"
|
import { sign } from "../crypto"
|
||||||
import { getExtensionSettingsAsync, ytUrlResolversSettings } from "../settings"
|
import { getExtensionSettingsAsync, ytUrlResolversSettings } from "../settings"
|
||||||
import { LbryPathnameCache } from "./urlCache"
|
import { LbryPathnameCache } from "./urlCache"
|
||||||
|
import path from "path"
|
||||||
|
|
||||||
const QUERY_CHUNK_SIZE = 100
|
const QUERY_CHUNK_SIZE = 100
|
||||||
|
|
||||||
|
@ -41,6 +42,7 @@ export async function resolveById(params: Paramaters, progressCallback?: (progre
|
||||||
if (params.length === 0) return results
|
if (params.length === 0) return results
|
||||||
|
|
||||||
const url = new URL(`${urlResolverSetting.href}`)
|
const url = new URL(`${urlResolverSetting.href}`)
|
||||||
|
url.pathname = path.join(url.pathname, '/resolve')
|
||||||
url.searchParams.set('video_ids', params.filter((item) => item.type === 'video').map((item) => item.id).join(','))
|
url.searchParams.set('video_ids', params.filter((item) => item.type === 'video').map((item) => item.id).join(','))
|
||||||
url.searchParams.set('channel_ids', params.filter((item) => item.type === 'channel').map((item) => item.id).join(','))
|
url.searchParams.set('channel_ids', params.filter((item) => item.type === 'channel').map((item) => item.id).join(','))
|
||||||
if (urlResolverSetting.signRequest && publicKey && privateKey)
|
if (urlResolverSetting.signRequest && publicKey && privateKey)
|
||||||
|
|
|
@ -49,7 +49,12 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
</header>
|
</header>
|
||||||
: <header><a className='button filled' onClick={() => updateRoute('profile')} href="#profile">Your Profile</a>
|
: <header>
|
||||||
|
{
|
||||||
|
popupRoute === 'profile'
|
||||||
|
? <a onClick={() => updateRoute('')} className="button filled">⇐ Back</a>
|
||||||
|
: <a className='button filled' onClick={() => updateRoute('profile')} href="#profile">Your Profile</a>
|
||||||
|
}
|
||||||
</header>
|
</header>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,4 +25,4 @@ chrome.runtime.onMessage.addListener(({ json }, sender, sendResponse) => {
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => changeInfo.url && chrome.tabs.sendMessage(tabId, {}))
|
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => changeInfo.status === 'complete' && chrome.tabs.sendMessage(tabId, { message: 'url-changed' }))
|
|
@ -1,8 +1,9 @@
|
||||||
import { DEFAULT_SETTINGS, ExtensionSettings, getExtensionSettingsAsync } from '../common/settings'
|
import { DEFAULT_SETTINGS, ExtensionSettings, getExtensionSettingsAsync, sourcePlatfromSettings, targetPlatformSettings, ytUrlResolversSettings } from '../common/settings'
|
||||||
|
import { setSetting } from '../common/useSettings'
|
||||||
|
|
||||||
/** 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() {
|
||||||
const settings = await getExtensionSettingsAsync()
|
let settings = await getExtensionSettingsAsync()
|
||||||
|
|
||||||
// get all the values that aren't set and use them as a change set
|
// 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]]>)
|
const invalidEntries = (Object.entries(DEFAULT_SETTINGS) as Array<[keyof ExtensionSettings, ExtensionSettings[keyof ExtensionSettings]]>)
|
||||||
|
@ -11,10 +12,13 @@ async function initSettings() {
|
||||||
// fix our local var and set it in storage for later
|
// fix our local var and set it in storage for later
|
||||||
if (invalidEntries.length > 0) {
|
if (invalidEntries.length > 0) {
|
||||||
const changeSet = Object.fromEntries(invalidEntries)
|
const changeSet = Object.fromEntries(invalidEntries)
|
||||||
Object.assign(settings, changeSet)
|
|
||||||
chrome.storage.local.set(changeSet)
|
chrome.storage.local.set(changeSet)
|
||||||
|
settings = await getExtensionSettingsAsync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Object.keys(targetPlatformSettings).includes(settings.targetPlatform)) setSetting('targetPlatform', DEFAULT_SETTINGS.targetPlatform)
|
||||||
|
if (!Object.keys(ytUrlResolversSettings).includes(settings.urlResolver)) setSetting('urlResolver', DEFAULT_SETTINGS.urlResolver)
|
||||||
|
|
||||||
chrome.browserAction.setBadgeText({ text: settings.redirect ? 'ON' : 'OFF' })
|
chrome.browserAction.setBadgeText({ text: settings.redirect ? 'ON' : 'OFF' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@ async function requestResolveById(...params: Parameters<typeof resolveById>): Re
|
||||||
* history.pushState changes from a content script
|
* history.pushState changes from a content script
|
||||||
*/
|
*/
|
||||||
// Listen URL Change
|
// Listen URL Change
|
||||||
chrome.runtime.onMessage.addListener(() => updater())
|
chrome.runtime.onMessage.addListener(({ message }, sender) => message === 'url-changed' && updater())
|
||||||
|
|
||||||
async function getTargetByURL(url: URL) {
|
async function getTargetByURL(url: URL) {
|
||||||
if (url.pathname === '/watch' && url.searchParams.has('v')) {
|
if (url.pathname === '/watch' && url.searchParams.has('v')) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue