Merge pull request #1467 from Materialious/update/1.16.1

Update/1.16.1
This commit is contained in:
Ward
2026-02-18 14:57:28 +13:00
committed by GitHub
21 changed files with 151 additions and 94 deletions
+4 -5
View File
@@ -23,29 +23,28 @@
* Automatically import/export Invidious/Materialious.
- Invidious is optional.
* Enjoy Materialious without a Invidious instance via our YouTube backend.
* Local video fallback if Invidious fails loading videos.
- Invidious companion support.
- [Invidious API extended integration.](https://github.com/Materialious/api-extended)
- Local video fallback if Invidious fails loading videos.
- Android TV support.
- Support for disabling certificate validation for homelab users.
- Sync your watch progress between Invidious sessions.
- Watch sync parties.
- Sponsorblock built-in.
- [Return YouTube Dislike](https://github.com/Anarios/return-youtube-dislike) (RYD) built-in.
- [DeArrow](https://github.com/ajayyy/DeArrow) built-in (With local processing fallback).
- [DeArrow](https://github.com/ajayyy/DeArrow) built-in.
- No ads.
- No tracking.
- Light/Dark themes.
- Custom colour themes.
- Integrates with Invidious subscriptions, watch history & more.
- Integrates with Invidious subscriptions.
- Live stream support.
- Dash support.
- Chapters.
- Audio only mode.
- Mini player (PIP.)
- Playlists.
- PWA support.
- YT path redirects (So your redirect plugins should still work!)
- YT path redirects.
# Docker deployment
This version of Materialious is feature rich, has its own account system (optional) & is easier to setup.
+2 -2
View File
@@ -7,8 +7,8 @@ android {
applicationId "us.materialio.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 218
versionName "1.16.0"
versionCode 219
versionName "1.16.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -83,7 +83,11 @@
<release version="1.16.0" date="2026-2-18">
<release version="1.16.1" date="2026-2-18">
<url>https://github.com/Materialious/Materialious/releases/tag/1.16.1</url>
</release>
<release version="1.16.0" date="2026-2-18">
<url>https://github.com/Materialious/Materialious/releases/tag/1.16.0</url>
</release>
<release version="1.15.9" date="2026-2-17">
+37 -24
View File
@@ -1,12 +1,12 @@
{
"name": "Materialious",
"version": "1.16.0",
"version": "1.16.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "Materialious",
"version": "1.16.0",
"version": "1.16.1",
"license": "MIT",
"dependencies": {
"@capacitor-community/electron": "^5.0.0",
@@ -58,22 +58,22 @@
}
},
"node_modules/@asamuzakjp/dom-selector": {
"version": "6.7.6",
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.6.tgz",
"integrity": "sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==",
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz",
"integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==",
"license": "MIT",
"dependencies": {
"@asamuzakjp/nwsapi": "^2.3.9",
"bidi-js": "^1.0.3",
"css-tree": "^3.1.0",
"is-potential-custom-element-name": "^1.0.1",
"lru-cache": "^11.2.4"
"lru-cache": "^11.2.6"
}
},
"node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": {
"version": "11.2.4",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz",
"integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
"version": "11.2.6",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
"integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
@@ -85,6 +85,18 @@
"integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
"license": "MIT"
},
"node_modules/@bramus/specificity": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz",
"integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==",
"license": "MIT",
"dependencies": {
"css-tree": "^3.0.0"
},
"bin": {
"specificity": "bin/cli.js"
}
},
"node_modules/@bufbuild/protobuf": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz",
@@ -2788,15 +2800,15 @@
}
},
"node_modules/cssstyle": {
"version": "5.3.7",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz",
"integrity": "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.0.1.tgz",
"integrity": "sha512-IoJs7La+oFp/AB033wBStxNOJt4+9hHMxsXUPANcoXL2b3W4DZKghlJ2cI/eyeRZIQ9ysvYEorVhjrcYctWbog==",
"license": "MIT",
"dependencies": {
"@asamuzakjp/css-color": "^4.1.1",
"@csstools/css-syntax-patches-for-csstree": "^1.0.21",
"@asamuzakjp/css-color": "^4.1.2",
"@csstools/css-syntax-patches-for-csstree": "^1.0.26",
"css-tree": "^3.1.0",
"lru-cache": "^11.2.4"
"lru-cache": "^11.2.5"
},
"engines": {
"node": ">=20"
@@ -3142,9 +3154,9 @@
}
},
"node_modules/electron": {
"version": "40.2.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-40.2.1.tgz",
"integrity": "sha512-0zOeyN8LB1KHIjVV5jyMmQmkqx3J8OkkVlab3p7vOM28jI46blxW7M52Tcdi6X2m5o2jj8ejOlAh5+boL3w8aQ==",
"version": "40.4.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-40.4.1.tgz",
"integrity": "sha512-N1ZXybQZL8kYemO8vAeh9nrk4mSvqlAO8xs0QCHkXIvRnuB/7VGwEehjvQbsU5/f4bmTKpG+2GQERe/zmKpudQ==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -4491,15 +4503,16 @@
"license": "MIT"
},
"node_modules/jsdom": {
"version": "28.0.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.0.0.tgz",
"integrity": "sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==",
"version": "28.1.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz",
"integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==",
"license": "MIT",
"dependencies": {
"@acemir/cssom": "^0.9.31",
"@asamuzakjp/dom-selector": "^6.7.6",
"@asamuzakjp/dom-selector": "^6.8.1",
"@bramus/specificity": "^2.4.2",
"@exodus/bytes": "^1.11.0",
"cssstyle": "^5.3.7",
"cssstyle": "^6.0.1",
"data-urls": "^7.0.0",
"decimal.js": "^10.6.0",
"html-encoding-sniffer": "^6.0.0",
@@ -4510,7 +4523,7 @@
"saxes": "^6.0.0",
"symbol-tree": "^3.2.4",
"tough-cookie": "^6.0.0",
"undici": "^7.20.0",
"undici": "^7.21.0",
"w3c-xmlserializer": "^5.0.0",
"webidl-conversions": "^8.0.1",
"whatwg-mimetype": "^5.0.0",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "Materialious",
"version": "1.16.0",
"version": "1.16.1",
"description": "Modern material design for YouTube and Invidious.",
"author": {
"name": "Ward Pearce",
@@ -45,4 +45,4 @@
"capacitor",
"electron"
]
}
}
+11 -11
View File
@@ -1,12 +1,12 @@
{
"name": "materialious",
"version": "1.15.4",
"version": "1.16.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "materialious",
"version": "1.15.4",
"version": "1.16.1",
"hasInstallScript": true,
"dependencies": {
"@capacitor-community/electron": "^5.0.1",
@@ -2249,9 +2249,9 @@
}
},
"node_modules/@capacitor/core": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.0.0.tgz",
"integrity": "sha512-250HTVd/W/KdMygoqaedisvNbHbpbQTN2Hy/8ZYGm1nAqE0Fx7sGss4l0nDg33STxEdDhtVRoL2fIaaiukKseA==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.1.0.tgz",
"integrity": "sha512-UfMBMWc1v7J+14AhH03QmeNwV3HZx3qnOWhpwnHfzALEwAwlV/itQOQqcasMQYhOHWL0tiymc5ByaLTn7KKQxw==",
"license": "MIT",
"peer": true,
"dependencies": {
@@ -2286,9 +2286,9 @@
}
},
"node_modules/@capacitor/share": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@capacitor/share/-/share-8.0.0.tgz",
"integrity": "sha512-VU+xT4LFwr4keIC0UKDqGQVAiNlAHwoTMQg8wVVSxtn/k32VOvvtqFfu63qnXr40WKytZWrxJfVESvRjd761yg==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@capacitor/share/-/share-8.0.1.tgz",
"integrity": "sha512-3cSBKBCJVon54rKDROP2rqGyeGks4pBh9TbaEk9S375Kbek/ZHe72N50zIa0Vn9Eac/SuhwgehO/mmA4CsUOiw==",
"license": "MIT",
"peerDependencies": {
"@capacitor/core": ">=8.0.0"
@@ -4299,9 +4299,9 @@
}
},
"node_modules/@sveltejs/kit": {
"version": "2.51.0",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.51.0.tgz",
"integrity": "sha512-BkcfxVVYmfxHGYLugemb7TWW+y/MNZbxXHXJ141lyyi2ozq0Q4kbqoV0cBGlh7F0vNtCMvfr7UPnxIaBjcP9gg==",
"version": "2.52.0",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.52.0.tgz",
"integrity": "sha512-zG+HmJuSF7eC0e7xt2htlOcEMAdEtlVdb7+gAr+ef08EhtwUsjLxcAwBgUCJY3/5p08OVOxVZti91WfXeuLvsg==",
"dev": true,
"license": "MIT",
"peer": true,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "materialious",
"version": "1.16.0",
"version": "1.16.1",
"private": true,
"scripts": {
"dev": "npm run patch:github && vite dev",
+17 -2
View File
@@ -68,6 +68,20 @@ export function setRegion(url: URL): URL {
return url;
}
export class HTTPError {
msg: string;
response: Response;
constructor(msg: string, response: Response) {
this.msg = msg;
this.response = response;
}
toString() {
return this.msg;
}
}
export async function fetchErrorHandle(response: Response): Promise<Response> {
if (!response.ok) {
let message = 'Internal error';
@@ -80,8 +94,9 @@ export async function fetchErrorHandle(response: Response): Promise<Response> {
// Continue regardless of error
}
throw Error(
`${response.status} - ${response.statusText}\n${decodeURIComponent(response.url)}\n${message}`
throw new HTTPError(
`${response.status} - ${response.statusText}\n${decodeURIComponent(response.url)}\n${message}`,
response
);
}
+4 -3
View File
@@ -6,9 +6,10 @@
interface Props {
classes?: string;
size?: string;
}
let { classes = '' }: Props = $props();
let { classes = '', size = '70px' }: Props = $props();
let fillColor = $state('');
@@ -35,8 +36,8 @@
<svg
version="1.1"
class={classes}
height="100%"
width="70px"
height={size}
width={size}
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
viewBox="50.17 104.37 109.67 88.27"
@@ -975,9 +975,11 @@
</p>
{#if !$isAndroidTvStore}
<CaptionSettings {player} video={data.video} />
<Settings {player} {playerElement} />
<Airplay {playerElement} />
<Pip {playerElement} />
{#if playerElement}
<Settings {player} {playerElement} />
<Airplay {playerElement} />
<Pip {playerElement} />
{/if}
<FullscreenToggle {toggleFullscreen} {playerIsFullscreen} />
{/if}
</nav>
@@ -1,5 +1,7 @@
<script lang="ts">
let { playerElement }: { playerElement: HTMLMediaElement | undefined } = $props();
import { onMount } from 'svelte';
let { playerElement }: { playerElement: HTMLMediaElement } = $props();
function hasWebkitShowPlaybackTargetPicker(
el: HTMLMediaElement
@@ -13,8 +15,8 @@
}
}
$effect(() => {
if (!playerElement || playerElement.hasAttribute('x-webkit-airplay')) return;
onMount(() => {
if (playerElement.hasAttribute('x-webkit-airplay')) return;
if (hasWebkitShowPlaybackTargetPicker(playerElement)) {
playerElement.setAttribute('x-webkit-airplay', 'allow');
@@ -1,5 +1,5 @@
<script lang="ts">
let { playerElement }: { playerElement: HTMLMediaElement | undefined } = $props();
let { playerElement }: { playerElement: HTMLMediaElement } = $props();
</script>
{#if document.pictureInPictureEnabled}
@@ -7,10 +7,8 @@
import { playbackRates } from '$lib/player/index';
import { onMount } from 'svelte';
let {
player,
playerElement
}: { player: shaka.Player; playerElement: HTMLMediaElement | undefined } = $props();
let { player, playerElement }: { player: shaka.Player; playerElement: HTMLMediaElement } =
$props();
let playerSettings: 'quality' | 'speed' | 'language' | 'root' = $state('root');
let playerCurrentVideoTrack: shaka.extern.VideoTrack | undefined = $state(undefined);
@@ -22,6 +20,11 @@
setActiveVideoTrack();
setActiveAudioTrack();
});
player.addEventListener('trackschanged', () => {
setActiveVideoTrack();
setActiveAudioTrack();
});
});
function setActiveVideoTrack() {
+1
View File
@@ -72,6 +72,7 @@ export async function initI18n(selectedLocale: string = getUserLocale()): Promis
await i18next.init(options);
locale.set(langToLoad);
_.set(i18next.t.bind(i18next));
await loadDayjsLocale(selectedLocale);
+4 -4
View File
@@ -35,10 +35,10 @@ export const playbackRates = [
export const playerDoubleTapSeek = 10.0;
export function goToPreviousVideo(playlistId: string | null) {
playerState.set(undefined);
const previousVideos = get(playerPlaylistHistory);
if (previousVideos.length > 1) {
playerState.set(undefined);
goto(
resolve('/watch/[videoId]?playlist=[playlistId]', {
videoId: previousVideos[1],
@@ -50,8 +50,6 @@ export function goToPreviousVideo(playlistId: string | null) {
}
export async function goToNextVideo(video: VideoPlay, playlistId: string | null) {
playerState.set(undefined);
const isAndroidTv = get(isAndroidTvStore);
if (!playlistId) {
@@ -68,6 +66,8 @@ export async function goToNextVideo(video: VideoPlay, playlistId: string | null)
return;
}
playerState.set(undefined);
const playlist = await loadEntirePlaylist(playlistId);
const playlistVideoIds = playlist.videos.map((value) => {
return value.videoId;
+2
View File
@@ -375,3 +375,5 @@ export const channelCacheStore: Writable<{
displayContent: { [key: string]: ChannelContent };
};
}> = writable({});
export const hideSearchStore: Writable<boolean> = writable(false);
+7 -9
View File
@@ -24,7 +24,8 @@
rawMasterKeyStore,
syncPartyPeerStore,
themeColorStore,
backendInUseStore
backendInUseStore,
hideSearchStore
} from '$lib/store';
import { Capacitor } from '@capacitor/core';
import 'beercss';
@@ -67,10 +68,6 @@
requestAnimationFrame(() => resetScroll());
});
playerState.subscribe(() => {
requestAnimationFrame(() => resetScroll());
});
async function login() {
if (isOwnBackend()?.internalAuth) {
goto(resolve('/internal/login', {}));
@@ -241,10 +238,11 @@
<div class="max m l"></div>
<div class="m l">
<Search />
</div>
{#if !$hideSearchStore}
<div class="m l">
<Search />
</div>
{/if}
{#if !mobileSearchShow}
<div class="max"></div>
{/if}
+28 -11
View File
@@ -1,23 +1,40 @@
<script lang="ts">
import ItemsList from '$lib/components/layout/ItemsList.svelte';
import { _ } from '$lib/i18n/index';
import { feedCacheStore } from '$lib/store';
import Logo from '$lib/components/Logo.svelte';
import Search from '$lib/components/Search.svelte';
import { feedCacheStore, hideSearchStore } from '$lib/store';
import { onDestroy, onMount } from 'svelte';
let { data } = $props();
onMount(() => {
if (data.popularDisabled) {
hideSearchStore.set(true);
}
});
onDestroy(() => {
hideSearchStore.set(false);
});
</script>
{#if data.popularDisabled}
<div class="center-align">
<div class="center">
<div class="space"></div>
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 24 24"
><path
style="fill: var(--secondary);"
d="M11.25 21.975Q9.3 21.825 7.612 21t-2.937-2.162q-1.25-1.338-1.963-3.1T2 12q0-2.075.788-3.9t2.137-3.175q1.35-1.35 3.175-2.137T12 2q2.075 0 3.9.788t3.175 2.137q1.35 1.35 2.138 3.175T22 12v.475q-.425-.275-.988-.537t-1.037-.438q-.2-3.15-2.488-5.325T12 4q-1.4 0-2.638.45T7.1 5.7l6.125 6.125q-.475.2-.912.463t-.863.562L5.7 7.1q-.8 1.025-1.25 2.263T4 12q0 2.475 1.338 4.438T8.8 19.325q.45.7 1.125 1.425t1.325 1.225ZM17 20q1.475 0 2.738-.675T21.75 17.5q-.75-1.15-2.013-1.825T17 15q-1.475 0-2.738.675T12.25 17.5q.75 1.15 2.013 1.825T17 20Zm0 2q-2.4 0-4.288-1.263T10 17.5q.825-1.975 2.713-3.238T17 13q2.4 0 4.288 1.263T24 17.5q-.825 1.975-2.713 3.238T17 22Zm0-3q-.625 0-1.063-.438T15.5 17.5q0-.625.438-1.063T17 16q.625 0 1.063.438T18.5 17.5q0 .625-.438 1.063T17 19Z"
/></svg
>
<h1>{$_('disabled')}</h1>
<p>{$_('popularPageDisabled')}</p>
<Logo size="200px" />
<h1>Materialious</h1>
<Search />
</div>
{:else}
<ItemsList items={$feedCacheStore.popular ?? []} />
{/if}
<style>
.center {
height: 50%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
</style>
+6 -6
View File
@@ -1,6 +1,6 @@
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { getPopular } from '$lib/api/index';
import { getPopular, HTTPError } from '$lib/api/index';
import { isYTBackend } from '$lib/misc';
import { feedCacheStore, invidiousInstanceStore } from '$lib/store';
import { error } from '@sveltejs/kit';
@@ -22,18 +22,18 @@ export async function load() {
let popularDisabled: boolean = false;
if (!popular) {
let errorMsg: Error | undefined;
let errorObj: HTTPError | undefined;
try {
popular = await getPopular();
} catch (popularError) {
errorMsg = popularError as Error;
if (popularError instanceof HTTPError) errorObj = popularError;
}
if (errorMsg) {
if (errorMsg.toString() === 'Error: Administrator has disabled this endpoint.') {
if (errorObj) {
if (errorObj.response.status === 403) {
popularDisabled = true;
} else {
throw error(500, errorMsg);
throw error(500, errorObj.toString());
}
}
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.0"
LATEST_VERSION = "1.16.1"
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")