Add option to resume video through multiple instances
Closes #3 See merge request Ealhad/peertubeify!4
This commit is contained in:
commit
8aa3910352
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 });
|
||||||
|
};
|
Loading…
Reference in New Issue