Detecting manually determined video and player elements implemented

This commit is contained in:
Tamius Han 2019-06-10 23:45:15 +02:00
parent 03d8a99885
commit 08c257ec00
6 changed files with 262 additions and 224 deletions

View File

@ -927,7 +927,30 @@ var ExtensionConf = {
stretch: Stretch.Default, stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default, videoAlignment: VideoAlignment.Default,
keyboardShortcutsEnabled: ExtensionMode.Default, keyboardShortcutsEnabled: ExtensionMode.Default,
DOM: {
video: {
manual: false,
querySelectors: '',
},
player: {
manual: true,
useRelativeAncestor: true,
querySelectors: '',
videoAncestor: 1,
playerNodeCss: '',
}
}
// videoElement: { // extra stuff for video tag
// querySelectors: [], // array of strings with css selectors
// userCss: [], // additional styles that user can define for video element
// },
// playerElement: {
// querySelectors: [], // array of strings with css selectors
// videoAncestor: 1, // if not falsey, the number represents how far up the DOM (in nodes)
// from video the player lies. Can also be object (valid properties are
// 'fullscreen', 'embed' and 'normal')
// userCss: [],
// }
}, },
} }
} }

View File

@ -236,8 +236,10 @@ class Settings {
site = window.location.hostname; site = window.location.hostname;
if (!site) { if (!site) {
console.log("[Settings::canStartExtension] window.location.hostname is null or undefined:", window.location.hostname) if (Debug.debug) {
console.log("active settings:", this.active) console.log("[Settings::canStartExtension] window.location.hostname is null or undefined:", window.location.hostname)
console.log("active settings:", this.active)
}
return ExtensionMode.Disabled; return ExtensionMode.Disabled;
} }
} }
@ -288,17 +290,17 @@ class Settings {
// } // }
try{ try{
// if site is not defined, we use default mode: // if site is not defined, we use default mode:
if (! this.active.sites[site]) { if (! this.active.sites[site]) {
return this.active.sites['@global'].mode === ExtensionMode.Enabled; return this.active.sites['@global'].mode === ExtensionMode.Enabled;
} }
if(this.active.sites['@global'].mode === ExtensionMode.Enabled) { if(this.active.sites['@global'].mode === ExtensionMode.Enabled) {
return this.active.sites[site].mode !== ExtensionMode.Disabled; return this.active.sites[site].mode !== ExtensionMode.Disabled;
} else if (this.active.sites['@global'].mode === ExtensionMode.Whitelist) { } else if (this.active.sites['@global'].mode === ExtensionMode.Whitelist) {
return this.active.sites[site].mode === ExtensionMode.Enabled; return this.active.sites[site].mode === ExtensionMode.Enabled;
} else { } else {
return false; return false;
} }
} catch(e){ } catch(e){
if(Debug.debug){ if(Debug.debug){
console.log("[Settings.js::canStartExtension] Something went wrong — are settings defined/has init() been called?\nSettings object:", this) console.log("[Settings.js::canStartExtension] Something went wrong — are settings defined/has init() been called?\nSettings object:", this)

View File

@ -65,11 +65,30 @@ class PageInfo {
this.actionHandlerInitQueue = []; this.actionHandlerInitQueue = [];
} }
getVideos(host) {
if (this.settings.active.sites[host]
&& this.settings.active.sites[host].DOM
&& this.settings.active.sites[host].DOM.video
&& this.settings.active.sites[host].DOM.video.manual
&& this.settings.active.sites[host].DOM.video.querySelector){
const videos = document.querySelectorAll(this.settings.active.sites[host].DOM.video.querySelector);
if (videos.length) {
return videos;
} else {
if (Debug.debug) {
console.log("[PageInfo::getVideos] Finding videos by querySelector failed. Trying fallback mode as well.");
}
}
}
return document.getElementsByTagName('video');
}
rescan(rescanReason){ rescan(rescanReason){
const oldVideoCount = this.videos.length; const oldVideoCount = this.videos.length;
try{ try{
var vids = document.getElementsByTagName('video'); var vids = this.getVideos(window.location.host);
if(!vids || vids.length == 0){ if(!vids || vids.length == 0){
this.hasVideos = false; this.hasVideos = false;

View File

@ -224,16 +224,115 @@ class PlayerData {
} }
collectionHas(collection, element) {
for (let i = 0, len = a.length; i < len; i++) {
if (a[i] == b) {
return true;
}
}
return false;
}
getPlayerDimensions(elementNames){ getPlayer() {
// element names — reserved for future use. If element names are provided, this function should return first element that const host = window.location.host;
// has classname or id that matches at least one in the elementNames array. let element = this.video.parentNode;
var element = this.video.parentNode;
if(! element ){ if(! element ){
if(Debug.debug) if(Debug.debug) {
console.log("[PlayerDetect::_pd_getPlayerDimensions] element is not valid, doing nothing.", element) console.log("[PlayerDetect::_pd_getPlayer] element is not valid, doing nothing.", element)
}
if(this.element) {
const ths = this;
}
this.element = undefined;
this.dimensions = undefined;
return;
}
if (this.settings.active.sites[host]
&& this.settings.active.sites[host].DOM
&& this.settings.active.sites[host].DOM.player
&& this.settings.active.sites[host].DOM.player.manual) {
if (this.settings.active.sites[host].DOM.player.useRelativeAncestor
&& this.settings.active.sites[host].DOM.player.videoAncestor) {
let parentsLeft = this.settings.active.sites[host].DOM.player.videoAncestor - 1;
while (parentsLeft --> 0) {
element = element.parentNode;
}
if (element) {
return element;
}
} else if (this.settings.active.sites[host].DOM.player.querySelectors) {
const allSelectors = document.querySelectorAll(this.settings.active.sites[host].DOM.player.querySelectors);
while (element && !this.collectionHas(allSelectors, element)) {
element = element.parentNode;
}
if (element) {
return element;
}
}
}
var trustCandidateAfterGrows = 2; // if candidate_width or candidate_height increases in either dimensions this many
// times, we say we found our player. (This number ignores weird elements)
// in case our <video> is bigger than player in one dimension but smaller in the other
// if site is coded properly, player can't be wider than that
var candidate_width = Math.max(element.offsetWidth, window.innerWidth);
var candidate_height = Math.max(element.offsetHeight, window.innerHeight);
var playerCandidateNode = element;
// if we haven't found element using fancy methods, we resort to the good old fashioned way
var grows = trustCandidateAfterGrows;
while(element != undefined){
// odstranimo čudne elemente, ti bi pokvarili zadeve
// remove weird elements, those would break our stuff
if ( element.offsetWidth == 0 || element.offsetHeight == 0){
element = element.parentNode;
continue;
}
if ( element.offsetHeight <= candidate_height &&
element.offsetWidth <= candidate_width ){
// if we're in fullscreen, we only consider elements that are exactly as big as the monitor.
if( ! isFullScreen ||
(element.offsetWidth == window.innerWidth && element.offsetHeight == window.innerHeight) ){
playerCandidateNode = element;
candidate_width = element.offsetWidth;
candidate_height = element.offsetHeight;
grows = trustCandidateAfterGrows;
if(Debug.debug){
console.log("Found new candidate for player. Dimensions: w:", candidate_width, "h:",candidate_height, "node:", playerCandidateNode);
}
}
}
else if(grows --<= 0){
if(Debug.debug && Debug.playerDetect){
console.log("Current element grew in comparrison to the child. We probably found the player. breaking loop, returning current result");
}
break;
}
element = element.parentNode;
}
return element;
}
getPlayerDimensions(){
let element = this.getPlayer();
if(! element ){
if(Debug.debug) {
console.log("[PlayerDetect::_pd_getPlayer] element is not valid, doing nothing.", element)
}
if(this.element) { if(this.element) {
const ths = this; const ths = this;
} }
@ -243,57 +342,6 @@ class PlayerData {
} }
var isFullScreen = PlayerData.isFullScreen(); var isFullScreen = PlayerData.isFullScreen();
var trustCandidateAfterGrows = 2; // if candidate_width or candidate_height increases in either dimensions this many
// times, we say we found our player. (This number ignores weird elements)
// in case our <video> is bigger than player in one dimension but smaller in the other
// if site is coded properly, player can't be wider than that
var candidate_width = Math.max(element.offsetWidth, window.innerWidth);
var candidate_height = Math.max(element.offsetHeight, window.innerHeight);
var playerCandidateNode = element;
try {
var grows = trustCandidateAfterGrows;
while(element != undefined){
// odstranimo čudne elemente, ti bi pokvarili zadeve
// remove weird elements, those would break our stuff
if ( element.offsetWidth == 0 || element.offsetHeight == 0){
element = element.parentNode;
continue;
}
if ( element.offsetHeight <= candidate_height &&
element.offsetWidth <= candidate_width ){
// if we're in fullscreen, we only consider elements that are exactly as big as the monitor.
if( ! isFullScreen ||
(element.offsetWidth == window.innerWidth && element.offsetHeight == window.innerHeight) ){
playerCandidateNode = element;
candidate_width = element.offsetWidth;
candidate_height = element.offsetHeight;
grows = trustCandidateAfterGrows;
if(Debug.debug){
console.log("Found new candidate for player. Dimensions: w:", candidate_width, "h:",candidate_height, "node:", playerCandidateNode);
}
}
}
else if(grows --<= 0){
if(Debug.debug && Debug.playerDetect){
console.log("Current element grew in comparrison to the child. We probably found the player. breaking loop, returning current result");
}
break;
}
element = element.parentNode;
}
}
catch (e) {
console.log("pdeeee,",e);
}
if (isFullScreen && playerCandidateNode == element) { if (isFullScreen && playerCandidateNode == element) {
this.dimensions = { this.dimensions = {
@ -301,21 +349,18 @@ class PlayerData {
height: window.innerHeight, height: window.innerHeight,
fullscreen: true fullscreen: true
} }
const ths = this;
if (this.element != element) { if (this.element != element) {
this.element = element; this.element = element;
this.makeOverlay() this.makeOverlay()
} }
} else { } else {
this.dimensions = { this.dimensions = {
width: candidate_width, width: element.offsetWidth,
height: candidate_height, height: element.offsetWidth,
fullscreen: isFullScreen fullscreen: isFullScreen
}; };
const ths = this; if(this.element != element) {
if(this.element != playerCandidateNode) { this.element = element;
this.element = playerCandidateNode;
this.makeOverlay(); this.makeOverlay();
} }
} }

View File

@ -223,10 +223,9 @@ class VideoData {
} }
isPlaying() { isPlaying() {
// console.log("is playing? video:", this.video, "ctime:", this.video.currentTime,
console.log("is playing? video:", this.video, "ctime:", this.video.currentTime, // "paused/ended:", this.video.paused, this.video.ended,
"paused/ended:", this.video.paused, this.video.ended, // "is playing?", this.video && this.video.currentTime > 0 && !this.video.paused && !this.video.ended);
"is playing?", this.video && this.video.currentTime > 0 && !this.video.paused && !this.video.ended);
return this.video && this.video.currentTime > 0 && !this.video.paused && !this.video.ended; return this.video && this.video.currentTime > 0 && !this.video.paused && !this.video.ended;
} }

View File

@ -1,94 +1,69 @@
<template> <template>
<div class="w100 flex flex-column" style="padding-bottom: 20px"> <div class="w100 flex flex-column" style="padding-bottom: 20px">
<div class="label">Video detection settings<br/><small>for {{site}}</small></div> <div class="label">
Video detection settings<br/><small>for {{site}}</small>
</div>
<div class="description">Video is just the moving picture bit without the player.</div> <div class="description">Video is just the moving picture bit without the player.</div>
<div class="indent"> <div class="">
<div class="flex flex-row row-padding"> <div class="">
<div class="flex label-secondary form-label">Manually specify video element</div> <input :checked="!videoManualQs"
<div class="flex flex-input"> @change="toggleVideoManualQs"
<input :checked="videoManualQs" type="checkbox"
@change="toggleVideoManualQs" /> Detect automatically
type="checkbox"
/>
</div>
</div> </div>
<div class="flex flex-column"> <div class="flex flex-column">
<div class="flex label-secondary form-label">Query selectors</div> <div class="flex label-secondary form-label">Query selectors</div>
<QuerySelectorSetting v-for="(qs, index) of siteVideoQuerySelectors" <input type="text"
:disabled="!videoManualQs" v-model="videoQs"
:key="index" :disabled="!videoManualQs"
@update="updateQuerySelector('video', index, $event)" @change="updateVideoQuerySelector"
@delete="deleteQuerySelector('video', index)" @blur="updateVideoQuerySelector"
/>
<div class="flex label-secondary form-label">Add new:</div>
<QuerySelectorSetting v-if="videoManualQs"
adding
@create="addQuerySelector('video', $event)"
/> />
</div> </div>
</div> </div>
<div class="label">Player detection settings<br/><small>for {{site}}</small></div> <div class="label">
<div class="description">Player is the frame around the video. Extension crops/stretches the video to fit the player.</div> Player detection settings<br/><small>for {{site}}</small>
<div class="indent"> </div>
<div class="flex flex-row"> <div class="description">
<div class="flex label-secondary form-label">Detect automatically</div> Player is the frame around the video. Extension crops/stretches the video to fit the player.
<div class="flex flex-input"> </div>
<input :checked="playerManualQs" <div class="">
@change="togglePlayerManualQs" <div class="">
type="checkbox" <input :checked="!playerManualQs"
/> @change="togglePlayerManualQs"
</div> type="checkbox"
</div> /> Detect automatically
<div class="flex flex-row">
<div class="flex label-secondary form-label">Specify player node parent index instead of query selector</div>
<div class="flex flex-input">
<input :checked="playerByNodeIndex"
@change="toggleByNodeIndex"
type="checkbox"
/>
</div>
</div> </div>
<div class="flex flex-column"> <div class="flex flex-column">
<div class="flex label-secondary form-label">Query selectors</div> <div class="">Query selectors:</div>
<QuerySelectorSetting v-for="(qs, index) of sitePlayerQuerySelectors" <input type="text"
:disabled="!playerManualQs || playerByNodeIndex" v-model="playerQs"
:key="index" @change="updatePlayerQuerySelector"
@update="updateQuerySelector('video', index, $event)" @blur="updatePlayerQuerySelector"
@delete="deleteQuerySelector('video', index)" :disabled="playerByNodeIndex || !playerManualQs"
/>
<div class="flex label-secondary form-label">Add new:</div>
<QuerySelectorSetting v-if="videoManualQs"
adding
@create="addQuerySelector('video', $event)"
/> />
</div> </div>
<div class="">
<div v-if="playerByNodeIndex"> <input :checked="playerByNodeIndex"
<div class="flex flex-row row-padding"> :disabled="!playerManualQs"
<div class="flex label-secondary form-label">Player node parent index</div> @change="toggleByNodeIndex"
<div class="flex flex-input"> type="checkbox"
<input :value="playerByNodeIndex" /> Specify player node parent index instead
@change="toggleByNodeIndex" </div>
type="number"
/> <div class="flex flex-column">
</div> <div class="">Player node parent index:</div>
</div> <input v-model="playerParentNodeIndex"
<div class="flex flex-row row-padding"> :disabled="!playerByNodeIndex || !playerManualQs"
<div class="flex label-secondary form-label">Player node css</div> @change="updatePlayerParentNodeIndex"
<div class="flex flex-input"> @blur="updatePlayerParentNodeIndex"
<input :value="playerByNodeIndex" type="number"
@change="toggleByNodeIndex" />
type="number"
/>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
@ -103,64 +78,33 @@ export default {
}, },
data() { data() {
return { return {
videoManualQs: false,
videoQs: '',
playerManualQs: false,
playerQs: '',
playerByNodeIndex: false,
playerParentNodeIndex: undefined,
}; };
}, },
props: { props: {
site: String, site: String,
settings: Object, settings: Object,
}, },
computed: { created() {
siteVideoQuerySelectors() { try {
try { this.videoManualQs = settings.active.sites[this.site].DOM.video.manual || this.videoManualQs;
return settings.active.sites[this.site].DOM.video.querySelectors; this.videoQs = settings.active.sites[this.site].DOM.video.querySelectors;
} catch (e) { } catch (e) {
return []; // that's here just in case relevant settings for this site don't exist yet
} }
},
sitePlayerQuerySelectors() { try {
try { this.playerManualQs = settings.active.sites[this.site].DOM.player.manual || this.playerManualQs;
return settings.active.sites[this.site].DOM.player.querySelectors; this.playerQs = settings.active.sites[this.site].DOM.player.querySelectors;
} catch (e) { this.playerByNodeIndex = settings.active.sites[this.site].DOM.player.useRelativeAncestor;
return []; this.playerParentNodeIndex = settings.active.sites[this.site].DOM.player.videoAncestor;
} } catch (e) {
}, // that's here just in case relevant settings for this site don't exist yet
videoManualQs: function() {
try {
console.log("this.settings.active.sites[this.site].DOM.video.enabled", this.settings.active.sites[this.site].DOM.video.enabled)
return this.settings.active.sites[this.site].DOM.video.enabled
} catch (e) {
console.log("e",e)
return false;
}
},
playerManualQs() {
try {
return this.settings.active.sites[this.site].DOM.player.enabled
} catch (e) {
return false;
}
},
playerByNodeIndex() {
try {
return this.settings.active.sites[this.site].DOM.player.byNodeIndex
} catch (e) {
return false;
}
},
playerNodeIndex() {
try {
return this.settings.active.sites[this.site].DOM.player.nodeIndex
} catch (e) {
return undefined;
}
},
playerNodeIndexCss() {
try {
return this.settings.active.sites[this.site].DOM.player.nodeIndexCss
} catch (e) {
return undefined;
}
} }
}, },
methods: { methods: {
@ -180,39 +124,45 @@ export default {
} }
if (! this.settings.active.sites[this.site].DOM[scope]) { if (! this.settings.active.sites[this.site].DOM[scope]) {
this.settings.active.sites[this.site].DOM[scope] = { this.settings.active.sites[this.site].DOM[scope] = {
enabled: false, manual: false,
querySelectors: [], querySelectors: '',
byNodeIndex: scope === 'player' ? false : undefined, useRelativeAncestor: scope === 'player' ? false : undefined,
nodeIndex: undefined, videoAncestor: undefined,
nodeIndexCss: scope === 'player' ? '' : undefined, playerNodeCss: scope === 'player' ? '' : undefined,
} }
} }
}, },
updateQuerySelector(scope, index, $event) { updateVideoQuerySelector() {
this.ensureSettings(scope); this.ensureSettings('video');
this.settings.active.sites[this.site].DOM[scope].querySelectors[index] = $event; this.settings.active.sites[this.site].DOM.video.querySelectors = this.videoQs;
this.settings.save(); this.settings.save();
}, },
addQuerySelector(scope, index, $event) { updatePlayerQuerySelector() {
this.ensureSettings(scope); this.ensureSettings('player');
this.settings.active.sites[this.site].DOM[scope].querySelectors.push($event); this.settings.active.sites[this.site].DOM.player.querySelectors = this.playerQs;
this.settings.save();
}, },
deleteQuerySelector(scope, index) { updatePlayerParentNodeIndex() {
this.settings.active.sites[this.site].DOM[scope].querySelectors.splice(index, 1); this.ensureSettings('player');
this.settings.active.sites[this.site].DOM.player.videoAncestor = this.playerParentNodeIndex;
this.settings.save();
}, },
toggleVideoManualQs($event) { toggleVideoManualQs() {
this.ensureSettings('video'); this.ensureSettings('video');
this.settings.active.sites[this.site].DOM.video.enabled = !this.settings.active.sites[this.site].DOM.video.enabled; this.settings.active.sites[this.site].DOM.video.enabled = !this.settings.active.sites[this.site].DOM.video.enabled;
this.videoManualQs = !this.videoManualQs;
this.settings.save(); this.settings.save();
}, },
togglePlayerManualQs($event) { togglePlayerManualQs() {
this.ensureSettings('player'); this.ensureSettings('player');
this.settings.active.sites[this.site].DOM.player.enabled = !this.settings.active.sites[this.site].DOM.player.enabled; this.settings.active.sites[this.site].DOM.player.enabled = !this.settings.active.sites[this.site].DOM.player.enabled;
this.playerManualQs = !this.playerManualQs;
this.settings.save(); this.settings.save();
}, },
toggleByNodeIndex($event) { toggleByNodeIndex() {
this.ensureSettings('player'); this.ensureSettings('player');
this.settings.active.sites[this.site].DOM.player.byNodeIndex = !this.settings.active.sites[this.site].DOM.player.byNodeIndex; this.settings.active.sites[this.site].DOM.player.useRelativeAncestor = !this.settings.active.sites[this.site].DOM.player.useRelativeAncestor;
this.playerByNodeIndex = !this.playerByNodeIndex;
this.settings.save(); this.settings.save();
}, },
} }