mirror of
https://github.com/LBRYFoundation/Watch-on-LBRY.git
synced 2025-08-23 17:47:26 +00:00
🔥 Modified for Watch on Odysee
This commit is contained in:
parent
f072c95051
commit
3a44267fdf
10 changed files with 50 additions and 250 deletions
23
.github/workflows/contributors.yml
vendored
23
.github/workflows/contributors.yml
vendored
|
@ -1,23 +0,0 @@
|
||||||
name: Add contributors
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '20 20 * * *'
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
add-contributors:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: BobAnkh/add-contributors@master
|
|
||||||
with:
|
|
||||||
CONTRIBUTOR: '## Contributors'
|
|
||||||
COLUMN_PER_ROW: '6'
|
|
||||||
ACCESS_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
|
||||||
IMG_WIDTH: '100'
|
|
||||||
FONT_SIZE: '14'
|
|
||||||
PATH: '/README.md'
|
|
||||||
COMMIT_MESSAGE: 'docs(README): update contributors'
|
|
||||||
AVATAR_SHAPE: 'round'
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 22 KiB |
Binary file not shown.
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 4 KiB |
|
@ -1,6 +1,6 @@
|
||||||
:root {
|
:root {
|
||||||
--color-master: #499375;
|
--color-master: #cf3352;
|
||||||
--color-slave: #43889d;
|
--color-slave: #f77937;
|
||||||
--color-error: rgb(245, 81, 69);
|
--color-error: rgb(245, 81, 69);
|
||||||
--color-gradient-0: linear-gradient(130deg, var(--color-master), var(--color-slave));
|
--color-gradient-0: linear-gradient(130deg, var(--color-master), var(--color-slave));
|
||||||
--color-gradient-1: linear-gradient(130deg, #ff7a18, #af002d 41.07%, #319197 76.05%);
|
--color-gradient-1: linear-gradient(130deg, #ff7a18, #af002d 41.07%, #319197 76.05%);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { chunk } from "lodash"
|
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 { lbryUrlCache } from "./urlCache"
|
import { lbryUrlCache } from "./urlCache"
|
||||||
|
|
||||||
const QUERY_CHUNK_SIZE = 100
|
const QUERY_CHUNK_SIZE = 100
|
||||||
|
@ -18,7 +17,7 @@ interface ApiResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resolveById(params: Paramaters, progressCallback?: (progress: number) => void): Promise<Results> {
|
export async function resolveById(params: Paramaters, progressCallback?: (progress: number) => void): Promise<Results> {
|
||||||
const { urlResolver: urlResolverSettingName, privateKey, publicKey } = await getExtensionSettingsAsync()
|
const { urlResolver: urlResolverSettingName } = await getExtensionSettingsAsync()
|
||||||
const urlResolverSetting = ytUrlResolversSettings[urlResolverSettingName]
|
const urlResolverSetting = ytUrlResolversSettings[urlResolverSettingName]
|
||||||
|
|
||||||
async function requestChunk(params: Paramaters) {
|
async function requestChunk(params: Paramaters) {
|
||||||
|
@ -46,11 +45,6 @@ export async function resolveById(params: Paramaters, progressCallback?: (progre
|
||||||
url.pathname = path.join(url.pathname, '/resolve')
|
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)
|
|
||||||
url.searchParams.set('keys', JSON.stringify({
|
|
||||||
signature: await sign(url.searchParams.toString(), privateKey),
|
|
||||||
publicKey
|
|
||||||
}))
|
|
||||||
|
|
||||||
const apiResponse = await fetch(url.toString(), { cache: 'no-store' })
|
const apiResponse = await fetch(url.toString(), { cache: 'no-store' })
|
||||||
if (apiResponse.ok) {
|
if (apiResponse.ok) {
|
||||||
|
|
|
@ -1,23 +1,14 @@
|
||||||
import { h, render } from 'preact'
|
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 { lbryUrlCache } from '../../modules/yt/urlCache'
|
import { lbryUrlCache } from '../../modules/yt/urlCache'
|
||||||
import { getTargetPlatfromSettingsEntiries, getYtUrlResolversSettingsEntiries, setExtensionSetting, useExtensionSettings } from '../../settings'
|
import { setExtensionSetting, targetPlatformSettings, useExtensionSettings } from '../../settings'
|
||||||
import { openImportPopup } from '../import/main'
|
|
||||||
|
|
||||||
|
function WatchOnLbryPopup(params: {}) {
|
||||||
/** Gets all the options for redirect destinations as selection options */
|
const { redirect } = useExtensionSettings()
|
||||||
const targetPlatforms = getTargetPlatfromSettingsEntiries()
|
|
||||||
const ytUrlResolverOptions = getYtUrlResolversSettingsEntiries()
|
|
||||||
|
|
||||||
function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfile>> | null }) {
|
|
||||||
const { redirect, targetPlatform, urlResolver, privateKey, publicKey } = useExtensionSettings()
|
|
||||||
let [loading, updateLoading] = useState(() => false)
|
let [loading, updateLoading] = useState(() => false)
|
||||||
let [route, updateRoute] = useState<string | null>(() => null)
|
|
||||||
|
|
||||||
const dialogManager = createDialogManager()
|
const dialogManager = createDialogManager()
|
||||||
const nickname = params.profile ? params.profile.nickname ?? 'No Nickname' : '...'
|
|
||||||
|
|
||||||
|
|
||||||
async function loads<T>(operation: Promise<T>) {
|
async function loads<T>(operation: Promise<T>) {
|
||||||
|
@ -35,151 +26,38 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
||||||
return <div id='popup'>
|
return <div id='popup'>
|
||||||
<Dialogs manager={dialogManager} />
|
<Dialogs manager={dialogManager} />
|
||||||
{
|
{
|
||||||
publicKey
|
<header>
|
||||||
? <header>
|
<section>
|
||||||
<section>
|
<img id="logo" src={targetPlatformSettings.odysee.button.icon}></img>
|
||||||
<label>{nickname}</label>
|
<label>Watch on Odysee</label>
|
||||||
<p>{friendlyPublicKey(publicKey)}</p>
|
</section>
|
||||||
<span><b>Score: {params.profile?.score ?? '...'}</b> - <a target='_blank' href="https://finder.madiator.com/leaderboard" class="filled">🔗Leaderboard</a></span>
|
</header>
|
||||||
{urlResolver !== 'madiatorFinder' && <span class="error">You need to use Madiator Finder API for scoring to work</span>}
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
{
|
|
||||||
route === 'profile'
|
|
||||||
? <a onClick={() => updateRoute('')} className="filled">⇐ Back</a>
|
|
||||||
: <a className='filled' onClick={() => updateRoute('profile')} href="#profile">Profile Settings</a>
|
|
||||||
}
|
|
||||||
</section>
|
|
||||||
</header>
|
|
||||||
: <header>
|
|
||||||
{
|
|
||||||
route === 'profile'
|
|
||||||
? <a onClick={() => updateRoute('')} className="filled">⇐ Back</a>
|
|
||||||
: <a className='filled' onClick={() => updateRoute('profile')} href="#profile">Profile Settings</a>
|
|
||||||
}
|
|
||||||
</header>
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
route === 'profile' ?
|
<main>
|
||||||
publicKey ?
|
<section>
|
||||||
<main>
|
<label>Pick a mode:</label>
|
||||||
<section>
|
<div className='options'>
|
||||||
<div className='options'>
|
<a onClick={() => setExtensionSetting('redirect', true)} className={`button ${redirect ? 'active' : ''}`}>
|
||||||
<a onClick={() => loads(generateProfileAndSetNickname(dialogManager)).then(() => renderPopup())} className={`button active`}>
|
Redirect
|
||||||
Change Nickname
|
|
||||||
</a>
|
|
||||||
<a onClick={async () => {
|
|
||||||
if (!await dialogManager.confirm("This will delete your keypair from this device."
|
|
||||||
+ "\nStill wanna continue?"
|
|
||||||
+ "\n\nNOTE: Without keypair you can't purge your data online."
|
|
||||||
+ "\nSo if you wish to purge, please use purging instead.")) return
|
|
||||||
resetProfileSettings()
|
|
||||||
renderPopup()
|
|
||||||
}}
|
|
||||||
className={`button`}
|
|
||||||
>
|
|
||||||
Forget/Logout
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<label>Backup your account</label>
|
|
||||||
<p>Import and export your unique keypair.</p>
|
|
||||||
<div className='options'>
|
|
||||||
<a onClick={() => exportProfileKeysAsFile()} className={`button active`}>
|
|
||||||
Export
|
|
||||||
</a>
|
|
||||||
<a onClick={() => openImportPopup()}
|
|
||||||
className={`button`}
|
|
||||||
>
|
|
||||||
Import
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<label>Purge your profile and data!</label>
|
|
||||||
<p>Purge your profile data online and offline.</p>
|
|
||||||
<div className='options'>
|
|
||||||
<div className="purge-aaaaaaa">
|
|
||||||
<span className='filled'>(╯°□°)╯︵ ┻━┻</span>
|
|
||||||
</div>
|
|
||||||
<a onClick={() => loads(purgeProfile(dialogManager)).then(() => renderPopup())} className={`button`}>
|
|
||||||
Purge Everything!!
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<label>Generate new profile</label>
|
|
||||||
<p>Generate a new keypair.</p>
|
|
||||||
<div className='options'>
|
|
||||||
<a onClick={async () => await dialogManager.confirm("This will overwrite your old keypair.\nStill wanna continue?\n\nNOTE: Without keypair you can't purge your data online.\nSo if you wish to purge, please use purging instead.")
|
|
||||||
&& loads(generateProfileAndSetNickname(dialogManager, true)).then(() => renderPopup())
|
|
||||||
}
|
|
||||||
className={`button`}
|
|
||||||
>
|
|
||||||
Generate New Account
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
:
|
|
||||||
<main>
|
|
||||||
<section>
|
|
||||||
<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={() => openImportPopup()} className={`button`}>
|
|
||||||
Import
|
|
||||||
</a>
|
|
||||||
<a onClick={() => loads(generateProfileAndSetNickname(dialogManager)).then(() => renderPopup())} className={`button active`}>
|
|
||||||
Generate
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
:
|
|
||||||
<main>
|
|
||||||
<section>
|
|
||||||
<label>Pick a mode:</label>
|
|
||||||
<div className='options'>
|
|
||||||
<a onClick={() => setExtensionSetting('redirect', true)} className={`button ${redirect ? 'active' : ''}`}>
|
|
||||||
Redirect
|
|
||||||
</a>
|
|
||||||
<a onClick={() => setExtensionSetting('redirect', false)} className={`button ${redirect ? '' : 'active'}`}>
|
|
||||||
Show a button
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<label>Which platform you would like to redirect?</label>
|
|
||||||
<div className='options'>
|
|
||||||
{targetPlatforms.map(([name, value]) =>
|
|
||||||
<a onClick={() => setExtensionSetting('targetPlatform', name)} className={`button ${targetPlatform === name ? 'active' : ''}`}>
|
|
||||||
{value.displayName}
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<label>Which resolver API you want to use?</label>
|
|
||||||
<div className='options'>
|
|
||||||
{ytUrlResolverOptions.map(([name, value]) =>
|
|
||||||
<a onClick={() => setExtensionSetting('urlResolver', name)} className={`button ${urlResolver === name ? 'active' : ''}`}>
|
|
||||||
{value.name}
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<a onClick={() => loads(lbryUrlCache.clearAll().then(() => dialogManager.alert("Cleared Cache!")))} className={`button active`}>
|
|
||||||
Clear Resolver Cache
|
|
||||||
</a>
|
</a>
|
||||||
</section>
|
<a onClick={() => setExtensionSetting('redirect', false)} className={`button ${redirect ? '' : 'active'}`}>
|
||||||
<section>
|
Show a button
|
||||||
<label>Tools</label>
|
|
||||||
<a target='_blank' href='/pages/YTtoLBRY/index.html' className={`filled`}>
|
|
||||||
Subscription Converter
|
|
||||||
</a>
|
</a>
|
||||||
</section>
|
</div>
|
||||||
</main>
|
</section>
|
||||||
|
<section>
|
||||||
|
<a onClick={() => loads(lbryUrlCache.clearAll().then(() => dialogManager.alert("Cleared Cache!")))} className={`button active`}>
|
||||||
|
Clear Resolver Cache
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<label>Tools</label>
|
||||||
|
<a target='_blank' href='/pages/YTtoLBRY/index.html' className={`filled`}>
|
||||||
|
Subscription Converter
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
}
|
}
|
||||||
{loading && <div class="overlay">
|
{loading && <div class="overlay">
|
||||||
<span>Loading...</span>
|
<span>Loading...</span>
|
||||||
|
@ -188,8 +66,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPopup() {
|
function renderPopup() {
|
||||||
render(<WatchOnLbryPopup profile={null} />, document.getElementById('root')!)
|
render(<WatchOnLbryPopup />, document.getElementById('root')!)
|
||||||
getProfile().then((profile) => render(<WatchOnLbryPopup profile={profile} />, document.getElementById('root')!))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPopup()
|
renderPopup()
|
||||||
|
|
|
@ -8,6 +8,10 @@ header {
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
width: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 2em;
|
gap: 2em;
|
||||||
|
|
|
@ -143,23 +143,7 @@ async function requestResolveById(...params: Parameters<typeof resolveById>): Re
|
||||||
videoElement.pause()
|
videoElement.pause()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (platfrom === targetPlatformSettings.app) {
|
location.replace(url.toString())
|
||||||
if (document.hidden) await new Promise((resolve) => document.addEventListener('visibilitychange', resolve, { once: true }))
|
|
||||||
|
|
||||||
// On redirect with app, people might choose to cancel browser's dialog
|
|
||||||
// So we dont destroy the current window automatically for them
|
|
||||||
// And also we are keeping the same window for less distiraction
|
|
||||||
if (settings.redirect) {
|
|
||||||
location.replace(url.toString())
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
open(url.toString(), '_blank')
|
|
||||||
if (window.history.length === 1) window.close()
|
|
||||||
else window.history.back()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
location.replace(url.toString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let removeVideoTimeUpdateListener: (() => void) | null = null
|
let removeVideoTimeUpdateListener: (() => void) | null = null
|
||||||
|
|
|
@ -5,16 +5,12 @@ export interface ExtensionSettings {
|
||||||
redirect: boolean
|
redirect: boolean
|
||||||
targetPlatform: TargetPlatformName
|
targetPlatform: TargetPlatformName
|
||||||
urlResolver: YTUrlResolverName
|
urlResolver: YTUrlResolverName
|
||||||
publicKey: string | null,
|
|
||||||
privateKey: string | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_SETTINGS: ExtensionSettings = {
|
export const DEFAULT_SETTINGS: ExtensionSettings = {
|
||||||
redirect: true,
|
redirect: true,
|
||||||
targetPlatform: 'odysee',
|
targetPlatform: 'odysee',
|
||||||
urlResolver: 'odyseeApi',
|
urlResolver: 'odyseeApi'
|
||||||
privateKey: null,
|
|
||||||
publicKey: null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getExtensionSettingsAsync(): Promise<ExtensionSettings> {
|
export function getExtensionSettingsAsync(): Promise<ExtensionSettings> {
|
||||||
|
@ -74,19 +70,6 @@ export const getTargetPlatfromSettingsEntiries = () => {
|
||||||
return Object.entries(targetPlatformSettings) as any as [Extract<keyof typeof targetPlatformSettings, string>, TargetPlatform][]
|
return Object.entries(targetPlatformSettings) as any as [Extract<keyof typeof targetPlatformSettings, string>, TargetPlatform][]
|
||||||
}
|
}
|
||||||
export const targetPlatformSettings = {
|
export const targetPlatformSettings = {
|
||||||
'madiator.com': targetPlatform({
|
|
||||||
domainPrefix: 'https://madiator.com/',
|
|
||||||
displayName: 'Madiator.com',
|
|
||||||
theme: 'linear-gradient(130deg, #499375, #43889d)',
|
|
||||||
button: {
|
|
||||||
text: 'Watch on',
|
|
||||||
icon: chrome.runtime.getURL('assets/icons/lbry/madiator-logo.svg'),
|
|
||||||
style: {
|
|
||||||
button: { flexDirection: 'row-reverse' },
|
|
||||||
icon: { transform: 'scale(1.2)' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
odysee: targetPlatform({
|
odysee: targetPlatform({
|
||||||
domainPrefix: 'https://odysee.com/',
|
domainPrefix: 'https://odysee.com/',
|
||||||
displayName: 'Odysee',
|
displayName: 'Odysee',
|
||||||
|
@ -95,16 +78,7 @@ export const targetPlatformSettings = {
|
||||||
text: 'Watch on Odysee',
|
text: 'Watch on Odysee',
|
||||||
icon: chrome.runtime.getURL('assets/icons/lbry/odysee-logo.svg')
|
icon: chrome.runtime.getURL('assets/icons/lbry/odysee-logo.svg')
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
app: targetPlatform({
|
|
||||||
domainPrefix: 'lbry://',
|
|
||||||
displayName: 'LBRY App',
|
|
||||||
theme: 'linear-gradient(130deg, #499375, #43889d)',
|
|
||||||
button: {
|
|
||||||
text: 'Watch on LBRY',
|
|
||||||
icon: chrome.runtime.getURL('assets/icons/lbry/lbry-logo.svg')
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -125,19 +99,19 @@ export function getSourcePlatfromSettingsFromHostname(hostname: string) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
export const sourcePlatfromSettings = {
|
export const sourcePlatfromSettings = {
|
||||||
"yewtu.be": sourcePlatform({
|
|
||||||
hostnames: ['yewtu.be', 'vid.puffyan.us', 'invidio.xamh.de', 'invidious.kavin.rocks'],
|
|
||||||
htmlQueries: {
|
|
||||||
mountButtonBefore: '#watch-on-youtube',
|
|
||||||
videoPlayer: '#player-container video'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"youtube.com": sourcePlatform({
|
"youtube.com": sourcePlatform({
|
||||||
hostnames: ['www.youtube.com'],
|
hostnames: ['www.youtube.com'],
|
||||||
htmlQueries: {
|
htmlQueries: {
|
||||||
mountButtonBefore: 'ytd-video-owner-renderer~#subscribe-button',
|
mountButtonBefore: 'ytd-video-owner-renderer~#subscribe-button',
|
||||||
videoPlayer: '#ytd-player video'
|
videoPlayer: '#ytd-player video'
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
"yewtu.be": sourcePlatform({
|
||||||
|
hostnames: ['yewtu.be', 'vid.puffyan.us', 'invidio.xamh.de', 'invidious.kavin.rocks'],
|
||||||
|
htmlQueries: {
|
||||||
|
mountButtonBefore: '#watch-on-youtube',
|
||||||
|
videoPlayer: '#player-container video'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,15 +128,5 @@ export const ytUrlResolversSettings = {
|
||||||
name: "Odysee",
|
name: "Odysee",
|
||||||
href: "https://api.odysee.com/yt",
|
href: "https://api.odysee.com/yt",
|
||||||
signRequest: false
|
signRequest: false
|
||||||
}),
|
})
|
||||||
madiatorFinder: ytUrlResolver({
|
|
||||||
name: "Madiator Finder",
|
|
||||||
href: "https://finder.madiator.com/api/v1",
|
|
||||||
signRequest: true
|
|
||||||
}),
|
|
||||||
/* madiatorFinderLocal: ytUrlResolver({
|
|
||||||
name: "Madiator Finder Local",
|
|
||||||
href: "http://127.0.0.1:3001/api/v1",
|
|
||||||
signRequest: true
|
|
||||||
}) */
|
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue