From a280310d7a29ede8c182b9ccd4f7e27d2e252875 Mon Sep 17 00:00:00 2001 From: Gerben Date: Sun, 20 Oct 2019 02:19:17 +0530 Subject: [PATCH] WIP creating bookmarks --- app/_locales/en/messages.json | 8 +++ app/audio-player/contentscript.js | 10 ++-- app/create-bookmarks/background.js | 30 +++++++++++ app/create-bookmarks/contentscript.js | 76 +++++++++++++++++++++++++++ app/manifest.json | 2 + app/scripts/background.js | 1 + app/scripts/contentscript.js | 1 + package-lock.json | 23 ++++++++ package.json | 3 +- 9 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 app/create-bookmarks/background.js create mode 100644 app/create-bookmarks/contentscript.js diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index aabb829..20003ec 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -10,5 +10,13 @@ "appDescription": { "message": "Browser extension to bookmark fragments of audio files", "description": "The description of the application" + }, + "bookmarkSelectionContextMenuItemForFragment": { + "message": "Bookmark selected fragment", + "description": "The entry in the context ('right-click') menu when selecting an audio fragment" + }, + "bookmarkSelectionContextMenuItemForSingleMoment": { + "message": "Bookmark selected moment", + "description": "The entry in the context ('right-click') menu when selecting a single moment" } } diff --git a/app/audio-player/contentscript.js b/app/audio-player/contentscript.js index 7df4b95..f56e5c2 100644 --- a/app/audio-player/contentscript.js +++ b/app/audio-player/contentscript.js @@ -61,7 +61,7 @@ async function init() { // Bind window hash change to update player - window.addEventListener('hashchange', async function() { + window.addEventListener('hashchange', async function() { if(playlist.isPlaying()) { eventEmitter.emit('stop') // pause needs a small delay, coz the lib doesn't @@ -72,7 +72,6 @@ async function init() { eventEmitter.emit('play'); }); - // Load Playlist await playlist.load([{ src: document.URL @@ -83,6 +82,11 @@ async function init() { // Start playing. A tiny delay seems needed in Firefox to show the cursor at the right place. requestAnimationFrame(() => eventEmitter.emit('play')); + + return playlist; } -init(); +init().then(playlist => { + // Store playlist as a shared global variable. + self.playlist = playlist; +}); diff --git a/app/create-bookmarks/background.js b/app/create-bookmarks/background.js new file mode 100644 index 0000000..f41a710 --- /dev/null +++ b/app/create-bookmarks/background.js @@ -0,0 +1,30 @@ +import { makeRemotelyCallable, remoteFunction } from 'webextension-rpc'; + +function createPreciseUrl(url, selector) { + const properUrl = url.split('#')[0]; + + let fragmentIdentifier; + if (selector.type === 'FragmentSelector') { + fragmentIdentifier = selector.value; + } else { + throw new Error('Unsupported selector type'); + } + + return properUrl + '#' + fragmentIdentifier; +} + +async function createBookmark({ tab }) { + const selector = await remoteFunction('describeSelection', { tabId: tab.id })(); + const bookmarkUrl = createPreciseUrl(tab.url, selector); + const filenameMatch = tab.url.match(/.*\/([^\/#?]+)(?:\?.*)?(?:#.*)?/); + const filename = filenameMatch ? filenameMatch[1] : 'Audio fragment'; + + const bookmarkTitle = `${filename} ${start}–${end}`; + + await browser.bookmarks.create({ + title: bookmarkTitle, + url: bookmarkUrl, + }); +} + +makeRemotelyCallable({ createBookmark }, { insertExtraArg: true }); diff --git a/app/create-bookmarks/contentscript.js b/app/create-bookmarks/contentscript.js new file mode 100644 index 0000000..7742f15 --- /dev/null +++ b/app/create-bookmarks/contentscript.js @@ -0,0 +1,76 @@ +import delay from 'delay'; +import { makeRemotelyCallable, remoteFunction } from 'webextension-rpc'; + +function describeSelection() { + const playlist = self.playlist; + if (!playlist) { + throw new Error('Player has not been initialised.'); + } + + let { start, end } = playlist.getTimeSelection(); + // Waveform Playlist tells us end=start when the selection is just a single line. + if (end === start) end = undefined; + // Round the numbers to two decimals. + if (start !== undefined) start = Math.round(start * 100) / 100; + if (end !== undefined) end = Math.round(end * 100) / 100; + + const fragmentIdentifier = `t=${start || ''}` + (end ? `,${end}` : ''); + + const selector = { + type: 'FragmentSelector', + conformsTo: 'http://www.w3.org/TR/media-frags/', + value: fragmentIdentifier, + }; + return selector; +} + +makeRemotelyCallable({ + describeSelection, +}); + +async function init() { + let menuEl; + + function cleanupMenu() { + if (!menuEl) return; + menuEl.parentNode.removeChild(menuEl); + menuEl = undefined; + } + + function onContextMenu(event) { + event.preventDefault(); + const left = event.pageX; + const top = event.pageY; + + const playlist = self.playlist; + if (!playlist) { + throw new Error('Player has not been initialised.'); + } + let { start, end } = playlist.getTimeSelection(); + const fragmentIsSelected = start !== end; + + cleanupMenu(); + menuEl = document.createElement('ul'); + const liEl = document.createElement('li'); + menuEl.appendChild(liEl); + menuEl.setAttribute('style', `list-style: none; margin: 0; padding: 0; position: absolute; top: ${top}; left: ${left}; z-index: 9999999; background: #eee; border: 1px solid black;`); + + const buttonEl = document.createElement('button'); + buttonEl.innerText = browser.i18n.getMessage(fragmentIsSelected + ? 'bookmarkSelectionContextMenuItemForFragment' + : 'bookmarkSelectionContextMenuItemForSingleMoment' + ); + buttonEl.addEventListener('click', remoteFunction('createBookmark')); + + liEl.appendChild(buttonEl); + document.body.appendChild(menuEl); + } + + document.addEventListener('contextmenu', onContextMenu, false); + + document.addEventListener('click', event => { + cleanupMenu(); + }) +} + +init(); diff --git a/app/manifest.json b/app/manifest.json index dcef13b..d11f6fe 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -10,6 +10,8 @@ }, "permissions": [ "", + "bookmarks", + "tabs", "webRequest" ] } diff --git a/app/scripts/background.js b/app/scripts/background.js index 8663d22..a5866e1 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1 +1,2 @@ import '../audio-player/background.js' +import '../create-bookmarks/background.js' diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index e33d912..ccccdce 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -1 +1,2 @@ import '../audio-player/contentscript.js' +import '../create-bookmarks/contentscript.js' diff --git a/package-lock.json b/package-lock.json index c4ff48b..d430a0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5524,6 +5524,29 @@ "integrity": "sha512-ISB42vlgMyM7xE1u6pREeCqmmXjLsYu/nqAR8Dl/gIAnylb+KpRpvKbVkUYNFePhhXn0Obkkc3jasOII9ztUtg==", "dev": true }, + "webextension-rpc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/webextension-rpc/-/webextension-rpc-0.1.0.tgz", + "integrity": "sha512-9JCBkXmeP7ossYauXgfvxqQurO5E0VK7uQnlIyirj1zzhGzV92TvOC4FhhqhYBphV9d0doUg+pCQtvRkGsk8+A==", + "requires": { + "@babel/runtime": "^7.6.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz", + "integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + } + } + }, "webextension-toolbox": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/webextension-toolbox/-/webextension-toolbox-3.0.0.tgz", diff --git a/package.json b/package.json index 95138db..7e40a6f 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "delay": "^4.3.0", - "waveform-playlist": "^3.0.4" + "waveform-playlist": "^3.0.4", + "webextension-rpc": "^0.1.0" } }