Merge pull request #654 from Materialious/update/1.6.13

Update/1.6.13
This commit is contained in:
Ward
2024-10-18 11:42:51 +13:00
committed by GitHub
18 changed files with 207 additions and 104 deletions
+9
View File
@@ -17,6 +17,7 @@ Materialious allows you to customize various settings by overwriting the default
+ [Letter case for titles](#letter-case-for-titles)
+ [Auto expand comments](#auto-expand-comments)
+ [Auto expand description](#auto-expand-desc)
+ [Low bandwidth mode](#low-bandwidth-mode)
* [Player](#player)
+ [Mini player](#mini-player)
+ [Autoplay video](#autoplay-video)
@@ -128,6 +129,14 @@ Automatically expands video descriptions.
"autoExpandDesc": true
```
### Low bandwidth mode
Avoids loading images.
```json
"lowBandwidthMode": true
```
## Player
### Mini player
+2 -2
View File
@@ -7,8 +7,8 @@ android {
applicationId "us.materialio.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 63
versionName "1.6.12"
versionCode 64
versionName "1.6.13"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "Materialious",
"version": "1.6.12",
"version": "1.6.13",
"description": "Modern material design for Invidious.",
"author": {
"name": "Ward Pearce",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "materialious",
"version": "1.6.12",
"version": "1.6.13",
"private": true,
"scripts": {
"dev": "vite dev",
+18 -12
View File
@@ -1,14 +1,18 @@
<script lang="ts">
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import type { Channel } from './Api/model';
import { cleanNumber, getBestThumbnail, proxyGoogleImage, truncate } from './misc';
import { interfaceLowBandwidthMode } from './store';
export let channel: Channel;
let channelPfp: HTMLImageElement | undefined;
onMount(async () => {
if (get(interfaceLowBandwidthMode)) return;
const img = new Image();
img.src = proxyGoogleImage(getBestThumbnail(channel.authorThumbnails));
@@ -20,18 +24,20 @@
<a href={`/channel/${channel.authorId}`} class="wave" style="min-width: 100%;min-height: 100%;">
<div class="padding">
<div class="center-align">
{#if !channelPfp}
<progress class="circle"></progress>
{:else}
<img
class="circle"
style="width: 90px;height: 90px;"
src={channelPfp.src}
alt={channel.author}
/>
{/if}
</div>
{#if !$interfaceLowBandwidthMode}
<div class="center-align">
{#if !channelPfp}
<progress class="circle"></progress>
{:else}
<img
class="circle"
style="width: 90px;height: 90px;"
src={channelPfp.src}
alt={channel.author}
/>
{/if}
</div>
{/if}
<h5 class="center-align">{truncate(channel.author, 14)}</h5>
<h6 style="margin-top: 0;" class="center-align grey-text medium-text">
{cleanNumber(channel.subCount)}
+8 -6
View File
@@ -3,6 +3,7 @@
import { getComments } from './Api';
import { type Comment, type Comments } from './Api/model';
import { getBestThumbnail, numberWithCommas, proxyGoogleImage } from './misc';
import { interfaceLowBandwidthMode } from './store';
export let comment: Comment;
export let videoId: string;
@@ -31,12 +32,13 @@
</script>
<div class="comment">
<img
class="circle small"
src={proxyGoogleImage(getBestThumbnail(comment.authorThumbnails))}
alt="comment profile"
/>
{#if !$interfaceLowBandwidthMode}
<img
class="circle small"
src={proxyGoogleImage(getBestThumbnail(comment.authorThumbnails))}
alt="comment profile"
/>
{/if}
<div>
<div class="row">
<a href={`/channel/${comment.authorId}`}>
+6 -1
View File
@@ -413,12 +413,14 @@
if (Number(widthHeight[0]) > Number(widthHeight[1])) {
await StatusBar.setOverlaysWebView({ overlay: true });
await StatusBar.hide();
await ScreenOrientation.lock({ orientation: 'landscape' });
} else {
await ScreenOrientation.lock({ orientation: 'portrait' });
}
} else {
await StatusBar.setOverlaysWebView({ overlay: false });
await StatusBar.show();
await ScreenOrientation.lock({
orientation: (originalOrigination as ScreenOrientationResult).type
});
@@ -529,6 +531,7 @@
if (originalOrigination) {
await StatusBar.setOverlaysWebView({ overlay: false });
await StatusBar.show();
await ScreenOrientation.lock({
orientation: originalOrigination.type
});
@@ -537,7 +540,9 @@
if (typeof silenceSkipperInterval !== 'undefined') {
clearInterval(silenceSkipperInterval);
}
savePlayerPos();
try {
savePlayerPos();
} catch (error) {}
await player.pause();
player.destroy();
playerPosSet = false;
@@ -1,8 +1,10 @@
<script lang="ts">
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import type { Playlist } from './Api/model';
import { getBestThumbnail, letterCase, truncate } from './misc';
import { interfaceLowBandwidthMode } from './store';
export let playlist: Playlist;
export let disabled: boolean = false;
@@ -15,6 +17,8 @@
const playlistLink = `/playlist/${playlist.playlistId}`;
onMount(() => {
if (get(interfaceLowBandwidthMode)) return;
img = new Image();
if (playlist.videos.length > 0) {
img.src = getBestThumbnail(playlist.videos[0].videoThumbnails) || '';
@@ -42,7 +46,7 @@
style="width: 100%; overflow: hidden;min-height:100px;"
class="wave"
>
{#if playlist.videoCount > 0}
{#if playlist.videoCount > 0 && !$interfaceLowBandwidthMode}
{#if loading}
<progress class="circle"></progress>
{:else if img.src !== ''}
+39 -20
View File
@@ -21,6 +21,7 @@
interfaceAutoExpandComments,
interfaceAutoExpandDesc,
interfaceForceCase,
interfaceLowBandwidthMode,
interfacePreviewVideoOnHoverStore,
interfaceRegionStore,
interfaceSearchSuggestionsStore,
@@ -177,6 +178,29 @@
</a>
</nav>
<div class="page padding" class:active={activeTab === 'interface'}>
{#if Capacitor.isNativePlatform()}
<form
on:submit|preventDefault={() => {
instanceStore.set(ensureNoTrailingSlash(invidiousInstance));
authStore.set(null);
goto('/', { replaceState: true });
ui('#dialog-settings');
}}
>
<nav>
<div class="field label border max">
<input bind:value={invidiousInstance} name="invidious-instance" type="text" />
<label for="invidious-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
{/if}
<div class="margin"></div>
<button on:click={toggleDarkMode} class="no-margin">
{#if !$darkModeStore}
<i>dark_mode</i>
@@ -212,26 +236,21 @@
</div>
{/if}
{#if Capacitor.isNativePlatform()}
<form
on:submit|preventDefault={() => {
instanceStore.set(ensureNoTrailingSlash(invidiousInstance));
authStore.set(null);
goto('/', { replaceState: true });
ui('#dialog-settings');
}}
>
<nav>
<div class="field label border max">
<input bind:value={invidiousInstance} name="invidious-instance" type="text" />
<label for="invidious-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
{/if}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.lowBandwidthMode')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceLowBandwidthMode}
on:click={() => interfaceLowBandwidthMode.set(!$interfaceLowBandwidthMode)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
+45 -31
View File
@@ -18,6 +18,7 @@
authStore,
deArrowEnabledStore,
deArrowTitlesOnly,
interfaceLowBandwidthMode,
interfacePreviewVideoOnHoverStore,
playerProxyVideosStore,
playerSavePlaybackPositionStore,
@@ -114,6 +115,8 @@
calcThumbnailPlaceholderHeight();
});
if (get(interfaceLowBandwidthMode)) return;
// Load author details in background.
if (!sideways) {
loadAuthor();
@@ -283,32 +286,37 @@
data-sveltekit-preload-data="off"
on:click={syncChangeVideo}
>
{#if loading}
<div class="secondary-container" style="width: 100%;height: {placeholderHeight}px;"></div>
{:else if loaded}
{#if showVideoPreview && videoPreview}
<div style="max-width: 100%; max-height: {imgHeight}px;">
<video
id="video-preview"
style="max-width: 100%; max-height: {imgHeight}px;"
autoplay
poster={img.src}
width="100%"
height="100%"
muted={videoPreviewMuted}
controls={false}
volume={videoPreviewVolume}
src={proxyVideos
? proxyVideoUrl(videoPreview.formatStreams[0].url)
: videoPreview.formatStreams[0].url}
>
</video>
</div>
{#if !$interfaceLowBandwidthMode}
{#if loading}
<div
class="secondary-container"
style="width: 100%;height: {placeholderHeight}px;"
></div>
{:else if loaded}
{#if showVideoPreview && videoPreview}
<div style="max-width: 100%; max-height: {imgHeight}px;">
<video
id="video-preview"
style="max-width: 100%; max-height: {imgHeight}px;"
autoplay
poster={img.src}
width="100%"
height="100%"
muted={videoPreviewMuted}
controls={false}
volume={videoPreviewVolume}
src={proxyVideos
? proxyVideoUrl(videoPreview.formatStreams[0].url)
: videoPreview.formatStreams[0].url}
>
</video>
</div>
{:else}
<img class="responsive" src={img.src} alt="Thumbnail for video" />
{/if}
{:else}
<img class="responsive" src={img.src} alt="Thumbnail for video" />
<p>{$_('thumbnail.failedToLoadImage')}</p>
{/if}
{:else}
<p>{$_('thumbnail.failedToLoadImage')}</p>
{/if}
{#if progress}
<progress
@@ -320,18 +328,24 @@
{/if}
{#if !('liveVideo' in video) || !video.liveVideo}
{#if video.lengthSeconds !== 0}
<div
class="absolute right bottom small-margin black white-text small-text thumbnail-corner"
>
&nbsp;{videoLength(video.lengthSeconds)}&nbsp;
</div>
{#if !$interfaceLowBandwidthMode}
<div
class="absolute right bottom small-margin black white-text small-text thumbnail-corner"
>
&nbsp;{videoLength(video.lengthSeconds)}&nbsp;
</div>
{:else}
<h3>{videoLength(video.lengthSeconds)}</h3>
{/if}
{/if}
{:else}
{:else if video.lengthSeconds !== 0}
<div
class="absolute right bottom small-margin red white-text small-text thumbnail-corner"
>
{$_('thumbnail.live')}
</div>
{:else}
<h3>{$_('thumbnail.live')}</h3>
{/if}
</a>
{#if showVideoPreview && videoPreview}
@@ -354,7 +368,7 @@
</div>
<div class="thumbnail-details video-title">
{#if !sideways}
{#if !sideways && !$interfaceLowBandwidthMode}
<div style="margin-right: 1em;">
{#if authorImg}
<img src={authorImg.src} alt="Author" class="circle small" />
+6
View File
@@ -9,6 +9,7 @@ import {
interfaceAutoExpandComments,
interfaceAutoExpandDesc,
interfaceForceCase,
interfaceLowBandwidthMode,
interfacePreviewVideoOnHoverStore,
interfaceRegionStore,
interfaceSearchSuggestionsStore,
@@ -189,6 +190,11 @@ const persistedStores = [
name: 'showWarning',
store: showWarningStore,
type: 'boolean'
},
{
name: 'lowBandwidthMode',
store: interfaceLowBandwidthMode,
type: 'boolean'
}
];
@@ -100,6 +100,7 @@
"login": "Login",
"logout": "Logout",
"customize": "Customize",
"lowBandwidthMode": "Low bandwidth mode",
"theme": {
"theme": "Theme",
"darkMode": "Dark mode",
+1 -1
View File
@@ -64,7 +64,7 @@ export const interfaceForceCase: Writable<TitleCase> = persisted('forceCase', nu
export const interfaceAutoExpandComments: Writable<boolean> = persisted('autoExpandComments', true);
export const interfaceAutoExpandDesc: Writable<boolean> = persisted('autoExpandDesc', false);
export const interfaceAmoledTheme = persisted('amoledTheme', false);
export const interfaceLowBandwidthMode = persisted('lowBandwidthMode', false);
export const sponsorBlockStore = persisted('sponsorBlock', true);
export const sponsorBlockUrlStore: Writable<string | null | undefined> = persisted(
+36 -6
View File
@@ -1,17 +1,15 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { afterNavigate, beforeNavigate, goto } from '$app/navigation';
import { navigating } from '$app/stores';
import { getFeed } from '$lib/Api/index';
import type { Notification } from '$lib/Api/model';
import { bookmarkletLoadFromUrl, loadSettingsFromEnv } from '$lib/externalSettings';
import Logo from '$lib/Logo.svelte';
import MiniPlayer from '$lib/MiniPlayer.svelte';
import PageLoading from '$lib/PageLoading.svelte';
import '$lib/patches/androidRequests';
import Search from '$lib/Search.svelte';
import Settings from '$lib/Settings.svelte';
import SyncParty from '$lib/SyncParty.svelte';
import Thumbnail from '$lib/Thumbnail.svelte';
import { bookmarkletLoadFromUrl, loadSettingsFromEnv } from '$lib/externalSettings';
import '$lib/patches/androidRequests';
import {
activePageStore,
authStore,
@@ -22,14 +20,16 @@
syncPartyPeerStore,
themeColorStore
} from '$lib/store';
import SyncParty from '$lib/SyncParty.svelte';
import { setAmoledTheme, setTheme } from '$lib/theme';
import Thumbnail from '$lib/Thumbnail.svelte';
import { App } from '@capacitor/app';
import { Browser } from '@capacitor/browser';
import { Capacitor } from '@capacitor/core';
import 'beercss';
import ui from 'beercss';
import 'material-dynamic-colors';
import { onMount } from 'svelte';
import { onMount, tick } from 'svelte';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { pwaInfo } from 'virtual:pwa-info';
@@ -143,9 +143,39 @@
notifications = feed.notifications;
}
let scrollPositions = new Map();
let scrollableRoot: HTMLElement | null = null;
beforeNavigate(({ from }) => {
if (from?.url && scrollableRoot) {
// Save the scroll position of the root element
scrollPositions.set(from.url.pathname, scrollableRoot.scrollTop);
}
});
// Restore the scroll position after navigating to a new page
afterNavigate(async ({ to, type }) => {
if (!scrollableRoot) return;
if (to?.url && scrollPositions.has(to.url.pathname)) {
// Check user went back
if (type === 'popstate') {
await tick();
scrollableRoot.scrollTo(0, scrollPositions.get(to.url.pathname) || 0);
} else {
scrollPositions.delete(to.url.pathname);
}
} else {
// Default behavior: scroll to top
scrollableRoot.scrollTo(0, 0);
}
});
onMount(async () => {
ui();
scrollableRoot = document.querySelector('.root');
loadSettingsFromEnv();
// Should always be loaded after env settings
// So user preferences overwrite instance preferences.
@@ -6,7 +6,7 @@
import PlaylistThumbnail from '$lib/PlaylistThumbnail.svelte';
import VideoList from '$lib/VideoList.svelte';
import { cleanNumber, getBestThumbnail, proxyGoogleImage } from '$lib/misc';
import { activePageStore, authStore } from '$lib/store';
import { activePageStore, authStore, interfaceLowBandwidthMode } from '$lib/store';
import { Clipboard } from '@capacitor/clipboard';
import { Capacitor } from '@capacitor/core';
import { onMount } from 'svelte';
@@ -64,10 +64,12 @@
onMount(async () => {
displayContent = await getChannelContent(data.channel.authorId, { type: 'videos' });
const channelPfpResp = await fetch(
proxyGoogleImage(getBestThumbnail(data.channel.authorThumbnails))
);
channelPfp = URL.createObjectURL(await channelPfpResp.blob());
if (!get(interfaceLowBandwidthMode)) {
const channelPfpResp = await fetch(
proxyGoogleImage(getBestThumbnail(data.channel.authorThumbnails))
);
channelPfp = URL.createObjectURL(await channelPfpResp.blob());
}
if (get(authStore)) {
isSubscribed = await amSubscribed(data.channel.authorId);
@@ -86,7 +88,7 @@
</script>
<div class="padding">
{#if data.channel.authorBanners.length > 0}
{#if data.channel.authorBanners.length > 0 && !$interfaceLowBandwidthMode}
<img
src={proxyGoogleImage(data.channel.authorBanners[0].url)}
width="100%"
@@ -94,15 +96,17 @@
/>
{/if}
<div class="description">
{#if channelPfp}
<img
style="margin-right: 1em;"
class="circle extra m l"
src={channelPfp}
alt="Channel profile"
/>
{:else}
<progress style="padding: 15px;" class="circle"></progress>
{#if !$interfaceLowBandwidthMode}
{#if channelPfp}
<img
style="margin-right: 1em;"
class="circle extra m l"
src={channelPfp}
alt="Channel profile"
/>
{:else}
<progress style="padding: 15px;" class="circle"></progress>
{/if}
{/if}
<div>
@@ -30,6 +30,7 @@
authStore,
interfaceAutoExpandComments,
interfaceAutoExpandDesc,
interfaceLowBandwidthMode,
miniPlayerSrcStore,
playerAutoplayNextByDefaultStore,
playerListenByDefaultStore,
@@ -487,11 +488,13 @@
<nav>
<a href={`/channel/${data.video.authorId}`}>
<nav>
<img
class="circle large"
src={proxyGoogleImage(getBestThumbnail(data.video.authorThumbnails))}
alt="Channel profile"
/>
{#if !$interfaceLowBandwidthMode}
<img
class="circle large"
src={proxyGoogleImage(getBestThumbnail(data.video.authorThumbnails))}
alt="Channel profile"
/>
{/if}
<div>
<p style="margin: 0;" class="bold">{truncate(data.video.author, 16)}</p>
<p style="margin: 0;">{data.video.subCountText}</p>
+1 -1
View File
@@ -52,7 +52,7 @@ main.root {
padding-left: 7em !important;
padding-top: 5em !important;
max-height: 100vh;
overflow-y: scroll;
overflow-y: auto;
}
+1 -1
View File
@@ -5,7 +5,7 @@ import json
import os
import re
LATEST_VERSION = "1.6.12"
LATEST_VERSION = "1.6.13"
WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious")
ROOT_PACKAGE = os.path.join(WORKING_DIR, "package.json")