From a6472799a1ec0954575b5e006aa8a98bc0f2c845 Mon Sep 17 00:00:00 2001
From: Shiba <44804845+DeepDoge@users.noreply.github.com>
Date: Sun, 24 Jul 2022 20:41:51 +0000
Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=8D=99=20Bug=20fixes,=20and=20other?=
=?UTF-8?q?=20fixes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/scripts/ytContent.tsx | 195 +++++++++++++++++++++-----------------
src/settings/index.ts | 4 -
2 files changed, 108 insertions(+), 91 deletions(-)
diff --git a/src/scripts/ytContent.tsx b/src/scripts/ytContent.tsx
index e4294cc..86d1f7b 100644
--- a/src/scripts/ytContent.tsx
+++ b/src/scripts/ytContent.tsx
@@ -13,10 +13,14 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
time: number | null
}
- const sourcePlatform = getSourcePlatfromSettingsFromHostname(new URL(location.href).hostname)
- if (!sourcePlatform) return
- const targetPlatforms = getTargetPlatfromSettingsEntiries()
+ interface Source {
+ platform: SourcePlatform
+ id: string
+ type: ResolveUrlTypes
+ time: number | null
+ }
+ const targetPlatforms = getTargetPlatfromSettingsEntiries()
const settings = await getExtensionSettingsAsync()
// Listen Settings Change
chrome.storage.onChanged.addListener(async (changes, areaName) => {
@@ -27,8 +31,8 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
const mountPoint = document.createElement('div')
mountPoint.style.display = 'flex'
- function WatchOnLbryButton({ target }: { target?: Target }) {
- if (!target) return null
+ function WatchOnLbryButton({ source, target }: { source?: Source, target?: Target }) {
+ if (!target || !source) return null
const url = getLbryUrlByTarget(target)
return
@@ -45,104 +49,109 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
border: '0',
color: 'whitesmoke',
padding: '10px 16px',
- marginRight: target.type === 'channel' ? '10px' : '4px',
+ marginRight: source?.type === 'channel' ? '10px' : '4px',
fontSize: '14px',
textDecoration: 'none',
...target.platform.button.style?.button,
}}
- onClick={() => findVideoElementAwait().then((videoElement) => {
+ onClick={() => findVideoElementAwait(source).then((videoElement) => {
videoElement.pause()
})}
>

-
{target.platform.button.text}
+
{target.type === 'channel' ? 'Channel on' : 'Watch on'} {target.platform.displayName}
}
- function updateButton(target: Target | null): void {
- if (!target) return render(, mountPoint)
+ function updateButton(params: { source: Source, target: Target } | null): void {
+ if (!params) return render(, mountPoint)
- const sourcePlatform = getSourcePlatfromSettingsFromHostname(new URL(location.href).hostname)
- if (!sourcePlatform) return render(, mountPoint)
-
- const mountBefore = document.querySelector(sourcePlatform.htmlQueries.mountButtonBefore[target.type])
+ const mountBefore = document.querySelector(params.source.platform.htmlQueries.mountButtonBefore[params.source.type])
if (!mountBefore) return render(, mountPoint)
mountBefore.parentElement?.insertBefore(mountPoint, mountBefore)
- render(, mountPoint)
+ render(, mountPoint)
}
- async function findVideoElementAwait() {
- const sourcePlatform = getSourcePlatfromSettingsFromHostname(new URL(location.href).hostname)
- if (!sourcePlatform) throw new Error(`Unknown source of: ${location.href}`)
+ async function findVideoElementAwait(source: Source) {
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
}
- async function getTargetsByURL(...urls: URL[]) {
- const params: Parameters[0] = []
- const platform = targetPlatformSettings[settings.targetPlatform]
+ async function getSourceByUrl(url: URL): Promise {
+ const platform = getSourcePlatfromSettingsFromHostname(new URL(location.href).hostname)
+ if (!platform) return null
- const datas: Record = {}
-
- for (const url of urls) {
- if (url.pathname === '/watch' && url.searchParams.has('v')) {
- const id = url.searchParams.get('v')!
- const type: ResolveUrlTypes = 'video'
- params.push({ id, type })
- datas[id] = { url, type }
+ if (url.pathname === '/watch' && url.searchParams.has('v')) {
+ return {
+ id: url.searchParams.get('v')!,
+ platform,
+ time: url.searchParams.has('t') ? parseYouTubeURLTimeString(url.searchParams.get('t')!) : null,
+ type: 'video'
}
- else if (url.pathname.startsWith('/channel/')) {
- const id = url.pathname.substring("/channel/".length)
- const type: ResolveUrlTypes = 'channel'
- params.push({ id, type })
- datas[id] = { url, type }
+ }
+ else if (url.pathname.startsWith('/channel/')) {
+ return {
+ id: url.pathname.substring("/channel/".length),
+ 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
- // yt front end sucks anyway
- const content = await (await fetch(location.href)).text()
- const prefix = `https://www.youtube.com/feeds/videos.xml?channel_id=`
- const suffix = `"`
- const startsAt = content.indexOf(prefix) + prefix.length
- const endsAt = content.indexOf(suffix, startsAt)
- const id = content.substring(startsAt, endsAt)
- const type: ResolveUrlTypes = 'channel'
- params.push({ id, type })
- datas[id] = { url, type }
+ }
+ 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
+ // yt front end sucks anyway
+ const content = await (await fetch(location.href)).text()
+ const prefix = `https://www.youtube.com/feeds/videos.xml?channel_id=`
+ const suffix = `"`
+ const startsAt = content.indexOf(prefix) + prefix.length
+ const endsAt = content.indexOf(suffix, startsAt)
+ const id = content.substring(startsAt, endsAt)
+ return {
+ id,
+ platform,
+ time: null,
+ type: 'channel'
}
}
- const results = Object.entries(await requestResolveById(params))
- const targets: Record = Object.fromEntries(results.map(([id, result]) => {
- const data = datas[id]
+ return null
+ }
- if (!result) return [
- data.url.href,
- null
- ]
+ async function getTargetsBySources(...sources: Source[]) {
+ const params: Parameters[0] = sources.map((source) => ({ id: source.id, type: source.type }))
+ const platform = targetPlatformSettings[settings.targetPlatform]
- return [
- data.url.href,
- {
- type: data.type,
- lbryPathname: result?.id,
- platform,
- time: data.type === 'channel' ? null : (data.url.searchParams.has('t') ? parseYouTubeURLTimeString(data.url.searchParams.get('t')!) : null)
- }
- ]
- }))
+ const results = await requestResolveById(params)
+ const targets: Record = Object.fromEntries(
+ sources.map((source) => {
+ const result = results[source.id]
+ if (!result) return [
+ source.id,
+ null
+ ]
+
+ return [
+ source.id,
+ {
+ type: result.type,
+ lbryPathname: result.id,
+ platform,
+ time: source.time
+ }
+ ]
+ })
+ )
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): ReturnType {
const json = await new Promise((resolve) => chrome.runtime.sendMessage({ json: JSON.stringify(params) }, resolve))
- if (json === 'error')
- {
+ if (json === 'error') {
console.error("Background error on:", params)
throw new Error("Background error.")
}
@@ -160,24 +169,28 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
while (true) {
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 {
if (settings.redirect) {
- const target = (await getTargetsByURL(url))[url.href]
+ const target = (await getTargetsBySources(source))[source.id]
if (!target) continue
if (url === urlCache) continue
-
+
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.pause()
})
-
+
if (target.platform === targetPlatformSettings.app) {
if (document.hidden) await new Promise((resolve) => document.addEventListener('visibilitychange', resolve, { once: true }))
- // Its not gonna be able to replace anyway
- // This was empty window doesnt stay open
+ // Replace is being used so browser doesnt start an empty window
+ // Its not gonna be able to replace anyway, since its a LBRY Uri
location.replace(lbryURL)
}
else {
@@ -188,30 +201,35 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
}
else {
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) {
- const descriptionElement = document.querySelector(sourcePlatform.htmlQueries.videoDescription)
+ const descriptionElement = document.querySelector(source.platform.htmlQueries.videoDescription)
if (descriptionElement) {
const anchors = Array.from(descriptionElement.querySelectorAll('a'))
-
+
for (const anchor of anchors) {
if (!anchor.href) continue
const url = new URL(anchor.href)
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
lbryURL = new URL(url.searchParams.get('q')!)
}
+ // Just directly use the link itself on other platforms
else {
if (!targetPlatforms.some(([key, platform]) => url.href.startsWith(platform.domainPrefix))) continue
lbryURL = new URL(url.href)
}
-
+
if (lbryURL) {
target = {
lbryPathname: lbryURL.pathname.substring(1),
time: null,
- type: 'video',
+ type: lbryURL.pathname.substring(1).includes('/') ? 'video' : 'channel',
platform: targetPlatformSettings[settings.targetPlatform]
}
break
@@ -219,14 +237,17 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
}
}
}
-
- if (target?.type === 'video') {
- const videoElement = document.querySelector(sourcePlatform.htmlQueries.videoPlayer)
- if (videoElement) target.time = videoElement.currentTime > 3 && videoElement.currentTime < videoElement.duration - 1 ? videoElement.currentTime : null
+
+ if (!target) updateButton(null)
+ else {
+ // If target is a video target add timestampt to it
+ if (target.type === 'video') {
+ const videoElement = document.querySelector(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) {
console.error(error)
diff --git a/src/settings/index.ts b/src/settings/index.ts
index 017bcdd..26698f2 100644
--- a/src/settings/index.ts
+++ b/src/settings/index.ts
@@ -60,7 +60,6 @@ const targetPlatform = (o: {
displayName: string
theme: string
button: {
- text: string
icon: string
style?:
{
@@ -80,7 +79,6 @@ export const targetPlatformSettings = {
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' },
@@ -93,7 +91,6 @@ export const targetPlatformSettings = {
displayName: 'Odysee',
theme: 'linear-gradient(130deg, #c63d59, #f77937)',
button: {
- text: 'Watch on Odysee',
icon: chrome.runtime.getURL('assets/icons/lbry/odysee-logo.svg')
}
}),
@@ -102,7 +99,6 @@ export const targetPlatformSettings = {
displayName: 'LBRY App',
theme: 'linear-gradient(130deg, #499375, #43889d)',
button: {
- text: 'Watch on LBRY',
icon: chrome.runtime.getURL('assets/icons/lbry/lbry-logo.svg')
}
}),
From 29c9b2829a1f9a6850f750e08c82e4af3a3438c9 Mon Sep 17 00:00:00 2001
From: Shiba <44804845+DeepDoge@users.noreply.github.com>
Date: Sun, 24 Jul 2022 23:18:34 +0000
Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=8D=A3=20New=20button=20on=20the=20vi?=
=?UTF-8?q?deo=20player?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/scripts/ytContent.tsx | 77 +++++++++++++++++++++++++++++++++------
src/settings/index.ts | 36 +++++++++++++-----
2 files changed, 92 insertions(+), 21 deletions(-)
diff --git a/src/scripts/ytContent.tsx b/src/scripts/ytContent.tsx
index 86d1f7b..bc8e9e6 100644
--- a/src/scripts/ytContent.tsx
+++ b/src/scripts/ytContent.tsx
@@ -28,8 +28,11 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
Object.assign(settings, Object.fromEntries(Object.entries(changes).map(([key, change]) => [key, change.newValue])))
})
- const mountPoint = document.createElement('div')
- mountPoint.style.display = 'flex'
+ const buttonMountPoint = document.createElement('div')
+ buttonMountPoint.style.display = 'flex'
+
+ const playerButtonMountPoint = document.createElement('div')
+ playerButtonMountPoint.style.display = 'flex'
function WatchOnLbryButton({ source, target }: { source?: Source, target?: Target }) {
if (!target || !source) return null
@@ -60,19 +63,65 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
>
- {target.type === 'channel' ? 'Channel on' : 'Watch on'} {target.platform.displayName}
+ {target.type === 'channel' ? 'Channel on' : 'Watch on'} {target.platform.button.platformNameText}
+
+
+ }
+
+ function WatchOnLbryPlayerButton({ source, target }: { source?: Source, target?: Target }) {
+ if (!target || !source) return null
+ const url = getLbryUrlByTarget(target)
+
+ return
}
function updateButton(params: { source: Source, target: Target } | null): void {
- if (!params) return render(, mountPoint)
+ if (!params) {
+ render(, buttonMountPoint)
+ render(, playerButtonMountPoint)
+ return
+ }
- const mountBefore = document.querySelector(params.source.platform.htmlQueries.mountButtonBefore[params.source.type])
- if (!mountBefore) return render(, mountPoint)
+ const mountPlayerButtonBefore = params.source.platform.htmlQueries.mountPoints.mountPlayerButtonBefore ?
+ document.querySelector(params.source.platform.htmlQueries.mountPoints.mountPlayerButtonBefore) :
+ null
+ if (!mountPlayerButtonBefore) render(, playerButtonMountPoint)
+ else {
+ if (mountPlayerButtonBefore.previousSibling !== playerButtonMountPoint)
+ mountPlayerButtonBefore.parentElement?.insertBefore(playerButtonMountPoint, mountPlayerButtonBefore)
+ render(, playerButtonMountPoint)
+ }
- mountBefore.parentElement?.insertBefore(mountPoint, mountBefore)
- render(, mountPoint)
+ const mountButtonBefore = document.querySelector(params.source.platform.htmlQueries.mountPoints.mountButtonBefore[params.source.type])
+ if (!mountButtonBefore) render(, playerButtonMountPoint)
+ else {
+ if (mountButtonBefore.previousSibling !== buttonMountPoint)
+ mountButtonBefore.parentElement?.insertBefore(buttonMountPoint, mountButtonBefore)
+ render(, buttonMountPoint)
+ }
}
async function findVideoElementAwait(source: Source) {
@@ -205,9 +254,15 @@ import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, getTa
// There is no target found via API try to check Video Description for LBRY links.
if (!target) {
- const descriptionElement = document.querySelector(source.platform.htmlQueries.videoDescription)
- if (descriptionElement) {
- const anchors = Array.from(descriptionElement.querySelectorAll('a'))
+ const linksContainer =
+ source.type === 'video' ?
+ document.querySelector(source.platform.htmlQueries.videoDescription) :
+ source.platform.htmlQueries.channelLinks ? document.querySelector(source.platform.htmlQueries.channelLinks) : null
+
+ console.log(linksContainer)
+
+ if (linksContainer) {
+ const anchors = Array.from(linksContainer.querySelectorAll('a'))
for (const anchor of anchors) {
if (!anchor.href) continue
diff --git a/src/settings/index.ts b/src/settings/index.ts
index 26698f2..e71dd40 100644
--- a/src/settings/index.ts
+++ b/src/settings/index.ts
@@ -60,6 +60,7 @@ const targetPlatform = (o: {
displayName: string
theme: string
button: {
+ platformNameText: string,
icon: string
style?:
{
@@ -79,6 +80,7 @@ export const targetPlatformSettings = {
displayName: 'Madiator.com',
theme: 'linear-gradient(130deg, #499375, #43889d)',
button: {
+ platformNameText: '',
icon: chrome.runtime.getURL('assets/icons/lbry/madiator-logo.svg'),
style: {
button: { flexDirection: 'row-reverse' },
@@ -91,6 +93,7 @@ export const targetPlatformSettings = {
displayName: 'Odysee',
theme: 'linear-gradient(130deg, #c63d59, #f77937)',
button: {
+ platformNameText: 'Odysee',
icon: chrome.runtime.getURL('assets/icons/lbry/odysee-logo.svg')
}
}),
@@ -99,6 +102,7 @@ export const targetPlatformSettings = {
displayName: 'LBRY App',
theme: 'linear-gradient(130deg, #499375, #43889d)',
button: {
+ platformNameText: 'LBRY',
icon: chrome.runtime.getURL('assets/icons/lbry/lbry-logo.svg')
}
}),
@@ -109,9 +113,13 @@ export const targetPlatformSettings = {
const sourcePlatform = (o: {
hostnames: string[]
htmlQueries: {
- mountButtonBefore: Record,
+ mountPoints: {
+ mountButtonBefore: Record,
+ mountPlayerButtonBefore: string | null,
+ }
videoPlayer: string,
videoDescription: string
+ channelLinks: string | null
}
}) => o
export type SourcePlatform = ReturnType
@@ -126,24 +134,32 @@ export const sourcePlatfromSettings = {
"youtube.com": sourcePlatform({
hostnames: ['www.youtube.com'],
htmlQueries: {
- mountButtonBefore: {
- video: 'ytd-video-owner-renderer~#subscribe-button',
- channel: '#channel-header-container #buttons'
+ mountPoints: {
+ mountButtonBefore: {
+ video: 'ytd-video-owner-renderer~#subscribe-button',
+ channel: '#channel-header-container #buttons'
+ },
+ mountPlayerButtonBefore: 'ytd-player .ytp-right-controls',
},
videoPlayer: '#ytd-player video',
- videoDescription: 'ytd-video-secondary-info-renderer #description'
+ videoDescription: 'ytd-video-secondary-info-renderer #description',
+ channelLinks: '#channel-header #links-holder'
}
}),
"yewtu.be": sourcePlatform({
hostnames: ['yewtu.be', 'vid.puffyan.us', 'invidio.xamh.de', 'invidious.kavin.rocks'],
htmlQueries: {
- mountButtonBefore:
- {
- video: '#watch-on-youtube',
- channel: '#subscribe'
+ mountPoints: {
+ mountButtonBefore:
+ {
+ video: '#watch-on-youtube',
+ channel: '#subscribe'
+ },
+ mountPlayerButtonBefore: null,
},
videoPlayer: '#player-container video',
- videoDescription: '#descriptionWrapper'
+ videoDescription: '#descriptionWrapper',
+ channelLinks: null
}
})
}