Merge pull request #1138 from Materialious/update/1.10.15

Update/1.10.15
This commit is contained in:
Ward
2025-10-15 03:09:45 +13:00
committed by GitHub
42 changed files with 314 additions and 350 deletions
-3
View File
@@ -174,9 +174,6 @@ Help [translate Materialious via Weblate](https://toolate.othing.xyz/projects/ma
## Player
![Preview of player](./previews/player-preview.png)
## Transcript
![Preview of player transcript](./previews/transcript-preview.png)
## Settings
![Preview of settings](./previews/setting-preview.png)
+2 -2
View File
@@ -4,10 +4,10 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-10-08T03:32:17.403921789Z">
<DropdownSelection timestamp="2025-10-14T13:17:54.830930475Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/home/ward/.android/avd/Television_4K.avd" />
<DeviceId pluginId="LocalEmulator" identifier="path=/home/ward/.android/avd/Pixel_9.avd" />
</handle>
</Target>
</DropdownSelection>
+2 -2
View File
@@ -7,8 +7,8 @@ android {
applicationId "us.materialio.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 155
versionName "1.10.14"
versionCode 156
versionName "1.10.15"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -72,7 +72,11 @@
<release version="1.10.14" date="2025-10-08">
<release version="1.10.15" date="2025-10-15">
<url>https://github.com/Materialious/Materialious/releases/tag/1.10.15</url>
</release>
<release version="1.10.14" date="2025-10-08">
<url>https://github.com/Materialious/Materialious/releases/tag/1.10.14</url>
</release>
<release version="1.10.13" date="2025-10-08">
+6 -6
View File
@@ -1,12 +1,12 @@
{
"name": "Materialious",
"version": "1.10.13",
"version": "1.10.15",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "Materialious",
"version": "1.10.13",
"version": "1.10.15",
"license": "MIT",
"dependencies": {
"@capacitor-community/electron": "^5.0.0",
@@ -22,7 +22,7 @@
"jsonfile": "^6.2.0"
},
"devDependencies": {
"electron": "^38.2.1",
"electron": "^38.2.2",
"electron-builder": "^26.0.12",
"electron-rebuild": "^3.2.9",
"typescript": "^5.9.3"
@@ -2558,9 +2558,9 @@
}
},
"node_modules/electron": {
"version": "38.2.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-38.2.1.tgz",
"integrity": "sha512-P4pE2RpRg3kM8IeOK+heg6iAxR5wcXnNHrbVchn7M3GBnYAhjfJRkROusdOro5PlKzdtfKjesbbqaG4MqQXccg==",
"version": "38.2.2",
"resolved": "https://registry.npmjs.org/electron/-/electron-38.2.2.tgz",
"integrity": "sha512-OXSaVNXDlonXDjMRsFNQo1j5tzTKwKXh5/m46IjAFccBcZJZMISI+EjSI07oexIuhvKM8AZLuFuihVn4YjWWrA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "Materialious",
"version": "1.10.14",
"version": "1.10.15",
"description": "Modern material design for Invidious.",
"author": {
"name": "Ward Pearce",
@@ -35,7 +35,7 @@
"jsonfile": "^6.2.0"
},
"devDependencies": {
"electron": "^38.2.1",
"electron": "^38.2.2",
"electron-builder": "^26.0.12",
"electron-rebuild": "^3.2.9",
"typescript": "^5.9.3"
@@ -44,4 +44,4 @@
"capacitor",
"electron"
]
}
}
+2 -2
View File
@@ -104,8 +104,8 @@ export class ElectronCapacitorApp {
join(app.getAppPath(), 'assets', process.platform === 'win32' ? 'appIcon.ico' : 'appIcon.png')
);
this.mainWindowState = windowStateKeeper({
defaultWidth: 1000,
defaultHeight: 800
defaultWidth: 1920,
defaultHeight: 1080
});
// Setup preload script path and construct our main window.
const preloadPath = join(app.getAppPath(), 'build', 'src', 'preload.js');
+18 -18
View File
@@ -1,23 +1,23 @@
{
"name": "materialious",
"version": "1.10.12",
"version": "1.10.15",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "materialious",
"version": "1.10.12",
"version": "1.10.15",
"hasInstallScript": true,
"dependencies": {
"@capacitor-community/electron": "^5.0.1",
"@capacitor-community/safe-area": "^7.0.0-alpha.1",
"@capacitor/android": "^7.0.0",
"@capacitor/android": "^7.4.3",
"@capacitor/app": "^7.1.0",
"@capacitor/browser": "^7.0.0",
"@capacitor/clipboard": "^7.0.0",
"@capacitor/core": "^7.4.3",
"@capacitor/screen-orientation": "^7.0.0",
"beercss": "^3.8.0",
"beercss": "^3.12.11",
"bgutils-js": "^3.2.0",
"capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz",
"fuse.js": "^7.0.0",
@@ -42,7 +42,7 @@
"@capacitor/cli": "^7.4.3",
"@sveltejs/adapter-auto": "^6.0.1",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.37.0",
"@sveltejs/kit": "^2.46.5",
"@sveltejs/vite-plugin-svelte": "^6.1.4",
"@types/eslint": "^8.56.7",
"@types/event-source-polyfill": "^1.0.5",
@@ -59,7 +59,7 @@
"prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.4.0",
"svelte": "^5.19.7",
"svelte-check": "^4.0.0",
"svelte-check": "^4.3.3",
"tslib": "^2.7.0",
"typescript": "^5.6.3",
"vite": "^7.1.9"
@@ -1727,9 +1727,9 @@
}
},
"node_modules/@capacitor/android": {
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/@capacitor/android/-/android-7.4.2.tgz",
"integrity": "sha512-FZ7M9NwFkljR7EP5eXiE32mAIfZNcYw2CzRMCG3rQu0u0ZaIoeOeq5/oK4YcDnGpNmu8jpngKJqZ+9OiSQSwDg==",
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@capacitor/android/-/android-7.4.3.tgz",
"integrity": "sha512-VpjvnOcmYGPLgvXRhe3CGLs62Cg7sxOyp77NddCr+Y06qqgnoaj6OGeBVTc2DZlqZ6bSmh15JvFu82pkvmdgfQ==",
"license": "MIT",
"peerDependencies": {
"@capacitor/core": "^7.4.0"
@@ -3272,9 +3272,9 @@
}
},
"node_modules/@sveltejs/kit": {
"version": "2.37.0",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.37.0.tgz",
"integrity": "sha512-xgKtpjQ6Ry4mdShd01ht5AODUsW7+K1iValPDq7QX8zI1hWOKREH9GjG8SRCN5tC4K7UXmMhuQam7gbLByVcnw==",
"version": "2.46.5",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.46.5.tgz",
"integrity": "sha512-7TSvMrCdmig5TMyYDW876C5FljhA0wlGixtvASCiqUqtLfmyEEpaysXjC7GhR5mWcGRrCGF+L2Bl1eEaW1wTCA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4307,9 +4307,9 @@
}
},
"node_modules/beercss": {
"version": "3.10.8",
"resolved": "https://registry.npmjs.org/beercss/-/beercss-3.10.8.tgz",
"integrity": "sha512-LWwkkhF5F3VAD2z3pkDuWD540l/OUKdway9n+ZW+6naX+bKYPl9PKh/nD47/djYqYeH4jGu9QZcpk7p3Xv7YfQ==",
"version": "3.12.11",
"resolved": "https://registry.npmjs.org/beercss/-/beercss-3.12.11.tgz",
"integrity": "sha512-9NQWL3kxSOb5RPWAV5BPVs64CS72F5Z4HgxusqD07Ho4asaTJB33/SkJBMclQeUQs7hFH2CJOgGgDnIsRk2RyQ==",
"license": "MIT",
"dependencies": {
"material-dynamic-colors": "^1.1.2"
@@ -11673,9 +11673,9 @@
}
},
"node_modules/svelte-check": {
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.1.6.tgz",
"integrity": "sha512-P7w/6tdSfk3zEVvfsgrp3h3DFC75jCdZjTQvgGJtjPORs1n7/v2VMPIoty3PWv7jnfEm3x0G/p9wH4pecTb0Wg==",
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.3.tgz",
"integrity": "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==",
"dev": true,
"license": "MIT",
"dependencies": {
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "materialious",
"version": "1.10.14",
"version": "1.10.15",
"private": true,
"scripts": {
"dev": "vite dev",
@@ -18,7 +18,7 @@
"@capacitor/cli": "^7.4.3",
"@sveltejs/adapter-auto": "^6.0.1",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.37.0",
"@sveltejs/kit": "^2.46.5",
"@sveltejs/vite-plugin-svelte": "^6.1.4",
"@types/eslint": "^8.56.7",
"@types/event-source-polyfill": "^1.0.5",
@@ -35,7 +35,7 @@
"prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.4.0",
"svelte": "^5.19.7",
"svelte-check": "^4.0.0",
"svelte-check": "^4.3.3",
"tslib": "^2.7.0",
"typescript": "^5.6.3",
"vite": "^7.1.9"
@@ -44,13 +44,13 @@
"dependencies": {
"@capacitor-community/electron": "^5.0.1",
"@capacitor-community/safe-area": "^7.0.0-alpha.1",
"@capacitor/android": "^7.0.0",
"@capacitor/android": "^7.4.3",
"@capacitor/app": "^7.1.0",
"@capacitor/browser": "^7.0.0",
"@capacitor/clipboard": "^7.0.0",
"@capacitor/core": "^7.4.3",
"@capacitor/screen-orientation": "^7.0.0",
"beercss": "^3.8.0",
"beercss": "^3.12.11",
"bgutils-js": "^3.2.0",
"capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz",
"fuse.js": "^7.0.0",
@@ -70,4 +70,4 @@
"svelte-persisted-store": "^0.12.0",
"youtubei.js": "^15.1.1"
}
}
}
@@ -12,9 +12,10 @@
interface Props {
comment: Comment;
videoId: string;
isSubComp?: boolean;
}
let { comment, videoId }: Props = $props();
let { comment, videoId, isSubComp = false }: Props = $props();
let replies: Comments | undefined = $state(undefined);
@@ -53,7 +54,7 @@
});
</script>
<div class="comment">
<article class="comment" class:border={!isSubComp}>
{#if !$interfaceLowBandwidthMode}
<div class="comment-header">
{#if userPfp}
@@ -95,17 +96,16 @@
{#if replies}
<div style="margin-left: 5em;">
{#each replies.comments as reply}
<CommentSelf comment={reply} {videoId} />
<CommentSelf comment={reply} {videoId} isSubComp={true} />
{/each}
</div>
{/if}
</div>
</article>
<style>
.comment {
display: flex;
flex-direction: column;
margin-bottom: 1em;
white-space: pre-line;
}
@@ -71,7 +71,6 @@
}
function setupAndroidTVNavigation() {
console.log('gridElement', gridElement);
if (!$isAndroidTvStore || !gridElement) return;
focusableItems = Array.from(
@@ -117,6 +116,11 @@
switch (direction) {
case 'left':
if (currentFocusIndex % columns === 0) {
lastFocusIndex = currentFocusIndex;
updateTabIndex(currentFocusIndex);
return true;
}
newIndex = Math.max(0, currentFocusIndex - 1);
break;
case 'right':
@@ -128,7 +132,6 @@
// Store current position before leaving and ensure it stays focusable
lastFocusIndex = currentFocusIndex;
updateTabIndex(currentFocusIndex);
// Allow the event to bubble up to parent navigation
return true;
}
newIndex = Math.max(0, currentFocusIndex - columns);
@@ -218,12 +221,11 @@
</script>
<div class={classes} class:android-container={$isAndroidTvStore}>
<div class="space"></div>
<div class="grid" bind:this={gridElement}>
{#each items as item, index}
<ContentColumn>
<article
class="no-padding android-tv-item"
class="no-padding android-tv-item border"
class:android-tv-focused={$isAndroidTvStore}
style="height: 100%;"
id={extractUniqueId(item)}
+1 -1
View File
@@ -34,9 +34,9 @@
<svg
version="1.1"
id="svg1"
class={classes}
height="100%"
width="70px"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
viewBox="50.17 104.37 109.67 88.27"
@@ -33,6 +33,8 @@
}
];
const tabIds = tabs.map((tab) => tab.id);
let dialogType = $state('');
function checkWidth() {
@@ -81,14 +83,35 @@
});
</script>
<dialog id="dialog-settings" class={dialogType}>
<nav style="margin-bottom: 1em;">
<dialog id="dialog-settings" class={dialogType + ' surface-container'}>
<nav class="s m" style="margin-bottom: 1em;">
<h4 class="max">{$_('layout.settings')}</h4>
<button class="circle transparent" data-ui="#dialog-settings"><i>close</i></button>
</nav>
<div>
<nav class="tabbed small" style="outline: none" role="tablist" tabindex="0">
<nav class="wrap s">
<button class="large small-round secondary max" data-ui="#tab-menu">
<i>{tabs[tabIds.indexOf(activeTab)].icon}</i>
<span>{tabs[tabIds.indexOf(activeTab)].label}</span>
<menu style="width: 100%;" data-ui="#tab-menu" id="tab-menu">
{#each tabs as tab, _}
<li
data-ui="#tab-menu"
onclick={() => {
activeTab = tab.id;
}}
role="presentation"
>
<i>{tab.icon}</i>
<span>{tab.label}</span>
</li>
{/each}
</menu>
</button>
</nav>
<nav class="tabbed small m l" style="outline: none" role="tablist" tabindex="0">
{#each tabs as tab, index}
<a
role="tab"
@@ -128,6 +151,8 @@
</a>
{/if}
</nav>
<div class="space"></div>
<div class="divider"></div>
{#each tabs as tab, _}
<div
class="page padding"
@@ -146,35 +171,7 @@
</dialog>
<style>
dialog {
width: 1320px;
}
.tabbed > a {
flex: none;
}
@media screen and (max-width: 1320px) {
.tabbed {
display: flex !important;
gap: 0 !important;
flex: 0 !important;
align-items: start !important;
padding: 1em 0 !important;
overflow-x: scroll !important;
background-color: transparent;
border-bottom: solid 1px var(--outline-variant);
}
.tabbed > a {
font-size: 1.5rem !important;
padding: 0.5em 0.3em !important;
border-radius: 3em !important;
}
.tabbed.small {
block-size: 100% !important;
border-radius: 0 !important;
}
}
</style>
@@ -245,7 +245,7 @@
}
.sideways-root {
height: 110px;
height: 112px;
display: flex;
align-items: flex-start;
}
@@ -22,7 +22,6 @@
{numberWithCommas(video.viewCount)}
{$_('views')}{video.publishedText}
</div>
<i>expand_more</i>
</nav>
</summary>
<div class="space"></div>
@@ -30,13 +29,15 @@
<div style="white-space: pre-line; overflow-wrap: break-word;">
{@html description}
</div>
</div>
<nav class="scroll">
{#if video.keywords}
{#each video.keywords as keyword}
<a href={`/search/${encodeURIComponent(keyword)}`} class="chip">{keyword}</a>
{/each}
{/if}
</nav>
<article class="border">
<nav class="scroll">
{#if video.keywords}
{#each video.keywords as keyword}
<a href={`/search/${encodeURIComponent(keyword)}`} class="chip">{keyword}</a>
{/each}
{/if}
</nav>
</article>
</div>
</details>
@@ -9,21 +9,19 @@
</script>
{#await returnYTDislikes then returnYTDislikes}
{#if returnYTDislikes}
<nav class="no-space">
<button style="cursor: default;" class="border left-round">
<nav class="no-space">
{#if returnYTDislikes}
<button style="cursor: default;margin-right: 0.5em;" class="border">
<i class="small">thumb_up</i>
<span>{cleanNumber(returnYTDislikes.likes)}</span>
</button>
<button style="cursor: default;margin-right: 0.5em;" class="border right-round">
<i class="small">thumb_down_alt</i>
<i class="small" style="margin-left: 0.5em;">thumb_down_alt</i>
<span>{cleanNumber(returnYTDislikes.dislikes)}</span>
</button>
</nav>
{:else}
<button style="cursor: default;margin-right: 0.5em;" class="border">
<i class="small">thumb_up</i>
<span>{cleanNumber(video.likeCount)}</span>
</button>
{/if}
{:else}
<button style="cursor: default;margin-right: 0.5em;" class="border">
<i class="small">thumb_up</i>
<span>{cleanNumber(video.likeCount)}</span>
</button>
{/if}
</nav>
{/await}
+10 -25
View File
@@ -1,3 +1,13 @@
article {
box-shadow: none !important;
}
a,
button,
.button {
border-radius: 0.5rem !important;
}
.thumbnail-corner {
padding: 0.1em 0.4em;
}
@@ -20,45 +30,20 @@ nav.bottom {
}
main.root {
padding-top: 7em !important;
padding-bottom: 5em !important;
max-height: 100vh;
overflow-y: auto;
}
nav.left {
padding-top: 2em;
}
dialog {
padding-top: calc(var(--safe-area-inset-top) + 1rem) !important;
}
.root-not-tv {
padding-left: 7em !important;
}
.row {
padding: 1em !important;
}
button.row {
background-color: transparent !important;
border: none !important;
color: var(--on-surface) !important;
width: 100%;
margin: 0 !important;
}
button.row:hover {
box-shadow: none !important;
background-color: transparent !important;
width: 100%;
}
@media screen and (max-width: 1000px) {
menu.mobile {
z-index: 9999 !important;
position: fixed !important;
top: 20% !important;
width: 100% !important;
@@ -18,12 +18,14 @@
visibility: visible !important;
z-index: 99999999 !important;
pointer-events: auto !important;
pointer-events: auto !important;
max-height: 400px !important;
}
.shaka-overflow-menu button,
.shaka-settings-menu button {
color: var(--text) !important;
border: none !important;
outline: none !important;
}
@media screen and (max-width: 650px) {
@@ -44,7 +46,6 @@
left: 50% !important;
height: 50vh !important;
transform: translate(-50%, -50%) !important;
pointer-events: all !important;
pointer-events: auto !important;
opacity: 1 !important;
visibility: visible !important;
+5 -2
View File
@@ -174,7 +174,8 @@
"searchHistory": "Suchverlauf",
"companionUrl": "Companion URL",
"expandChapters": "Kapitel standardmäßig ausklappen",
"displayThumbnailAvatars": "Vorschaubild-Avatare (Langsam)"
"displayThumbnailAvatars": "Vorschaubild-Avatare (Langsam)",
"about": "Über"
},
"subscribe": "Abonnieren",
"recommendedVideos": "Vorgeschlagene Videos",
@@ -185,5 +186,7 @@
"invalidInstance": "Bitte prüfe die URL. Wenn sie korrekt ist, ist die Instanz möglicherweise außer Betrieb oder unterstützt keine Clients von Drittanbietern.",
"login": "Anmeldung",
"premium": "YouTube-Premium-Inhalte können auf Materialious nicht angeschaut werden.",
"isUpcoming": "Video ist noch nicht in einem anschaubaren Zustand."
"isUpcoming": "Video ist noch nicht in einem anschaubaren Zustand.",
"version": "Version",
"contributors": "Mitwirkende"
}
+5 -2
View File
@@ -86,7 +86,8 @@
"syncPartyWarning": "Bitte denk dra, dass dini IP für die vo dir iigladne Benutzer sichtbar isch.",
"displayThumbnailAvatars": "Vorschaubild-Avatar (Langsam)",
"expandDescription": "Beschriibig standardmässig erwiitere",
"disableAutoUpdate": "Automatischi Updates deaktiviere"
"disableAutoUpdate": "Automatischi Updates deaktiviere",
"about": "Über"
},
"videos": "Videos",
"cancel": "Abbreche",
@@ -185,5 +186,7 @@
"invalidInstance": "Bitte prüef d URL. Wenn si korrekt isch, isch d Instanz möglicherwiis usser Betriib oder unterstützt kei Clients vo Drittaabüüter.",
"loginRequired": "Aameldig erforderlich",
"isUpcoming": "Video isch nonig imene aaluegbare Zuestand.",
"premium": "YouTube-Premium-Inhält chönd uf Materialious nonig aagluegt werde."
"premium": "YouTube-Premium-Inhält chönd uf Materialious nonig aagluegt werde.",
"version": "Version",
"contributors": "Mitwirkendi"
}
+5 -2
View File
@@ -107,7 +107,8 @@
"titleOnly": "Csak cím"
},
"logout": "Kijelentkezés",
"companionUrl": "Kiegészítő webcím"
"companionUrl": "Kiegészítő webcím",
"about": "Névjegy"
},
"enabled": "Engedélyezve",
"donate": "Adományozás",
@@ -185,5 +186,7 @@
"letterCase": "Címek nagybetűs írásmódja",
"unsubscribe": "Leiratkozás",
"subscribe": "Feliratkozás",
"invidiousBlockWarning": "Az Invidious jelenleg le van tiltva a Google által. Ha ebben az esetben a videók nem töltődnek be, akkor használja ezt a példányt a Materialious-on {android} vagy {desktop} platformon, hogy helyi videó visszatéréssel megkerülje ezt a problémát."
"invidiousBlockWarning": "Az Invidious jelenleg le van tiltva a Google által. Ha ebben az esetben a videók nem töltődnek be, akkor használja ezt a példányt a Materialious-on {android} vagy {desktop} platformon, hogy helyi videó visszatéréssel megkerülje ezt a problémát.",
"version": "Verzió",
"contributors": "Közreműködők"
}
+112 -148
View File
@@ -253,14 +253,7 @@
<svelte:head>
{@html webManifestLink}
{#if $isAndroidTvStore}
<style>
:focus {
outline: 4px solid var(--primary);
box-shadow: none !important;
}
</style>
{:else if Capacitor.getPlatform() === 'android'}
{#if Capacitor.getPlatform() === 'android' && !$isAndroidTvStore}
<style>
nav.top {
height: 120px;
@@ -269,166 +262,136 @@
{/if}
</svelte:head>
{#if !$isAndroidTvStore}
<nav class="left m l small">
<header></header>
<div>
<nav class="left m l surface-container" class:tv-nav={$isAndroidTvStore}>
<header
role="presentation"
onclick={() => goto($interfaceDefaultPage)}
style="cursor: pointer;"
tabindex="-1"
class="small-padding"
>
<Logo />
</header>
{#each getPages() as navPage}
{#if !navPage.requiresAuth || isLoggedIn}
<a
href={navPage.href}
class:active={$page.url.href.endsWith(navPage.href)}
data-sveltekit-preload-data="off"
<a href={navPage.href} class:active={$page.url.href.endsWith(navPage.href)}
><i>{navPage.icon}</i>
<div>{navPage.name}</div>
</a>
{/if}
{/each}
</nav>
{/if}
<nav class="top" id="top-content">
{#if !mobileSearchShow}
<button
onclick={() => (mobileSearchShow = !mobileSearchShow)}
class="transparent s circle large"
>
<i>search</i>
</button>
{/if}
{#if Capacitor.getPlatform() === 'electron'}
<nav class="no-space">
<button onclick={() => window.history.back()} class="border left-round">
<i>arrow_back</i>
<nav class="top" id="top-content" class:tv-nav={$isAndroidTvStore}>
{#if !mobileSearchShow}
<button
onclick={() => (mobileSearchShow = !mobileSearchShow)}
class="transparent s circle large"
>
<i>search</i>
</button>
<button onclick={() => window.history.forward()} class="border right-round">
<i>arrow_forward</i>
</button>
</nav>
{/if}
{/if}
<nav
role="presentation"
onclick={() => goto($interfaceDefaultPage)}
style="cursor: pointer;"
class="m l"
tabindex="-1"
>
<Logo />
<h6 class="l">Materialious</h6>
</nav>
{#if Capacitor.getPlatform() === 'electron'}
<nav class="no-space">
<button onclick={() => window.history.back()} class="border">
<i>arrow_back</i>
</button>
<button onclick={() => window.history.forward()} class="border">
<i>arrow_forward</i>
</button>
</nav>
{/if}
<div class="max m l"></div>
<div class="max m l"></div>
<div class="m l">
<Search />
</div>
{#if !mobileSearchShow}
<div class="max"></div>
{/if}
{#if mobileSearchShow}
<div style="width: 100%;">
<Search on:searchCancelled={() => (mobileSearchShow = false)} />
<div class="m l">
<Search />
</div>
{:else}
{#if !Capacitor.isNativePlatform()}
<button data-ui="#sync-party" class="circle large transparent">
<i class:primary-text={$syncPartyPeerStore}>group</i>
<div class="tooltip bottom">{$_('layout.syncParty')}</div>
</button>
{/if}
{#if isLoggedIn}
<button class="circle large transparent" onclick={() => ui('#dialog-notifications')}
><i>notifications</i>
<div class="tooltip bottom">{$_('layout.notifications')}</div>
</button>
{/if}
<button class="circle large transparent" onclick={() => ui('#dialog-settings')}>
<i>settings</i>
<div class="tooltip bottom">{$_('layout.settings')}</div>
</button>
{#if !isLoggedIn}
<button onclick={login} class="circle large transparent">
<i>login</i>
<div class="tooltip bottom">{$_('layout.login')}</div>
</button>
{#if !mobileSearchShow}
<div class="max"></div>
{/if}
{#if mobileSearchShow}
<div style="width: 100%;">
<Search on:searchCancelled={() => (mobileSearchShow = false)} />
</div>
{:else}
<button onclick={logout} class="circle large transparent">
<i>logout</i>
<div class="tooltip bottom">{$_('layout.logout')}</div>
{#if !Capacitor.isNativePlatform()}
<button data-ui="#sync-party" class="circle large transparent">
<i class:primary-text={$syncPartyPeerStore}>group</i>
<div class="tooltip bottom">{$_('layout.syncParty')}</div>
</button>
{/if}
{#if isLoggedIn}
<button class="circle large transparent" onclick={() => ui('#dialog-notifications')}
><i>notifications</i>
<div class="tooltip bottom">{$_('layout.notifications')}</div>
</button>
{/if}
<button class="circle large transparent" onclick={() => ui('#dialog-settings')}>
<i>settings</i>
<div class="tooltip bottom">{$_('layout.settings')}</div>
</button>
{#if !isLoggedIn}
<button onclick={login} class="circle large transparent">
<i>login</i>
<div class="tooltip bottom">{$_('layout.login')}</div>
</button>
{:else}
<button onclick={logout} class="circle large transparent">
<i>logout</i>
<div class="tooltip bottom">{$_('layout.logout')}</div>
</button>
{/if}
{/if}
{/if}
</nav>
<nav class="bottom s">
{#each getPages() as navPage}
{#if !navPage.requiresAuth || isLoggedIn}
<a
class="round"
href={navPage.href}
class:active={$page.url.href.endsWith(navPage.href)}
data-sveltekit-preload-data="off"
><i>{navPage.icon}</i>
<span style="font-size: .8em;">{navPage.name}</span>
</a>
{/if}
{/each}
</nav>
<Settings />
<dialog class="right" id="dialog-notifications">
<nav>
<h5 class="max">{$_('layout.notifications')}</h5>
<button class="circle transparent" data-ui="#dialog-notifications"><i>close</i></button>
</nav>
{#if notifications.length === 0}
<p>{$_('layout.noNewNotifications')}</p>
{:else}
{#each notifications as notification}
<article class="no-padding">
<Thumbnail video={notification}></Thumbnail>
</article>
<nav class="bottom s">
{#each getPages() as navPage}
{#if !navPage.requiresAuth || isLoggedIn}
<a
class="round"
href={navPage.href}
class:active={$page.url.href.endsWith(navPage.href)}
data-sveltekit-preload-data="off"
><i>{navPage.icon}</i>
<span style="font-size: .8em;">{navPage.name}</span>
</a>
{/if}
{/each}
{/if}
</dialog>
</nav>
<main
id="main-content"
class="responsive max root"
tabindex="0"
role="region"
class:root-not-tv={!$isAndroidTvStore}
>
{#if $isAndroidTvStore}
<div class="tabs">
{#each getPages() as navPage}
{#if !navPage.requiresAuth || isLoggedIn}
<a
href={navPage.href}
class:active={$page.url.href.endsWith(navPage.href)}
class="active"
data-sveltekit-preload-data="off"
>
<i>{navPage.icon}</i>
<span>{navPage.name}</span>
</a>
{/if}
<Settings />
<dialog class="right" id="dialog-notifications">
<nav>
<h5 class="max">{$_('layout.notifications')}</h5>
<button class="circle transparent" data-ui="#dialog-notifications"><i>close</i></button>
</nav>
{#if notifications.length === 0}
<p>{$_('layout.noNewNotifications')}</p>
{:else}
{#each notifications as notification}
<article class="no-padding border">
<Thumbnail video={notification}></Thumbnail>
</article>
{/each}
</div>
{/if}
{#if $navigating}
<PageLoading />
{:else}
{@render children?.()}
{/if}
{/if}
</dialog>
<SyncParty />
</main>
<main id="main-content" class="responsive max root" tabindex="0" role="region">
{#if $navigating}
<PageLoading />
{:else}
{@render children?.()}
{/if}
<SyncParty />
</main>
</div>
<dialog class="modal" id="tv-login">
<h5>{$_('loginRequired')}</h5>
@@ -458,7 +421,8 @@
</dialog>
<style>
nav.left a {
font-size: 0.8em;
.tv-nav {
min-inline-size: 0.5rem;
padding: 0;
}
</style>
@@ -6,8 +6,6 @@
</script>
{#if data.popularDisabled}
<div class="space"></div>
<nav class="center-align">
<p>Popular page has been disabled</p>
</nav>
@@ -10,9 +10,7 @@
let { data } = $props();
</script>
<div class="space"></div>
<article>
<article class="border padding">
{#if data.playlist.videos}
<nav>
<a
@@ -53,7 +53,7 @@
}
</script>
<div style="margin-top: 1em;">
<div style="margin-bottom: 1em;">
<div class="tabs left-align min scroll">
<a class:active={currentType === 'all'} href="#all" onclick={async () => changeType('all')}>
<i>home</i>
@@ -22,14 +22,6 @@
}
</script>
<div class="space"></div>
<nav class="right-align">
<a class="button" href="/subscriptions/manage">
<i>subscriptions</i>
<span>{$_('subscriptions.manageSubscriptions')}</span>
</a>
</nav>
<ItemsList items={videos ?? []} />
<InfiniteLoading on:infinite={loadMore} />
@@ -303,7 +303,7 @@
clearTimeout(pauseTimeout);
}
pauseTimeout = setTimeout(() => {
// player.pause();
playerElement?.pause();
pauseTimerSeconds = 0;
clearTimeout(pauseTimeout);
}, pauseTimerSeconds * 1000);
@@ -313,11 +313,9 @@
</script>
<svelte:head>
<title>{data.video.title} | Materialious</title>
<title>{data.video.title}</title>
</svelte:head>
<div class="space"></div>
<div class="grid">
<div class={`s12 m12 l${theatreMode ? '12' : '9'}`}>
<div style="display: flex;justify-content: center;">
@@ -343,15 +341,14 @@
{#if data.video.lengthSeconds > 360 && !data.video.hlsUrl}
<button
onclick={() => {
if (pauseTimeout) {
clearTimeout(pauseTimeout);
if (pauseTimerSeconds < 1) {
pauseTimerSeconds = 300;
}
pauseTimerSeconds = 0;
ui('#pause-timer');
}}
class:border={pauseTimerSeconds < 1}
>
<i>schedule</i>
<i>snooze</i>
<div class="tooltip">{$_('player.pauseTimer')}</div>
</button>
{/if}
@@ -417,22 +414,20 @@
</div>
</div>
<article>
<article class="border">
<Description video={data.video} description={data.content.description} />
</article>
{#if data.content.timestamps.length > 0}
<article>
<article class="border">
<details>
<summary id="chapter-section" class="bold none">
<nav>
<div class="max">
<p>{$_('player.chapters')}</p>
</div>
<i>expand_more</i>
</nav>
</summary>
<div class="space"></div>
<div class="chapter-list" id="chapters">
<ul class="list">
{#each data.content.timestamps as timestamp}
@@ -465,12 +460,11 @@
{/if}
{#if comments && comments.comments.length > 0}
<article>
<article class="border">
<details>
<summary id="comment-section" class="none bold">
<nav>
<div class="max">{numberWithCommas(comments.commentCount)} {$_('comments')}</div>
<i>expand_more</i>
</nav>
</summary>
@@ -497,9 +491,9 @@
<article
style="height: 85vh; position: relative;scrollbar-width: none;"
id="playlist"
class="scroll no-padding surface-container-high"
class="scroll no-padding surface-container border"
>
<article class="no-elevate" style="position: sticky; top: 0; z-index: 3;">
<article class="no-elevate border" style="position: sticky; top: 0; z-index: 3;">
<h6>{$playlistCacheStore[data.playlistId].info.title}</h6>
<p>
{cleanNumber($playlistCacheStore[data.playlistId].info.viewCount)}
@@ -552,10 +546,10 @@
{#each $playlistCacheStore[data.playlistId].videos as playlistVideo}
<article
class="no-padding primary-border"
class="no-padding border"
style="margin: .7em;"
id={playlistVideo.videoId}
class:border={playlistVideo.videoId === data.video.videoId}
class:primary-border={playlistVideo.videoId === data.video.videoId}
>
{#key playlistVideo.videoId}
<Thumbnail
@@ -569,7 +563,7 @@
</article>
{:else if data.video.recommendedVideos}
{#each data.video.recommendedVideos as recommendedVideo}
<article class="no-padding">
<article class="no-padding border">
{#key recommendedVideo.videoId}
<Thumbnail video={recommendedVideo} sideways={true} />
{/key}
@@ -580,34 +574,39 @@
{/if}
</div>
<dialog class="modal" id="pause-timer">
<div class="field middle-align">
<label class="slider">
<input
type="range"
bind:value={pauseTimerSeconds}
min="0"
step="60"
max={data.video.lengthSeconds - playerCurrentTime - 60}
/>
<span></span>
</label>
{#if pauseTimerSeconds > 0}
<span class="helper">{$_('player.pauseVideoIn')} {humanizeSeconds(pauseTimerSeconds)}</span>
{/if}
<dialog
id="pause-timer"
onclose={(event: Event) => {
if (pauseTimerSeconds > 0) setPauseTimer();
(event.target as HTMLDialogElement).close();
}}
>
<div>
<h6>{$_('player.pauseVideoIn')} {humanizeSeconds(pauseTimerSeconds)}</h6>
<nav class="group">
<button onclick={() => (pauseTimerSeconds += 300)} class="left-round">+5 mins</button>
<button onclick={() => (pauseTimerSeconds += 1800)} class="no-round">+30 mins</button>
<button onclick={() => (pauseTimerSeconds += 3600)} class="no-round">+1 hr</button>
<button onclick={() => (pauseTimerSeconds += 7200)} class="right-round">+2 hrs</button>
</nav>
<div class="space"></div>
<nav class="wrap">
<button
onclick={() => {
pauseTimerSeconds = 0;
clearTimeout(pauseTimeout);
ui('#pause-timer');
}}
class="secondary max"
>
<i>delete</i>
<span>Clear</span>
</button>
</nav>
</div>
<nav class="right-align no-space">
<button
class="transparent link"
onclick={() => {
ui('#pause-timer');
pauseTimerSeconds = 0;
}}>Cancel</button
>
<button disabled={pauseTimerSeconds < 1} class="transparent link" onclick={setPauseTimer}
>Confirm</button
>
</nav>
</dialog>
<style>
@@ -123,7 +123,6 @@
<article id="shown-info" transition:fade>
<h5>{letterCase(data.video.title)}</h5>
<Author bind:subscribed video={data.video} />
<div class="space"></div>
<LikesDislikes video={data.video} returnYTDislikes={data.streamed.returnYTDislikes} />
<article class="border">
<Description video={data.video} description={data.content.description} />
@@ -176,5 +175,7 @@
height: 50%;
z-index: 101;
overflow-y: scroll;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}
</style>
+18
View File
@@ -0,0 +1,18 @@
<script lang="ts">
import { isAndroidTvStore } from '$lib/store';
let { children } = $props();
</script>
<svelte:head>
{#if $isAndroidTvStore}
<style>
:focus {
outline: 4px solid var(--primary);
box-shadow: none !important;
}
</style>
{/if}
</svelte:head>
{@render children?.()}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 MiB

After

Width:  |  Height:  |  Size: 5.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 MiB

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 MiB

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 928 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 923 KiB

+1 -1
View File
@@ -3,7 +3,7 @@ import os
import re
from datetime import datetime
LATEST_VERSION = "1.10.14"
LATEST_VERSION = "1.10.15"
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")