Add the possibility to display a link when on another PeerTube instance
This commit is contained in:
parent
59027cb927
commit
f197253e52
|
@ -4,6 +4,10 @@ PeerTubeify is a browser extension to help discovering which YouTube videos are
|
|||
also available on [[https://joinpeertube.org/][PeerTube]], by displaying a link and a thumbnail below the video
|
||||
title, when watching a video on YouTube.
|
||||
|
||||
It also allows you to choose a preferred PeerTube instance; when you watch a
|
||||
video on another instance, a link to the same video in your preferred instance
|
||||
is displayed.
|
||||
|
||||
PeerTube is a federated video streaming platform.
|
||||
|
||||
PeerTubeify is not affiliated with PeerTube.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "PeerTubeify",
|
||||
"version": "0.2.5",
|
||||
"version": "0.3.0",
|
||||
"description": "On YouTube, displays a link to the same video on PeerTube, if it exists.",
|
||||
"homepage_url": "https://gitlab.com/Ealhad/peertubeify",
|
||||
"icons": {
|
||||
|
@ -20,6 +20,9 @@
|
|||
"content_scripts": [{
|
||||
"matches": ["*://*.youtube.com/*"],
|
||||
"js": ["dist/youtube.js"]
|
||||
}, {
|
||||
"matches": ["https://*/videos/watch/*"],
|
||||
"js": ["dist/peertube.js"]
|
||||
}],
|
||||
"options_ui": {
|
||||
"page": "dist/options.html"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "peertubeify",
|
||||
"version": "0.2.5",
|
||||
"version": "0.3.0",
|
||||
"description": "PeerTubeify is a browser extension to help discovering which YouTube videos are also available on PeerTube.",
|
||||
"main": "webpack.config.js",
|
||||
"dependencies": {},
|
||||
|
|
|
@ -17,14 +17,15 @@
|
|||
import * as _ from 'lodash/fp';
|
||||
import * as browser from 'webextension-polyfill';
|
||||
|
||||
import { MessageKind } from './types';
|
||||
import { constants } from './constants';
|
||||
|
||||
const buildSearchURL = (instance: string, query: string): string => `https://${instance}/api/v1/search/videos?search=${encodeURIComponent(query)}`;
|
||||
const buildSearchByNameURL = (instance: string, query: string): string => `https://${instance}/api/v1/search/videos?search=${encodeURIComponent(query)}`;
|
||||
|
||||
const search = query => new Promise(async (resolve, reject) => {
|
||||
const searchByName = query => new Promise(async (resolve, reject) => {
|
||||
const instance = _.getOr(constants.defaultInstance, 'searchInstance', await browser.storage.local.get()).toString();
|
||||
|
||||
fetch(buildSearchURL(instance, query))
|
||||
fetch(buildSearchByNameURL(instance, query))
|
||||
.then(res => res.json())
|
||||
.then(function(data) {
|
||||
if (data.total > 0) {
|
||||
|
@ -37,7 +38,23 @@ const search = query => new Promise(async (resolve, reject) => {
|
|||
});
|
||||
});
|
||||
|
||||
const buildSearchByIDURL = (instance: string, id: string): string => `https://${instance}/api/v1/videos/${id}`;
|
||||
|
||||
const searchByID = id => new Promise(async (resolve, reject) => {
|
||||
const instance = _.getOr(constants.defaultInstance, 'searchInstance', await browser.storage.local.get()).toString();
|
||||
|
||||
fetch(buildSearchByIDURL(instance, id))
|
||||
.then(res => res.json())
|
||||
.then(function (video) {
|
||||
resolve({ url: `https://${instance}/videos/watch/${id}`, video });
|
||||
})
|
||||
})
|
||||
|
||||
browser.runtime.onMessage.addListener(function(message, sender) {
|
||||
return search(message.query)
|
||||
switch (message.kind) {
|
||||
case MessageKind.SearchByName:
|
||||
return searchByName(message.query);
|
||||
case MessageKind.SearchByID:
|
||||
return searchByID(message.id);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -22,6 +22,13 @@
|
|||
<input type="checkbox" id="open-in-original-instance">
|
||||
</label>
|
||||
|
||||
<label style="margin-bottom: 1rem; display: flex; align-items: center;">
|
||||
<p style="margin-right: 1rem">
|
||||
Show a message on other PeerTube instances to redirect to your preferred one?
|
||||
</p>
|
||||
<input type="checkbox" id="show-on-peertube">
|
||||
</label>
|
||||
|
||||
<button style="display: block; margin-left: auto" type="submit">Save</button>
|
||||
</form>
|
||||
|
||||
|
|
|
@ -21,14 +21,16 @@ import { constants } from './constants';
|
|||
|
||||
function id(id: string): Element { return document.getElementById(id); }
|
||||
|
||||
const searchInstanceInput = () => id('search-instance') as HTMLInputElement;
|
||||
const openInOriginalInstanceInput = () => id('open-in-original-instance') as HTMLInputElement;
|
||||
const searchInstance = () => id('search-instance') as HTMLInputElement;
|
||||
const openInOriginalInstance = () => id('open-in-original-instance') as HTMLInputElement;
|
||||
const showOnPeertube = () => id('show-on-peertube') as HTMLInputElement;
|
||||
|
||||
function saveOptions(e) {
|
||||
e.preventDefault();
|
||||
browser.storage.local.set({
|
||||
searchInstance: _.defaultTo(constants.defaultInstance, stripProtocol(searchInstanceInput().value)),
|
||||
openInOriginalInstance: _.defaultTo(true, openInOriginalInstanceInput().checked),
|
||||
searchInstance: _.defaultTo(constants.defaultInstance, stripProtocol(searchInstance().value)),
|
||||
openInOriginalInstance: _.defaultTo(true, openInOriginalInstance().checked),
|
||||
showOnPeertube: _.defaultTo(false, showOnPeertube().checked),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -36,8 +38,9 @@ const stripProtocol = _.replace(/^https?:\/\//, '');
|
|||
|
||||
function restoreOptions() {
|
||||
browser.storage.local.get().then(result => {
|
||||
searchInstanceInput().value = _.defaultTo(constants.defaultInstance, result.searchInstance as string);
|
||||
openInOriginalInstanceInput().checked = _.defaultTo(true, result.openInOriginalInstance as boolean);
|
||||
searchInstance().value = _.defaultTo(constants.defaultInstance, result.searchInstance as string);
|
||||
openInOriginalInstance().checked = _.defaultTo(true, result.openInOriginalInstance as boolean);
|
||||
showOnPeertube().checked = _.defaultTo(false, result.showOnPeertube as boolean);
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/* This file is part of PeerTubeify.
|
||||
*
|
||||
* PeerTubeify is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* PeerTubeify is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* PeerTubeify. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import * as _ from 'lodash/fp';
|
||||
import * as browser from 'webextension-polyfill';
|
||||
|
||||
import { htmlToElement } from './util';
|
||||
import { MessageKind } from './types';
|
||||
import { constants } from './constants';
|
||||
|
||||
const watchURL = (host, uuid) => `https://${host}/videos/watch/${uuid}`;
|
||||
const thumbnailURL = (host, path) => `https://${host}${path}`;
|
||||
|
||||
const LINK_ID = 'peertube-link';
|
||||
|
||||
async function peertubeify() {
|
||||
const options = await browser.storage.local.get();
|
||||
const showOnPeertube = _.getOr(false, 'showOnPeertube', options);
|
||||
const preferredInstance = _.getOr(constants.defaultInstance, 'searchInstance', options);
|
||||
const isPreferredInstance = _.equals(preferredInstance, location.hostname)
|
||||
if (showOnPeertube && !isPreferredInstance) {
|
||||
const id = _.last(_.split('/', location.href));
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
kind: MessageKind.SearchByID,
|
||||
id
|
||||
}).then(async ({video, url}) => {
|
||||
const link = videoLink(url, video);
|
||||
|
||||
removeVideoLink();
|
||||
document.querySelector('body').appendChild(link);
|
||||
}).catch(removeVideoLink);
|
||||
}
|
||||
}
|
||||
|
||||
const throttledPeertubeify = _.throttle(1000, peertubeify);
|
||||
const observer = new MutationObserver(function(mutationsList) {
|
||||
for (const mutation of mutationsList) {
|
||||
if ((mutation.target as Element).id =='video-element-wrapper') {
|
||||
throttledPeertubeify();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
})
|
||||
|
||||
const videoLink = (url, video) => htmlToElement(`
|
||||
<div id="${LINK_ID}"
|
||||
style="
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
z-index: 10000;
|
||||
box-shadow: inset 0px 4px 8px -3px rgba(17, 17, 17, .06);
|
||||
background-color: #000;
|
||||
">
|
||||
|
||||
<button
|
||||
onclick="document.getElementById('${LINK_ID}').remove()"
|
||||
style="
|
||||
all: unset;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
position: absolute;
|
||||
line-height: .8;
|
||||
top: 1rem;
|
||||
right: 1rem;">
|
||||
⨯
|
||||
</button>
|
||||
|
||||
<a
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;"
|
||||
href=${url}>
|
||||
<img src="${thumbnailURL(video.account.host, video.thumbnailPath)}">
|
||||
<div style="
|
||||
font-size: 18px;
|
||||
margin-left: 1rem;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
">
|
||||
<p style="margin: 0 0 1rem;">
|
||||
${video.name}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
`);
|
||||
|
||||
function removeVideoLink() {
|
||||
const existingLink = document.getElementById(LINK_ID);
|
||||
existingLink && existingLink.remove();
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export enum MessageKind {
|
||||
SearchByName,
|
||||
SearchByID,
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export function htmlToElement(html: string): Element {
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = html.trim();
|
||||
return template.content.firstElementChild;
|
||||
}
|
|
@ -17,6 +17,8 @@
|
|||
import * as _ from 'lodash/fp';
|
||||
import * as browser from 'webextension-polyfill';
|
||||
|
||||
import { htmlToElement } from './util';
|
||||
import { MessageKind } from './types';
|
||||
import { constants } from './constants';
|
||||
|
||||
const watchURL = (host, uuid) => `https://${host}/videos/watch/${uuid}`;
|
||||
|
@ -26,7 +28,8 @@ const LINK_ID = 'peertube-link';
|
|||
|
||||
function peertubeify(query: string) {
|
||||
browser.runtime.sendMessage({
|
||||
query
|
||||
kind: MessageKind.SearchByName,
|
||||
query,
|
||||
}).then(async video => {
|
||||
const options = await browser.storage.local.get();
|
||||
const openInOriginalInstance = _.getOr(true, 'openInOriginalInstance', options);
|
||||
|
@ -58,12 +61,6 @@ observer.observe(document.body, {
|
|||
subtree: true,
|
||||
})
|
||||
|
||||
function htmlToElement(html: string): Element {
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = html.trim();
|
||||
return template.content.firstElementChild;
|
||||
}
|
||||
|
||||
const videoLink = (url, video) => htmlToElement(`
|
||||
<div id="${LINK_ID}"
|
||||
style="
|
||||
|
|
|
@ -5,6 +5,7 @@ module.exports = {
|
|||
entry: {
|
||||
background: 'background.ts',
|
||||
youtube: 'youtube.ts',
|
||||
peertube: 'peertube.ts',
|
||||
options: ['options.ts', 'options.html'],
|
||||
},
|
||||
output: {
|
||||
|
|
Loading…
Reference in New Issue