Merge pull request #2055 from 9ary/sponsorblock-for-upstream

Add more options for sponsorblock
This commit is contained in:
Kavin 2023-02-08 00:32:56 +00:00 committed by GitHub
commit f9d405781a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 152 additions and 161 deletions

View file

@ -184,83 +184,13 @@
/> />
</label> </label>
<div v-if="sponsorBlock"> <div v-if="sponsorBlock">
<label class="pref" for="chkSkipSponsors"> <label v-for="[name, item] in skipOptions" class="pref" :for="'ddlSkip_' + name" :key="name">
<strong v-t="'actions.skip_sponsors'" /> <strong v-t="item.label" />
<input <select :id="'ddlSkip_' + name" v-model="item.value" class="select w-auto" @change="onChange($event)">
id="chkSkipSponsors" <option v-t="'actions.no'" value="no" />
v-model="skipSponsor" <option v-t="'actions.skip_button_only'" value="button" />
class="checkbox" <option v-t="'actions.skip_automatically'" value="auto" />
type="checkbox" </select>
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkSkipIntro">
<strong v-t="'actions.skip_intro'" />
<input id="chkSkipIntro" v-model="skipIntro" class="checkbox" type="checkbox" @change="onChange($event)" />
</label>
<label class="pref" for="chkSkipOutro">
<strong v-t="'actions.skip_outro'" />
<input id="chkSkipOutro" v-model="skipOutro" class="checkbox" type="checkbox" @change="onChange($event)" />
</label>
<label class="pref" for="chkSkipPreview">
<strong v-t="'actions.skip_preview'" />
<input
id="chkSkipPreview"
v-model="skipPreview"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkSkipInteraction">
<strong v-t="'actions.skip_interaction'" />
<input
id="chkSkipInteraction"
v-model="skipInteraction"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkSkipSelfPromo">
<strong v-t="'actions.skip_self_promo'" />
<input
id="chkSkipSelfPromo"
v-model="skipSelfPromo"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkSkipNonMusic">
<strong v-t="'actions.skip_non_music'" />
<input
id="chkSkipNonMusic"
v-model="skipMusicOffTopic"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkSkipHighlight">
<strong v-t="'actions.skip_highlight'" />
<input
id="chkSkipHighlight"
v-model="skipHighlight"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkSkipFiller">
<strong v-t="'actions.skip_filler_tangent'" />
<input
id="chkSkipFiller"
v-model="skipFiller"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label> </label>
<label class="pref" for="chkShowMarkers"> <label class="pref" for="chkShowMarkers">
<strong v-t="'actions.show_markers'" /> <strong v-t="'actions.show_markers'" />
@ -272,6 +202,16 @@
@change="onChange($event)" @change="onChange($event)"
/> />
</label> </label>
<label class="pref" for="txtMinSegmentLength">
<strong v-t="'actions.min_segment_length'" />
<input
id="txtMinSegmentLength"
v-model="minSegmentLength"
class="input w-24"
type="text"
@change="onChange($event)"
/>
</label>
</div> </div>
<h2 class="text-center" v-t="'titles.instance'" /> <h2 class="text-center" v-t="'titles.instance'" />
<label class="pref" for="ddlInstanceSelection"> <label class="pref" for="ddlInstanceSelection">
@ -391,16 +331,19 @@ export default {
selectedAuthInstance: null, selectedAuthInstance: null,
instances: [], instances: [],
sponsorBlock: true, sponsorBlock: true,
skipSponsor: true, skipOptions: new Map([
skipIntro: false, ["sponsor", { value: "auto", label: "actions.skip_sponsors" }],
skipOutro: false, ["intro", { value: "no", label: "actions.skip_intro" }],
skipPreview: false, ["outro", { value: "no", label: "actions.skip_outro" }],
skipInteraction: true, ["preview", { value: "no", label: "actions.skip_preview" }],
skipSelfPromo: true, ["interaction", { value: "auto", label: "actions.skip_interaction" }],
skipMusicOffTopic: true, ["selfpromo", { value: "auto", label: "actions.skip_self_promo" }],
skipHighlight: false, ["music_offtopic", { value: "auto", label: "actions.skip_non_music" }],
skipFiller: false, ["poi_highlight", { value: "no", label: "actions.skip_highlight" }],
["filler", { value: "no", label: "actions.skip_filler_tangent" }],
]),
showMarkers: true, showMarkers: true,
minSegmentLength: 0,
selectedTheme: "dark", selectedTheme: "dark",
autoPlayVideo: true, autoPlayVideo: true,
listen: false, listen: false,
@ -497,55 +440,25 @@ export default {
this.selectedAuthInstance = this.getPreferenceString("auth_instance_url", this.selectedInstance); this.selectedAuthInstance = this.getPreferenceString("auth_instance_url", this.selectedInstance);
this.sponsorBlock = this.getPreferenceBoolean("sponsorblock", true); this.sponsorBlock = this.getPreferenceBoolean("sponsorblock", true);
if (localStorage.getItem("selectedSkip") !== null) { var skipOptions, skipList;
var skipList = localStorage.getItem("selectedSkip").split(","); if ((skipOptions = this.getPreferenceJSON("skipOptions")) !== undefined) {
this.skipSponsor = Object.entries(skipOptions).forEach(([key, value]) => {
this.skipIntro = var opt = this.skipOptions.get(key);
this.skipOutro = if (opt !== undefined) opt.value = value;
this.skipPreview = else console.log("Unknown sponsor type: " + key);
this.skipInteraction = });
this.skipSelfPromo = } else if ((skipList = this.getPreferenceString("selectedSkip")) !== undefined) {
this.skipMusicOffTopic = skipList = skipList.split(",");
this.skipHighlight = this.skipOptions.forEach(opt => (opt.value = "no"));
this.skipFiller =
false;
skipList.forEach(skip => { skipList.forEach(skip => {
switch (skip) { var opt = this.skipOptions.get(skip);
case "sponsor": if (opt !== undefined) opt.value = "auto";
this.skipSponsor = true; else console.log("Unknown sponsor type: " + skip);
break;
case "intro":
this.skipIntro = true;
break;
case "outro":
this.skipOutro = true;
break;
case "preview":
this.skipPreview = true;
break;
case "interaction":
this.skipInteraction = true;
break;
case "selfpromo":
this.skipSelfPromo = true;
break;
case "music_offtopic":
this.skipMusicOffTopic = true;
break;
case "poi_highlight":
this.skipHighlight = true;
break;
case "filler":
this.skipFiller = true;
break;
default:
console.log("Unknown sponsor type: " + skip);
break;
}
}); });
} }
this.showMarkers = this.getPreferenceBoolean("showMarkers", true); this.showMarkers = this.getPreferenceBoolean("showMarkers", true);
this.minSegmentLength = Math.max(this.getPreferenceNumber("minSegmentLength", 0), 0);
this.selectedTheme = this.getPreferenceString("theme", "dark"); this.selectedTheme = this.getPreferenceString("theme", "dark");
this.autoPlayVideo = this.getPreferenceBoolean("playerAutoPlay", true); this.autoPlayVideo = this.getPreferenceBoolean("playerAutoPlay", true);
this.listen = this.getPreferenceBoolean("listen", false); this.listen = this.getPreferenceBoolean("listen", false);
@ -594,19 +507,12 @@ export default {
localStorage.setItem("auth_instance_url", this.selectedAuthInstance); localStorage.setItem("auth_instance_url", this.selectedAuthInstance);
localStorage.setItem("sponsorblock", this.sponsorBlock); localStorage.setItem("sponsorblock", this.sponsorBlock);
var sponsorSelected = []; var skipOptions = {};
if (this.skipSponsor) sponsorSelected.push("sponsor"); this.skipOptions.forEach((v, k) => (skipOptions[k] = v.value));
if (this.skipIntro) sponsorSelected.push("intro"); localStorage.setItem("skipOptions", JSON.stringify(skipOptions));
if (this.skipOutro) sponsorSelected.push("outro");
if (this.skipPreview) sponsorSelected.push("preview");
if (this.skipInteraction) sponsorSelected.push("interaction");
if (this.skipSelfPromo) sponsorSelected.push("selfpromo");
if (this.skipMusicOffTopic) sponsorSelected.push("music_offtopic");
if (this.skipHighlight) sponsorSelected.push("poi_highlight");
if (this.skipFiller) sponsorSelected.push("filler");
localStorage.setItem("selectedSkip", sponsorSelected);
localStorage.setItem("showMarkers", this.showMarkers); localStorage.setItem("showMarkers", this.showMarkers);
localStorage.setItem("minSegmentLength", this.minSegmentLength);
localStorage.setItem("theme", this.selectedTheme); localStorage.setItem("theme", this.selectedTheme);
localStorage.setItem("playerAutoPlay", this.autoPlayVideo); localStorage.setItem("playerAutoPlay", this.autoPlayVideo);
localStorage.setItem("listen", this.listen); localStorage.setItem("listen", this.listen);

View file

@ -6,6 +6,17 @@
:class="{ 'player-container': !isEmbed }" :class="{ 'player-container': !isEmbed }"
> >
<video ref="videoEl" class="w-full" data-shaka-player :autoplay="shouldAutoPlay" :loop="selectedAutoLoop" /> <video ref="videoEl" class="w-full" data-shaka-player :autoplay="shouldAutoPlay" :loop="selectedAutoLoop" />
<button
v-if="inSegment"
class="skip-segment-button"
type="button"
:aria-label="$t('actions.skip_segment')"
aria-pressed="false"
@click="onClickSkipSegment"
>
<span v-t="'actions.skip_segment'" />
<i class="material-icons-round">skip_next</i>
</button>
</div> </div>
</template> </template>
@ -51,6 +62,7 @@ export default {
lastUpdate: new Date().getTime(), lastUpdate: new Date().getTime(),
initialSeekComplete: false, initialSeekComplete: false,
destroying: false, destroying: false,
inSegment: false,
}; };
}, },
computed: { computed: {
@ -88,7 +100,7 @@ export default {
this.hotkeysPromise.then(() => { this.hotkeysPromise.then(() => {
var self = this; var self = this;
this.$hotkeys( this.$hotkeys(
"f,m,j,k,l,c,space,up,down,left,right,0,1,2,3,4,5,6,7,8,9,shift+n,shift+,,shift+.", "f,m,j,k,l,c,space,up,down,left,right,0,1,2,3,4,5,6,7,8,9,shift+n,shift+,,shift+.,return",
function (e, handler) { function (e, handler) {
const videoEl = self.$refs.videoEl; const videoEl = self.$refs.videoEl;
switch (handler.key) { switch (handler.key) {
@ -184,6 +196,9 @@ export default {
case "shift+.": case "shift+.":
self.$player.trickPlay(Math.min(videoEl.playbackRate + 0.25, 2)); self.$player.trickPlay(Math.min(videoEl.playbackRate + 0.25, 2));
break; break;
case "return":
self.skipSegment(videoEl);
break;
} }
}, },
); );
@ -361,19 +376,13 @@ export default {
this.$emit("timeupdate", time); this.$emit("timeupdate", time);
this.updateProgressDatabase(time); this.updateProgressDatabase(time);
if (this.sponsors && this.sponsors.segments) { if (this.sponsors && this.sponsors.segments) {
this.sponsors.segments.map(segment => { const segment = this.findCurrentSegment(time);
if (!segment.skipped || this.selectedAutoLoop) { this.inSegment = !!segment;
const end = segment.segment[1]; if (segment?.autoskip && (!segment.skipped || this.selectedAutoLoop)) {
if (time >= segment.segment[0] && time < end) { this.skipSegment(videoEl, segment);
console.log("Skipped segment at " + time);
videoEl.currentTime = end;
segment.skipped = true;
return;
} }
} }
}); });
}
});
videoEl.addEventListener("volumechange", () => { videoEl.addEventListener("volumechange", () => {
this.setPreference("volume", videoEl.volume, true); this.setPreference("volume", videoEl.volume, true);
@ -398,6 +407,21 @@ export default {
//TODO: Add sponsors on seekbar: https://github.com/ajayyy/SponsorBlock/blob/e39de9fd852adb9196e0358ed827ad38d9933e29/src/js-components/previewBar.ts#L12 //TODO: Add sponsors on seekbar: https://github.com/ajayyy/SponsorBlock/blob/e39de9fd852adb9196e0358ed827ad38d9933e29/src/js-components/previewBar.ts#L12
}, },
findCurrentSegment(time) {
return this.sponsors?.segments?.find(s => time >= s.segment[0] && time < s.segment[1]);
},
onClickSkipSegment() {
const videoEl = this.$refs.videoEl;
this.skipSegment(videoEl);
},
skipSegment(videoEl, segment) {
const time = videoEl.currentTime;
if (!segment) segment = this.findCurrentSegment(time);
if (!segment) return;
console.log("Skipped segment at " + time);
videoEl.currentTime = segment.segment[1];
segment.skipped = true;
},
setPlayerAttrs(localPlayer, videoEl, uri, mime, shaka) { setPlayerAttrs(localPlayer, videoEl, uri, mime, shaka) {
const url = "/watch?v=" + this.video.id; const url = "/watch?v=" + this.video.id;
@ -678,6 +702,11 @@ export default {
}, },
watch: { watch: {
sponsors() { sponsors() {
const skipOptions = this.getPreferenceJSON("skipOptions", {});
this.sponsors?.segments?.forEach(segment => {
const option = skipOptions[segment.category];
segment.autoskip = option === undefined || option === "auto";
});
if (this.getPreferenceBoolean("showMarkers", true)) { if (this.getPreferenceBoolean("showMarkers", true)) {
this.shakaPromise.then(() => { this.shakaPromise.then(() => {
this.updateMarkers(); this.updateMarkers();
@ -719,4 +748,34 @@ export default {
background-color: rgba(0, 0, 0, 0.6) !important; background-color: rgba(0, 0, 0, 0.6) !important;
padding: 0.09em 0; padding: 0.09em 0;
} }
.skip-segment-button {
/* position button above player overlay */
z-index: 1000;
position: absolute;
transform: translate(0, -50%);
top: 50%;
right: 0;
background-color: rgb(0 0 0 / 0.5);
border: 2px rgba(255, 255, 255, 0.75) solid;
border-right: 0;
border-radius: 0.75em;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
padding: 0.5em;
/* center text vertically */
display: flex;
align-items: center;
justify-content: center;
line-height: 1.5em;
}
.skip-segment-button .material-icons-round {
font-size: 1.6em !important;
line-height: inherit !important;
}
</style> </style>

View file

@ -367,15 +367,28 @@ export default {
return this.fetchJson(this.apiUrl() + "/streams/" + this.getVideoId()); return this.fetchJson(this.apiUrl() + "/streams/" + this.getVideoId());
}, },
async fetchSponsors() { async fetchSponsors() {
return await this.fetchJson(this.apiUrl() + "/sponsors/" + this.getVideoId(), { var selectedSkip = this.getPreferenceString(
category: "selectedSkip",
'["' + "sponsor,interaction,selfpromo,music_offtopic",
this.getPreferenceString("selectedSkip", "sponsor,interaction,selfpromo,music_offtopic").replaceAll( ).split(",");
",", const skipOptions = this.getPreferenceJSON("skipOptions");
'","', if (skipOptions !== undefined) {
) + selectedSkip = Object.keys(skipOptions).filter(
'"]', k => skipOptions[k] !== undefined && skipOptions[k] !== "no",
);
}
const sponsors = await this.fetchJson(this.apiUrl() + "/sponsors/" + this.getVideoId(), {
category: JSON.stringify(selectedSkip),
}); });
const minSegmentLength = Math.max(this.getPreferenceNumber("minSegmentLength", 0), 0);
sponsors.segments = sponsors.segments.filter(segment => {
const length = segment.segment[1] - segment.segment[0];
return length >= minSegmentLength;
});
return sponsors;
}, },
toggleComments() { toggleComments() {
this.showComments = !this.showComments; this.showComments = !this.showComments;

View file

@ -30,6 +30,8 @@
"back": "Back", "back": "Back",
"uses_api_from": "Uses the API from ", "uses_api_from": "Uses the API from ",
"enable_sponsorblock": "Enable Sponsorblock", "enable_sponsorblock": "Enable Sponsorblock",
"skip_button_only": "Show skip button",
"skip_automatically": "Automatically",
"skip_sponsors": "Skip Sponsors", "skip_sponsors": "Skip Sponsors",
"skip_intro": "Skip Intermission/Intro Animation", "skip_intro": "Skip Intermission/Intro Animation",
"skip_outro": "Skip Endcards/Credits", "skip_outro": "Skip Endcards/Credits",
@ -40,6 +42,8 @@
"skip_highlight": "Skip Highlight", "skip_highlight": "Skip Highlight",
"skip_filler_tangent": "Skip Filler Tangent", "skip_filler_tangent": "Skip Filler Tangent",
"show_markers": "Show Markers on Player", "show_markers": "Show Markers on Player",
"min_segment_length": "Minimum Segment Length (in seconds)",
"skip_segment": "Skip Segment",
"theme": "Theme", "theme": "Theme",
"auto": "Auto", "auto": "Auto",
"dark": "Dark", "dark": "Dark",

View file

@ -162,6 +162,15 @@ const mixin = {
return Number(value); return Number(value);
} else return defaultVal; } else return defaultVal;
}, },
getPreferenceJSON(key, defaultVal) {
var value;
if (
(value = new URLSearchParams(window.location.search).get(key)) !== null ||
(this.testLocalStorage && (value = localStorage.getItem(key)) !== null)
) {
return JSON.parse(value);
} else return defaultVal;
},
apiUrl() { apiUrl() {
return this.getPreferenceString("instance", "https://pipedapi.kavin.rocks"); return this.getPreferenceString("instance", "https://pipedapi.kavin.rocks");
}, },