Add option to resume video through multiple instances

This commit is contained in:
Booteille 2018-11-22 15:09:35 +00:00 committed by Ealhad
parent f827e9b848
commit 029de08d4d
5 changed files with 86 additions and 11 deletions

View File

@ -15,6 +15,22 @@
<input style="flex-grow: 1" type="text" id="search-instance"> <input style="flex-grow: 1" type="text" id="search-instance">
</label> </label>
<section>
<label style="display: flex; align-items: center;">
<p style="margin-right: 1rem">
Open videos in original instance?
</p>
<input type="checkbox" id="open-in-original-instance">
</label>
<label style="display: flex; align-items: center;">
<p style="margin-right: 1rem">
Automatically resume videos?
</p>
<input type="checkbox" id="resume-uncompleted-videos">
</label>
</section>
<section> <section>
<h2> <h2>
YouTube YouTube
@ -36,13 +52,6 @@
Redirect Redirect
</label> </label>
</div> </div>
<label style="margin-bottom: 1rem; display: flex; align-items: center;">
<p style="margin-right: 1rem">
Open videos in original instance?
</p>
<input type="checkbox" id="open-in-original-instance">
</label>
</section> </section>
<section> <section>

View File

@ -23,6 +23,7 @@ function id(id: string): Element { return document.getElementById(id); }
const searchInstance = () => id('search-instance') as HTMLInputElement; const searchInstance = () => id('search-instance') as HTMLInputElement;
const openInOriginalInstance = () => id('open-in-original-instance') as HTMLInputElement; const openInOriginalInstance = () => id('open-in-original-instance') as HTMLInputElement;
const resumeUncompletedVideo = () => id('resume-uncompleted-videos') as HTMLInputElement;
const checked = name => (document.querySelector(`input[name="${name}"]:checked`) as HTMLInputElement).value; const checked = name => (document.querySelector(`input[name="${name}"]:checked`) as HTMLInputElement).value;
const check = name => value => (document.querySelector(`input[name="${name}"][value="${value}"]`) as HTMLInputElement).checked = true const check = name => value => (document.querySelector(`input[name="${name}"][value="${value}"]`) as HTMLInputElement).checked = true
@ -30,6 +31,7 @@ const check = name => value => (document.querySelector(`input[name="${name}"][va
Preferences.getPreferences().then(prefs => { Preferences.getPreferences().then(prefs => {
searchInstance().value = prefs.searchInstance; searchInstance().value = prefs.searchInstance;
openInOriginalInstance().checked = prefs.openInOriginalInstance; openInOriginalInstance().checked = prefs.openInOriginalInstance;
resumeUncompletedVideo().checked = prefs.resumeUncompletedVideo;
check('redirectPeertube')(prefs.redirectPeertube) check('redirectPeertube')(prefs.redirectPeertube)
check('redirectYoutube')(prefs.redirectYoutube) check('redirectYoutube')(prefs.redirectYoutube)
@ -37,8 +39,9 @@ Preferences.getPreferences().then(prefs => {
e.preventDefault(); e.preventDefault();
prefs.searchInstance = searchInstance().value; prefs.searchInstance = searchInstance().value;
prefs.openInOriginalInstance = openInOriginalInstance().checked; prefs.openInOriginalInstance = openInOriginalInstance().checked;
prefs.redirectPeertube = checked('redirectPeertube') as RedirectType prefs.resumeUncompletedVideo = resumeUncompletedVideo().checked;
prefs.redirectYoutube = checked('redirectYoutube') as RedirectType prefs.redirectPeertube = checked('redirectPeertube') as RedirectType;
prefs.redirectYoutube = checked('redirectYoutube') as RedirectType;
prefs.save(); prefs.save();
} }

View File

@ -19,6 +19,7 @@ import * as browser from 'webextension-polyfill';
import { htmlToElement, getPeertubeVideoURL } from './util'; import { htmlToElement, getPeertubeVideoURL } from './util';
import { MessageKind, RedirectType } from './types'; import { MessageKind, RedirectType } from './types';
import { runResume } from './resume';
import Preferences from './preferences'; import Preferences from './preferences';
const thumbnailURL = (host, path) => `https://${host}${path}`; const thumbnailURL = (host, path) => `https://${host}${path}`;
@ -64,9 +65,11 @@ const observer = new MutationObserver(function(mutationsList) {
for (const mutation of mutationsList) { for (const mutation of mutationsList) {
if ((mutation.target as Element).id == 'video-element-wrapper') { if ((mutation.target as Element).id == 'video-element-wrapper') {
throttledPeertubeify(); throttledPeertubeify();
runResume();
} }
} }
}); });
runResume();
observer.observe(document.body, { observer.observe(document.body, {
childList: true, childList: true,

View File

@ -24,14 +24,16 @@ const stripProtocol = _.replace(/^https?:\/\//, '');
export default class Preferences { export default class Preferences {
private _searchInstance: string; private _searchInstance: string;
openInOriginalInstance: boolean; openInOriginalInstance: boolean;
resumeUncompletedVideo: boolean;
redirectYoutube: RedirectType; redirectYoutube: RedirectType;
redirectPeertube: RedirectType; redirectPeertube: RedirectType;
constructor(localStorage) { constructor(localStorage) {
this.searchInstance = _.defaultTo(constants.peertubeAPI.defaultInstance, localStorage.searchInstance as string); this.searchInstance = _.defaultTo(constants.peertubeAPI.defaultInstance, localStorage.searchInstance as string);
this.openInOriginalInstance = _.defaultTo(true, localStorage.openInOriginalInstance as boolean); this.openInOriginalInstance = _.defaultTo(true, localStorage.openInOriginalInstance as boolean);
this.redirectYoutube = _.defaultTo(RedirectType.Show, localStorage.redirectYoutube) this.resumeUncompletedVideo = _.defaultTo(true, localStorage.resumeUncompletedVideo as boolean);
this.redirectPeertube = _.defaultTo(RedirectType.None, localStorage.redirectPeertube) this.redirectYoutube = _.defaultTo(RedirectType.Show, localStorage.redirectYoutube);
this.redirectPeertube = _.defaultTo(RedirectType.None, localStorage.redirectPeertube);
} }
static async getPreferences() { static async getPreferences() {
@ -43,6 +45,7 @@ export default class Preferences {
await browser.storage.local.set({ await browser.storage.local.set({
searchInstance: this.searchInstance, searchInstance: this.searchInstance,
openInOriginalInstance: this.openInOriginalInstance, openInOriginalInstance: this.openInOriginalInstance,
resumeUncompletedVideo: this.resumeUncompletedVideo,
redirectYoutube: this.redirectYoutube, redirectYoutube: this.redirectYoutube,
redirectPeertube: this.redirectPeertube, redirectPeertube: this.redirectPeertube,
}) })

57
src/resume.ts Normal file
View File

@ -0,0 +1,57 @@
import * as _ from 'lodash/fp';
import * as browser from 'webextension-polyfill';
import Preferences from './preferences';
interface VideoEntry {
readonly uuid: string,
currentTime: number,
}
export async function runResume() {
const prefs = await Preferences.getPreferences();
if (prefs.resumeUncompletedVideo) {
const videoElement = document.getElementById('vjs_video_3_html5_api') as HTMLVideoElement;
resumeVideo(videoElement);
window.setInterval(() => updateStorage(videoElement), 1000);
}
}
async function resumeVideo(videoElement: HTMLVideoElement) {
const storage = await browser.storage.local.get('uncompletedVideos');
const uncompletedVideos: Array<VideoEntry> = storage.uncompletedVideos;
const uuid = _.last(_.split('/', location.href));
const savedEntry = _.find((e: VideoEntry) => e.uuid == uuid, uncompletedVideos);
if (savedEntry) {
videoElement.currentTime = savedEntry.currentTime;
browser.storage.local.set({ uncompletedVideos });
}
}
async function updateStorage(videoElement: HTMLVideoElement) {
const storage = await browser.storage.local.get('uncompletedVideos');
const uuid = _.last(_.split('/', location.href));
const uncompletedVideos: Array<VideoEntry> = storage.uncompletedVideos || [];
if (!videoElement.ended) {
const entry: VideoEntry = {
uuid,
currentTime: videoElement.currentTime
};
const savedEntry = _.find((e: VideoEntry) => e.uuid == uuid, uncompletedVideos);
if (savedEntry) {
savedEntry.currentTime = entry.currentTime;
} else {
uncompletedVideos.push(entry);
}
} else {
uncompletedVideos.splice(_.findIndex((e: VideoEntry) => e.uuid == uuid, uncompletedVideos), 1);
}
browser.storage.local.set({ uncompletedVideos });
};