From 029de08d4d3b82b1bb70700ee82828dd0acd2eed Mon Sep 17 00:00:00 2001 From: Booteille Date: Thu, 22 Nov 2018 15:09:35 +0000 Subject: [PATCH] Add option to resume video through multiple instances --- src/options.html | 23 +++++++++++++------ src/options.ts | 7 ++++-- src/peertube.ts | 3 +++ src/preferences.ts | 7 ++++-- src/resume.ts | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 src/resume.ts diff --git a/src/options.html b/src/options.html index f024d87..1f340b9 100644 --- a/src/options.html +++ b/src/options.html @@ -15,6 +15,22 @@ +
+ + + +
+

YouTube @@ -36,13 +52,6 @@ Redirect - -

diff --git a/src/options.ts b/src/options.ts index 0aef2a5..a5f3c47 100644 --- a/src/options.ts +++ b/src/options.ts @@ -23,6 +23,7 @@ function id(id: string): Element { return document.getElementById(id); } const searchInstance = () => id('search-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 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 => { searchInstance().value = prefs.searchInstance; openInOriginalInstance().checked = prefs.openInOriginalInstance; + resumeUncompletedVideo().checked = prefs.resumeUncompletedVideo; check('redirectPeertube')(prefs.redirectPeertube) check('redirectYoutube')(prefs.redirectYoutube) @@ -37,8 +39,9 @@ Preferences.getPreferences().then(prefs => { e.preventDefault(); prefs.searchInstance = searchInstance().value; prefs.openInOriginalInstance = openInOriginalInstance().checked; - prefs.redirectPeertube = checked('redirectPeertube') as RedirectType - prefs.redirectYoutube = checked('redirectYoutube') as RedirectType + prefs.resumeUncompletedVideo = resumeUncompletedVideo().checked; + prefs.redirectPeertube = checked('redirectPeertube') as RedirectType; + prefs.redirectYoutube = checked('redirectYoutube') as RedirectType; prefs.save(); } diff --git a/src/peertube.ts b/src/peertube.ts index 76db0d9..726bd2f 100644 --- a/src/peertube.ts +++ b/src/peertube.ts @@ -19,6 +19,7 @@ import * as browser from 'webextension-polyfill'; import { htmlToElement, getPeertubeVideoURL } from './util'; import { MessageKind, RedirectType } from './types'; +import { runResume } from './resume'; import Preferences from './preferences'; const thumbnailURL = (host, path) => `https://${host}${path}`; @@ -64,9 +65,11 @@ const observer = new MutationObserver(function(mutationsList) { for (const mutation of mutationsList) { if ((mutation.target as Element).id == 'video-element-wrapper') { throttledPeertubeify(); + runResume(); } } }); +runResume(); observer.observe(document.body, { childList: true, diff --git a/src/preferences.ts b/src/preferences.ts index ada960a..44e3357 100644 --- a/src/preferences.ts +++ b/src/preferences.ts @@ -24,14 +24,16 @@ const stripProtocol = _.replace(/^https?:\/\//, ''); export default class Preferences { private _searchInstance: string; openInOriginalInstance: boolean; + resumeUncompletedVideo: boolean; redirectYoutube: RedirectType; redirectPeertube: RedirectType; constructor(localStorage) { this.searchInstance = _.defaultTo(constants.peertubeAPI.defaultInstance, localStorage.searchInstance as string); this.openInOriginalInstance = _.defaultTo(true, localStorage.openInOriginalInstance as boolean); - this.redirectYoutube = _.defaultTo(RedirectType.Show, localStorage.redirectYoutube) - this.redirectPeertube = _.defaultTo(RedirectType.None, localStorage.redirectPeertube) + this.resumeUncompletedVideo = _.defaultTo(true, localStorage.resumeUncompletedVideo as boolean); + this.redirectYoutube = _.defaultTo(RedirectType.Show, localStorage.redirectYoutube); + this.redirectPeertube = _.defaultTo(RedirectType.None, localStorage.redirectPeertube); } static async getPreferences() { @@ -43,6 +45,7 @@ export default class Preferences { await browser.storage.local.set({ searchInstance: this.searchInstance, openInOriginalInstance: this.openInOriginalInstance, + resumeUncompletedVideo: this.resumeUncompletedVideo, redirectYoutube: this.redirectYoutube, redirectPeertube: this.redirectPeertube, }) diff --git a/src/resume.ts b/src/resume.ts new file mode 100644 index 0000000..dabddac --- /dev/null +++ b/src/resume.ts @@ -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 = 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 = 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 }); +};