From c676af242a77a6bcd69bf4aac816ee4e04c64651 Mon Sep 17 00:00:00 2001 From: Booteille Date: Sun, 11 Nov 2018 16:29:38 +0100 Subject: [PATCH] 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; }