Implemented sponsorskip on timeline and add support for automatic,

manual or timeline only
This commit is contained in:
WardPearce
2026-01-06 09:12:54 +13:00
parent 7efc9ed916
commit ddf1080dd4
6 changed files with 96 additions and 49 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "Materialious",
"version": "1.13.0",
"version": "1.13.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "Materialious",
"version": "1.13.0",
"version": "1.13.1",
"license": "MIT",
"dependencies": {
"@capacitor-community/electron": "^5.0.0",
+50 -14
View File
@@ -38,6 +38,7 @@
sponsorBlockCategoriesStore,
sponsorBlockDisplayToastStore,
sponsorBlockStore,
sponsorBlockTimelineStore,
sponsorBlockUrlStore,
synciousInstanceStore,
synciousStore
@@ -71,7 +72,8 @@
showControls = false
}: Props = $props();
let segments: Segment[] = [];
let segments: Segment[] = $state([]);
let segmentManualSkip: Segment | undefined = $state();
let snackBarAlert = $state('');
let originalOrigination: ScreenOrientationResult | undefined;
@@ -264,20 +266,31 @@
document.addEventListener('fullscreenchange', onAndroidFullscreenChange);
}
function skipSegment(segment: Segment) {
if (!playerElement) return;
segmentManualSkip = undefined;
if (Math.round(playerElement.currentTime) >= Math.round(playerElement.duration)) {
return;
}
playerElement.currentTime = segment.endTime + 1;
if (!get(sponsorBlockDisplayToastStore)) {
snackBarAlert = `${get(_)('skipping')} ${segment.category}`;
ui('#snackbar-alert');
}
}
async function setupSponsorSkip() {
if (!$sponsorBlockUrlStore || !$sponsorBlockCategoriesStore || !$sponsorBlockStore) return;
if (
$sponsorBlockCategoriesStore.length > 0 &&
$sponsorBlockUrlStore &&
$sponsorBlockUrlStore !== ''
) {
if ($sponsorBlockCategoriesStore && $sponsorBlockUrlStore && $sponsorBlockUrlStore !== '') {
const sponsorBlock = new SponsorBlock('', { baseURL: $sponsorBlockUrlStore });
try {
segments = await sponsorBlock.getSegments(
data.video.videoId,
$sponsorBlockCategoriesStore as Category[]
Object.keys($sponsorBlockCategoriesStore) as Category[]
);
playerElement?.addEventListener('timeupdate', () => {
@@ -288,12 +301,13 @@
playerElement.currentTime >= segment.startTime &&
playerElement.currentTime <= segment.endTime
) {
if (Math.round(playerElement.currentTime) >= Math.round(playerElement.duration)) {
return;
}
playerElement.currentTime = segment.endTime + 1;
if (!get(sponsorBlockDisplayToastStore)) {
snackBarAlert = `${get(_)('skipping')} ${segment.category}`;
const segmentTrigger = $sponsorBlockCategoriesStore[segment.category];
if (segmentTrigger === 'automatic') {
skipSegment(segment);
} else if (segmentTrigger === 'manual') {
snackBarAlert = `${get(_)('upcomingSegment')} ${segment.category}`;
segmentManualSkip = segment;
ui('#snackbar-alert');
}
}
@@ -747,6 +761,12 @@
playerElement.currentTime = playerElement.currentTime - playerDoubleTapSeek;
return false;
});
Mousetrap.bind('enter', () => {
if (segmentManualSkip) {
skipSegment(segmentManualSkip);
}
});
}
Mousetrap.bind('c', () => {
@@ -1057,7 +1077,7 @@
window.removeEventListener('resize', updateVideoPlayerHeight);
Mousetrap.unbind(['left', 'right', 'space', 'c', 'f', 'shift+left', 'shift+right']);
Mousetrap.unbind(['left', 'right', 'space', 'c', 'f', 'shift+left', 'shift+right', 'enter']);
if (watchProgressInterval) clearTimeout(watchProgressInterval);
@@ -1170,6 +1190,15 @@
)}
></div>
{/each}
{#if !$sponsorBlockTimelineStore}
{#each segments as segment (segment)}
<div
class="chapter-marker segment-marker"
style:left="{(segment.startTime / playerMaxKnownTime) * 100}%"
style:width={getMarkerWidth(segment.startTime, segment.endTime)}
></div>
{/each}
{/if}
</label>
{#if playerTimelineTooltipVisible}
@@ -1443,6 +1472,9 @@
{#if !isEmbed}
<div class="snackbar" id="snackbar-alert">
<span class="bold" style="text-transform: capitalize;">{snackBarAlert}</span>
{#if segmentManualSkip}
<button onclick={() => skipSegment(segmentManualSkip as Segment)}>{$_('skip')}</button>
{/if}
</div>
{/if}
@@ -1547,6 +1579,10 @@
pointer-events: none;
}
.segment-marker {
background-color: var(--tertiary);
}
menu.mobile {
transform: scale(1) translateY(-40%) translateX(0);
width: 300px;
@@ -8,14 +8,10 @@
sponsorBlockCategoriesStore,
sponsorBlockDisplayToastStore,
sponsorBlockStore,
sponsorBlockTimelineStore,
sponsorBlockUrlStore
} from '../../store';
let sponsorCategoriesList: string[] = $state([]);
sponsorBlockCategoriesStore.subscribe((value) => {
sponsorCategoriesList = value;
});
let sponsorBlockInstance = $state(get(sponsorBlockUrlStore));
const sponsorCategories = [
@@ -28,13 +24,21 @@
{ name: $_('layout.sponsors.tangentJokes'), category: 'filler' }
];
function toggleSponsor(category: string) {
if (sponsorCategoriesList.includes(category)) {
sponsorBlockCategoriesStore.set(sponsorCategoriesList.filter((value) => value !== category));
function onSponsorSet(
category: string,
event: Event & { currentTarget: EventTarget & HTMLSelectElement }
) {
const value = event.currentTarget.value as 'automatic' | 'manual' | 'timeline' | 'disabled';
const categories = get(sponsorBlockCategoriesStore);
if (value !== 'disabled') {
categories[category] = value;
} else {
sponsorCategoriesList.push(category);
sponsorBlockCategoriesStore.set(sponsorCategoriesList);
delete categories[category];
}
sponsorBlockCategoriesStore.set(categories);
}
</script>
@@ -89,7 +93,7 @@
</label>
</nav>
<!-- <nav class="no-padding">
<nav class="no-padding">
<div class="max">
<p>{$_('layout.sponsors.disableTimeline')}</p>
</div>
@@ -101,27 +105,36 @@
/>
<span></span>
</label>
</nav> -->
</nav>
<hr style="margin: 1em 0;" />
<p class="bold">{$_('layout.sponsors.Catagories')}</p>
{#each sponsorCategories as sponsor (sponsor)}
{@const currentCatergoryTrigger = $sponsorBlockCategoriesStore[sponsor.category]}
<div class="field middle-align no-margin">
<nav class="no-padding">
<div class="max">
<p>{sponsor.name}</p>
</div>
<label class="switch" tabindex="0">
<input
type="checkbox"
checked={sponsorCategoriesList.includes(sponsor.category)}
onclick={() => toggleSponsor(sponsor.category)}
role="switch"
/>
<span></span>
</label>
<div class="field suffix border">
<select onchange={(event) => onSponsorSet(sponsor.category, event)}>
<option selected={currentCatergoryTrigger === undefined} value="disabled"
>{$_('disabled')}</option
>
<option selected={currentCatergoryTrigger === 'automatic'} value="automatic"
>{$_('layout.sponsors.automatic')}</option
>
<option selected={currentCatergoryTrigger === 'manual'} value="manual"
>{$_('layout.sponsors.manual')}</option
>
<option selected={currentCatergoryTrigger === 'timeline'} value="timeline"
>{$_('layout.sponsors.timeline')}</option
>
</select>
<i>arrow_drop_down</i>
</div>
</nav>
</div>
{/each}
-6
View File
@@ -25,7 +25,6 @@ import {
returnYTDislikesInstanceStore,
returnYtDislikesStore,
showWarningStore,
sponsorBlockCategoriesStore,
sponsorBlockDisplayToastStore,
sponsorBlockStore,
sponsorBlockUrlStore,
@@ -109,11 +108,6 @@ const persistedStores: {
store: sponsorBlockUrlStore,
type: 'string'
},
{
name: 'sponsorBlockCategories',
store: sponsorBlockCategoriesStore,
type: 'array'
},
{
name: 'deArrowInstance',
store: deArrowInstanceStore,
+6 -1
View File
@@ -29,6 +29,8 @@
"donate": "Donate",
"deleteAllHistory": "Delete all history",
"skipping": "Skipping",
"skip": "Skip",
"upcomingSegment": "Upcoming ",
"comments": "comments",
"reply": "reply",
"replies": "replies",
@@ -203,7 +205,10 @@
"intermissionIntroAnimation": "Intermission/Intro Animation",
"credits": "Endcards/Credits",
"preViewRecapHook": "Preview/Recap/Hook",
"tangentJokes": "Filler Tangent/Jokes"
"tangentJokes": "Filler Tangent/Jokes",
"automatic": "Auto skip",
"manual": "Manual skip",
"timeline": "Timeline only"
},
"deArrow": {
"title": "DeArrow",
+4 -5
View File
@@ -279,11 +279,10 @@ export const sponsorBlockUrlStore: Writable<string | null | undefined> = persist
createStorage(),
'sponsorBlockUrl'
);
export const sponsorBlockCategoriesStore: Writable<string[]> = persist(
writable([]),
createStorage(),
'sponsorBlockCategories'
);
export const sponsorBlockCategoriesStore: Writable<
Record<string, 'automatic' | 'manual' | 'timeline' | undefined>
> = persist(writable({}), createStorage(), 'sponsorBlockCategoriesv2');
export const sponsorBlockDisplayToastStore: Writable<boolean> = persist(
writable(false),