@@ -7,8 +7,8 @@ android {
|
||||
applicationId "us.materialio.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 32
|
||||
versionName "1.3.3"
|
||||
versionCode 33
|
||||
versionName "1.3.4"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.3.3",
|
||||
"version": "1.3.4",
|
||||
"description": "Modern material design for Invidious.",
|
||||
"author": {
|
||||
"name": "Ward Pearce",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.3.3",
|
||||
"version": "1.3.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { page } from '$app/stores';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import type { Page } from '@sveltejs/kit';
|
||||
import { SponsorBlock, type Category } from 'sponsorblock-api';
|
||||
import { SponsorBlock, type Category, type Segment } from 'sponsorblock-api';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
@@ -32,6 +32,7 @@
|
||||
sponsorBlockCategoriesStore,
|
||||
sponsorBlockDisplayToastStore,
|
||||
sponsorBlockStore,
|
||||
sponsorBlockTimelineStore,
|
||||
sponsorBlockUrlStore,
|
||||
synciousInstanceStore,
|
||||
synciousStore
|
||||
@@ -43,6 +44,7 @@
|
||||
export let player: MediaPlayerElement;
|
||||
export let isSyncing: boolean = false;
|
||||
export let isEmbed: boolean = false;
|
||||
export let segments: Segment[] = [];
|
||||
|
||||
let src: PlayerSrc = [];
|
||||
let categoryBeingSkipped = '';
|
||||
@@ -54,6 +56,50 @@
|
||||
|
||||
let silenceSkipperInterval: NodeJS.Timeout;
|
||||
|
||||
let sponsorBlockElements: Element[] = [];
|
||||
|
||||
function setSponsorTimeline() {
|
||||
if (!get(sponsorBlockTimelineStore)) return;
|
||||
if (segments.length === 0) return;
|
||||
|
||||
const timeline = document.getElementsByClassName('vds-time-slider')[0];
|
||||
|
||||
if (sponsorBlockElements.length > 0) {
|
||||
sponsorBlockElements.forEach((barDiv) => {
|
||||
if (timeline.contains(barDiv)) {
|
||||
timeline.removeChild(barDiv);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const segmentColors = {
|
||||
sponsor: '00d400',
|
||||
selfpromo: 'ffff00',
|
||||
interaction: 'cc00ff',
|
||||
intro: '00ffff',
|
||||
outro: '0202ed',
|
||||
preview: '008fd6',
|
||||
music_offtopic: 'ff9900',
|
||||
filler: '7300FF'
|
||||
};
|
||||
|
||||
segments.forEach((segment) => {
|
||||
const startPercent = (segment.startTime / data.video.lengthSeconds) * 100;
|
||||
const endPercent = (segment.endTime / data.video.lengthSeconds) * 100;
|
||||
const widthPercent = endPercent - startPercent;
|
||||
|
||||
const barDiv = document.createElement('div');
|
||||
barDiv.classList.add('sponsorskip-bar');
|
||||
barDiv.style.left = `${startPercent}%`;
|
||||
barDiv.style.width = `${widthPercent}%`;
|
||||
barDiv.style.backgroundColor =
|
||||
segment.category in segmentColors ? `#${segmentColors[segment.category]}` : 'grey';
|
||||
|
||||
timeline.appendChild(barDiv);
|
||||
sponsorBlockElements.push(barDiv);
|
||||
});
|
||||
}
|
||||
|
||||
function loadTimeFromUrl(page: Page): boolean {
|
||||
if (player) {
|
||||
const timeGivenUrl = page.url.searchParams.get('time');
|
||||
@@ -243,11 +289,17 @@
|
||||
const sponsorBlock = new SponsorBlock('', { baseURL: sponsorBlockUrl });
|
||||
|
||||
try {
|
||||
const segments = await sponsorBlock.getSegments(
|
||||
segments = await sponsorBlock.getSegments(
|
||||
data.video.videoId,
|
||||
get(sponsorBlockCategoriesStore) as Category[]
|
||||
);
|
||||
|
||||
setSponsorTimeline();
|
||||
|
||||
addEventListener('resize', () => {
|
||||
setSponsorTimeline();
|
||||
});
|
||||
|
||||
player.addEventListener('time-update', (event: MediaTimeUpdateEvent) => {
|
||||
segments.forEach((segment) => {
|
||||
if (
|
||||
@@ -275,6 +327,9 @@
|
||||
}
|
||||
|
||||
player.addEventListener('dash-can-play', async () => {
|
||||
if (get(playerAutoPlayStore)) {
|
||||
player.play();
|
||||
}
|
||||
await loadPlayerPos();
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
sponsorBlockCategoriesStore,
|
||||
sponsorBlockDisplayToastStore,
|
||||
sponsorBlockStore,
|
||||
sponsorBlockTimelineStore,
|
||||
sponsorBlockUrlStore,
|
||||
synciousInstanceStore,
|
||||
synciousStore,
|
||||
@@ -552,6 +553,20 @@
|
||||
</label>
|
||||
</nav>
|
||||
|
||||
<nav class="no-padding">
|
||||
<div class="max">
|
||||
<p>{$_('layout.sponsors.disableTimeline')}</p>
|
||||
</div>
|
||||
<label class="switch">
|
||||
<input
|
||||
bind:checked={$sponsorBlockTimelineStore}
|
||||
on:click={() => sponsorBlockTimelineStore.set(!$sponsorBlockTimelineStore)}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span></span>
|
||||
</label>
|
||||
</nav>
|
||||
|
||||
<hr style="margin: 1em 0;" />
|
||||
|
||||
<p class="bold">{$_('layout.sponsors.Catagories')}</p>
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
"sponsors": {
|
||||
"sponsor": "Sponsor",
|
||||
"disableToast": "Disable toast",
|
||||
"disableTimeline": "Disable segments on timeline",
|
||||
"Catagories": "Catagories",
|
||||
"unpaidSelfPromotion": "Unpaid/Self Promotion",
|
||||
"interactionReminder": "Interaction Reminder (Subscribe)",
|
||||
|
||||
@@ -72,6 +72,10 @@ export const sponsorBlockDisplayToastStore: Writable<boolean> = persisted(
|
||||
'sponsorBlockDisplayToast',
|
||||
false
|
||||
);
|
||||
export const sponsorBlockTimelineStore: Writable<boolean> = persisted(
|
||||
'sponsorBlockTimeline',
|
||||
true
|
||||
);
|
||||
|
||||
export const deArrowInstanceStore = persisted(
|
||||
'deArrowInstance',
|
||||
@@ -93,5 +97,4 @@ export const playlistSettingsStore: Writable<Record<string, { shuffle: boolean;
|
||||
export const miniPlayerSrcStore: Writable<{ video: VideoPlay; time: number; } | null> =
|
||||
writable(null);
|
||||
|
||||
|
||||
export const silenceSkipperStore: Writable<boolean> = persisted('silenceSkipper', false);
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
} from '$lib/store';
|
||||
import { mergeMediaFromDASH } from '$lib/videoDownload';
|
||||
import type { DataConnection } from 'peerjs';
|
||||
import { type Segment } from 'sponsorblock-api';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
@@ -74,6 +75,8 @@
|
||||
let audioMode = get(playerListenByDefaultStore);
|
||||
let player: MediaPlayerElement;
|
||||
|
||||
let segments: Segment[] = [];
|
||||
|
||||
let showTranscript = false;
|
||||
|
||||
playlistSettingsStore.subscribe((playlistSetting) => {
|
||||
@@ -454,7 +457,13 @@
|
||||
<div
|
||||
style="max-height: 80vh;max-width: calc(80vh * 16 / 9);overflow: hidden;position: relative;flex: 1;"
|
||||
>
|
||||
<Player {data} {audioMode} isSyncing={$syncPartyPeerStore !== null} bind:player />
|
||||
<Player
|
||||
bind:segments
|
||||
{data}
|
||||
{audioMode}
|
||||
isSyncing={$syncPartyPeerStore !== null}
|
||||
bind:player
|
||||
/>
|
||||
</div>
|
||||
{/key}
|
||||
</div>
|
||||
@@ -482,8 +491,8 @@
|
||||
<img class="circle large" src={pfp} alt="Channel profile" />
|
||||
{/await}
|
||||
<div>
|
||||
<p class="bold">{data.video.author}</p>
|
||||
<p>{data.video.subCountText}</p>
|
||||
<p style="margin: 0;" class="bold">{data.video.author}</p>
|
||||
<p style="margin: 0;">{data.video.subCountText}</p>
|
||||
</div>
|
||||
</nav>
|
||||
</a>
|
||||
|
||||
@@ -27,6 +27,19 @@
|
||||
margin-right: 5px !important;
|
||||
}
|
||||
|
||||
.sponsorskip-bar {
|
||||
position: absolute;
|
||||
margin-top: 2px;
|
||||
height: 5px;
|
||||
top: -1;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
.vds-time-slider {
|
||||
position: relative !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.vds-poster :where(img) {
|
||||
object-fit: cover !important;
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import json
|
||||
import os
|
||||
import re
|
||||
|
||||
LATEST_VERSION = "1.3.3"
|
||||
LATEST_VERSION = "1.3.4"
|
||||
WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious")
|
||||
|
||||
ROOT_PACKAGE = os.path.join(WORKING_DIR, "package.json")
|
||||
|
||||
Reference in New Issue
Block a user