mirror of
https://github.com/LBRYFoundation/Watch-on-LBRY.git
synced 2025-08-23 17:47:26 +00:00
Merge 3b21528efe
into 8c26ae0872
This commit is contained in:
commit
ab66b062a8
5 changed files with 158 additions and 10180 deletions
|
@ -22,7 +22,8 @@
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"bierner.folder-source-actions",
|
"bierner.folder-source-actions",
|
||||||
"jbockle.jbockle-format-files",
|
"jbockle.jbockle-format-files",
|
||||||
"eamodio.gitlens"
|
"eamodio.gitlens",
|
||||||
|
"GitHub.copilot"
|
||||||
],
|
],
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
// "forwardPorts": [],
|
// "forwardPorts": [],
|
||||||
|
|
7
.github/workflows/extension-build.js.yml
vendored
7
.github/workflows/extension-build.js.yml
vendored
|
@ -1,6 +1,8 @@
|
||||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||||
|
|
||||||
|
# Also uses `python2` to build the webextension
|
||||||
|
|
||||||
name: Node.js CI
|
name: Node.js CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
@ -24,7 +26,10 @@ jobs:
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: yarn
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get install python2
|
||||||
|
yarn
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
- run: npm run
|
- run: npm run
|
||||||
- run: npm run build:webext
|
- run: npm run build:webext
|
||||||
|
|
|
@ -1,36 +1,69 @@
|
||||||
import { resolveById } from "../modules/yt/urlResolve"
|
import { resolveById } from "../modules/yt/urlResolve"
|
||||||
|
|
||||||
const onGoingLbryPathnameRequest: Record<string, ReturnType<typeof resolveById>> = {}
|
interface BackgroundMethod<P extends any[], R> {
|
||||||
|
PARAMS_TYPE: P
|
||||||
|
RESULT_TYPE: R
|
||||||
|
call(sender: chrome.runtime.MessageSender, paramsJson: string, ...params: P): Promise<R>
|
||||||
|
}
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener(({ method, data }, sender, sendResponse) => {
|
// Satifies BackgroundMethod
|
||||||
function resolve(result: Awaited<ReturnType<typeof resolveById>>) {
|
function backgroundMethod<P extends any[], R>(method: BackgroundMethod<P, R>['call']): BackgroundMethod<P, R> {
|
||||||
sendResponse(JSON.stringify(result))
|
return {
|
||||||
|
PARAMS_TYPE: null as any,
|
||||||
|
RESULT_TYPE: null as any,
|
||||||
|
call: method
|
||||||
}
|
}
|
||||||
(async () => {
|
}
|
||||||
|
|
||||||
switch (method) {
|
|
||||||
case 'openTab':
|
|
||||||
{
|
const resolveUrlOnGoingRequest: Record<string, ReturnType<typeof resolveById>> = {}
|
||||||
const { href }: { href: string } = JSON.parse(data)
|
|
||||||
chrome.tabs.create({ url: href, active: sender.tab?.active, index: sender.tab ? sender.tab.index + 1 : undefined })
|
export type BackgroundMethods = typeof methods
|
||||||
}
|
const methods = {
|
||||||
break
|
|
||||||
case 'resolveUrl':
|
/*
|
||||||
try {
|
This method is needed to open a new tab from a content script,
|
||||||
const params: Parameters<typeof resolveById> = JSON.parse(data)
|
because using window.open() from a content script is blocked by Chrome while using redirect feature.
|
||||||
// Don't create a new Promise for same ID until on going one is over.
|
*/
|
||||||
const promise = onGoingLbryPathnameRequest[data] ?? (onGoingLbryPathnameRequest[data] = resolveById(...params))
|
openTab: backgroundMethod<[{ href: string }], void>(async (sender, json, { href }) => {
|
||||||
resolve(await promise)
|
chrome.tabs.create({ url: href, active: sender.tab?.active, index: sender.tab ? sender.tab.index + 1 : undefined })
|
||||||
} catch (error) {
|
}),
|
||||||
sendResponse(`error: ${(error as any).toString()}`)
|
|
||||||
console.error(error)
|
|
||||||
}
|
/*
|
||||||
finally {
|
This method is needed to resolve a YouTube URL from a content script,
|
||||||
delete onGoingLbryPathnameRequest[data]
|
this is on the background script because so we can cache the result and avoid making multiple requests for the same ID.
|
||||||
}
|
*/
|
||||||
break
|
resolveUrlById: backgroundMethod<Parameters<typeof resolveById>, Awaited<ReturnType<typeof resolveById>>>(async (sender, json, ...params) => {
|
||||||
|
try {
|
||||||
|
// Don't create a new Promise for same ID until on going one is over.
|
||||||
|
const promise = resolveUrlOnGoingRequest[json] ?? (resolveUrlOnGoingRequest[json] = resolveById(...params))
|
||||||
|
return await promise
|
||||||
}
|
}
|
||||||
})()
|
catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
delete resolveUrlOnGoingRequest[json]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
} as const
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
chrome.runtime.onMessage.addListener(({ method, data }: { method: keyof BackgroundMethods, data: string }, sender, sendResponse) => {
|
||||||
|
try {
|
||||||
|
const methodData = methods[method]
|
||||||
|
if (!methodData) throw new Error(`Unknown method: ${method}`)
|
||||||
|
methodData.call(sender, data, ...JSON.parse(data))
|
||||||
|
.then(result => sendResponse(JSON.stringify(result ?? null)))
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
sendResponse(`error: ${error?.toString?.()}`)
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
})
|
})
|
|
@ -1,19 +1,23 @@
|
||||||
import { h, render } from 'preact'
|
import { h, render } from 'preact'
|
||||||
import { parseYouTubeURLTimeString } from '../modules/yt'
|
import { parseYouTubeURLTimeString } from '../modules/yt'
|
||||||
import type { resolveById, ResolveUrlTypes } from '../modules/yt/urlResolve'
|
import type { resolveById, ResolveUrlTypes } from '../modules/yt/urlResolve'
|
||||||
import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTargetPlatfromSettingsEntiries, SourcePlatform, sourcePlatfromSettings, TargetPlatform, targetPlatformSettings } from '../settings';
|
import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTargetPlatfromSettingsEntiries, SourcePlatform, sourcePlatfromSettings, TargetPlatform, targetPlatformSettings } from '../settings'
|
||||||
|
import type { BackgroundMethods } from './background'
|
||||||
|
|
||||||
(async () => {
|
(async () =>
|
||||||
|
{
|
||||||
const sleep = (t: number) => new Promise(resolve => setTimeout(resolve, t))
|
const sleep = (t: number) => new Promise(resolve => setTimeout(resolve, t))
|
||||||
|
|
||||||
interface Target {
|
interface Target
|
||||||
|
{
|
||||||
platform: TargetPlatform
|
platform: TargetPlatform
|
||||||
lbryPathname: string
|
lbryPathname: string
|
||||||
type: ResolveUrlTypes
|
type: ResolveUrlTypes
|
||||||
time: number | null
|
time: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Source {
|
interface Source
|
||||||
|
{
|
||||||
platform: SourcePlatform
|
platform: SourcePlatform
|
||||||
id: string
|
id: string
|
||||||
type: ResolveUrlTypes
|
type: ResolveUrlTypes
|
||||||
|
@ -23,19 +27,27 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
|
|
||||||
const targetPlatforms = getTargetPlatfromSettingsEntiries()
|
const targetPlatforms = getTargetPlatfromSettingsEntiries()
|
||||||
const settings = await getExtensionSettingsAsync()
|
const settings = await getExtensionSettingsAsync()
|
||||||
// Listen Settings Change
|
chrome.storage.onChanged.addListener(async (changes, areaName) =>
|
||||||
chrome.storage.onChanged.addListener(async (changes, areaName) => {
|
{
|
||||||
if (areaName !== 'local') return
|
if (areaName !== 'local') return
|
||||||
Object.assign(settings, Object.fromEntries(Object.entries(changes).map(([key, change]) => [key, change.newValue])))
|
Object.assign(settings, Object.fromEntries(Object.entries(changes).map(([key, change]) => [key, change.newValue])))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function callBackgroundMethod<T extends keyof BackgroundMethods>(method: T, ...params: BackgroundMethods[T]['PARAMS_TYPE']): Promise<BackgroundMethods[T]['RESULT_TYPE']>
|
||||||
|
{
|
||||||
|
const response = await new Promise<string>((resolve) => chrome.runtime.sendMessage({ method, data: JSON.stringify(params) }, resolve))
|
||||||
|
if (response.startsWith('error:')) throw new Error(`Background Error: ${response.substring('error:'.length).trim()}`)
|
||||||
|
return JSON.parse(response)
|
||||||
|
}
|
||||||
|
|
||||||
const buttonMountPoint = document.createElement('div')
|
const buttonMountPoint = document.createElement('div')
|
||||||
buttonMountPoint.style.display = 'inline-flex'
|
buttonMountPoint.style.display = 'inline-flex'
|
||||||
|
|
||||||
const playerButtonMountPoint = document.createElement('div')
|
const playerButtonMountPoint = document.createElement('div')
|
||||||
playerButtonMountPoint.style.display = 'inline-flex'
|
playerButtonMountPoint.style.display = 'inline-flex'
|
||||||
|
|
||||||
function WatchOnLbryButton({ source, target }: { source?: Source, target?: Target }) {
|
function WatchOnLbryButton({ source, target }: { source?: Source, target?: Target })
|
||||||
|
{
|
||||||
if (!target || !source) return null
|
if (!target || !source) return null
|
||||||
const url = getLbryUrlByTarget(target)
|
const url = getLbryUrlByTarget(target)
|
||||||
|
|
||||||
|
@ -65,7 +77,8 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
backgroundImage: target.platform.theme,
|
backgroundImage: target.platform.theme,
|
||||||
...target.platform.button.style?.button,
|
...target.platform.button.style?.button,
|
||||||
}}
|
}}
|
||||||
onClick={() => findVideoElementAwait(source).then((videoElement) => {
|
onClick={() => findVideoElementAwait(source).then((videoElement) =>
|
||||||
|
{
|
||||||
videoElement.pause()
|
videoElement.pause()
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
@ -75,7 +88,8 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
function WatchOnLbryPlayerButton({ source, target }: { source?: Source, target?: Target }) {
|
function WatchOnLbryPlayerButton({ source, target }: { source?: Source, target?: Target })
|
||||||
|
{
|
||||||
if (!target || !source) return null
|
if (!target || !source) return null
|
||||||
const url = getLbryUrlByTarget(target)
|
const url = getLbryUrlByTarget(target)
|
||||||
|
|
||||||
|
@ -102,7 +116,8 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
...target.platform.button.style?.button,
|
...target.platform.button.style?.button,
|
||||||
}}
|
}}
|
||||||
onClick={() => findVideoElementAwait(source).then((videoElement) => {
|
onClick={() => findVideoElementAwait(source).then((videoElement) =>
|
||||||
|
{
|
||||||
videoElement.pause()
|
videoElement.pause()
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
@ -112,8 +127,10 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateButtons(params: { source: Source, target: Target } | null): void {
|
function updateButtons(params: { source: Source, target: Target } | null): void
|
||||||
if (!params) {
|
{
|
||||||
|
if (!params)
|
||||||
|
{
|
||||||
render(<WatchOnLbryButton />, buttonMountPoint)
|
render(<WatchOnLbryButton />, buttonMountPoint)
|
||||||
render(<WatchOnLbryPlayerButton />, playerButtonMountPoint)
|
render(<WatchOnLbryPlayerButton />, playerButtonMountPoint)
|
||||||
return
|
return
|
||||||
|
@ -124,8 +141,10 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
document.querySelector(params.source.platform.htmlQueries.mountPoints.mountPlayerButtonBefore) :
|
document.querySelector(params.source.platform.htmlQueries.mountPoints.mountPlayerButtonBefore) :
|
||||||
null
|
null
|
||||||
if (!mountPlayerButtonBefore) render(<WatchOnLbryPlayerButton />, playerButtonMountPoint)
|
if (!mountPlayerButtonBefore) render(<WatchOnLbryPlayerButton />, playerButtonMountPoint)
|
||||||
else {
|
else
|
||||||
if (playerButtonMountPoint.getAttribute('data-id') !== params.source.id) {
|
{
|
||||||
|
if (playerButtonMountPoint.getAttribute('data-id') !== params.source.id)
|
||||||
|
{
|
||||||
mountPlayerButtonBefore.parentElement?.insertBefore(playerButtonMountPoint, mountPlayerButtonBefore)
|
mountPlayerButtonBefore.parentElement?.insertBefore(playerButtonMountPoint, mountPlayerButtonBefore)
|
||||||
playerButtonMountPoint.setAttribute('data-id', params.source.id)
|
playerButtonMountPoint.setAttribute('data-id', params.source.id)
|
||||||
}
|
}
|
||||||
|
@ -138,8 +157,10 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
document.querySelector(params.source.platform.htmlQueries.mountPoints.mountButtonBefore[params.source.type]) :
|
document.querySelector(params.source.platform.htmlQueries.mountPoints.mountButtonBefore[params.source.type]) :
|
||||||
null
|
null
|
||||||
if (!mountButtonBefore) render(<WatchOnLbryButton />, buttonMountPoint)
|
if (!mountButtonBefore) render(<WatchOnLbryButton />, buttonMountPoint)
|
||||||
else {
|
else
|
||||||
if (buttonMountPoint.getAttribute('data-id') !== params.source.id) {
|
{
|
||||||
|
if (buttonMountPoint.getAttribute('data-id') !== params.source.id)
|
||||||
|
{
|
||||||
mountButtonBefore.parentElement?.insertBefore(buttonMountPoint, mountButtonBefore)
|
mountButtonBefore.parentElement?.insertBefore(buttonMountPoint, mountButtonBefore)
|
||||||
buttonMountPoint.setAttribute('data-id', params.source.id)
|
buttonMountPoint.setAttribute('data-id', params.source.id)
|
||||||
}
|
}
|
||||||
|
@ -148,17 +169,20 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findVideoElementAwait(source: Source) {
|
async function findVideoElementAwait(source: Source)
|
||||||
|
{
|
||||||
let videoElement: HTMLVideoElement | null = null
|
let videoElement: HTMLVideoElement | null = null
|
||||||
while (!(videoElement = document.querySelector(source.platform.htmlQueries.videoPlayer))) await sleep(200)
|
while (!(videoElement = document.querySelector(source.platform.htmlQueries.videoPlayer))) await sleep(200)
|
||||||
return videoElement
|
return videoElement
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSourceByUrl(url: URL): Promise<Source | null> {
|
async function getSourceByUrl(url: URL): Promise<Source | null>
|
||||||
|
{
|
||||||
const platform = getSourcePlatfromSettingsFromHostname(new URL(location.href).hostname)
|
const platform = getSourcePlatfromSettingsFromHostname(new URL(location.href).hostname)
|
||||||
if (!platform) return null
|
if (!platform) return null
|
||||||
|
|
||||||
if (url.pathname === '/watch' && url.searchParams.has('v')) {
|
if (url.pathname === '/watch' && url.searchParams.has('v'))
|
||||||
|
{
|
||||||
return {
|
return {
|
||||||
id: url.searchParams.get('v')!,
|
id: url.searchParams.get('v')!,
|
||||||
platform,
|
platform,
|
||||||
|
@ -167,7 +191,8 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (url.pathname.startsWith('/channel/')) {
|
else if (url.pathname.startsWith('/channel/'))
|
||||||
|
{
|
||||||
return {
|
return {
|
||||||
id: url.pathname.substring("/channel/".length),
|
id: url.pathname.substring("/channel/".length),
|
||||||
platform,
|
platform,
|
||||||
|
@ -176,7 +201,8 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (url.pathname.startsWith('/c/') || url.pathname.startsWith('/user/')) {
|
else if (url.pathname.startsWith('/c/') || url.pathname.startsWith('/user/'))
|
||||||
|
{
|
||||||
// We have to download the page content again because these parts of the page are not responsive
|
// We have to download the page content again because these parts of the page are not responsive
|
||||||
// yt front end sucks anyway
|
// yt front end sucks anyway
|
||||||
const content = await (await fetch(location.href)).text()
|
const content = await (await fetch(location.href)).text()
|
||||||
|
@ -197,13 +223,13 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getTargetsBySources(...sources: Source[]) {
|
async function getTargetsBySources(...sources: Source[])
|
||||||
const params: Parameters<typeof requestResolveById>[0] = sources.map((source) => ({ id: source.id, type: source.type }))
|
{
|
||||||
const platform = targetPlatformSettings[settings.targetPlatform]
|
const platform = targetPlatformSettings[settings.targetPlatform]
|
||||||
|
const results = await callBackgroundMethod('resolveUrlById', sources.map((source) => ({ id: source.id, type: source.type })))
|
||||||
const results = await requestResolveById(params) ?? []
|
|
||||||
const targets: Record<string, Target | null> = Object.fromEntries(
|
const targets: Record<string, Target | null> = Object.fromEntries(
|
||||||
sources.map((source) => {
|
sources.map((source) =>
|
||||||
|
{
|
||||||
const result = results[source.id]
|
const result = results[source.id]
|
||||||
if (!result) return [
|
if (!result) return [
|
||||||
source.id,
|
source.id,
|
||||||
|
@ -224,47 +250,39 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
|
|
||||||
return targets
|
return targets
|
||||||
}
|
}
|
||||||
// We should get this from background, so the caching works and we don't get errors in the future if yt decides to impliment CORS
|
|
||||||
async function requestResolveById(...params: Parameters<typeof resolveById>): ReturnType<typeof resolveById> {
|
|
||||||
const response = await new Promise<string | null | 'error'>((resolve) => chrome.runtime.sendMessage({ method: 'resolveUrl', data: JSON.stringify(params) }, resolve))
|
|
||||||
if (response?.startsWith('error:')) {
|
|
||||||
console.error("Background error on:", params)
|
|
||||||
throw new Error(`Background error. ${response ?? ''}`)
|
|
||||||
}
|
|
||||||
return response ? JSON.parse(response) : null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request new tab
|
function findTargetFromSourcePage(source: Source): Target | null
|
||||||
async function openNewTab(url: URL) {
|
{
|
||||||
chrome.runtime.sendMessage({ method: 'openTab', data: JSON.stringify({ href: url.href }) })
|
|
||||||
}
|
|
||||||
|
|
||||||
function findTargetFromSourcePage(source: Source): Target | null {
|
|
||||||
const linksContainer =
|
const linksContainer =
|
||||||
source.type === 'video' ?
|
source.type === 'video' ?
|
||||||
document.querySelector(source.platform.htmlQueries.videoDescription) :
|
document.querySelector(source.platform.htmlQueries.videoDescription) :
|
||||||
source.platform.htmlQueries.channelLinks ? document.querySelector(source.platform.htmlQueries.channelLinks) : null
|
source.platform.htmlQueries.channelLinks ? document.querySelector(source.platform.htmlQueries.channelLinks) : null
|
||||||
|
|
||||||
if (linksContainer) {
|
if (linksContainer)
|
||||||
|
{
|
||||||
const anchors = Array.from(linksContainer.querySelectorAll<HTMLAnchorElement>('a'))
|
const anchors = Array.from(linksContainer.querySelectorAll<HTMLAnchorElement>('a'))
|
||||||
|
|
||||||
for (const anchor of anchors) {
|
for (const anchor of anchors)
|
||||||
|
{
|
||||||
if (!anchor.href) continue
|
if (!anchor.href) continue
|
||||||
const url = new URL(anchor.href)
|
const url = new URL(anchor.href)
|
||||||
let lbryURL: URL | null = null
|
let lbryURL: URL | null = null
|
||||||
|
|
||||||
// Extract real link from youtube's redirect link
|
// Extract real link from youtube's redirect link
|
||||||
if (source.platform === sourcePlatfromSettings['youtube.com']) {
|
if (source.platform === sourcePlatfromSettings['youtube.com'])
|
||||||
|
{
|
||||||
if (!targetPlatforms.some(([key, platform]) => url.searchParams.get('q')?.startsWith(platform.domainPrefix))) continue
|
if (!targetPlatforms.some(([key, platform]) => url.searchParams.get('q')?.startsWith(platform.domainPrefix))) continue
|
||||||
lbryURL = new URL(url.searchParams.get('q')!)
|
lbryURL = new URL(url.searchParams.get('q')!)
|
||||||
}
|
}
|
||||||
// Just directly use the link itself on other platforms
|
// Just directly use the link itself on other platforms
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
if (!targetPlatforms.some(([key, platform]) => url.href.startsWith(platform.domainPrefix))) continue
|
if (!targetPlatforms.some(([key, platform]) => url.href.startsWith(platform.domainPrefix))) continue
|
||||||
lbryURL = new URL(url.href)
|
lbryURL = new URL(url.href)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lbryURL) {
|
if (lbryURL)
|
||||||
|
{
|
||||||
return {
|
return {
|
||||||
lbryPathname: lbryURL.pathname.substring(1),
|
lbryPathname: lbryURL.pathname.substring(1),
|
||||||
time: null,
|
time: null,
|
||||||
|
@ -277,31 +295,28 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLbryUrlByTarget(target: Target) {
|
function getLbryUrlByTarget(target: Target)
|
||||||
|
{
|
||||||
const url = new URL(`${target.platform.domainPrefix}${target.lbryPathname}`)
|
const url = new URL(`${target.platform.domainPrefix}${target.lbryPathname}`)
|
||||||
if (target.time) url.searchParams.set('t', target.time.toFixed(0))
|
if (target.time) url.searchParams.set('t', target.time.toFixed(0))
|
||||||
|
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let url = new URL(location.href), urlHrefCache: string | null = null; ; urlHrefCache = url.href, url = new URL(location.href))
|
||||||
// Master Loop
|
{
|
||||||
for (
|
|
||||||
let url = new URL(location.href),
|
|
||||||
urlHrefCache: string | null = null;
|
|
||||||
;
|
|
||||||
urlHrefCache = url.href,
|
|
||||||
url = new URL(location.href)
|
|
||||||
) {
|
|
||||||
await sleep(500)
|
await sleep(500)
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
const source = await getSourceByUrl(new URL(location.href))
|
const source = await getSourceByUrl(new URL(location.href))
|
||||||
if (!source) {
|
if (!source)
|
||||||
|
{
|
||||||
updateButtons(null)
|
updateButtons(null)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const target = (await getTargetsBySources(source))[source.id] ?? findTargetFromSourcePage(source)
|
const target = (await getTargetsBySources(source))[source.id] ?? findTargetFromSourcePage(source)
|
||||||
if (!target) {
|
if (!target)
|
||||||
|
{
|
||||||
updateButtons(null)
|
updateButtons(null)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -309,7 +324,8 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
// Update Buttons
|
// Update Buttons
|
||||||
if (urlHrefCache !== url.href) updateButtons(null)
|
if (urlHrefCache !== url.href) updateButtons(null)
|
||||||
// If target is a video target add timestampt to it
|
// If target is a video target add timestampt to it
|
||||||
if (target.type === 'video') {
|
if (target.type === 'video')
|
||||||
|
{
|
||||||
const videoElement = document.querySelector<HTMLVideoElement>(source.platform.htmlQueries.videoPlayer)
|
const videoElement = document.querySelector<HTMLVideoElement>(source.platform.htmlQueries.videoPlayer)
|
||||||
if (videoElement) target.time = videoElement.currentTime > 3 && videoElement.currentTime < videoElement.duration - 1 ? videoElement.currentTime : null
|
if (videoElement) target.time = videoElement.currentTime > 3 && videoElement.currentTime < videoElement.duration - 1 ? videoElement.currentTime : null
|
||||||
}
|
}
|
||||||
|
@ -332,30 +348,36 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
source.type === 'channel'
|
source.type === 'channel'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
) {
|
)
|
||||||
|
{
|
||||||
if (url.href === urlHrefCache) continue
|
if (url.href === urlHrefCache) continue
|
||||||
|
|
||||||
const lbryURL = getLbryUrlByTarget(target)
|
const lbryURL = getLbryUrlByTarget(target)
|
||||||
|
|
||||||
if (source.type === 'video') {
|
if (source.type === 'video')
|
||||||
|
{
|
||||||
findVideoElementAwait(source).then((videoElement) => videoElement.pause())
|
findVideoElementAwait(source).then((videoElement) => videoElement.pause())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.platform === targetPlatformSettings.app) {
|
if (target.platform === targetPlatformSettings.app)
|
||||||
|
{
|
||||||
if (document.hidden) await new Promise((resolve) => document.addEventListener('visibilitychange', resolve, { once: true }))
|
if (document.hidden) await new Promise((resolve) => document.addEventListener('visibilitychange', resolve, { once: true }))
|
||||||
// Replace is being used so browser doesnt start an empty window
|
// Replace is being used so browser doesnt start an empty window
|
||||||
// Its not gonna be able to replace anyway, since its a LBRY Uri
|
// Its not gonna be able to replace anyway, since its a LBRY Uri
|
||||||
location.replace(lbryURL)
|
location.replace(lbryURL)
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
openNewTab(lbryURL)
|
{
|
||||||
if (window.history.length === 1)
|
callBackgroundMethod('openTab', { href: lbryURL.href })
|
||||||
|
if (window.history.length === 1)
|
||||||
window.close()
|
window.close()
|
||||||
else
|
else
|
||||||
window.history.back()
|
window.history.back()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue