@@ -7,8 +7,8 @@ android {
|
||||
applicationId "us.materialio.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 121
|
||||
versionName "1.9.4"
|
||||
versionCode 122
|
||||
versionName "1.9.5"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -56,7 +56,11 @@
|
||||
</content_rating>
|
||||
<releases>
|
||||
|
||||
<release version="1.9.4" date="2025-6-08">
|
||||
|
||||
<release version="1.9.5" date="2025-6-09">
|
||||
<url>https://github.com/Materialious/Materialious/releases/tag/1.9.5</url>
|
||||
</release>
|
||||
<release version="1.9.4" date="2025-6-08">
|
||||
<url>https://github.com/Materialious/Materialious/releases/tag/1.9.4</url>
|
||||
</release>
|
||||
<release version="1.9.3" date="2025-6-05">
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.9.3",
|
||||
"version": "1.9.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "Materialious",
|
||||
"version": "1.9.3",
|
||||
"version": "1.9.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@capacitor-community/electron": "^5.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.9.4",
|
||||
"version": "1.9.5",
|
||||
"description": "Modern material design for Invidious.",
|
||||
"author": {
|
||||
"name": "Ward Pearce",
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.9.1",
|
||||
"version": "1.9.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "materialious",
|
||||
"version": "1.9.1",
|
||||
"version": "1.9.4",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@capacitor-community/electron": "^5.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.9.4",
|
||||
"version": "1.9.5",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
|
||||
@@ -27,9 +27,11 @@
|
||||
playerAndroidLockOrientation,
|
||||
playerAutoPlayStore,
|
||||
playerDefaultLanguage,
|
||||
playerDefaultPlaybackSpeed,
|
||||
playerDefaultQualityStore,
|
||||
playerProxyVideosStore,
|
||||
playerSavePlaybackPositionStore,
|
||||
playerStatisticsByDefault,
|
||||
playerYouTubeJsFallback,
|
||||
sponsorBlockCategoriesStore,
|
||||
sponsorBlockDisplayToastStore,
|
||||
@@ -41,6 +43,7 @@
|
||||
import { getDynamicTheme, setStatusBarColor } from '../theme';
|
||||
import { injectSABR } from '$lib/sabr';
|
||||
import { patchYoutubeJs } from '$lib/patches/youtubejs';
|
||||
import { playbackRates } from '$lib/const';
|
||||
|
||||
interface Props {
|
||||
data: { video: VideoPlay; content: PhasedDescription; playlistId: string | null };
|
||||
@@ -317,9 +320,20 @@
|
||||
ui('#snackbar-alert');
|
||||
}
|
||||
|
||||
restoreQualityPreference();
|
||||
restoreQualityPreference();
|
||||
restoreDefaultLanguage();
|
||||
|
||||
if ($playerDefaultPlaybackSpeed && playerElement) {
|
||||
playerElement.playbackRate = $playerDefaultPlaybackSpeed;
|
||||
}
|
||||
|
||||
if ($playerStatisticsByDefault) {
|
||||
// Appears to be no native way in shaka to toggle statistics on and off
|
||||
const shakaStatisticsButton = document.querySelector('.shaka-statistics-button') as
|
||||
| HTMLButtonElement
|
||||
| undefined;
|
||||
shakaStatisticsButton?.click();
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadVideo() {
|
||||
@@ -394,7 +408,7 @@
|
||||
'language',
|
||||
'statistics'
|
||||
],
|
||||
playbackRates: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3],
|
||||
playbackRates: playbackRates,
|
||||
enableTooltips: false,
|
||||
seekBarColors: {
|
||||
played: (await getDynamicTheme())['--primary']
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import { _ } from '$lib/i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import { ensureNoTrailingSlash } from '../../misc';
|
||||
import { getPages } from '../../navPages';
|
||||
import { getPages, type Pages } from '../../navPages';
|
||||
import {
|
||||
authStore,
|
||||
darkModeStore,
|
||||
@@ -109,6 +109,11 @@
|
||||
await window.electronAPI.setAllowInsecureSSL(isAllowed);
|
||||
}
|
||||
});
|
||||
|
||||
let pages: Pages = $state([]);
|
||||
authStore.subscribe(() => {
|
||||
pages = getPages();
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if Capacitor.isNativePlatform()}
|
||||
@@ -314,7 +319,7 @@
|
||||
bind:value={defaultPage}
|
||||
onchange={() => interfaceDefaultPage.set(defaultPage)}
|
||||
>
|
||||
{#each getPages() as page}
|
||||
{#each pages as page}
|
||||
{#if !page.requiresAuth || get(authStore)}
|
||||
<option selected={$interfaceDefaultPage === page.href} value={page.href}>{page.name}</option
|
||||
>
|
||||
|
||||
@@ -10,13 +10,16 @@
|
||||
playerAutoPlayStore,
|
||||
playerAutoplayNextByDefaultStore,
|
||||
playerDefaultLanguage,
|
||||
playerDefaultPlaybackSpeed,
|
||||
playerDefaultQualityStore,
|
||||
playerProxyVideosStore,
|
||||
playerSavePlaybackPositionStore,
|
||||
playerStatisticsByDefault,
|
||||
playerTheatreModeByDefaultStore,
|
||||
playerYouTubeJsAlways,
|
||||
playerYouTubeJsFallback
|
||||
} from '../../store';
|
||||
import { playbackRates } from '$lib/const';
|
||||
|
||||
let defaultLanguage = $state(get(playerDefaultLanguage));
|
||||
|
||||
@@ -36,6 +39,11 @@
|
||||
playerDefaultQualityStore.set(value);
|
||||
}
|
||||
|
||||
function onSpeedChange(event: Event) {
|
||||
const value = (event.target as HTMLSelectElement).value;
|
||||
playerDefaultPlaybackSpeed.set(Number(value));
|
||||
}
|
||||
|
||||
function onLocalVideoFallbackChange() {
|
||||
switch (localVideoFallback) {
|
||||
case 'enabled':
|
||||
@@ -90,6 +98,21 @@
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
|
||||
<div class="field label suffix border">
|
||||
<select
|
||||
name="quality"
|
||||
id="quality"
|
||||
bind:value={$playerDefaultPlaybackSpeed}
|
||||
onchange={onSpeedChange}
|
||||
>
|
||||
{#each playbackRates as speed}
|
||||
<option value={speed}>x{speed}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<label for="quality">{$_('layout.player.defaultPlaybackSpeed')}</label>
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
|
||||
{#if Capacitor.isNativePlatform()}
|
||||
<div class="field suffix border label">
|
||||
<select name="ytfallback" bind:value={localVideoFallback} onchange={onLocalVideoFallbackChange}>
|
||||
@@ -217,3 +240,19 @@
|
||||
</label>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="field no-margin">
|
||||
<nav class="no-padding">
|
||||
<div class="max">
|
||||
<div>{$_('layout.player.playerStatistics')}</div>
|
||||
</div>
|
||||
<label class="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={$playerStatisticsByDefault}
|
||||
onclick={() => playerStatisticsByDefault.set(!playerStatisticsByDefault)}
|
||||
/>
|
||||
<span></span>
|
||||
</label>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -21,10 +21,13 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if ($feedLastItemId)
|
||||
if ($feedLastItemId) {
|
||||
document
|
||||
.getElementById($feedLastItemId)
|
||||
?.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });
|
||||
|
||||
feedLastItemId.set(undefined);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const playbackRates = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3];
|
||||
@@ -207,7 +207,7 @@
|
||||
}
|
||||
|
||||
.shaka-overflow-button-label span:not(.shaka-current-selection-span) {
|
||||
font-weight: bold !important;
|
||||
font-weight: 200 !important;
|
||||
}
|
||||
|
||||
.youtube-theme .shaka-overflow-menu span + span {
|
||||
|
||||
@@ -32,14 +32,16 @@ import {
|
||||
synciousInstanceStore,
|
||||
synciousStore,
|
||||
themeColorStore,
|
||||
interfaceAutoExpandChapters
|
||||
interfaceAutoExpandChapters,
|
||||
playerDefaultPlaybackSpeed,
|
||||
playerStatisticsByDefault
|
||||
} from '$lib/store';
|
||||
import { get, type Writable } from 'svelte/store';
|
||||
|
||||
const persistedStores: {
|
||||
name: string;
|
||||
store: Writable<any>;
|
||||
type: 'string' | 'boolean' | 'array';
|
||||
type: 'string' | 'boolean' | 'array' | 'number';
|
||||
}[] = [
|
||||
{
|
||||
name: 'returnYTDislikesInstance',
|
||||
@@ -205,6 +207,16 @@ const persistedStores: {
|
||||
name: 'autoExpandChapters',
|
||||
store: interfaceAutoExpandChapters,
|
||||
type: 'boolean'
|
||||
},
|
||||
{
|
||||
name: 'defaultPlaybackSpeed',
|
||||
store: playerDefaultPlaybackSpeed,
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
name: 'playerStatistics',
|
||||
store: playerStatisticsByDefault,
|
||||
type: 'boolean'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -221,6 +233,8 @@ function setStores(toSet: Record<string, any>) {
|
||||
|
||||
if (store.type === 'array') {
|
||||
value = paramValue.split(',');
|
||||
} else if (store.type === 'number') {
|
||||
value = Number(paramValue);
|
||||
} else if (store.type === 'boolean') {
|
||||
value = typeof paramValue === 'string' ? paramValue === 'true' : paramValue;
|
||||
} else {
|
||||
|
||||
@@ -150,7 +150,9 @@
|
||||
"localVideoFallback": "Local video fallback",
|
||||
"youtubeJsFallback": "Local video processing fallback",
|
||||
"youtubeJsAlways": "Always use",
|
||||
"lockOrientation": "Orientation locked to aspect ratio"
|
||||
"lockOrientation": "Orientation locked to aspect ratio",
|
||||
"defaultPlaybackSpeed": "Default playback speed",
|
||||
"playerStatistics": "Show player statistics by default"
|
||||
},
|
||||
"bookmarklet": "Bookmarklet",
|
||||
"instanceUrl": "Instance URL",
|
||||
|
||||
@@ -80,6 +80,8 @@ export type feedItems = feedItem[];
|
||||
export function extractUniqueId(item: feedItem): string {
|
||||
if ('videoId' in item) {
|
||||
return item.videoId;
|
||||
} else if ('playlistId' in item) {
|
||||
return item.playlistId;
|
||||
} else if ('authorId' in item) {
|
||||
return item.authorId;
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { _ } from '$lib/i18n';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export type Pages = { icon: string; href: string; name: string; requiresAuth: boolean }[];
|
||||
|
||||
// Must be a func do to how i18n is loaded
|
||||
export function getPages(): { icon: string; href: string; name: string; requiresAuth: boolean }[] {
|
||||
export function getPages(): Pages {
|
||||
return [
|
||||
{
|
||||
icon: 'home',
|
||||
|
||||
@@ -46,6 +46,8 @@ export const playerYouTubeJsFallback = persisted('youTubeJsFallback', true);
|
||||
export const playerYouTubeJsAlways = persisted('youTubeJsAlways', false);
|
||||
export const playerAndroidLockOrientation = persisted('androidLockOrientation', true);
|
||||
export const playerDefaultLanguage = persisted('defaultLanguage', '');
|
||||
export const playerDefaultPlaybackSpeed: Writable<number> = persisted('defaultPlaybackSpeed', 1);
|
||||
export const playerStatisticsByDefault = persisted('playerStatistics', false);
|
||||
|
||||
export const returnYtDislikesStore = persisted('returnYtDislikes', false);
|
||||
export const returnYTDislikesInstanceStore: Writable<string | null | undefined> = persisted(
|
||||
|
||||
@@ -18,9 +18,11 @@ export async function load() {
|
||||
}
|
||||
}
|
||||
|
||||
feedCacheStore.set({ popular });
|
||||
feedCacheStore.set({ ...get(feedCacheStore), popular });
|
||||
} else {
|
||||
getPopular().then((newPopular) => feedCacheStore.set({ popular: newPopular }));
|
||||
getPopular().then((newPopular) =>
|
||||
feedCacheStore.set({ ...get(feedCacheStore), popular: newPopular })
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
document
|
||||
.getElementById($feedLastItemId)
|
||||
?.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });
|
||||
|
||||
feedLastItemId.set(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -94,7 +96,7 @@
|
||||
class="no-padding"
|
||||
style="height: 100%;"
|
||||
role="presentation"
|
||||
onclick={() => feedLastItemId.set(extractUniqueId(item))}
|
||||
onclick={async () => feedLastItemId.set(await extractUniqueId(item))}
|
||||
id={extractUniqueId(item)}
|
||||
>
|
||||
{#if item.type === 'video'}
|
||||
|
||||
@@ -20,13 +20,17 @@ export async function load({ params, url }) {
|
||||
|
||||
if (!search) {
|
||||
try {
|
||||
searchCacheStore.set({ [searchStoreId]: await getSearch(params.slug, { type: type }) });
|
||||
searchCacheStore.set({
|
||||
...get(searchCacheStore),
|
||||
[searchStoreId]: await getSearch(params.slug, { type: type })
|
||||
});
|
||||
} catch (errorMessage: any) {
|
||||
error(500, errorMessage);
|
||||
}
|
||||
} else {
|
||||
getSearch(params.slug, { type: type }).then((newSearch) => {
|
||||
searchCacheStore.set({
|
||||
...get(searchCacheStore),
|
||||
[searchStoreId]: excludeDuplicateFeeds(search, newSearch) as (
|
||||
| Channel
|
||||
| Video
|
||||
|
||||
@@ -18,11 +18,12 @@ export async function load() {
|
||||
|
||||
videos = [...feeds.notifications, ...feeds.videos];
|
||||
|
||||
feedCacheStore.set({ subscription: videos });
|
||||
feedCacheStore.set({ ...get(feedCacheStore), subscription: videos });
|
||||
} else {
|
||||
await getFeed(100, 1).then((feeds) => {
|
||||
const newVideos = [...feeds.notifications, ...feeds.videos, ...videos];
|
||||
feedCacheStore.set({
|
||||
...get(feedCacheStore),
|
||||
subscription: excludeDuplicateFeeds(videos, newVideos) as (
|
||||
| VideoBase
|
||||
| Video
|
||||
|
||||
@@ -13,8 +13,10 @@ export async function load() {
|
||||
error(500, errorMessage);
|
||||
}
|
||||
|
||||
feedCacheStore.set({ trending });
|
||||
feedCacheStore.set({ ...get(feedCacheStore), trending });
|
||||
} else {
|
||||
getTrending().then((newTrending) => feedCacheStore.set({ trending: newTrending }));
|
||||
getTrending().then((newTrending) =>
|
||||
feedCacheStore.set({ ...get(feedCacheStore), trending: newTrending })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-13
@@ -3,19 +3,15 @@ import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
LATEST_VERSION = "1.9.4"
|
||||
LATEST_VERSION = "1.9.5"
|
||||
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"
|
||||
)
|
||||
WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious")
|
||||
|
||||
ROOT_PACKAGE = os.path.join(WORKING_DIR, "package.json")
|
||||
ELECTRON_PACKAGE = os.path.join(WORKING_DIR, "electron", "package.json")
|
||||
ANDROID_PACKAGE = os.path.join(WORKING_DIR, "android", "app", "build.gradle")
|
||||
METAINFO_FILE = os.path.join(
|
||||
WORKING_DIR, "electron", "materialious.metainfo.xml"
|
||||
)
|
||||
METAINFO_FILE = os.path.join(WORKING_DIR, "electron", "materialious.metainfo.xml")
|
||||
|
||||
|
||||
def package_json_update_ver(location: str) -> None:
|
||||
@@ -38,9 +34,7 @@ def update_android_version() -> None:
|
||||
|
||||
if version_code_match and version_name_match:
|
||||
version_code = int(version_code_match.group(1)) + 1
|
||||
contents = re.sub(
|
||||
r"versionCode\s+\d+", f"versionCode {version_code}", contents
|
||||
)
|
||||
contents = re.sub(r"versionCode\s+\d+", f"versionCode {version_code}", contents)
|
||||
contents = re.sub(
|
||||
r'versionName\s+"[^"]+"',
|
||||
f'versionName "{LATEST_VERSION}"',
|
||||
@@ -66,9 +60,7 @@ def update_metainfo_release() -> None:
|
||||
</release>"""
|
||||
|
||||
# Insert the new release after the opening <releases> tag
|
||||
updated_contents = re.sub(
|
||||
r"(<releases>\s*)", rf"\1{new_release}\n", contents
|
||||
)
|
||||
updated_contents = re.sub(r"(<releases>\s*)", rf"\1{new_release}\n", contents)
|
||||
|
||||
with open(METAINFO_FILE, "w") as f_:
|
||||
f_.write(updated_contents)
|
||||
|
||||
Reference in New Issue
Block a user