@@ -7,8 +7,8 @@ android {
|
||||
applicationId "us.materialio.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 60
|
||||
versionName "1.6.9"
|
||||
versionCode 61
|
||||
versionName "1.6.10"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -12,6 +12,8 @@ dependencies {
|
||||
implementation project(':capacitor-app')
|
||||
implementation project(':capacitor-browser')
|
||||
implementation project(':capacitor-clipboard')
|
||||
implementation project(':capacitor-screen-orientation')
|
||||
implementation project(':capacitor-status-bar')
|
||||
implementation project(':capgo-inappbrowser')
|
||||
implementation project(':mediagrid-capacitor-native-audio')
|
||||
implementation project(':capacitor-nodejs')
|
||||
|
||||
@@ -11,6 +11,12 @@ project(':capacitor-browser').projectDir = new File('../node_modules/@capacitor/
|
||||
include ':capacitor-clipboard'
|
||||
project(':capacitor-clipboard').projectDir = new File('../node_modules/@capacitor/clipboard/android')
|
||||
|
||||
include ':capacitor-screen-orientation'
|
||||
project(':capacitor-screen-orientation').projectDir = new File('../node_modules/@capacitor/screen-orientation/android')
|
||||
|
||||
include ':capacitor-status-bar'
|
||||
project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')
|
||||
|
||||
include ':capgo-inappbrowser'
|
||||
project(':capgo-inappbrowser').projectDir = new File('../node_modules/@capgo/inappbrowser/android')
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.6.9",
|
||||
"version": "1.6.10",
|
||||
"description": "Modern material design for Invidious.",
|
||||
"author": {
|
||||
"name": "Ward Pearce",
|
||||
|
||||
Generated
+45
-19
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.6.9",
|
||||
"version": "1.6.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "materialious",
|
||||
"version": "1.6.9",
|
||||
"version": "1.6.10",
|
||||
"dependencies": {
|
||||
"@capacitor-community/electron": "^5.0.1",
|
||||
"@capacitor/android": "6.1.2",
|
||||
@@ -15,6 +15,8 @@
|
||||
"@capacitor/cli": "6.1.2",
|
||||
"@capacitor/clipboard": "^6.0.1",
|
||||
"@capacitor/core": "^6.1.2",
|
||||
"@capacitor/screen-orientation": "^6.0.2",
|
||||
"@capacitor/status-bar": "^6.0.1",
|
||||
"@capgo/inappbrowser": "^6.6.8",
|
||||
"@ffmpeg/ffmpeg": "^0.12.10",
|
||||
"@mediagrid/capacitor-native-audio": "^1.0.0",
|
||||
@@ -2190,6 +2192,24 @@
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@capacitor/screen-orientation": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/screen-orientation/-/screen-orientation-6.0.2.tgz",
|
||||
"integrity": "sha512-CF+RFUIUYrvztlX+voHwvONdAbf/F9xuJWfMDnxjiGliu+oPTNLLTWxAFPmoyi3sVJcNoeQ+qwWzUfXa7ag59Q==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@capacitor/core": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@capacitor/status-bar": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-6.0.1.tgz",
|
||||
"integrity": "sha512-Usd9hZZQVAqy+jJfL7jRcYI7dcsxN09Na1yttwdl+F1bk3Ztoukk7CGPDm5VgKUSs53ihQBOy1+sczCACxhNiw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@capacitor/core": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@capgo/inappbrowser": {
|
||||
"version": "6.6.8",
|
||||
"resolved": "https://registry.npmjs.org/@capgo/inappbrowser/-/inappbrowser-6.6.8.tgz",
|
||||
@@ -2794,10 +2814,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@polka/url": {
|
||||
"version": "1.0.0-next.25",
|
||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz",
|
||||
"integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==",
|
||||
"dev": true
|
||||
"version": "1.0.0-next.28",
|
||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz",
|
||||
"integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@prettier/plugin-xml": {
|
||||
"version": "2.2.0",
|
||||
@@ -3141,15 +3162,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/kit": {
|
||||
"version": "2.5.27",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.27.tgz",
|
||||
"integrity": "sha512-CcbRTzl+65oWljAASL6UlxM4x3NWwd0fjq5fQOfP243vs50myFQ8lil0fr3Im6HeeQqYUCtnv8HjO8REWVPjTw==",
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.7.1.tgz",
|
||||
"integrity": "sha512-TBVnkwgYQT3EafGQK6Eyh5FlLEBlRhCmqPTwcdOs+QdnyUc3eCAxRWtXlFxIWtmk6pqv11zdng8qTpThdTogew==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.6.0",
|
||||
"cookie": "^0.6.0",
|
||||
"devalue": "^5.0.0",
|
||||
"devalue": "^5.1.0",
|
||||
"esm-env": "^1.0.0",
|
||||
"import-meta-resolve": "^4.1.0",
|
||||
"kleur": "^4.1.5",
|
||||
@@ -3157,7 +3179,7 @@
|
||||
"mrmime": "^2.0.0",
|
||||
"sade": "^1.8.1",
|
||||
"set-cookie-parser": "^2.6.0",
|
||||
"sirv": "^2.0.4",
|
||||
"sirv": "^3.0.0",
|
||||
"tiny-glob": "^0.2.9"
|
||||
},
|
||||
"bin": {
|
||||
@@ -5614,10 +5636,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/devalue": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.0.0.tgz",
|
||||
"integrity": "sha512-gO+/OMXF7488D+u3ue+G7Y4AA3ZmUnB3eHJXmBTgNHvr4ZNzl36A0ZtG+XCRNYCkYx/bFmw4qtkoFLa+wSrwAA==",
|
||||
"dev": true
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz",
|
||||
"integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dezalgo": {
|
||||
"version": "1.0.4",
|
||||
@@ -8866,6 +8889,7 @@
|
||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
|
||||
"integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
@@ -13569,17 +13593,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sirv": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
|
||||
"integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz",
|
||||
"integrity": "sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@polka/url": "^1.0.0-next.24",
|
||||
"mrmime": "^2.0.0",
|
||||
"totalist": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/sisteransi": {
|
||||
@@ -14807,6 +14832,7 @@
|
||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
|
||||
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.6.9",
|
||||
"version": "1.6.10",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
@@ -50,6 +50,8 @@
|
||||
"@capacitor/cli": "6.1.2",
|
||||
"@capacitor/clipboard": "^6.0.1",
|
||||
"@capacitor/core": "^6.1.2",
|
||||
"@capacitor/screen-orientation": "^6.0.2",
|
||||
"@capacitor/status-bar": "^6.0.1",
|
||||
"@capgo/inappbrowser": "^6.6.8",
|
||||
"@ffmpeg/ffmpeg": "^0.12.10",
|
||||
"@mediagrid/capacitor-native-audio": "^1.0.0",
|
||||
@@ -74,4 +76,4 @@
|
||||
"vidstack": "^1.12.9",
|
||||
"youtubei.js": "^10.5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let largeCol = '2';
|
||||
|
||||
function checkWidth() {
|
||||
if (innerWidth <= 1750) {
|
||||
largeCol = '4';
|
||||
} else {
|
||||
largeCol = '2';
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
checkWidth();
|
||||
|
||||
addEventListener('resize', () => checkWidth());
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="s12 m6 l{largeCol}">
|
||||
<slot />
|
||||
</div>
|
||||
@@ -3,13 +3,15 @@
|
||||
|
||||
import { page } from '$app/stores';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { ScreenOrientation } from '@capacitor/screen-orientation';
|
||||
import { StatusBar } from '@capacitor/status-bar';
|
||||
import { AudioPlayer } from '@mediagrid/capacitor-native-audio';
|
||||
import type { Page } from '@sveltejs/kit';
|
||||
import { SponsorBlock, type Category, type Segment } from 'sponsorblock-api';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import type { MediaTimeUpdateEvent, PlayerSrc } from 'vidstack';
|
||||
import type { FullscreenChangeEvent, MediaTimeUpdateEvent, PlayerSrc } from 'vidstack';
|
||||
import type { MediaPlayerElement } from 'vidstack/elements';
|
||||
import { deleteVideoProgress, getVideoProgress, saveVideoProgress } from './Api';
|
||||
import type { VideoPlay } from './Api/model';
|
||||
@@ -26,6 +28,7 @@
|
||||
miniPlayerSrcStore,
|
||||
playerAlwaysLoopStore,
|
||||
playerAndroidBgPlayer,
|
||||
playerAndroidLockOrientation,
|
||||
playerAutoPlayStore,
|
||||
playerProxyVideosStore,
|
||||
playerSavePlaybackPositionStore,
|
||||
@@ -195,7 +198,7 @@
|
||||
kind: 'captions',
|
||||
language: caption.language_code,
|
||||
// Need if captions are generated when youtube.js is being used.
|
||||
src: caption.url.startsWith('blob:')
|
||||
src: caption.url.startsWith('http')
|
||||
? caption.url
|
||||
: `${get(instanceStore)}${caption.url}`
|
||||
});
|
||||
@@ -344,57 +347,80 @@
|
||||
await loadPlayerPos();
|
||||
});
|
||||
|
||||
if (
|
||||
Capacitor.getPlatform() === 'android' &&
|
||||
get(playerAndroidBgPlayer) &&
|
||||
data.video.adaptiveFormats.length > 0
|
||||
) {
|
||||
const highestBitrateAudio = data.video.adaptiveFormats
|
||||
.filter((format) => format.type.startsWith('audio/'))
|
||||
.reduce((prev, current) => {
|
||||
return parseInt(prev.bitrate) > parseInt(current.bitrate) ? prev : current;
|
||||
});
|
||||
if (Capacitor.getPlatform() === 'android' && data.video.adaptiveFormats.length > 0) {
|
||||
if (get(playerAndroidBgPlayer)) {
|
||||
const highestBitrateAudio = data.video.adaptiveFormats
|
||||
.filter((format) => format.type.startsWith('audio/'))
|
||||
.reduce((prev, current) => {
|
||||
return parseInt(prev.bitrate) > parseInt(current.bitrate) ? prev : current;
|
||||
});
|
||||
|
||||
const audioId = { audioId: data.video.videoId };
|
||||
const audioId = { audioId: data.video.videoId };
|
||||
|
||||
let isPlayingInBackground = false;
|
||||
let isPlayingInBackground = false;
|
||||
|
||||
await AudioPlayer.create({
|
||||
...audioId,
|
||||
audioSource: !data.video.fallbackPatch
|
||||
? proxyVideoUrl(highestBitrateAudio.url)
|
||||
: highestBitrateAudio.url,
|
||||
friendlyTitle: data.video.title,
|
||||
useForNotification: true,
|
||||
loop: player.loop,
|
||||
isBackgroundMusic: false
|
||||
});
|
||||
|
||||
AudioPlayer.onAppGainsFocus(audioId, async () => {
|
||||
if (!isPlayingInBackground) return;
|
||||
|
||||
isPlayingInBackground = false;
|
||||
|
||||
await AudioPlayer.pause(audioId);
|
||||
|
||||
const audioPlayerTime = await AudioPlayer.getCurrentTime(audioId);
|
||||
|
||||
player.currentTime = Math.round(audioPlayerTime.currentTime);
|
||||
await player.play();
|
||||
});
|
||||
|
||||
AudioPlayer.onAppLosesFocus(audioId, async () => {
|
||||
if (player.paused) return;
|
||||
|
||||
isPlayingInBackground = true;
|
||||
await AudioPlayer.play(audioId);
|
||||
await AudioPlayer.seek({
|
||||
await AudioPlayer.create({
|
||||
...audioId,
|
||||
timeInSeconds: Math.round(player.currentTime)
|
||||
audioSource: !data.video.fallbackPatch
|
||||
? proxyVideoUrl(highestBitrateAudio.url)
|
||||
: highestBitrateAudio.url,
|
||||
friendlyTitle: data.video.title,
|
||||
useForNotification: true,
|
||||
loop: player.loop,
|
||||
isBackgroundMusic: false
|
||||
});
|
||||
});
|
||||
|
||||
await AudioPlayer.initialize(audioId);
|
||||
AudioPlayer.onAppGainsFocus(audioId, async () => {
|
||||
if (!isPlayingInBackground) return;
|
||||
|
||||
isPlayingInBackground = false;
|
||||
|
||||
await AudioPlayer.pause(audioId);
|
||||
|
||||
const audioPlayerTime = await AudioPlayer.getCurrentTime(audioId);
|
||||
|
||||
player.currentTime = Math.round(audioPlayerTime.currentTime);
|
||||
await player.play();
|
||||
});
|
||||
|
||||
AudioPlayer.onAppLosesFocus(audioId, async () => {
|
||||
if (player.paused) return;
|
||||
|
||||
isPlayingInBackground = true;
|
||||
await AudioPlayer.play(audioId);
|
||||
await AudioPlayer.seek({
|
||||
...audioId,
|
||||
timeInSeconds: Math.round(player.currentTime)
|
||||
});
|
||||
});
|
||||
|
||||
await AudioPlayer.initialize(audioId);
|
||||
}
|
||||
|
||||
if (get(playerAndroidLockOrientation)) {
|
||||
const videoFormats = data.video.adaptiveFormats.filter((format) =>
|
||||
format.type.startsWith('video/')
|
||||
);
|
||||
|
||||
const originalOrigination = await ScreenOrientation.orientation();
|
||||
player.addEventListener('fullscreen-change', async (event: FullscreenChangeEvent) => {
|
||||
if (event.detail && videoFormats[0].resolution) {
|
||||
const widthHeight = videoFormats[0].resolution.split('x');
|
||||
|
||||
if (widthHeight.length !== 2) return;
|
||||
|
||||
if (Number(widthHeight[0]) > Number(widthHeight[1])) {
|
||||
await StatusBar.setOverlaysWebView({ overlay: true });
|
||||
await ScreenOrientation.lock({ orientation: 'landscape' });
|
||||
} else {
|
||||
await ScreenOrientation.lock({ orientation: 'portrait' });
|
||||
}
|
||||
} else {
|
||||
await StatusBar.setOverlaysWebView({ overlay: false });
|
||||
await ScreenOrientation.lock({ orientation: originalOrigination.type });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
playerIsLive = true;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
deArrowThumbnailInstanceStore,
|
||||
deArrowTitlesOnly,
|
||||
instanceStore,
|
||||
interfaceAmoledTheme,
|
||||
interfaceAutoExpandComments,
|
||||
interfaceAutoExpandDesc,
|
||||
interfaceForceCase,
|
||||
@@ -25,6 +26,7 @@
|
||||
interfaceSearchSuggestionsStore,
|
||||
playerAlwaysLoopStore,
|
||||
playerAndroidBgPlayer,
|
||||
playerAndroidLockOrientation,
|
||||
playerAutoPlayStore,
|
||||
playerAutoplayNextByDefaultStore,
|
||||
playerListenByDefaultStore,
|
||||
@@ -46,6 +48,7 @@
|
||||
synciousStore,
|
||||
themeColorStore
|
||||
} from './store';
|
||||
import { setAmoledTheme } from './theme';
|
||||
|
||||
let sponsorCategoriesList: string[] = [];
|
||||
sponsorBlockCategoriesStore.subscribe((value) => (sponsorCategoriesList = value));
|
||||
@@ -82,6 +85,7 @@
|
||||
const target = color.target;
|
||||
const hex = (target as { value: string }).value;
|
||||
await ui('theme', hex);
|
||||
setAmoledTheme();
|
||||
themeColorStore.set(hex);
|
||||
}
|
||||
|
||||
@@ -190,6 +194,24 @@
|
||||
|
||||
<div class="margin"></div>
|
||||
|
||||
{#if $darkModeStore}
|
||||
<div class="field no-margin">
|
||||
<nav class="no-padding">
|
||||
<div class="max">
|
||||
<div>{$_('layout.theme.AmoledTheme')}</div>
|
||||
</div>
|
||||
<label class="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={$interfaceAmoledTheme}
|
||||
on:click={() => interfaceAmoledTheme.set(!$interfaceAmoledTheme)}
|
||||
/>
|
||||
<span></span>
|
||||
</label>
|
||||
</nav>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if Capacitor.isNativePlatform()}
|
||||
<form
|
||||
on:submit|preventDefault={() => {
|
||||
@@ -386,6 +408,22 @@
|
||||
</label>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="field no-margin">
|
||||
<nav class="no-padding">
|
||||
<div class="max">
|
||||
<div>{$_('layout.player.lockOrientation')}</div>
|
||||
</div>
|
||||
<label class="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={$playerAndroidLockOrientation}
|
||||
on:click={() => playerAndroidLockOrientation.set(!$playerAndroidLockOrientation)}
|
||||
/>
|
||||
<span></span>
|
||||
</label>
|
||||
</nav>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if Capacitor.isNativePlatform()}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import { removePlaylistVideo } from './Api';
|
||||
import type { Notification, PlaylistPageVideo, Video, VideoBase } from './Api/model';
|
||||
import ContentColumn from './ContentColumn.svelte';
|
||||
import Thumbnail from './Thumbnail.svelte';
|
||||
import { authStore } from './store';
|
||||
|
||||
@@ -20,20 +20,6 @@
|
||||
await removePlaylistVideo(playlistId, indexId);
|
||||
hiddenVideos = [...hiddenVideos, videoId];
|
||||
}
|
||||
|
||||
function checkWidth() {
|
||||
if (innerWidth <= 1750) {
|
||||
largeCol = '4';
|
||||
} else {
|
||||
largeCol = '2';
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
checkWidth();
|
||||
|
||||
addEventListener('resize', () => checkWidth());
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="page right active">
|
||||
@@ -41,7 +27,7 @@
|
||||
<div class="grid">
|
||||
{#each videos as video}
|
||||
{#if !hiddenVideos.includes(video.videoId)}
|
||||
<div class="s12 m6 l{largeCol}">
|
||||
<ContentColumn>
|
||||
<article class="no-padding" style="height: 100%;">
|
||||
<Thumbnail
|
||||
on:videoHidden={() => (hiddenVideos = [...hiddenVideos, video.videoId])}
|
||||
@@ -60,7 +46,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
</article>
|
||||
</div>
|
||||
</ContentColumn>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
"theme": "Theme",
|
||||
"darkMode": "Dark mode",
|
||||
"lightMode": "Light mode",
|
||||
"AmoledTheme": "Amoled theme",
|
||||
"color": "Color"
|
||||
},
|
||||
"searchSuggestions": "Search suggestions",
|
||||
@@ -127,7 +128,8 @@
|
||||
"dash": "Dash",
|
||||
"silenceSkipper": "Silence skipper (Experimental)",
|
||||
"youtubeJsFallback": "Local video processing fallback",
|
||||
"youtubeJsAlways": "Always use local video processing"
|
||||
"youtubeJsAlways": "Always use local video processing",
|
||||
"lockOrientation": "Orientation locked to aspect ratio"
|
||||
},
|
||||
"bookmarklet": "Bookmarklet",
|
||||
"instanceUrl": "Instance URL",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Image, Thumbnail, VideoBase, VideoPlay } from '$lib/Api/model';
|
||||
import type { AdaptiveFormats, Captions, Image, Thumbnail, VideoBase, VideoPlay } from '$lib/Api/model';
|
||||
import { numberWithCommas } from '$lib/misc';
|
||||
import { interfaceRegionStore, poTokenCacheStore } from '$lib/store';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
@@ -85,6 +85,8 @@ export async function patchYoutubeJs(videoId: string): Promise<VideoPlay> {
|
||||
|
||||
const video = await youtube.getInfo(videoId);
|
||||
|
||||
console.log(video);
|
||||
|
||||
if (!video.primary_info || !video.secondary_info) {
|
||||
throw new Error('Unable to pull video info from youtube.js');
|
||||
}
|
||||
@@ -104,7 +106,8 @@ export async function patchYoutubeJs(videoId: string): Promise<VideoPlay> {
|
||||
|
||||
let authorThumbnails: Image[];
|
||||
if (video.basic_info.channel_id) {
|
||||
authorThumbnails = (await youtube.getChannel(video.basic_info.channel_id)).metadata.avatar as Image[];
|
||||
const channel = await youtube.getChannel(video.basic_info.channel_id);
|
||||
authorThumbnails = channel.metadata.avatar as Image[];
|
||||
} else {
|
||||
authorThumbnails = [];
|
||||
}
|
||||
@@ -122,6 +125,31 @@ export async function patchYoutubeJs(videoId: string): Promise<VideoPlay> {
|
||||
});
|
||||
});
|
||||
|
||||
let captions: Captions[] = [];
|
||||
video.captions?.caption_tracks?.forEach(caption => {
|
||||
captions.push({
|
||||
label: caption.name.toString(),
|
||||
language_code: caption.language_code,
|
||||
url: caption.base_url
|
||||
});
|
||||
});
|
||||
|
||||
let adaptiveFormats: AdaptiveFormats[] = [];
|
||||
video.streaming_data?.adaptive_formats.forEach(format => {
|
||||
adaptiveFormats.push({
|
||||
index: format.index_range?.start.toString() || '',
|
||||
bitrate: format.bitrate.toString(),
|
||||
init: format.init_range?.start.toString() || '',
|
||||
url: format.url || '',
|
||||
itag: format.itag.toString(),
|
||||
type: format.mime_type,
|
||||
clen: '',
|
||||
lmt: '',
|
||||
projectionType: 0,
|
||||
resolution: format.width ? `${format.width}x${format.height}` : undefined
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'video',
|
||||
title: video.primary_info.title ? video.primary_info.title.toString() : '',
|
||||
@@ -136,11 +164,11 @@ export async function patchYoutubeJs(videoId: string): Promise<VideoPlay> {
|
||||
genre: video.basic_info.category || '',
|
||||
genreUrl: '',
|
||||
dashUrl: dashUri,
|
||||
adaptiveFormats: [],
|
||||
adaptiveFormats: adaptiveFormats,
|
||||
formatStreams: [],
|
||||
recommendedVideos: recommendedVideos,
|
||||
authorThumbnails: authorThumbnails,
|
||||
captions: [],
|
||||
captions: captions,
|
||||
authorId: video.basic_info.channel_id || '',
|
||||
authorUrl: `/channel/${video.basic_info.channel_id}`,
|
||||
authorVerified: false,
|
||||
|
||||
@@ -41,6 +41,7 @@ export const playerMiniPlayerStore = persisted('miniPlayer', true);
|
||||
export const playerYouTubeJsFallback = persisted('youTubeJsFallback', true);
|
||||
export const playerYouTubeJsAlways = persisted('youTubeJsAlways', false);
|
||||
export const playerAndroidBgPlayer = persisted('androidBgPlayer', true);
|
||||
export const playerAndroidLockOrientation = persisted('androidLockOrientation', true);
|
||||
|
||||
export const returnYtDislikesStore = persisted('returnYtDislikes', false);
|
||||
export const returnYTDislikesInstanceStore: Writable<string | null | undefined> = persisted(
|
||||
@@ -60,6 +61,8 @@ export const interfacePreviewVideoOnHoverStore = persisted('previewVideoOnHover'
|
||||
export const interfaceForceCase: Writable<TitleCase> = persisted('forceCase', null);
|
||||
export const interfaceAutoExpandComments: Writable<boolean> = persisted('autoExpandComments', true);
|
||||
export const interfaceAutoExpandDesc: Writable<boolean> = persisted('autoExpandDesc', false);
|
||||
export const interfaceAmoledTheme = persisted('amoledTheme', false);
|
||||
|
||||
|
||||
export const sponsorBlockStore = persisted('sponsorBlock', true);
|
||||
export const sponsorBlockUrlStore: Writable<string | null | undefined> = persisted(
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import ui from 'beercss';
|
||||
import { get } from 'svelte/store';
|
||||
import { darkModeStore, interfaceAmoledTheme } from './store';
|
||||
|
||||
export async function getDynamicTheme(mode?: string): Promise<Record<string, string>> {
|
||||
const givenSettings = (await ui('theme'));
|
||||
@@ -12,3 +14,46 @@ export async function getDynamicTheme(mode?: string): Promise<Record<string, str
|
||||
});
|
||||
return themeVars;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function setAmoledTheme() {
|
||||
const isAmoled = get(interfaceAmoledTheme);
|
||||
const isDark = get(darkModeStore);
|
||||
|
||||
if (isAmoled && isDark) {
|
||||
const rootVars = [
|
||||
'--surface-container',
|
||||
'--surface',
|
||||
'--surface-container-lowest',
|
||||
'--surface-container-low',
|
||||
'--surface-container-high',
|
||||
'--surface-container-highest'
|
||||
];
|
||||
rootVars.forEach((varName) => {
|
||||
document.body.style.setProperty(varName, '#000');
|
||||
});
|
||||
} else {
|
||||
setTheme();
|
||||
}
|
||||
}
|
||||
|
||||
export function setTheme() {
|
||||
const isDark = get(darkModeStore);
|
||||
|
||||
if (isDark === null) {
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
darkModeStore.set(true);
|
||||
ui('mode', 'dark');
|
||||
} else {
|
||||
darkModeStore.set(false);
|
||||
ui('mode', 'light');
|
||||
}
|
||||
} else {
|
||||
if (isDark) {
|
||||
ui('mode', 'dark');
|
||||
} else {
|
||||
ui('mode', 'light');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,11 @@
|
||||
authStore,
|
||||
darkModeStore,
|
||||
instanceStore,
|
||||
interfaceAmoledTheme,
|
||||
syncPartyPeerStore,
|
||||
themeColorStore
|
||||
} from '$lib/store';
|
||||
import { setAmoledTheme, setTheme } from '$lib/theme';
|
||||
import { App } from '@capacitor/app';
|
||||
import { Browser } from '@capacitor/browser';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
@@ -76,12 +78,13 @@
|
||||
}
|
||||
];
|
||||
|
||||
darkModeStore.subscribe((isDark) => {
|
||||
if (isDark) {
|
||||
ui('mode', 'dark');
|
||||
} else {
|
||||
ui('mode', 'light');
|
||||
}
|
||||
interfaceAmoledTheme.subscribe(() => {
|
||||
setAmoledTheme();
|
||||
});
|
||||
|
||||
darkModeStore.subscribe(() => {
|
||||
setTheme();
|
||||
setAmoledTheme();
|
||||
});
|
||||
|
||||
App.addListener('appUrlOpen', (data) => {
|
||||
@@ -147,29 +150,14 @@
|
||||
// So user preferences overwrite instance preferences.
|
||||
bookmarkletLoadFromUrl();
|
||||
|
||||
const isDark = get(darkModeStore);
|
||||
|
||||
if (isDark === null) {
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
darkModeStore.set(true);
|
||||
ui('mode', 'dark');
|
||||
} else {
|
||||
darkModeStore.set(false);
|
||||
ui('mode', 'light');
|
||||
}
|
||||
} else {
|
||||
if (isDark) {
|
||||
ui('mode', 'dark');
|
||||
} else {
|
||||
ui('mode', 'light');
|
||||
}
|
||||
}
|
||||
|
||||
const themeHex = get(themeColorStore);
|
||||
if (themeHex) {
|
||||
await ui('theme', themeHex);
|
||||
}
|
||||
|
||||
setTheme();
|
||||
setAmoledTheme();
|
||||
|
||||
if (isLoggedIn) {
|
||||
loadNotifications().catch(() => authStore.set(null));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { amSubscribed, deleteUnsubscribe, getChannelContent, postSubscribe } from '$lib/Api';
|
||||
import type { ChannelContentPlaylists, ChannelContentVideos } from '$lib/Api/model';
|
||||
import ContentColumn from '$lib/ContentColumn.svelte';
|
||||
import PageLoading from '$lib/PageLoading.svelte';
|
||||
import PlaylistThumbnail from '$lib/PlaylistThumbnail.svelte';
|
||||
import Thumbnail from '$lib/Thumbnail.svelte';
|
||||
import VideoList from '$lib/VideoList.svelte';
|
||||
import { cleanNumber, getBestThumbnail, proxyGoogleImage } from '$lib/misc';
|
||||
import { activePageStore, authStore } from '$lib/store';
|
||||
import { Clipboard } from '@capacitor/clipboard';
|
||||
@@ -184,28 +185,22 @@
|
||||
</div>
|
||||
|
||||
{#if displayContent}
|
||||
<div class="page right active">
|
||||
<div class="space"></div>
|
||||
<div class="grid">
|
||||
{#if 'videos' in displayContent}
|
||||
{#each displayContent.videos as video}
|
||||
<div class="s12 m6 l2">
|
||||
<article class="no-padding" style="height: 100%;">
|
||||
<Thumbnail {video} />
|
||||
</article>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
{#if 'videos' in displayContent}
|
||||
<VideoList videos={displayContent.videos} />
|
||||
{:else}
|
||||
<div class="page right active">
|
||||
<div class="space"></div>
|
||||
<div class="grid">
|
||||
{#each displayContent.playlists as playlist}
|
||||
<div class="s12 m6 l2">
|
||||
<ContentColumn>
|
||||
<article class="no-padding" style="height: 100%;">
|
||||
<PlaylistThumbnail {playlist} />
|
||||
</article>
|
||||
</div>
|
||||
</ContentColumn>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<InfiniteLoading on:infinite={loadMore} />
|
||||
{:else}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { getSearch } from '$lib/Api';
|
||||
import ChannelThumbnail from '$lib/ChannelThumbnail.svelte';
|
||||
import ContentColumn from '$lib/ContentColumn.svelte';
|
||||
import PageLoading from '$lib/PageLoading.svelte';
|
||||
import PlaylistThumbnail from '$lib/PlaylistThumbnail.svelte';
|
||||
import Thumbnail from '$lib/Thumbnail.svelte';
|
||||
@@ -79,7 +80,7 @@
|
||||
<div class="space"></div>
|
||||
<div class="grid">
|
||||
{#each data.search as item}
|
||||
<div class="s12 m6 l2">
|
||||
<ContentColumn>
|
||||
{#key item}
|
||||
<article class="no-padding" style="height: 100%;">
|
||||
{#if item.type === 'video'}
|
||||
@@ -91,7 +92,7 @@
|
||||
{/if}
|
||||
</article>
|
||||
{/key}
|
||||
</div>
|
||||
</ContentColumn>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -698,8 +698,6 @@
|
||||
</div>
|
||||
</details>
|
||||
</article>
|
||||
{:else}
|
||||
<h6>{$_('player.unableToLoadComments')}</h6>
|
||||
{/if}
|
||||
</div>
|
||||
{#if !theatreMode}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import json
|
||||
import os
|
||||
import re
|
||||
|
||||
LATEST_VERSION = "1.6.9"
|
||||
LATEST_VERSION = "1.6.10"
|
||||
WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious")
|
||||
|
||||
ROOT_PACKAGE = os.path.join(WORKING_DIR, "package.json")
|
||||
|
||||
Reference in New Issue
Block a user