Skip to content

Commit

Permalink
Merge pull request #75 from DeepDoge/yewtube-added
Browse files Browse the repository at this point in the history
[Feature] Yewtube support added
  • Loading branch information
kodxana authored Dec 19, 2021
2 parents b17a702 + 8f30825 commit 279f7fa
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 114 deletions.
3 changes: 2 additions & 1 deletion src/common/components/ButtonRadio.sass
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
.ButtonRadio
display: flex
justify-content: center
flex-wrap: wrap
gap: .25em

.radio-button
@extend .button
margin: 6px

.radio-button.checked
@extend .button.active
Expand Down
81 changes: 64 additions & 17 deletions src/common/settings.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,76 @@
export type PlatformName = 'madiator.com' | 'odysee' | 'app'
export interface ExtensionSettings {
redirect: boolean
targetPlatform: TargetPlatformName
}

export const DEFAULT_SETTINGS: ExtensionSettings = { redirect: true, targetPlatform: 'odysee' };

export function getExtensionSettingsAsync<K extends Array<keyof ExtensionSettings>>(...keys: K): Promise<Pick<ExtensionSettings, K[number]>> {
return new Promise(resolve => chrome.storage.local.get(keys, o => resolve(o as any)));
}


export interface PlatformSettings
{

export type TargetPlatformName = 'madiator.com' | 'odysee' | 'app'
export interface TargetPlatformSettings {
domainPrefix: string
display: string
displayName: string
theme: string
}

export const platformSettings: Record<PlatformName, PlatformSettings> = {
'madiator.com': { domainPrefix: 'https://madiator.com/', display: 'Madiator.com', theme: '#075656' },
odysee: { domainPrefix: 'https://odysee.com/', display: 'Odysee', theme: '#1e013b' },
app: { domainPrefix: 'lbry://', display: 'App', theme: '#075656' },
export const TargetPlatformSettings: Record<TargetPlatformName, TargetPlatformSettings> = {
'madiator.com': {
domainPrefix: 'https://madiator.com/',
displayName: 'Madiator.com',
theme: '#075656'
},
odysee: {
domainPrefix: 'https://odysee.com/',
displayName: 'Odysee',
theme: '#1e013b'
},
app: {
domainPrefix: 'lbry://',
displayName: 'LBRY App',
theme: '#075656'
},
};

export const getPlatfromSettingsEntiries = () => {
return Object.entries(platformSettings) as any as [Extract<keyof typeof platformSettings, string>, PlatformSettings][]
export const getTargetPlatfromSettingsEntiries = () => {
return Object.entries(TargetPlatformSettings) as any as [Extract<keyof typeof TargetPlatformSettings, string>, TargetPlatformSettings][]
}

export interface LbrySettings {
enabled: boolean
platform: PlatformName
}

export const DEFAULT_SETTINGS: LbrySettings = { enabled: true, platform: 'odysee' };

export function getSettingsAsync<K extends Array<keyof LbrySettings>>(...keys: K): Promise<Pick<LbrySettings, K[number]>> {
return new Promise(resolve => chrome.storage.local.get(keys, o => resolve(o as any)));
export type SourcePlatfromName = 'youtube.com' | 'yewtu.be'
export interface SourcePlatfromSettings {
hostnames: string[]
htmlQueries: {
mountButtonBefore: string,
videoPlayer: string
}
}

export const SourcePlatfromSettings: Record<SourcePlatfromName, SourcePlatfromSettings> = {
"yewtu.be": {
hostnames: ['yewtu.be'],
htmlQueries: {
mountButtonBefore: '#watch-on-youtube',
videoPlayer: '#player-container video'
}
},
"youtube.com": {
hostnames: ['www.youtube.com'],
htmlQueries: {
mountButtonBefore: 'ytd-video-owner-renderer~#subscribe-button',
videoPlayer: '#ytd-player video'
}
}
}

export function getSourcePlatfromSettingsFromHostname(hostname: string) {
const values = Object.values(SourcePlatfromSettings)
for (const settings of values)
if (settings.hostnames.includes(hostname)) return settings
return null
}
13 changes: 6 additions & 7 deletions src/common/style.sass
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,29 @@ $btn-color: #075656 !default
$btn-select: teal !default

body
width: 400px
width: 30em
text-align: center
background-color: $background-color
color: $text-color
font-family: sans-serif
padding: 1em

.container
display: block
text-align: center
margin: 0 32px
margin-bottom: 15px

.button
border-radius: 5px
border-radius: .5em
background-color: $btn-color
border: 4px solid $btn-color
border: .2em solid $btn-color
color: $text-color
font-size: 0.8rem
font-weight: 400
padding: 4px 15px
padding: .5em
text-align: center

&.active
border: 4px solid $btn-select
border-color: $btn-select

&:focus
outline: none
6 changes: 3 additions & 3 deletions src/common/yt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface YtResolverResponse {
};
}

interface YtSubscription {
interface YtExportedJsonSubscription {
id: string;
etag: string;
title: string;
Expand Down Expand Up @@ -72,7 +72,7 @@ export const ytService = {
* @returns the channel IDs
*/
readJson(jsonContents: string): string[] {
const subscriptions: YtSubscription[] = JSON.parse(jsonContents);
const subscriptions: YtExportedJsonSubscription[] = JSON.parse(jsonContents);
jsonContents = ''
return subscriptions.map(sub => sub.snippet.resourceId.channelId);
},
Expand All @@ -86,7 +86,7 @@ export const ytService = {
readCsv(csvContent: string): string[] {
const rows = csvContent.split('\n')
csvContent = ''
return rows.map((row) => row.substr(0, row.indexOf(',')))
return rows.map((row) => row.substring(0, row.indexOf(',')))
},

/**
Expand Down
6 changes: 3 additions & 3 deletions src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"version": "1.7.5",
"permissions": [
"https://www.youtube.com/",
"https://invidio.us/channel/*",
"https://invidio.us/watch?v=*",
"https://yewtu.be/",
"https://api.odysee.com/*",
"https://lbry.tv/*",
"https://odysee.com/*",
Expand All @@ -15,7 +14,8 @@
"content_scripts": [
{
"matches": [
"https://www.youtube.com/*"
"https://www.youtube.com/*",
"https://yewtu.be/*"
],
"js": [
"scripts/ytContent.js"
Expand Down
11 changes: 10 additions & 1 deletion src/popup/popup.sass
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
.radio-label
font-size: 1.1rem
margin: 15px auto
display: block

.container
display: grid
grid-auto-flow: row
gap: 1.5em

.container > section
display: grid
grid-auto-flow: row
gap: 1em
38 changes: 21 additions & 17 deletions src/popup/popup.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
import { h, render } from 'preact'
import ButtonRadio, { SelectionOption } from '../common/components/ButtonRadio'
import { getPlatfromSettingsEntiries, LbrySettings, PlatformName } from '../common/settings'
import { getTargetPlatfromSettingsEntiries, ExtensionSettings, TargetPlatformName } from '../common/settings'
import { useLbrySettings } from '../common/useSettings'
import './popup.sass'



/** Utilty to set a setting in the browser */
const setSetting = <K extends keyof LbrySettings>(setting: K, value: LbrySettings[K]) => chrome.storage.local.set({ [setting]: value });
const setSetting = <K extends keyof ExtensionSettings>(setting: K, value: ExtensionSettings[K]) => chrome.storage.local.set({ [setting]: value });

/** Gets all the options for redirect destinations as selection options */
const platformOptions: SelectionOption[] = getPlatfromSettingsEntiries()
.map(([value, { display }]) => ({ value, display }));
const platformOptions: SelectionOption[] = getTargetPlatfromSettingsEntiries()
.map(([value, { displayName: display }]) => ({ value, display }));

function WatchOnLbryPopup() {
const { enabled, platform } = useLbrySettings();
const { redirect, targetPlatform } = useLbrySettings();

return <div className='container'>
<label className='radio-label'>Enable Redirection:</label>
<ButtonRadio value={enabled ? 'YES' : 'NO'} options={['YES', 'NO']}
onChange={enabled => setSetting('enabled', enabled.toLowerCase() === 'yes')} />
<label className='radio-label'>Where would you like to redirect?</label>
<ButtonRadio value={platform} options={platformOptions}
onChange={(platform: PlatformName) => setSetting('platform', platform)} />
<label className='radio-label'>Other useful tools:</label>
<a href='/tools/YTtoLBRY.html' target='_blank'>
<button type='button' className='btn1 button is-primary'>Subscriptions Converter</button>
</a>
<section>
<label className='radio-label'>Enable Redirection:</label>
<ButtonRadio value={redirect ? 'YES' : 'NO'} options={['YES', 'NO']}
onChange={redirect => setSetting('redirect', redirect.toLowerCase() === 'yes')} />
</section>
<section>
<label className='radio-label'>Where would you like to redirect?</label>
<ButtonRadio value={targetPlatform} options={platformOptions}
onChange={(platform: TargetPlatformName) => setSetting('targetPlatform', platform)} />
</section>
<section>
<label className='radio-label'>Other useful tools:</label>
<a href='/tools/YTtoLBRY.html' target='_blank'>
<button type='button' className='btn1 button is-primary'>Subscriptions Converter</button>
</a>
</section>
</div>;
}

Expand Down
12 changes: 6 additions & 6 deletions src/scripts/storageSetup.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { DEFAULT_SETTINGS, LbrySettings, getSettingsAsync } from '../common/settings';
import { DEFAULT_SETTINGS, ExtensionSettings, getExtensionSettingsAsync } from '../common/settings';

/** Reset settings to default value and update the browser badge text */
async function initSettings() {
const settings = await getSettingsAsync(...Object.keys(DEFAULT_SETTINGS) as Array<keyof LbrySettings>);
const settings = await getExtensionSettingsAsync(...Object.keys(DEFAULT_SETTINGS) as Array<keyof ExtensionSettings>);

// get all the values that aren't set and use them as a change set
const invalidEntries = (Object.entries(DEFAULT_SETTINGS) as Array<[keyof LbrySettings, LbrySettings[keyof LbrySettings]]>)
const invalidEntries = (Object.entries(DEFAULT_SETTINGS) as Array<[keyof ExtensionSettings, ExtensionSettings[keyof ExtensionSettings]]>)
.filter(([k]) => settings[k] === null || settings[k] === undefined);

// fix our local var and set it in storage for later
Expand All @@ -15,12 +15,12 @@ async function initSettings() {
chrome.storage.local.set(changeSet);
}

chrome.browserAction.setBadgeText({ text: settings.enabled ? 'ON' : 'OFF' });
chrome.browserAction.setBadgeText({ text: settings.redirect ? 'ON' : 'OFF' });
}

chrome.storage.onChanged.addListener((changes, areaName) => {
if (areaName !== 'local' || !changes.enabled) return;
chrome.browserAction.setBadgeText({ text: changes.enabled.newValue ? 'ON' : 'OFF' });
if (areaName !== 'local' || !changes.redirect) return;
chrome.browserAction.setBadgeText({ text: changes.redirect.newValue ? 'ON' : 'OFF' });
});


Expand Down
39 changes: 21 additions & 18 deletions src/scripts/tabOnUpdated.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { appRedirectUrl, parseProtocolUrl } from '../common/lbry-url'
import { getSettingsAsync, PlatformName } from '../common/settings'
import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, TargetPlatformName } from '../common/settings'
import { YTDescriptor, ytService } from '../common/yt'
export interface UpdateContext {
descriptor: YTDescriptor
/** LBRY URL fragment */
pathname: string
enabled: boolean
platform: PlatformName
lbryPathname: string
redirect: boolean
targetPlatform: TargetPlatformName
}

async function resolveYT(descriptor: YTDescriptor) {
Expand All @@ -16,26 +16,31 @@ async function resolveYT(descriptor: YTDescriptor) {
return segments.join('/');
}

const pathnameCache: Record<string, string | undefined> = {};
const lbryPathnameCache: Record<string, string | undefined> = {};

async function ctxFromURL(url: string): Promise<UpdateContext | void> {
if (!url || !(url.startsWith('https://www.youtube.com/watch?v=') || url.startsWith('https://www.youtube.com/channel/'))) return;
url = new URL(url).href;
const { enabled, platform } = await getSettingsAsync('enabled', 'platform');
const descriptor = ytService.getId(url);
async function ctxFromURL(href: string): Promise<UpdateContext | void> {
if (!href) return;

const url = new URL(href);
if (!getSourcePlatfromSettingsFromHostname(url.hostname)) return
if (url.pathname.startsWith('/watch?')) return
if (url.pathname.startsWith('/channel?')) return

const { redirect, targetPlatform } = await getExtensionSettingsAsync('redirect', 'targetPlatform');
const descriptor = ytService.getId(href);
if (!descriptor) return; // couldn't get the ID, so we're done

const res = url in pathnameCache ? pathnameCache[url] : await resolveYT(descriptor);
pathnameCache[url] = res;
const res = href in lbryPathnameCache ? lbryPathnameCache[href] : await resolveYT(descriptor);
lbryPathnameCache[href] = res;
if (!res) return; // couldn't find it on lbry, so we're done

return { descriptor, pathname: res, enabled, platform };
return { descriptor, lbryPathname: res, redirect, targetPlatform };
}

// handles lbry.tv -> lbry app redirect
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, { url: tabUrl }) => {
const { enabled, platform } = await getSettingsAsync('enabled', 'platform');
if (!enabled || platform !== 'app' || !changeInfo.url || !tabUrl?.startsWith('https://odysee.com/')) return;
const { redirect, targetPlatform } = await getExtensionSettingsAsync('redirect', 'targetPlatform');
if (!redirect || targetPlatform !== 'app' || !changeInfo.url || !tabUrl?.startsWith('https://odysee.com/')) return;

const url = appRedirectUrl(tabUrl, { encode: true });
if (!url) return;
Expand All @@ -61,7 +66,5 @@ chrome.runtime.onMessage.addListener(({ url }: { url: string }, sender, sendResp

// relay youtube link changes to the content script
chrome.tabs.onUpdated.addListener((tabId, changeInfo, { url }) => {
if (!changeInfo.url || !url || !(url.startsWith('https://www.youtube.com/watch?v=') || url.startsWith('https://www.youtube.com/channel/'))) return;

ctxFromURL(url).then(ctx => chrome.tabs.sendMessage(tabId, ctx));
if (url) ctxFromURL(url).then(ctx => chrome.tabs.sendMessage(tabId, ctx));
});
Loading

0 comments on commit 279f7fa

Please sign in to comment.