Merge pull request #979 from Materialious/update/1.9.8

Update/1.9.8
This commit is contained in:
Ward
2025-06-18 06:19:57 +12:00
committed by GitHub
parent d4e25689a9
commit 06680bba57
17 changed files with 198 additions and 110 deletions
+2 -2
View File
@@ -7,8 +7,8 @@ android {
applicationId "us.materialio.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 124
versionName "1.9.7"
versionCode 125
versionName "1.9.8"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -59,7 +59,11 @@
<release version="1.9.7" date="2025-6-15">
<release version="1.9.8" date="2025-6-18">
<url>https://github.com/Materialious/Materialious/releases/tag/1.9.8</url>
</release>
<release version="1.9.7" date="2025-6-15">
<url>https://github.com/Materialious/Materialious/releases/tag/1.9.7</url>
</release>
<release version="1.9.6" date="2025-6-14">
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "Materialious",
"version": "1.9.7",
"version": "1.9.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "Materialious",
"version": "1.9.7",
"version": "1.9.8",
"license": "MIT",
"dependencies": {
"@capacitor-community/electron": "^5.0.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "Materialious",
"version": "1.9.7",
"version": "1.9.8",
"description": "Modern material design for Invidious.",
"author": {
"name": "Ward Pearce",
+6 -6
View File
@@ -1,12 +1,12 @@
{
"name": "materialious",
"version": "1.9.4",
"version": "1.9.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "materialious",
"version": "1.9.4",
"version": "1.9.8",
"hasInstallScript": true,
"dependencies": {
"@capacitor-community/electron": "^5.0.1",
@@ -37,7 +37,7 @@
"sponsorblock-api": "^0.2.4",
"svelte-infinite-loading": "^1.4.0",
"svelte-persisted-store": "^0.12.0",
"youtubei.js": "^13.4.0"
"youtubei.js": "^14.0.0"
},
"devDependencies": {
"@capacitor/assets": "^3.0.5",
@@ -13748,9 +13748,9 @@
}
},
"node_modules/youtubei.js": {
"version": "13.4.0",
"resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-13.4.0.tgz",
"integrity": "sha512-+fmIZU/dWAjsROONrASy1REwVpy6umAPVuoNLr/4iNmZXl84LyBef0n3hrd1Vn9035EuINToGyQcBmifwUEemA==",
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-14.0.0.tgz",
"integrity": "sha512-KAFttOw+9fwwBUvBc1T7KzMNBLczDOuN/dfote8BA9CABxgx8MPgV+vZWlowdDB6DnHjSUYppv+xvJ4VNBLK9A==",
"funding": [
"https://github.com/sponsors/LuanRT"
],
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "materialious",
"version": "1.9.7",
"version": "1.9.8",
"private": true,
"scripts": {
"dev": "vite dev",
@@ -68,6 +68,6 @@
"sponsorblock-api": "^0.2.4",
"svelte-infinite-loading": "^1.4.0",
"svelte-persisted-store": "^0.12.0",
"youtubei.js": "^13.4.0"
"youtubei.js": "^14.0.0"
}
}
}
+65 -6
View File
@@ -16,13 +16,14 @@
import 'shaka-player/dist/controls.css';
import shaka from 'shaka-player/dist/shaka-player.ui';
import { SponsorBlock, type Category, type Segment } from 'sponsorblock-api';
import { onDestroy, onMount } from 'svelte';
import { onDestroy, onMount, tick } from 'svelte';
import { _ } from '$lib/i18n';
import { get } from 'svelte/store';
import { deleteVideoProgress, getVideoProgress, saveVideoProgress } from '../api';
import type { VideoPlay } from '../api/model';
import {
authStore,
darkModeStore,
instanceStore,
playerAndroidLockOrientation,
playerAutoPlayStore,
@@ -38,7 +39,8 @@
sponsorBlockStore,
sponsorBlockUrlStore,
synciousInstanceStore,
synciousStore
synciousStore,
themeColorStore
} from '../store';
import { getDynamicTheme, setStatusBarColor } from '../theme';
import { injectSABR } from '$lib/sabr';
@@ -74,6 +76,55 @@
const STORAGE_KEY_VOLUME = 'shaka-preferred-volume';
async function updateSeekBarTheme() {
await tick();
shakaUi.configure({
seekBarColors: {
played: (await getDynamicTheme())['--primary']
}
});
setChapterMarkers();
}
themeColorStore.subscribe(updateSeekBarTheme);
darkModeStore.subscribe(updateSeekBarTheme);
function setChapterMarkers() {
const shakaControls = shakaUi.getControls();
if (data.content.timestamps && shakaControls) {
const seekBar = shakaControls
.getControlsContainer()
.querySelector('.shaka-seek-bar-container');
data.content.timestamps.forEach((chapter, index) => {
const nextChapter = data.content.timestamps[index + 1];
const marker = document.createElement('div');
marker.classList.add('chapter-marker');
const startPercent = (chapter.time / data.video.lengthSeconds) * 100;
let widthPercent: number;
if (nextChapter) {
widthPercent = ((nextChapter.time - chapter.time) / data.video.lengthSeconds) * 100;
} else {
widthPercent = 100 - startPercent;
}
marker.style.left = `${startPercent}%`;
marker.style.width = `${widthPercent}%`;
const tooltip = document.createElement('div');
tooltip.classList.add('tooltip');
tooltip.textContent = chapter.title;
marker.appendChild(tooltip);
if (seekBar) seekBar.appendChild(marker);
});
}
}
function restoreDefaultLanguage() {
if ($playerDefaultLanguage) {
const audioLanguages = player.getAudioLanguages();
@@ -541,6 +592,8 @@
}
});
setChapterMarkers();
try {
await loadVideo();
} catch (error: unknown) {
@@ -651,7 +704,7 @@
<div
id="shaka-container"
class="youtube-theme"
class="player-theme"
data-shaka-player-container
class:hide={showVideoRetry}
>
@@ -695,19 +748,25 @@
{/if}
<style>
.youtube-theme {
.player-theme {
max-height: 80vh;
max-width: calc(80vh * 16 / 9);
overflow: hidden;
position: relative;
flex: 1;
background-color: black;
aspect-ratio: 16 / 9;
}
video {
width: 100%;
height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-height: 100%;
max-width: 100%;
object-fit: contain;
background-color: black;
}
.hide {
+12 -9
View File
@@ -1,6 +1,4 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { goto } from '$app/navigation';
import Mousetrap from 'mousetrap';
import { createEventDispatcher, onMount, tick } from 'svelte';
@@ -37,7 +35,9 @@
}, 250);
}
function handleSubmit() {
function handleSubmit(event: Event | undefined = undefined) {
if (event) event.preventDefault();
if (search.trim() === '') return;
selectedSuggestionIndex = -1;
@@ -98,7 +98,15 @@
});
</script>
<form onsubmit={preventDefault(handleSubmit)}>
<form
onsubmit={handleSubmit}
onclick={async () => {
showSearchBox = true;
await tick();
document.getElementById('search')?.focus();
}}
role="presentation"
>
<div class="field prefix fill no-margin search rounded">
<i class="front">search</i>
<input
@@ -106,11 +114,6 @@
placeholder={$_('searchPlaceholder')}
bind:value={search}
class="rounded"
onclick={async () => {
showSearchBox = true;
await tick();
document.getElementById('search')?.focus();
}}
/>
{#if showSearchBox}
<menu class="min suggestions-container rounded">
@@ -22,6 +22,7 @@
} else {
await Clipboard.write({ string: `${location.origin}/watch/${video.videoId}` });
}
(document.activeElement as HTMLElement)?.blur();
}}
>
<div class="min">{$_('player.share.materialiousLink')}</div>
@@ -29,16 +30,20 @@
<li
class="row"
role="presentation"
onclick={async () =>
await Clipboard.write({ string: `https://redirect.invidious.io/watch?v=${video.videoId}` })}
onclick={async () => {
await Clipboard.write({ string: `https://redirect.invidious.io/watch?v=${video.videoId}` });
(document.activeElement as HTMLElement)?.blur();
}}
>
<div class="min">{$_('player.share.invidiousRedirect')}</div>
</li>
<li
class="row"
role="presentation"
onclick={async () =>
await Clipboard.write({ string: `https://www.youtube.com/watch?v=${video.videoId}` })}
onclick={async () => {
await Clipboard.write({ string: `https://www.youtube.com/watch?v=${video.videoId}` });
(document.activeElement as HTMLElement)?.blur();
}}
>
<div class="min">{$_('player.share.youtubeLink')}</div>
</li>
+79 -58
View File
@@ -1,14 +1,14 @@
.youtube-theme {
.player-theme {
font-family: var(--font);
color: var(--on-surface);
}
.youtube-theme .shaka-video-container * {
.player-theme .shaka-video-container * {
font-family: var(--font);
color: var(--on-surface);
}
.youtube-theme .shaka-bottom-controls {
.player-theme .shaka-bottom-controls {
width: 100%;
padding: 0;
padding-bottom: 0;
@@ -17,30 +17,30 @@
flex-direction: column;
}
.youtube-theme .shaka-ad-controls {
.player-theme .shaka-ad-controls {
order: 1;
}
.youtube-theme .shaka-controls-button-panel {
.player-theme .shaka-controls-button-panel {
order: 2;
height: 2.5rem; /* 40px */
padding: 0 0.625rem; /* 10px */
}
.youtube-theme .shaka-range-container {
.player-theme .shaka-range-container {
margin: 0.25rem 0.625rem; /* 4px 10px */
top: 0;
}
.youtube-theme .shaka-small-play-button {
.player-theme .shaka-small-play-button {
order: -3;
}
.youtube-theme .shaka-mute-button {
.player-theme .shaka-mute-button {
order: -2;
}
.youtube-theme .shaka-controls-button-panel > * {
.player-theme .shaka-controls-button-panel > * {
margin: 0;
padding: 0.1875rem 0.5rem; /* 3px 8px */
color: #fff;
@@ -48,41 +48,41 @@
font-size: 2rem; /* 32px */
}
.youtube-theme .shaka-controls-button-panel > *:hover {
.player-theme .shaka-controls-button-panel > *:hover {
color: var(--primary);
}
.youtube-theme .shaka-controls-button-panel > *:focus {
.player-theme .shaka-controls-button-panel > *:focus {
outline: none;
box-shadow: inset 0 0 0 2px var(--primary);
color: var(--primary);
}
.youtube-theme .shaka-controls-button-panel .shaka-volume-bar-container {
.player-theme .shaka-controls-button-panel .shaka-volume-bar-container {
height: 0.1875rem; /* 3px */
padding: 0;
}
.youtube-theme .shaka-controls-button-panel .shaka-volume-bar-container:hover,
.youtube-theme .shaka-controls-button-panel .shaka-volume-bar-container:focus {
.player-theme .shaka-controls-button-panel .shaka-volume-bar-container:hover,
.player-theme .shaka-controls-button-panel .shaka-volume-bar-container:focus {
display: block;
opacity: 1;
padding: 0 0.375rem; /* 6px */
}
.youtube-theme .shaka-mute-button:hover + div {
.player-theme .shaka-mute-button:hover + div {
opacity: 1;
width: 3.125rem; /* 50px */
padding: 0 0.375rem; /* 6px */
}
.youtube-theme .shaka-current-time {
.player-theme .shaka-current-time {
padding: 0 0.625rem; /* 10px */
font-size: 1rem; /* 16px */
color: #fff !important;
}
.youtube-theme .shaka-seek-bar-container {
.player-theme .shaka-seek-bar-container {
height: 0.1875rem; /* 3px */
position: relative;
top: -0.0625rem; /* -1px */
@@ -90,30 +90,30 @@
border-radius: 0;
}
.youtube-theme .shaka-seek-bar-container .shaka-range-element {
.player-theme .shaka-seek-bar-container .shaka-range-element {
opacity: 0;
}
.youtube-theme .shaka-seek-bar-container:hover {
.player-theme .shaka-seek-bar-container:hover {
height: 0.3125rem; /* 5px */
top: 0;
cursor: pointer;
}
.youtube-theme .shaka-seek-bar-container:hover .shaka-range-element {
.player-theme .shaka-seek-bar-container:hover .shaka-range-element {
opacity: 1;
cursor: pointer;
}
.youtube-theme .shaka-seek-bar-container input[type='range']::-webkit-slider-thumb,
.youtube-theme .shaka-seek-bar-container input[type='range']::-moz-range-thumb,
.youtube-theme .shaka-seek-bar-container input[type='range']::-ms-thumb {
.player-theme .shaka-seek-bar-container input[type='range']::-webkit-slider-thumb,
.player-theme .shaka-seek-bar-container input[type='range']::-moz-range-thumb,
.player-theme .shaka-seek-bar-container input[type='range']::-ms-thumb {
background: var(--primary);
cursor: pointer;
}
.youtube-theme .shaka-overflow-menu,
.youtube-theme .shaka-settings-menu {
.player-theme .shaka-overflow-menu,
.player-theme .shaka-settings-menu {
border-radius: 0.25rem;
background: var(--surface-container-low);
right: 0.625rem; /* 10px */
@@ -128,31 +128,31 @@
pointer-events: auto !important;
}
.youtube-theme .shaka-settings-menu {
.player-theme .shaka-settings-menu {
padding-bottom: 0.5rem; /* 8px */
}
.youtube-theme .shaka-settings-menu button {
.player-theme .shaka-settings-menu button {
font-size: 1rem; /* 16px */
border: none;
margin: 0;
}
.youtube-theme .shaka-settings-menu button span {
.player-theme .shaka-settings-menu button span {
margin-left: 2.0625rem; /* 33px */
font-size: 1rem; /* 16px */
}
.youtube-theme .shaka-settings-menu button[aria-selected='true'] {
.player-theme .shaka-settings-menu button[aria-selected='true'] {
display: flex;
}
.youtube-theme .shaka-settings-menu button[aria-selected='true'] span {
.player-theme .shaka-settings-menu button[aria-selected='true'] span {
order: 2;
margin-left: 0;
}
.youtube-theme .shaka-settings-menu button[aria-selected='true'] i {
.player-theme .shaka-settings-menu button[aria-selected='true'] i {
order: 1;
font-size: 1rem; /* 16px */
padding-left: 0.3125rem; /* 5px */
@@ -163,8 +163,8 @@
pointer-events: auto !important;
}
.youtube-theme .shaka-overflow-menu button,
.youtube-theme .shaka-settings-menu button {
.player-theme .shaka-overflow-menu button,
.player-theme .shaka-settings-menu button {
padding: 0;
margin-bottom: 0.5em;
visibility: visible !important;
@@ -172,11 +172,11 @@
pointer-events: auto !important;
}
.youtube-theme .shaka-overflow-menu button i {
.player-theme .shaka-overflow-menu button i {
display: none;
}
.youtube-theme .shaka-overflow-menu button .shaka-overflow-button-label {
.player-theme .shaka-overflow-menu button .shaka-overflow-button-label {
display: flex;
align-items: start;
cursor: default;
@@ -198,7 +198,7 @@
justify-content: start;
}
.youtube-theme .shaka-overflow-menu button .shaka-overflow-button-label span {
.player-theme .shaka-overflow-menu button .shaka-overflow-button-label span {
flex-shrink: 0;
padding-left: 0.9375rem; /* 15px */
font-size: 1rem; /* 16px */
@@ -210,22 +210,22 @@
font-weight: 200 !important;
}
.youtube-theme .shaka-overflow-menu span + span {
.player-theme .shaka-overflow-menu span + span {
color: var(--primary-text);
font-weight: 400 !important;
font-size: 1rem !important;
padding-right: 0.5rem; /* 8px */
}
.youtube-theme .shaka-overflow-menu .shaka-pip-button span + span {
.player-theme .shaka-overflow-menu .shaka-pip-button span + span {
padding-right: 0.9375rem !important; /* 15px */
}
.youtube-theme .shaka-overflow-menu .shaka-pip-button span + span:after {
.player-theme .shaka-overflow-menu .shaka-pip-button span + span:after {
content: '';
}
.youtube-theme .shaka-back-to-overflow-button {
.player-theme .shaka-back-to-overflow-button {
padding: 0.5rem 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
font-size: 1rem; /* 16px */
@@ -233,50 +233,71 @@
height: 2.5rem; /* 40px */
}
.youtube-theme .shaka-back-to-overflow-button .material-icons-round {
.player-theme .shaka-back-to-overflow-button .material-icons-round {
font-size: 1rem; /* 16px */
padding-right: 0.625rem; /* 10px */
}
.youtube-theme .shaka-back-to-overflow-button span {
.player-theme .shaka-back-to-overflow-button span {
margin-left: 0.1875rem !important; /* 3px */
}
.youtube-theme .shaka-overflow-menu button:hover,
.youtube-theme .shaka-settings-menu button:hover {
.player-theme .shaka-overflow-menu button:hover,
.player-theme .shaka-settings-menu button:hover {
background-color: var(--surface-bright);
cursor: pointer;
}
.youtube-theme .shaka-overflow-menu button:hover label,
.youtube-theme .shaka-settings-menu button:hover label {
.player-theme .shaka-overflow-menu button:hover label,
.player-theme .shaka-settings-menu button:hover label {
cursor: pointer;
}
.youtube-theme .shaka-overflow-menu button:focus,
.youtube-theme .shaka-settings-menu button:focus {
.player-theme .shaka-overflow-menu button:focus,
.player-theme .shaka-settings-menu button:focus {
background-color: var(--surface-bright);
border: none;
outline: none;
}
.youtube-theme .shaka-overflow-menu button,
.youtube-theme .shaka-settings-menu button {
.player-theme .shaka-overflow-menu button,
.player-theme .shaka-settings-menu button {
color: var(--primary-text);
}
.youtube-theme .shaka-captions-off {
.player-theme .shaka-captions-off {
color: var(--surface-bright);
}
.youtube-theme .shaka-overflow-menu-button {
.player-theme .shaka-overflow-menu-button {
font-size: 1rem; /* 16px */
margin-right: 0.3125rem; /* 5px */
}
.player-theme .chapter-marker {
bottom: 100%;
height: 4px;
margin-bottom: 8px;
position: absolute;
background: linear-gradient(
to right,
rgba(255, 255, 255, 0.3) 0%,
rgba(255, 255, 255, 0.3) calc(100% - 5px),
transparent 100%
);
}
@media screen and (max-width: 650px) {
.youtube-theme .shaka-overflow-menu,
.youtube-theme .shaka-settings-menu {
.player-theme .shaka-end-time {
display: none;
}
.player-theme .chapter-marker {
height: 8px;
}
.player-theme .shaka-overflow-menu,
.player-theme .shaka-settings-menu {
width: 100% !important;
min-width: 100% !important;
position: fixed !important;
@@ -310,8 +331,8 @@
0s background-color;
}
.youtube-theme .shaka-overflow-menu::before,
.youtube-theme .shaka-settings-menu::before {
.player-theme .shaka-overflow-menu::before,
.player-theme .shaka-settings-menu::before {
content: '' !important;
position: fixed !important;
top: 0 !important;
@@ -322,8 +343,8 @@
pointer-events: auto !important;
}
.youtube-theme .shaka-overflow-menu:hover,
.youtube-theme .shaka-settings-menu:hover {
.player-theme .shaka-overflow-menu:hover,
.player-theme .shaka-settings-menu:hover {
pointer-events: auto !important;
opacity: 1 !important;
visibility: visible !important;
+2 -3
View File
@@ -66,8 +66,7 @@ export async function patchYoutubeJs(videoId: string): Promise<VideoPlay> {
parse: false
});
const rawNextResponse = await watchEndpoint.call(youtube.actions, {
override_endpoint: '/next',
...extraArgs
override_endpoint: '/next'
});
const video = new YT.VideoInfo(
[rawPlayerResponse, rawNextResponse],
@@ -99,7 +98,7 @@ export async function patchYoutubeJs(videoId: string): Promise<VideoPlay> {
? video.streaming_data.hls_manifest_url // HLS is preferred for DVR streams.
: `${video.streaming_data.dash_manifest_url}/mpd_version/7`;
} else {
dashUri = `data:application/dash+xml;base64,${btoa(await video.toDash(undefined, undefined))}`;
dashUri = `data:application/dash+xml;base64,${btoa(await video.toDash(undefined))}`;
}
}
@@ -9,7 +9,7 @@ export class EndTimeElement extends shaka.ui.Element {
this.video = (this.player as shaka.Player).getMediaElement() as HTMLMediaElement;
this.button = document.createElement('button');
this.button.classList.add('shaka-current-time');
this.button.classList.add('shaka-current-time', 'shaka-end-time');
this.button.disabled = true;
(this.parent as HTMLElement).appendChild(this.button);
+1 -1
View File
@@ -1,7 +1,7 @@
import { decodeHtmlCharCodes } from './misc';
import { convertToSeconds } from './numbers';
type Timestamps = { title: string; time: number; timePretty: string; endTime: number }[];
export type Timestamps = { title: string; time: number; timePretty: string; endTime: number }[];
export interface PhasedDescription {
description: string;
@@ -142,6 +142,7 @@
role="presentation"
onclick={async () => {
await Clipboard.write({ string: location.href });
(document.activeElement as HTMLElement)?.blur();
}}
>
{$_('player.share.materialiousLink')}
@@ -155,6 +156,7 @@
await Clipboard.write({
string: `https://www.youtube.com/channel/${data.channel.authorId}`
});
(document.activeElement as HTMLElement)?.blur();
}}
>
{$_('player.share.youtubeLink')}
@@ -93,6 +93,7 @@
role="presentation"
onclick={async () => {
await Clipboard.write({ string: location.href });
(document.activeElement as HTMLElement)?.blur();
}}
>
{$_('player.share.materialiousLink')}
@@ -105,6 +106,7 @@
await Clipboard.write({
string: `https://www.youtube.com/playlist?list=${data.playlist.playlistId}`
});
(document.activeElement as HTMLElement)?.blur();
}}
>
{$_('player.share.youtubeLink')}
@@ -420,15 +420,6 @@
ui('#pause-timer');
}
function goToChapter() {
const chaptersScrollable = document.getElementById('chapters');
const currentTimestamp = document.getElementById(`timestamp-${currentChapterStartTime}`);
if (!chaptersScrollable || !currentTimestamp) return;
chaptersScrollable.scrollTop = currentTimestamp.offsetTop - chaptersScrollable.offsetTop - 550;
}
</script>
<svelte:head>
@@ -556,7 +547,10 @@
<li
role="presentation"
class="row"
onclick={async () => await toggleVideoToPlaylist(personalPlaylist.playlistId)}
onclick={async () => {
await toggleVideoToPlaylist(personalPlaylist.playlistId);
(document.activeElement as HTMLElement)?.blur();
}}
>
<nav>
<span class="max">{personalPlaylist.title}</span>
@@ -619,7 +613,7 @@
{#if data.content.timestamps.length > 0}
<article>
<details>
<summary id="chapter-section" class="bold none" onclick={goToChapter}>
<summary id="chapter-section" class="bold none">
<nav>
<div class="max">
<p>{$_('player.chapters')}</p>
@@ -633,7 +627,6 @@
{#each data.content.timestamps as timestamp}
<li
role="presentation"
id={`timestamp-${timestamp.time}`}
onclick={() => {
if (playerElement) playerElement.currentTime = timestamp.time;
}}
+1 -1
View File
@@ -3,7 +3,7 @@ import os
import re
from datetime import datetime
LATEST_VERSION = "1.9.7"
LATEST_VERSION = "1.9.8"
RELEASE_DATE = datetime.now().strftime("%Y-%-m-%d") # Format: YYYY-M-D
WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious")