From c676af242a77a6bcd69bf4aac816ee4e04c64651 Mon Sep 17 00:00:00 2001 From: Booteille Date: Sun, 11 Nov 2018 16:29:38 +0100 Subject: [PATCH 1/4] Redirect before calling Youtube Add the ability to call Invidious API and use it to prevent any call to youtube servers. Refactor some methods and move them in util.js. --- extension/manifest.json | 4 +++- src/background.ts | 39 +++++++++++++++++++++++++++++++++++++++ src/constants.ts | 7 ++++++- src/invidious-api.ts | 26 ++++++++++++++++++++++++++ src/util.ts | 9 +++++++++ src/youtube.ts | 17 ++--------------- 6 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 src/invidious-api.ts diff --git a/extension/manifest.json b/extension/manifest.json index 961706a..63148d7 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -12,7 +12,9 @@ "permissions": [ "activeTab", "storage", - "" + "", + "webRequest", + "webRequestBlocking" ], "background": { "scripts": ["dist/background.js", "dist/vendors.js"] diff --git a/src/background.ts b/src/background.ts index 6e60c17..1530514 100644 --- a/src/background.ts +++ b/src/background.ts @@ -19,6 +19,39 @@ import * as browser from 'webextension-polyfill'; import { MessageKind } from './types'; import constants from './constants'; +import Preferences from './preferences'; +import InvidiousAPI from './invidious-api'; +import { getPeertubeVideoURL } from './util'; + +/** + * Retrieve the title of the video using Invidious API. + */ +const getTitle = async (id: string) => { + let data = await InvidiousAPI.getVideo(id); + return data.title; +} + +/** + * Prevent Youtube from loading if the video is found and preferences set on + * automatic redirection. + */ +const preventYoutube = async (r) => { + const prefs = await Preferences.getPreferences(); + + if (prefs.redirectYoutube === 'Auto') { + const query = new URLSearchParams(r.url.substring(r.url.indexOf('?') + 1)); + + let title = await getTitle(query.get('v')); + let video = await searchByName(title); + let url = getPeertubeVideoURL(video, prefs); + + return { + redirectUrl: url + }; + } + + return {}; +}; const buildSearchByNameURL = (instance: string, query: string): string => `https://${instance}/api/v1/search/videos?search=${encodeURIComponent(query)}`; @@ -58,3 +91,9 @@ browser.runtime.onMessage.addListener(function(message, sender) { return searchByID(message.id); } }); + +browser.webRequest.onBeforeRequest.addListener( + preventYoutube, + {urls: ['*://*.youtube.com/watch?v=*']}, + ['blocking'] +); diff --git a/src/constants.ts b/src/constants.ts index 2996c9c..15be776 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,3 +1,8 @@ export default { - defaultInstance: 'peertube.social' + defaultInstance: 'peertube.social', + invidiousAPI: { + url: 'https://invidio.us/api/v1', + videos: 'videos', + channels: 'channels' + } }; diff --git a/src/invidious-api.ts b/src/invidious-api.ts new file mode 100644 index 0000000..767796f --- /dev/null +++ b/src/invidious-api.ts @@ -0,0 +1,26 @@ +import * as _ from 'lodash/fp'; +import constants from './constants'; + +export default class InvidiousAPI { + static async _fetchAPI(action: string, params: any) { + let paramString = typeof params == 'string' + ? params + : Object.keys(params).map(function(key){ + return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); + }).join('&'); + + return fetch(`${constants.invidiousAPI.url}/${action}/${paramString}`) + .then(res => res.json()) + .catch(e => console.error( + 'An error occured while trying to fetch API used by PeerTubeify: ' + + e.message + )); + } + static async getVideo(id: string) { + return this._fetchAPI(constants.invidiousAPI.videos, id); + } + + static async getChannel(ucid: string) { + return this._fetchAPI(constants.invidiousAPI.channels, ucid); + } +} diff --git a/src/util.ts b/src/util.ts index f768668..956b162 100644 --- a/src/util.ts +++ b/src/util.ts @@ -3,3 +3,12 @@ export function htmlToElement(html: string): Element { template.innerHTML = html.trim(); return template.content.firstElementChild; } + +export function getPeertubeVideoURL(video, prefs) { + return `https://${getPeertubeHost(video.account.host, prefs)}/videos/watch/${video.uuid}` +} + +export function getPeertubeHost(host, prefs) +{ + return prefs.openInOriginalInstance ? host : prefs.searchInstance; +} diff --git a/src/youtube.ts b/src/youtube.ts index 3b86918..7fe2f57 100644 --- a/src/youtube.ts +++ b/src/youtube.ts @@ -17,11 +17,10 @@ import * as _ from 'lodash/fp'; import * as browser from 'webextension-polyfill'; -import { htmlToElement } from './util'; +import { htmlToElement, getPeertubeVideoURL } from './util'; import { MessageKind, RedirectType } from './types'; import Preferences from './preferences' -const watchURL = (host, uuid) => `https://${host}/videos/watch/${uuid}`; const thumbnailURL = (host, path) => `https://${host}${path}`; const LINK_ID = 'peertube-link'; @@ -33,10 +32,6 @@ function searchVideo(query) { }); } -function getCorrectURL(video: any, prefs: Preferences) { - return watchURL(prefs.openInOriginalInstance ? video.account.host : prefs.searchInstance, video.uuid) -} - async function peertubeify(query: String) { const prefs = await Preferences.getPreferences(); @@ -44,7 +39,7 @@ async function peertubeify(query: String) { case RedirectType.Show: { searchVideo(query) .then(async video => { - const url = getCorrectURL(video, prefs) + const url = getPeertubeVideoURL(video, prefs) const link = videoLink(url, video); removeVideoLink(); @@ -52,14 +47,6 @@ async function peertubeify(query: String) { }).catch(removeVideoLink); break; } - case RedirectType.Auto: { - searchVideo(query) - .then(async video => { - const url = getCorrectURL(video, prefs) - location.replace(url); - }); - break; - } case RedirectType.None: { break; } From 633fa729df2072faf1ed8fd1ca57137785f8638c Mon Sep 17 00:00:00 2001 From: Ealhad Date: Mon, 12 Nov 2018 15:24:12 +0100 Subject: [PATCH 2/4] Correct formatting --- src/background.ts | 32 ++++++++++++++++---------------- src/constants.ts | 12 ++++++------ src/invidious-api.ts | 38 +++++++++++++++++++------------------- src/util.ts | 9 ++++----- 4 files changed, 45 insertions(+), 46 deletions(-) diff --git a/src/background.ts b/src/background.ts index 1530514..d939e4a 100644 --- a/src/background.ts +++ b/src/background.ts @@ -27,8 +27,8 @@ import { getPeertubeVideoURL } from './util'; * Retrieve the title of the video using Invidious API. */ const getTitle = async (id: string) => { - let data = await InvidiousAPI.getVideo(id); - return data.title; + let data = await InvidiousAPI.getVideo(id); + return data.title; } /** @@ -36,21 +36,21 @@ const getTitle = async (id: string) => { * automatic redirection. */ const preventYoutube = async (r) => { - const prefs = await Preferences.getPreferences(); + const prefs = await Preferences.getPreferences(); - if (prefs.redirectYoutube === 'Auto') { - const query = new URLSearchParams(r.url.substring(r.url.indexOf('?') + 1)); + if (prefs.redirectYoutube === 'Auto') { + const query = new URLSearchParams(r.url.substring(r.url.indexOf('?') + 1)); - let title = await getTitle(query.get('v')); - let video = await searchByName(title); - let url = getPeertubeVideoURL(video, prefs); + let title = await getTitle(query.get('v')); + let video = await searchByName(title); + let url = getPeertubeVideoURL(video, prefs); - return { - redirectUrl: url - }; - } + return { + redirectUrl: url + }; + } - return {}; + return {}; }; const buildSearchByNameURL = (instance: string, query: string): string => `https://${instance}/api/v1/search/videos?search=${encodeURIComponent(query)}`; @@ -93,7 +93,7 @@ browser.runtime.onMessage.addListener(function(message, sender) { }); browser.webRequest.onBeforeRequest.addListener( - preventYoutube, - {urls: ['*://*.youtube.com/watch?v=*']}, - ['blocking'] + preventYoutube, + { urls: ['*://*.youtube.com/watch?v=*'] }, + ['blocking'] ); diff --git a/src/constants.ts b/src/constants.ts index 15be776..7614a01 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,8 +1,8 @@ export default { - defaultInstance: 'peertube.social', - invidiousAPI: { - url: 'https://invidio.us/api/v1', - videos: 'videos', - channels: 'channels' - } + defaultInstance: 'peertube.social', + invidiousAPI: { + url: 'https://invidio.us/api/v1', + videos: 'videos', + channels: 'channels' + } }; diff --git a/src/invidious-api.ts b/src/invidious-api.ts index 767796f..9049d07 100644 --- a/src/invidious-api.ts +++ b/src/invidious-api.ts @@ -2,25 +2,25 @@ import * as _ from 'lodash/fp'; import constants from './constants'; export default class InvidiousAPI { - static async _fetchAPI(action: string, params: any) { - let paramString = typeof params == 'string' - ? params - : Object.keys(params).map(function(key){ - return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); - }).join('&'); + static async _fetchAPI(action: string, params: any) { + let paramString = typeof params == 'string' + ? params + : Object.keys(params).map(function(key) { + return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); + }).join('&'); - return fetch(`${constants.invidiousAPI.url}/${action}/${paramString}`) - .then(res => res.json()) - .catch(e => console.error( - 'An error occured while trying to fetch API used by PeerTubeify: ' - + e.message - )); - } - static async getVideo(id: string) { - return this._fetchAPI(constants.invidiousAPI.videos, id); - } + return fetch(`${constants.invidiousAPI.url}/${action}/${paramString}`) + .then(res => res.json()) + .catch(e => console.error( + 'An error occured while trying to fetch API used by PeerTubeify: ' + + e.message + )); + } + static async getVideo(id: string) { + return this._fetchAPI(constants.invidiousAPI.videos, id); + } - static async getChannel(ucid: string) { - return this._fetchAPI(constants.invidiousAPI.channels, ucid); - } + static async getChannel(ucid: string) { + return this._fetchAPI(constants.invidiousAPI.channels, ucid); + } } diff --git a/src/util.ts b/src/util.ts index 956b162..502d097 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,14 +1,13 @@ export function htmlToElement(html: string): Element { - const template = document.createElement('template'); - template.innerHTML = html.trim(); - return template.content.firstElementChild; + const template = document.createElement('template'); + template.innerHTML = html.trim(); + return template.content.firstElementChild; } export function getPeertubeVideoURL(video, prefs) { return `https://${getPeertubeHost(video.account.host, prefs)}/videos/watch/${video.uuid}` } -export function getPeertubeHost(host, prefs) -{ +export function getPeertubeHost(host, prefs) { return prefs.openInOriginalInstance ? host : prefs.searchInstance; } From 0d7312d0c2bcabd63409d9759c602876f920bc7a Mon Sep 17 00:00:00 2001 From: Ealhad Date: Mon, 12 Nov 2018 15:54:49 +0100 Subject: [PATCH 3/4] Make some small style improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - let data = await InvidiousAPI.getVideo(id); + const data = await InvidiousAPI.getVideo(id); I try to use `const` whenever I don't have to use `let`. While it's not a guarantee as strong as what e.g. Rust can provide, I think it helps reducing cognitive load — we know that the symbol will always reference the same piece of data. - if (prefs.redirectYoutube === 'Auto') { + if (prefs.redirectYoutube == RedirectType.Auto) { Me nitpicking again. The string value of the RedirectYoutube enum is mainly there for the radio buttons in the options page; in the code, it's better to use the actual RedirectType.Auto symbol. - return {}; + throw new Error('No results.'); Under the hood, an `async` function returns a promise, which resolves on return and is rejected on throw. --- src/background.ts | 14 +++++++------- src/invidious-api.ts | 3 ++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/background.ts b/src/background.ts index d939e4a..2fc52e0 100644 --- a/src/background.ts +++ b/src/background.ts @@ -17,7 +17,7 @@ import * as _ from 'lodash/fp'; import * as browser from 'webextension-polyfill'; -import { MessageKind } from './types'; +import { MessageKind, RedirectType } from './types'; import constants from './constants'; import Preferences from './preferences'; import InvidiousAPI from './invidious-api'; @@ -27,7 +27,7 @@ import { getPeertubeVideoURL } from './util'; * Retrieve the title of the video using Invidious API. */ const getTitle = async (id: string) => { - let data = await InvidiousAPI.getVideo(id); + const data = await InvidiousAPI.getVideo(id); return data.title; } @@ -38,19 +38,19 @@ const getTitle = async (id: string) => { const preventYoutube = async (r) => { const prefs = await Preferences.getPreferences(); - if (prefs.redirectYoutube === 'Auto') { + if (prefs.redirectYoutube == RedirectType.Auto) { const query = new URLSearchParams(r.url.substring(r.url.indexOf('?') + 1)); - let title = await getTitle(query.get('v')); - let video = await searchByName(title); - let url = getPeertubeVideoURL(video, prefs); + const title = await getTitle(query.get('v')); + const video = await searchByName(title); + const url = getPeertubeVideoURL(video, prefs); return { redirectUrl: url }; } - return {}; + throw new Error('No results.'); }; const buildSearchByNameURL = (instance: string, query: string): string => `https://${instance}/api/v1/search/videos?search=${encodeURIComponent(query)}`; diff --git a/src/invidious-api.ts b/src/invidious-api.ts index 9049d07..c9a482b 100644 --- a/src/invidious-api.ts +++ b/src/invidious-api.ts @@ -3,7 +3,7 @@ import constants from './constants'; export default class InvidiousAPI { static async _fetchAPI(action: string, params: any) { - let paramString = typeof params == 'string' + const paramString = typeof params == 'string' ? params : Object.keys(params).map(function(key) { return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); @@ -16,6 +16,7 @@ export default class InvidiousAPI { + e.message )); } + static async getVideo(id: string) { return this._fetchAPI(constants.invidiousAPI.videos, id); } From 82c068f8f3bc667ec4528e84054a05fb9fa3516e Mon Sep 17 00:00:00 2001 From: Ealhad Date: Mon, 12 Nov 2018 15:55:09 +0000 Subject: [PATCH 4/4] Make `fetchAPI` method private --- src/invidious-api.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/invidious-api.ts b/src/invidious-api.ts index c9a482b..d44863f 100644 --- a/src/invidious-api.ts +++ b/src/invidious-api.ts @@ -2,7 +2,7 @@ import * as _ from 'lodash/fp'; import constants from './constants'; export default class InvidiousAPI { - static async _fetchAPI(action: string, params: any) { + private static async fetchAPI(action: string, params: any) { const paramString = typeof params == 'string' ? params : Object.keys(params).map(function(key) { @@ -18,10 +18,10 @@ export default class InvidiousAPI { } static async getVideo(id: string) { - return this._fetchAPI(constants.invidiousAPI.videos, id); + return this.fetchAPI(constants.invidiousAPI.videos, id); } static async getChannel(ucid: string) { - return this._fetchAPI(constants.invidiousAPI.channels, ucid); + return this.fetchAPI(constants.invidiousAPI.channels, ucid); } }