mirror of
https://github.com/LBRYFoundation/Watch-on-LBRY.git
synced 2025-08-23 17:47:26 +00:00
🍙 Bug fixes, and other fixes
This commit is contained in:
parent
ca4c486ea7
commit
a6472799a1
2 changed files with 108 additions and 91 deletions
|
@ -13,10 +13,14 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
time: number | null
|
time: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourcePlatform = getSourcePlatfromSettingsFromHostname(new URL(location.href).hostname)
|
interface Source {
|
||||||
if (!sourcePlatform) return
|
platform: SourcePlatform
|
||||||
const targetPlatforms = getTargetPlatfromSettingsEntiries()
|
id: string
|
||||||
|
type: ResolveUrlTypes
|
||||||
|
time: number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetPlatforms = getTargetPlatfromSettingsEntiries()
|
||||||
const settings = await getExtensionSettingsAsync()
|
const settings = await getExtensionSettingsAsync()
|
||||||
// Listen Settings Change
|
// Listen Settings Change
|
||||||
chrome.storage.onChanged.addListener(async (changes, areaName) => {
|
chrome.storage.onChanged.addListener(async (changes, areaName) => {
|
||||||
|
@ -27,8 +31,8 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
const mountPoint = document.createElement('div')
|
const mountPoint = document.createElement('div')
|
||||||
mountPoint.style.display = 'flex'
|
mountPoint.style.display = 'flex'
|
||||||
|
|
||||||
function WatchOnLbryButton({ target }: { target?: Target }) {
|
function WatchOnLbryButton({ source, target }: { source?: Source, target?: Target }) {
|
||||||
if (!target) return null
|
if (!target || !source) return null
|
||||||
const url = getLbryUrlByTarget(target)
|
const url = getLbryUrlByTarget(target)
|
||||||
|
|
||||||
return <div style={{ display: 'flex', justifyContent: 'center', flexDirection: 'column' }}>
|
return <div style={{ display: 'flex', justifyContent: 'center', flexDirection: 'column' }}>
|
||||||
|
@ -45,104 +49,109 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
border: '0',
|
border: '0',
|
||||||
color: 'whitesmoke',
|
color: 'whitesmoke',
|
||||||
padding: '10px 16px',
|
padding: '10px 16px',
|
||||||
marginRight: target.type === 'channel' ? '10px' : '4px',
|
marginRight: source?.type === 'channel' ? '10px' : '4px',
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
...target.platform.button.style?.button,
|
...target.platform.button.style?.button,
|
||||||
}}
|
}}
|
||||||
onClick={() => findVideoElementAwait().then((videoElement) => {
|
onClick={() => findVideoElementAwait(source).then((videoElement) => {
|
||||||
videoElement.pause()
|
videoElement.pause()
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<img src={target.platform.button.icon} height={16}
|
<img src={target.platform.button.icon} height={16}
|
||||||
style={{ transform: 'scale(1.5)', ...target.platform.button.style?.icon }} />
|
style={{ transform: 'scale(1.5)', ...target.platform.button.style?.icon }} />
|
||||||
<span>{target.platform.button.text}</span>
|
<span>{target.type === 'channel' ? 'Channel on' : 'Watch on'} {target.platform.displayName}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateButton(target: Target | null): void {
|
function updateButton(params: { source: Source, target: Target } | null): void {
|
||||||
if (!target) return render(<WatchOnLbryButton />, mountPoint)
|
if (!params) return render(<WatchOnLbryButton />, mountPoint)
|
||||||
|
|
||||||
const sourcePlatform = getSourcePlatfromSettingsFromHostname(new URL(location.href).hostname)
|
const mountBefore = document.querySelector(params.source.platform.htmlQueries.mountButtonBefore[params.source.type])
|
||||||
if (!sourcePlatform) return render(<WatchOnLbryButton />, mountPoint)
|
|
||||||
|
|
||||||
const mountBefore = document.querySelector(sourcePlatform.htmlQueries.mountButtonBefore[target.type])
|
|
||||||
if (!mountBefore) return render(<WatchOnLbryButton />, mountPoint)
|
if (!mountBefore) return render(<WatchOnLbryButton />, mountPoint)
|
||||||
|
|
||||||
mountBefore.parentElement?.insertBefore(mountPoint, mountBefore)
|
mountBefore.parentElement?.insertBefore(mountPoint, mountBefore)
|
||||||
render(<WatchOnLbryButton target={target} />, mountPoint)
|
render(<WatchOnLbryButton target={params.target} source={params.source} />, mountPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findVideoElementAwait() {
|
async function findVideoElementAwait(source: Source) {
|
||||||
const sourcePlatform = getSourcePlatfromSettingsFromHostname(new URL(location.href).hostname)
|
|
||||||
if (!sourcePlatform) throw new Error(`Unknown source of: ${location.href}`)
|
|
||||||
let videoElement: HTMLVideoElement | null = null
|
let videoElement: HTMLVideoElement | null = null
|
||||||
while (!(videoElement = document.querySelector(sourcePlatform.htmlQueries.videoPlayer))) await sleep(200)
|
while (!(videoElement = document.querySelector(source.platform.htmlQueries.videoPlayer))) await sleep(200)
|
||||||
return videoElement
|
return videoElement
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getTargetsByURL(...urls: URL[]) {
|
async function getSourceByUrl(url: URL): Promise<Source | null> {
|
||||||
const params: Parameters<typeof requestResolveById>[0] = []
|
const platform = getSourcePlatfromSettingsFromHostname(new URL(location.href).hostname)
|
||||||
const platform = targetPlatformSettings[settings.targetPlatform]
|
if (!platform) return null
|
||||||
|
|
||||||
const datas: Record<string, { url: URL, type: ResolveUrlTypes }> = {}
|
if (url.pathname === '/watch' && url.searchParams.has('v')) {
|
||||||
|
return {
|
||||||
for (const url of urls) {
|
id: url.searchParams.get('v')!,
|
||||||
if (url.pathname === '/watch' && url.searchParams.has('v')) {
|
platform,
|
||||||
const id = url.searchParams.get('v')!
|
time: url.searchParams.has('t') ? parseYouTubeURLTimeString(url.searchParams.get('t')!) : null,
|
||||||
const type: ResolveUrlTypes = 'video'
|
type: 'video'
|
||||||
params.push({ id, type })
|
|
||||||
datas[id] = { url, type }
|
|
||||||
}
|
}
|
||||||
else if (url.pathname.startsWith('/channel/')) {
|
}
|
||||||
const id = url.pathname.substring("/channel/".length)
|
else if (url.pathname.startsWith('/channel/')) {
|
||||||
const type: ResolveUrlTypes = 'channel'
|
return {
|
||||||
params.push({ id, type })
|
id: url.pathname.substring("/channel/".length),
|
||||||
datas[id] = { url, type }
|
platform,
|
||||||
|
time: null,
|
||||||
|
type: 'channel'
|
||||||
}
|
}
|
||||||
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
|
else if (url.pathname.startsWith('/c/') || url.pathname.startsWith('/user/')) {
|
||||||
// yt front end sucks anyway
|
// We have to download the page content again because these parts of the page are not responsive
|
||||||
const content = await (await fetch(location.href)).text()
|
// yt front end sucks anyway
|
||||||
const prefix = `https://www.youtube.com/feeds/videos.xml?channel_id=`
|
const content = await (await fetch(location.href)).text()
|
||||||
const suffix = `"`
|
const prefix = `https://www.youtube.com/feeds/videos.xml?channel_id=`
|
||||||
const startsAt = content.indexOf(prefix) + prefix.length
|
const suffix = `"`
|
||||||
const endsAt = content.indexOf(suffix, startsAt)
|
const startsAt = content.indexOf(prefix) + prefix.length
|
||||||
const id = content.substring(startsAt, endsAt)
|
const endsAt = content.indexOf(suffix, startsAt)
|
||||||
const type: ResolveUrlTypes = 'channel'
|
const id = content.substring(startsAt, endsAt)
|
||||||
params.push({ id, type })
|
return {
|
||||||
datas[id] = { url, type }
|
id,
|
||||||
|
platform,
|
||||||
|
time: null,
|
||||||
|
type: 'channel'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = Object.entries(await requestResolveById(params))
|
return null
|
||||||
const targets: Record<string, Target | null> = Object.fromEntries(results.map(([id, result]) => {
|
}
|
||||||
const data = datas[id]
|
|
||||||
|
|
||||||
if (!result) return [
|
async function getTargetsBySources(...sources: Source[]) {
|
||||||
data.url.href,
|
const params: Parameters<typeof requestResolveById>[0] = sources.map((source) => ({ id: source.id, type: source.type }))
|
||||||
null
|
const platform = targetPlatformSettings[settings.targetPlatform]
|
||||||
]
|
|
||||||
|
|
||||||
return [
|
const results = await requestResolveById(params)
|
||||||
data.url.href,
|
const targets: Record<string, Target | null> = Object.fromEntries(
|
||||||
{
|
sources.map((source) => {
|
||||||
type: data.type,
|
const result = results[source.id]
|
||||||
lbryPathname: result?.id,
|
if (!result) return [
|
||||||
platform,
|
source.id,
|
||||||
time: data.type === 'channel' ? null : (data.url.searchParams.has('t') ? parseYouTubeURLTimeString(data.url.searchParams.get('t')!) : null)
|
null
|
||||||
}
|
]
|
||||||
]
|
|
||||||
}))
|
return [
|
||||||
|
source.id,
|
||||||
|
{
|
||||||
|
type: result.type,
|
||||||
|
lbryPathname: result.id,
|
||||||
|
platform,
|
||||||
|
time: source.time
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
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
|
// 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> {
|
async function requestResolveById(...params: Parameters<typeof resolveById>): ReturnType<typeof resolveById> {
|
||||||
const json = await new Promise<string | null | 'error'>((resolve) => chrome.runtime.sendMessage({ json: JSON.stringify(params) }, resolve))
|
const json = await new Promise<string | null | 'error'>((resolve) => chrome.runtime.sendMessage({ json: JSON.stringify(params) }, resolve))
|
||||||
if (json === 'error')
|
if (json === 'error') {
|
||||||
{
|
|
||||||
console.error("Background error on:", params)
|
console.error("Background error on:", params)
|
||||||
throw new Error("Background error.")
|
throw new Error("Background error.")
|
||||||
}
|
}
|
||||||
|
@ -160,24 +169,28 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
while (true) {
|
while (true) {
|
||||||
await sleep(500)
|
await sleep(500)
|
||||||
|
|
||||||
const url: URL = (urlCache?.href === location.href) ? urlCache : new URL(location.href);
|
const url: URL = (urlCache?.href === location.href) ? urlCache : new URL(location.href)
|
||||||
|
const source = await getSourceByUrl(new URL(location.href))
|
||||||
|
if (!source) continue
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (settings.redirect) {
|
if (settings.redirect) {
|
||||||
const target = (await getTargetsByURL(url))[url.href]
|
const target = (await getTargetsBySources(source))[source.id]
|
||||||
if (!target) continue
|
if (!target) continue
|
||||||
if (url === urlCache) continue
|
if (url === urlCache) continue
|
||||||
|
|
||||||
const lbryURL = getLbryUrlByTarget(target)
|
const lbryURL = getLbryUrlByTarget(target)
|
||||||
|
|
||||||
findVideoElementAwait().then((videoElement) => {
|
// As soon as video play is ready and start playing, pause it.
|
||||||
|
findVideoElementAwait(source).then((videoElement) => {
|
||||||
videoElement.addEventListener('play', () => videoElement.pause(), { once: true })
|
videoElement.addEventListener('play', () => videoElement.pause(), { once: true })
|
||||||
videoElement.pause()
|
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 }))
|
||||||
// Its not gonna be able to replace anyway
|
// Replace is being used so browser doesnt start an empty window
|
||||||
// This was empty window doesnt stay open
|
// Its not gonna be able to replace anyway, since its a LBRY Uri
|
||||||
location.replace(lbryURL)
|
location.replace(lbryURL)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -188,30 +201,35 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (urlCache !== url) updateButton(null)
|
if (urlCache !== url) updateButton(null)
|
||||||
let target = (await getTargetsByURL(url))[url.href]
|
let target = (await getTargetsBySources(source))[source.id]
|
||||||
|
|
||||||
|
// There is no target found via API try to check Video Description for LBRY links.
|
||||||
if (!target) {
|
if (!target) {
|
||||||
const descriptionElement = document.querySelector(sourcePlatform.htmlQueries.videoDescription)
|
const descriptionElement = document.querySelector(source.platform.htmlQueries.videoDescription)
|
||||||
if (descriptionElement) {
|
if (descriptionElement) {
|
||||||
const anchors = Array.from(descriptionElement.querySelectorAll<HTMLAnchorElement>('a'))
|
const anchors = Array.from(descriptionElement.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
|
||||||
if (sourcePlatform === sourcePlatfromSettings['youtube.com']) {
|
|
||||||
|
// Extract real link from youtube's redirect link
|
||||||
|
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
|
||||||
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) {
|
||||||
target = {
|
target = {
|
||||||
lbryPathname: lbryURL.pathname.substring(1),
|
lbryPathname: lbryURL.pathname.substring(1),
|
||||||
time: null,
|
time: null,
|
||||||
type: 'video',
|
type: lbryURL.pathname.substring(1).includes('/') ? 'video' : 'channel',
|
||||||
platform: targetPlatformSettings[settings.targetPlatform]
|
platform: targetPlatformSettings[settings.targetPlatform]
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -219,14 +237,17 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target?.type === 'video') {
|
if (!target) updateButton(null)
|
||||||
const videoElement = document.querySelector<HTMLVideoElement>(sourcePlatform.htmlQueries.videoPlayer)
|
else {
|
||||||
if (videoElement) target.time = videoElement.currentTime > 3 && videoElement.currentTime < videoElement.duration - 1 ? videoElement.currentTime : null
|
// If target is a video target add timestampt to it
|
||||||
|
if (target.type === 'video') {
|
||||||
|
const videoElement = document.querySelector<HTMLVideoElement>(source.platform.htmlQueries.videoPlayer)
|
||||||
|
if (videoElement) target.time = videoElement.currentTime > 3 && videoElement.currentTime < videoElement.duration - 1 ? videoElement.currentTime : null
|
||||||
|
}
|
||||||
|
|
||||||
|
updateButton({ target, source })
|
||||||
}
|
}
|
||||||
|
|
||||||
// We run it anyway with null target to hide the button
|
|
||||||
updateButton(target)
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
|
@ -60,7 +60,6 @@ const targetPlatform = (o: {
|
||||||
displayName: string
|
displayName: string
|
||||||
theme: string
|
theme: string
|
||||||
button: {
|
button: {
|
||||||
text: string
|
|
||||||
icon: string
|
icon: string
|
||||||
style?:
|
style?:
|
||||||
{
|
{
|
||||||
|
@ -80,7 +79,6 @@ export const targetPlatformSettings = {
|
||||||
displayName: 'Madiator.com',
|
displayName: 'Madiator.com',
|
||||||
theme: 'linear-gradient(130deg, #499375, #43889d)',
|
theme: 'linear-gradient(130deg, #499375, #43889d)',
|
||||||
button: {
|
button: {
|
||||||
text: 'Watch on',
|
|
||||||
icon: chrome.runtime.getURL('assets/icons/lbry/madiator-logo.svg'),
|
icon: chrome.runtime.getURL('assets/icons/lbry/madiator-logo.svg'),
|
||||||
style: {
|
style: {
|
||||||
button: { flexDirection: 'row-reverse' },
|
button: { flexDirection: 'row-reverse' },
|
||||||
|
@ -93,7 +91,6 @@ export const targetPlatformSettings = {
|
||||||
displayName: 'Odysee',
|
displayName: 'Odysee',
|
||||||
theme: 'linear-gradient(130deg, #c63d59, #f77937)',
|
theme: 'linear-gradient(130deg, #c63d59, #f77937)',
|
||||||
button: {
|
button: {
|
||||||
text: 'Watch on Odysee',
|
|
||||||
icon: chrome.runtime.getURL('assets/icons/lbry/odysee-logo.svg')
|
icon: chrome.runtime.getURL('assets/icons/lbry/odysee-logo.svg')
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
@ -102,7 +99,6 @@ export const targetPlatformSettings = {
|
||||||
displayName: 'LBRY App',
|
displayName: 'LBRY App',
|
||||||
theme: 'linear-gradient(130deg, #499375, #43889d)',
|
theme: 'linear-gradient(130deg, #499375, #43889d)',
|
||||||
button: {
|
button: {
|
||||||
text: 'Watch on LBRY',
|
|
||||||
icon: chrome.runtime.getURL('assets/icons/lbry/lbry-logo.svg')
|
icon: chrome.runtime.getURL('assets/icons/lbry/lbry-logo.svg')
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
Loading…
Add table
Reference in a new issue