diff --git a/src/renderer/assets/img/loop.svg b/src/renderer/assets/img/loop.svg new file mode 100644 index 00000000..e7e24c6e --- /dev/null +++ b/src/renderer/assets/img/loop.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/components/ft-video-player/ft-video-player.js b/src/renderer/components/ft-video-player/ft-video-player.js index ab29039f..75205226 100644 --- a/src/renderer/components/ft-video-player/ft-video-player.js +++ b/src/renderer/components/ft-video-player/ft-video-player.js @@ -1,4 +1,5 @@ import Vue from 'vue' +import { mapActions } from 'vuex' import FtCard from '../ft-card/ft-card.vue' import $ from 'jquery' @@ -87,6 +88,7 @@ export default Vue.extend({ 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', + 'loopButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', @@ -156,6 +158,7 @@ export default Vue.extend({ } this.createFullWindowButton() + this.createLoopButton() this.determineFormatType() this.determineMaxFramerate() }, @@ -533,7 +536,53 @@ export default Vue.extend({ } }, - createFullWindowButton: function() { + createLoopButton: function () { + const v = this + const VjsButton = videojs.getComponent('Button') + const loopButton = videojs.extend(VjsButton, { + constructor: function(player, options) { + VjsButton.call(this, player, options) + }, + handleClick: function() { + v.toggleVideoLoop() + }, + createControlTextEl: function (button) { + return $(button).html($('
') + .attr('title', 'Toggle Loop')) + } + }) + videojs.registerComponent('loopButton', loopButton) + }, + + toggleVideoLoop: async function () { + if (!this.player.loop()) { + const currentTheme = localStorage.getItem('mainColor') + const colorNames = this.$store.state.utils.colorClasses + const colorValues = this.$store.state.utils.colorValues + + const nameIndex = colorNames.findIndex((color) => { + return color === currentTheme + }) + + const themeTextColor = await this.calculateColorLuminance(colorValues[nameIndex]) + + $('#loopButton').addClass('vjs-icon-loop-active') + + if (themeTextColor === '#000000') { + $('#loopButton').addClass('loop-black') + $('#loopButton').removeClass('loop-white') + } + + this.player.loop(true) + } else { + $('#loopButton').removeClass('vjs-icon-loop-active') + $('#loopButton').removeClass('loop-black') + $('#loopButton').addClass('loop-white') + this.player.loop(false) + } + }, + + createFullWindowButton: function () { const v = this const VjsButton = videojs.getComponent('Button') const fullWindowButton = videojs.extend(VjsButton, { @@ -827,6 +876,10 @@ export default Vue.extend({ break } } - } + }, + + ...mapActions([ + 'calculateColorLuminance' + ]) } }) diff --git a/src/renderer/videoJS.css b/src/renderer/videoJS.css index 12fad58f..767006db 100644 --- a/src/renderer/videoJS.css +++ b/src/renderer/videoJS.css @@ -449,7 +449,6 @@ body.vjs-full-window { content: url(assets/img/open_fullwindow.svg); } - .vjs-icon-fullwindow-exit, .video-js.vjs-fullwindow .vjs-fullwindow-control .vjs-icon-placeholder { font-family: VideoJS; font-weight: normal; @@ -459,6 +458,30 @@ body.vjs-full-window { content: url(assets/img/close_fullwindow.svg); } +.vjs-icon-loop, .video-js .vjs-fullwindow-control .vjs-icon-placeholder { + color: white !important; + margin-top: 5px !important; + padding-top: 5px !important; + cursor: pointer; +} + +.vjs-icon-loop-active { + background-color: var(--primary-color); +} + +.vjs-icon-loop:before, .video-js.vjs-fullwindow .vjs-fullwindow-control .vjs-icon-placeholder:before { + content: url(assets/img/loop.svg); + /* filter: invert(1) drop-shadow(1px 0px 0px var(--primary-color)); */ +} + +.loop-black:before { + filter: brightness(0%); +} + +.loop-white:before { + filter: none; +} + .vjs-full-window .video-js.vjs-fullscreen { position: fixed; overflow: hidden;