Merge pull request #1543 from Materialious/update/1.16.16

Update/1.16.16
This commit is contained in:
Ward
2026-03-09 00:08:15 +13:00
committed by GitHub
17 changed files with 119 additions and 90 deletions
+2 -2
View File
@@ -7,8 +7,8 @@ android {
applicationId "us.materialio.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 232
versionName "1.16.15"
versionCode 233
versionName "1.16.16"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -74,7 +74,11 @@
<release version="1.16.15" date="2026-3-08">
<release version="1.16.16" date="2026-3-08">
<url>https://github.com/Materialious/Materialious/releases/tag/1.16.16</url>
</release>
<release version="1.16.15" date="2026-3-08">
<url>https://github.com/Materialious/Materialious/releases/tag/1.16.15</url>
</release>
<release version="1.16.14" date="2026-3-08">
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "Materialious",
"version": "1.16.15",
"version": "1.16.16",
"description": "Modern material design for YouTube and Invidious.",
"author": {
"name": "Ward Pearce",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "materialious",
"version": "1.16.15",
"version": "1.16.16",
"private": true,
"scripts": {
"dev": "npm run patch:github && vite dev",
+11
View File
@@ -15,6 +15,7 @@ import { isOwnBackend } from './shared';
import { Browser } from '@capacitor/browser';
import { clearFeedYTjs } from './api/youtubejs/subscriptions';
import { ensureNoTrailingSlash, isYTBackend } from './misc';
import { deleteKeyValue } from './api/backend/keyvalue';
export function clearCaches() {
feedCacheStore.set({});
@@ -29,6 +30,12 @@ export function authProtected() {
}
}
async function removeAuthFromBackend() {
if (!get(rawMasterKeyStore)) return;
await deleteKeyValue('authToken');
}
export async function setInvidiousInstance(
instanceUrl: string | undefined | null
): Promise<boolean> {
@@ -64,6 +71,8 @@ export async function setInvidiousInstance(
invidiousInstanceStore.set(instance);
invidiousAuthStore.set(null);
await removeAuthFromBackend();
return true;
}
@@ -86,6 +95,8 @@ export async function goToInvidiousLogin() {
export async function invidiousLogout() {
invidiousAuthStore.set(null);
await removeAuthFromBackend();
goto(resolve('/', {}));
}
+1 -5
View File
@@ -46,11 +46,7 @@
}
</script>
<button
bind:this={shareButtonElement}
class="surface-container-highest"
onclick={(event: Event) => event.stopPropagation()}
>
<button bind:this={shareButtonElement} class="surface-container-highest">
<i>share</i>
{#if !iconOnly}
{$_('player.share.title')}
@@ -819,9 +819,11 @@
{videoLength(currentTime)} / {videoLength(playerMaxKnownTime)}
{/if}
</p>
<p class="chip surface-container-highest">
{playerVideoEndTimePretty}
</p>
{#if playerVideoEndTimePretty && playerVideoEndTimePretty !== ''}
<p class="chip surface-container-highest">
{playerVideoEndTimePretty}
</p>
{/if}
</div>
{/if}
<TouchControls
@@ -833,64 +835,62 @@
bind:playerMaxKnownTime
bind:playerIsBuffering
/>
{#if showControls}
<div id="player-controls" transition:fade>
<Timeline
{playerElement}
{currentTime}
{showPlayerUI}
{segments}
video={data.video}
content={data.content}
bind:userManualSeeking
bind:playerMaxKnownTime
/>
<nav>
{#if !$isAndroidTvStore}
<nav class="no-wrap">
<button class="surface-container-highest" onclick={toggleVideoPlaybackStatus}>
<i>
{#if playerCurrentPlaybackState}
pause
{:else}
play_arrow
{/if}
</i>
</button>
{#if !isMobile()}
<div class="player-slider volume m l" {...playerVolumeSlider.root}>
<div class="track">
<div class="range"></div>
<div {...playerVolumeSlider.thumb}></div>
</div>
</div>
{/if}
</nav>
{/if}
<div class="max"></div>
<div id="player-controls" class="hide" class:show={showControls}>
<Timeline
{playerElement}
{currentTime}
{showPlayerUI}
{segments}
video={data.video}
content={data.content}
bind:userManualSeeking
bind:playerMaxKnownTime
/>
<nav>
{#if !$isAndroidTvStore}
<nav class="no-wrap">
<p class="chip m l" style="height: 100%;">
{#if data.video.liveNow}
{$_('thumbnail.live')}
{:else}
{videoLength(currentTime)} / {videoLength(data.video.lengthSeconds)}
{/if}
</p>
{#if !$isAndroidTvStore}
<CaptionSettings video={data.video} />
{#if playerElement}
<Settings {player} {playerElement} />
<Airplay {playerElement} />
<Pip {playerElement} />
{/if}
<FullscreenToggle {toggleFullscreen} {playerIsFullscreen} />
<button class="surface-container-highest" onclick={toggleVideoPlaybackStatus}>
<i>
{#if playerCurrentPlaybackState}
pause
{:else}
play_arrow
{/if}
</i>
</button>
{#if !isMobile()}
<div class="player-slider volume m l" {...playerVolumeSlider.root}>
<div class="track">
<div class="range"></div>
<div {...playerVolumeSlider.thumb}></div>
</div>
</div>
{/if}
</nav>
{/if}
<div class="max"></div>
<nav class="no-wrap">
<p class="chip m l" style="height: 100%;">
{#if data.video.liveNow}
{$_('thumbnail.live')}
{:else}
{videoLength(currentTime)} / {videoLength(data.video.lengthSeconds)}
{/if}
</p>
{#if !$isAndroidTvStore}
<CaptionSettings video={data.video} />
{#if playerElement}
<Settings {player} {playerElement} />
<Airplay {playerElement} />
<Pip {playerElement} />
{/if}
<FullscreenToggle {toggleFullscreen} {playerIsFullscreen} />
{/if}
</nav>
</div>
{/if}
</nav>
</div>
</div>
{#if showVideoRetry}
@@ -366,15 +366,19 @@
{videoLength(timeInSeconds)}
{/snippet}
<div class="track">
{#if !userManualSeeking && playerShowTimelineThumbnail}
<div bind:this={playerTimelineTooltip} class="timeline tooltip">
{#if !userManualSeeking}
<div
bind:this={playerTimelineTooltip}
class="timeline tooltip"
class:hide={!playerShowTimelineThumbnail}
>
{@render timelineTooltip('timeline', playerTimelineTimeHover)}
</div>
{/if}
<div class="range"></div>
<div {...playerTimelineSlider.thumb}>
{#if playerSliderInteracted && playerShowTimelineThumbnail}
<div class="tooltip thumb">
{#if playerSliderInteracted}
<div class="tooltip thumb" class:hide={!playerShowTimelineThumbnail}>
{@render timelineTooltip('thumb', currentTime)}
</div>
{/if}
+11 -1
View File
@@ -28,7 +28,17 @@ body {
}
.hide {
display: none !important;
opacity: 0 !important;
visibility: hidden !important;
transition:
opacity 300ms linear,
visibility 0s linear 300ms !important;
}
.show {
opacity: 1 !important;
visibility: visible !important;
transition: opacity 300ms linear !important;
}
article {
@@ -9,22 +9,19 @@ import { addOrUpdateKeyValue, getKeyValue } from '$lib/api/backend/keyvalue';
import { rawMasterKeyStore } from '$lib/store';
import { getPublicEnv } from '$lib/misc';
const allowNullOverwrite = ['authToken'];
const dontAutoSync = ['authToken'];
export async function syncSettingsToBackend() {
if (!isOwnBackend() || !get(rawMasterKeyStore)) return;
await Promise.all(
persistedStores.map(async (store) => {
if (store.excludeFromBackendSync) return;
if (store.excludeFromBackendSync || dontAutoSync.includes(store.name)) return;
getKeyValue(store.name).then((currentKeyValue) => {
if (currentKeyValue !== null || allowNullOverwrite.includes(store.name)) {
if (currentKeyValue !== null) {
const currentKeyValueParsed = parseWithSchema(store.schema, currentKeyValue);
if (
(currentKeyValueParsed !== null && currentKeyValueParsed !== undefined) ||
allowNullOverwrite.includes(store.name)
) {
if (currentKeyValueParsed !== null && currentKeyValueParsed !== undefined) {
store.store.set(currentKeyValueParsed);
}
}
@@ -34,7 +31,7 @@ export async function syncSettingsToBackend() {
store.store.subscribe((value) => {
if (!get(rawMasterKeyStore)) return;
if (initialLoad && !allowNullOverwrite.includes(store.name)) {
if (initialLoad) {
initialLoad = false;
return;
}
@@ -317,6 +317,7 @@ export class ShakaPlayerAdapter implements SabrPlayerAdapter {
}
if (result) {
abortController.abort();
return this.createShakaResponse({
uri,
request,
+8
View File
@@ -25,6 +25,7 @@ import { isYTBackend } from '$lib/misc';
import { isOwnBackend } from '$lib/shared/index';
import '$lib/fetchProxy';
import { loadContentFilterFromURL } from '$lib/filtering/index.js';
import { getKeyValue } from '$lib/api/backend/keyvalue.js';
export const ssr = false;
export const prerender = false;
@@ -34,6 +35,13 @@ export async function load({ url }) {
await initI18n();
}
if (get(rawMasterKeyStore)) {
const authTokenFromCloud = await getKeyValue('authToken');
if (typeof authTokenFromCloud === 'string')
invidiousAuthStore.set(JSON.parse(authTokenFromCloud));
else invidiousAuthStore.set(null);
}
isAndroidTvStore.set((await androidTv.isAndroidTv()).value);
if (Capacitor.getPlatform() === 'android') {
@@ -146,12 +146,8 @@ async function proxyRequest(
const responseHeaders = new Headers(response.headers);
responseHeaders.delete('content-encoding');
responseHeaders.delete('access-control-allow-origin');
responseHeaders.delete('timing-allow-origin');
responseHeaders.delete('content-length');
responseHeaders.set('transfer-encoding', 'chunked');
return new Response(response.body, {
status: response.status,
headers: responseHeaders
@@ -3,7 +3,7 @@ import { getUser } from '$lib/server/user';
import { error, json } from '@sveltejs/kit';
import z from 'zod';
async function keyAllowed(key: string) {
function keyAllowed(key: string) {
if (!persistedStoreKeys.includes(key)) throw error(400);
}
@@ -19,7 +19,6 @@ export async function DELETE({ locals, params }) {
keyAllowed(params.key);
const user = await getUser(locals.userId);
await user.deleteKeyValue(params.key);
return new Response();
@@ -1,4 +1,5 @@
import { resolve } from '$app/paths';
import { addOrUpdateKeyValue } from '$lib/api/backend/keyvalue.js';
import { invidiousAuthStore } from '$lib/store';
import { redirect } from '@sveltejs/kit';
@@ -7,10 +8,12 @@ export async function load({ url }) {
const token = url.searchParams.get('token');
if (username && token) {
invidiousAuthStore.set({
const authToken = {
username: username,
token: token
});
};
invidiousAuthStore.set(authToken);
await addOrUpdateKeyValue('authToken', JSON.stringify(authToken));
}
throw redirect(302, resolve('/', {}));
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -3,7 +3,7 @@ import os
import re
from datetime import datetime
LATEST_VERSION = "1.16.15"
LATEST_VERSION = "1.16.16"
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")