埋め込みYouTube動画の再生位置を移動するボタン
何?
https://gyazo.com/27af9f0f863bdc7ed0f99884aab0ca5c
用途
講演やYouTuberの動画の再生地点をブックマークに残して見返す
一つの動画で複数箇所ブクマしたいときに便利
既知の問題
ページに複数の動画がある場合は常に最初の動画に対する操作になる
https://github.com/lefb766/scrapbox-video-seek-button/pull/2
スマートフォンではドラッグ操作がややしんどい
実装するかもしれない改善
YouTube以外の動画に対応
ScrapboxはVimeoにも対応していて、この拡張もVimeoに対応できそう
参考資料: YouTube and postMessage
onclickでジャンプできるようにしたい
上記の既知の問題が両方解決する
たとえば[> 1:23:45]という風に文字を装飾するとScrapboxの機能でclass="deco->"なspan要素ができる
これでスタイルを変えられる
onclick発火は作者のJS力が低くてどうすればできるか悩み中
https://github.com/actions/upload-release-asset
GitHub
#UserScript
code:script.js
(function () {function i(e,r){if(!(e instanceof r))throw new TypeError("Cannot call a class as a function")}function a(e,r){for(var t=0;t<r.length;t++){var n=rt;n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}function j(e,r,t){return r&&a(e.prototype,r),t&&a(e,t),e}function k(e,r){if(!(e instanceof r))throw new TypeError("Cannot call a class as a function")}function b(e,r){for(var t=0;t<r.length;t++){var a=rt;a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}function l(e,r,t){return r&&b(e.prototype,r),t&&b(e,t),e}var m=function(){function e(r){k(this,e),this.iframe=r}return l(e,{key:"seek",value:function(e){n(this.iframe,e)}},{key:"ensureControllable",value:function(){var e=this.iframe.src;/enablejsapi/.test(e)||(this.iframe.src=e+"&enablejsapi=1")}},[{key:"tryAccept",value:function(r){var t=r.src;return t&&t.startsWith("https://www.youtube.com/")?new e(r):null}}]),e}();function n(e,r){var t={event:"command",func:"seekTo",args:r,!0};e.contentWindow.postMessage(JSON.stringify(t),"https://www.youtube.com")}var o=".iframe-video-player > iframe",c=function(){function e(r){i(this,e),this.videos=r}return j(e,[{key:"topOrNull",value:function(){return this.videos.sort(function(e,r){return d(e)-d(r)}),0===this.videos.length?null:this.videos0}}],{key:"findFromDocument",value:function(r){var t=r.querySelectorAll(o);return new e(Array.from(t).map(function(e){return m.tryAccept(e)}).filter(function(e){return!!e}))}}),e}();function d(e){return e.iframe.getBoundingClientRect().top}function p(e,r){if(!(e instanceof r))throw new TypeError("Cannot call a class as a function")}function f(e,r){for(var n=0;n<r.length;n++){var t=rn;t.enumerable=t.enumerable||!1,t.configurable=!0,"value"in t&&(t.writable=!0),Object.defineProperty(e,t.key,t)}}function q(e,r,n){return r&&f(e.prototype,r),n&&f(e,n),e}var g=function(){function e(r){p(this,e),this.seconds=r}return q(e,[{key:"toFriendlyFormat",value:function(){var e=[],r=this.seconds%60,n=Math.round(this.seconds/60-.5);if(!n)return""+r;e.push(h(r));var t=Math.round(n/60-.5);return e.push(t?h(n%60):n%60),t&&e.push(""+t),e.reverse().join(":")}}],[{key:"parse",value:function(r){var n=r.match(/1-90-9*(:0-9+)*/);if(!n)return null;var t=n0.split(":");return t.length>3?null:new e(t.map(function(e){return parseInt(e)}).reduce(function(e,r){return 60*e+r},0))}}]),e}();function h(e){var r="0"+e;return r.slice(r.length-2,r.length)}setInterval(function(){c.findFromDocument(document).videos.forEach(function(o){o.ensureControllable()})},2e3),scrapbox.PopupMenu.addButton({title:function(o){var e=g.parse(o);return null===e?null:"Jump to ".concat(e.toFriendlyFormat())},onClick:function(o){var e=g.parse(o);if(null===e)return null;var n=c.findFromDocument(document).topOrNull();n&&n.seek(e.seconds)}});})();