Merge pull request #1276 from Materialious/update/1.12.9

Update/1.12.9
This commit is contained in:
Ward
2025-12-28 03:54:55 +00:00
committed by GitHub
28 changed files with 14536 additions and 14349 deletions
+5 -5
View File
@@ -1,19 +1,19 @@
apply plugin: 'com.android.application'
android {
namespace "us.materialio.app"
compileSdk rootProject.ext.compileSdkVersion
namespace = "us.materialio.app"
compileSdk = rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "us.materialio.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 180
versionName "1.12.8"
versionCode 181
versionName "1.12.9"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
@@ -10,7 +10,7 @@
android:theme="@style/AppTheme">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"
android:name=".MainActivity"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBarLaunch"
+2 -2
View File
@@ -6,8 +6,8 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.10.1'
classpath 'com.google.gms:google-services:4.4.2'
classpath 'com.android.tools.build:gradle:8.13.2'
classpath 'com.google.gms:google-services:4.4.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Binary file not shown.
+14 -11
View File
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -83,7 +85,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -111,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -144,7 +147,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -152,7 +155,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -201,16 +204,16 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
+14 -12
View File
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -57,22 +59,22 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
+13 -13
View File
@@ -1,16 +1,16 @@
ext {
minSdkVersion = 23
compileSdkVersion = 35
targetSdkVersion = 35
androidxActivityVersion = '1.9.2'
androidxAppCompatVersion = '1.7.0'
androidxCoordinatorLayoutVersion = '1.2.0'
androidxCoreVersion = '1.15.0'
androidxFragmentVersion = '1.8.4'
coreSplashScreenVersion = '1.0.1'
androidxWebkitVersion = '1.12.1'
minSdkVersion = 24
compileSdkVersion = 36
targetSdkVersion = 36
androidxActivityVersion = '1.11.0'
androidxAppCompatVersion = '1.7.1'
androidxCoordinatorLayoutVersion = '1.3.0'
androidxCoreVersion = '1.17.0'
androidxFragmentVersion = '1.8.9'
coreSplashScreenVersion = '1.2.0'
androidxWebkitVersion = '1.14.0'
junitVersion = '4.13.2'
androidxJunitVersion = '1.2.1'
androidxEspressoCoreVersion = '3.6.1'
cordovaAndroidVersion = '10.1.1'
androidxJunitVersion = '1.3.0'
androidxEspressoCoreVersion = '3.7.0'
cordovaAndroidVersion = '14.0.1'
}
+3
View File
@@ -7,6 +7,9 @@ const config: CapacitorConfig = {
plugins: {
CapacitorNodeJS: {
nodeDir: 'nodejs-android'
},
SystemBars: {
insetsHandling: 'disable'
}
},
server: {
@@ -7,6 +7,9 @@ const config: CapacitorConfig = {
plugins: {
CapacitorNodeJS: {
nodeDir: 'nodejs-android'
},
SystemBars: {
insetsHandling: 'disable'
}
},
server: {
@@ -77,7 +77,11 @@
<release version="1.12.8" date="2025-12-23">
<release version="1.12.9" date="2025-12-28">
<url>https://github.com/Materialious/Materialious/releases/tag/1.12.9</url>
</release>
<release version="1.12.8" date="2025-12-23">
<url>https://github.com/Materialious/Materialious/releases/tag/1.12.8</url>
</release>
<release version="1.12.7" date="2025-12-22">
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "Materialious",
"version": "1.12.8",
"version": "1.12.9",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "Materialious",
"version": "1.12.8",
"version": "1.12.9",
"license": "MIT",
"dependencies": {
"@capacitor-community/electron": "^5.0.0",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "Materialious",
"version": "1.12.8",
"version": "1.12.9",
"description": "Modern material design for Invidious.",
"author": {
"name": "Ward Pearce",
@@ -44,4 +44,4 @@
"capacitor",
"electron"
]
}
}
+14028 -14071
View File
File diff suppressed because it is too large Load Diff
+77 -76
View File
@@ -1,78 +1,79 @@
{
"name": "materialious",
"version": "1.12.8",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"eslint": "eslint .",
"format": "prettier --write .",
"patch:shaka": "node ./scripts/patchShaka.mjs",
"postinstall": "npm run patch:shaka"
},
"devDependencies": {
"@capacitor/assets": "^3.0.5",
"@capacitor/cli": "^7.4.4",
"@sveltejs/adapter-auto": "^6.0.1",
"@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/kit": "^2.49.1",
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@types/event-source-polyfill": "^1.0.5",
"@types/he": "^1.2.3",
"@types/human-number": "^1.0.2",
"@types/mousetrap": "^1.6.15",
"@vite-pwa/sveltekit": "^1.0.0",
"eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-svelte": "^3.13.0",
"patch-package": "^8.0.1",
"prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.4.0",
"svelte": "^5.45.6",
"svelte-check": "^4.3.3",
"tslib": "^2.7.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.47.0",
"vite": "^7.2.6"
},
"type": "module",
"dependencies": {
"@capacitor-community/electron": "^5.0.1",
"@capacitor-community/safe-area": "^7.0.0-alpha.1",
"@capacitor/android": "^7.4.4",
"@capacitor/app": "^7.1.0",
"@capacitor/browser": "^7.0.0",
"@capacitor/clipboard": "^7.0.0",
"@capacitor/core": "^7.4.4",
"@capacitor/preferences": "^7.0.2",
"@capacitor/screen-orientation": "^7.0.0",
"@capacitor/share": "^7.0.2",
"@macfja/serializer": "^1.1.4",
"@macfja/svelte-persistent-store": "^2.4.2",
"beercss": "^3.13.1",
"bgutils-js": "^3.2.0",
"capacitor-music-controls-plugin": "^6.1.0",
"capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz",
"dayjs": "^1.11.19",
"dexie": "^4.2.1",
"fuse.js": "^7.0.0",
"googlevideo": "^4.0.4",
"he": "^1.2.0",
"human-number": "^2.0.8",
"i18next": "^25.7.2",
"iso-3166": "^4.4.0",
"iso-639-1": "^3.1.5",
"material-dynamic-colors": "^1.1.1",
"media-captions": "^1.0.4",
"mousetrap": "^1.6.5",
"peerjs": "^1.5.5",
"shaka-player": "^4.16.11",
"sponsorblock-api": "^0.2.4",
"svelte-infinite-loading": "^1.4.0",
"youtubei.js": "^16.0.1"
}
"name": "materialious",
"version": "1.12.9",
"private": true,
"scripts": {
"dev": "npm run patch:github && vite dev",
"build": "npm run patch:github && vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"eslint": "eslint .",
"format": "prettier --write .",
"patch:shaka": "node ./scripts/patchShaka.mjs",
"patch:github": "node ./scripts/githubContributors.mjs",
"postinstall": "npm run patch:shaka"
},
"devDependencies": {
"@capacitor/assets": "^3.0.5",
"@capacitor/cli": "^8.0.0",
"@sveltejs/adapter-auto": "^6.0.1",
"@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/kit": "^2.49.1",
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@types/event-source-polyfill": "^1.0.5",
"@types/he": "^1.2.3",
"@types/human-number": "^1.0.2",
"@types/mousetrap": "^1.6.15",
"@vite-pwa/sveltekit": "^1.0.0",
"eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-svelte": "^3.13.0",
"patch-package": "^8.0.1",
"prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.4.0",
"svelte": "^5.45.6",
"svelte-check": "^4.3.3",
"tslib": "^2.7.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.47.0",
"vite": "^7.2.6"
},
"type": "module",
"dependencies": {
"@capacitor-community/electron": "^5.0.1",
"@capacitor-community/safe-area": "^7.0.0-alpha.1",
"@capacitor/android": "^8.0.0",
"@capacitor/app": "^8.0.0",
"@capacitor/browser": "^8.0.0",
"@capacitor/clipboard": "^8.0.0",
"@capacitor/core": "^8.0.0",
"@capacitor/preferences": "^8.0.0",
"@capacitor/screen-orientation": "^8.0.0",
"@capacitor/share": "^8.0.0",
"@macfja/serializer": "^1.1.4",
"@macfja/svelte-persistent-store": "^2.4.2",
"beercss": "^3.13.1",
"bgutils-js": "^3.2.0",
"capacitor-music-controls-plugin": "^6.1.0",
"capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz",
"dayjs": "^1.11.19",
"dexie": "^4.2.1",
"fuse.js": "^7.0.0",
"googlevideo": "^4.0.4",
"he": "^1.2.0",
"human-number": "^2.0.8",
"i18next": "^25.7.2",
"iso-3166": "^4.4.0",
"iso-639-1": "^3.1.5",
"material-dynamic-colors": "^1.1.1",
"media-captions": "^1.0.4",
"mousetrap": "^1.6.5",
"peerjs": "^1.5.5",
"shaka-player": "^4.16.11",
"sponsorblock-api": "^0.2.4",
"svelte-infinite-loading": "^1.4.0",
"youtubei.js": "^16.0.1"
}
}
@@ -0,0 +1,35 @@
import fs from 'fs';
import path from 'path';
import fetch from 'node-fetch';
import * as url from 'node:url';
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
const apiUrl = 'https://api.github.com/repos/Materialious/Materialious/contributors';
const outputDir = path.join(__dirname, '../static/localApi');
const outputFile = 'ghContributors.json';
async function main() {
try {
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
// Ensure the output directory exists
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// Write the data to a JSON file
fs.writeFileSync(path.join(outputDir, outputFile), JSON.stringify(data));
console.log('Github contributors fetched and written to', path.join(outputDir, outputFile));
} catch (error) {
console.error('Error fetching data:', error);
}
}
main();
+252 -91
View File
@@ -3,7 +3,7 @@
import { getBestThumbnail } from '$lib/images';
import { padTime, videoLength } from '$lib/numbers';
import { type PhasedDescription } from '$lib/timestamps';
import { SafeArea } from '@capacitor-community/safe-area';
import { SafeArea, SystemBarsStyle, SystemBarsType } from '@capacitor-community/safe-area';
import { Capacitor } from '@capacitor/core';
import { ScreenOrientation, type ScreenOrientationResult } from '@capacitor/screen-orientation';
import { error, type Page } from '@sveltejs/kit';
@@ -43,7 +43,12 @@
} from '../store';
import { setStatusBarColor } from '../theme';
import { patchYoutubeJs } from '$lib/patches/youtubejs';
import { goToNextVideo, goToPreviousVideo, playbackRates } from '$lib/player';
import {
goToNextVideo,
goToPreviousVideo,
playbackRates,
playerDoubleTapSeek
} from '$lib/player';
import { dashManifestDomainInclusion } from '$lib/android/youtube/dash';
import { injectSabr } from '$lib/sabr';
import type { SabrStreamingAdapter } from 'googlevideo/sabr-streaming-adapter';
@@ -68,16 +73,17 @@
let snackBarAlert = $state('');
let originalOrigination: ScreenOrientationResult | undefined;
// eslint-disable-next-line no-undef
let watchProgressTimeout: NodeJS.Timeout;
let watchProgressInterval: NodeJS.Timeout;
let showVideoRetry = $state(false);
let player: shaka.Player;
let sabrAdapter: SabrStreamingAdapter | null;
let playerContainer: HTMLElement;
let bufferBar: HTMLElement | undefined = $state();
let playerCurrentPlaybackState = $state(false);
let playerCurrentTime = $state(0);
let playerMaxKnownTime = $state(0);
let playerMaxKnownTime = $state(data.video.lengthSeconds);
let playerIsBuffering = $state(false);
let playerVolume = $state(0);
let playerSettings: 'quality' | 'speed' | 'language' | 'root' = $state('root');
@@ -89,6 +95,13 @@
let playerTimelineTimeHover: number = $state(0);
let playerTimelineMouseX: number = $state(0);
let playerTimelineLastUpdate: number = 0;
let playerVideoEndTimePretty: string = $state('');
let clickCount = $state(0);
// eslint-disable-next-line no-undef
let clickCounterTimeout: NodeJS.Timeout;
let seekDirection: 'forwards' | 'backwards' = $state('forwards');
const STORAGE_KEY_VOLUME = 'shaka-preferred-volume';
@@ -192,14 +205,14 @@
if (isFullScreen) {
// Ensure bar color is black while in fullscreen
await SafeArea.enable({
config: {
customColorsForSystemBars: true,
statusBarColor: '#00000000',
statusBarContent: 'light',
navigationBarColor: '#00000000',
navigationBarContent: 'light'
}
await SafeArea.setSystemBarsStyle({
style: SystemBarsStyle.Dark
});
await SafeArea.hideSystemBars({
type: SystemBarsType.NavigationBar
});
await SafeArea.hideSystemBars({
type: SystemBarsType.StatusBar
});
} else {
await setStatusBarColor();
@@ -340,6 +353,14 @@
if (playerElement) playerElement.currentTime = playerCurrentTime;
}
function toggleFullscreen() {
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
playerContainer.requestFullscreen();
}
}
async function loadVideo() {
showVideoRetry = false;
@@ -357,11 +378,11 @@
});
if (!data.video.liveNow) {
if (watchProgressTimeout) {
clearInterval(watchProgressTimeout);
if (watchProgressInterval) {
clearInterval(watchProgressInterval);
}
// Auto save watch progress every minute.
watchProgressTimeout = setInterval(() => savePlayerPos(), 60000);
watchProgressInterval = setInterval(() => savePlayerPos(), 60000);
setupSponsorSkip();
let dashUrl: string;
@@ -692,14 +713,14 @@
if (!$isAndroidTvStore) {
Mousetrap.bind('right', () => {
if (!playerElement) return;
playerElement.currentTime = playerElement.currentTime + 10;
playerElement.currentTime = playerElement.currentTime + playerDoubleTapSeek;
return false;
});
Mousetrap.bind('left', () => {
if (!playerElement) return;
playerElement.currentTime = playerElement.currentTime - 10;
playerElement.currentTime = playerElement.currentTime - playerDoubleTapSeek;
return false;
});
}
@@ -710,11 +731,7 @@
});
Mousetrap.bind('f', () => {
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
playerContainer.requestFullscreen();
}
toggleFullscreen();
return false;
});
@@ -788,7 +805,33 @@
playerCurrentTime = playerElement.currentTime ?? 0;
if (playerMaxKnownTime === 0 || playerCurrentTime > playerMaxKnownTime) {
playerMaxKnownTime = Number(playerElement?.currentTime);
playerMaxKnownTime = Number(playerElement.currentTime);
}
const remainingTime = playerMaxKnownTime - playerElement.currentTime;
const videoEnds = new Date(Date.now() + remainingTime * 1000);
playerVideoEndTimePretty = videoEnds.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
hour12: true
});
const buffered = playerElement.buffered;
if (buffered.length > 0 && bufferBar) {
const bufferedEnd = buffered.end(0); // Amount shaka has buffered
const startBuffer = Math.max(playerElement.currentTime, 0);
// Calculate the buffered width as a percentage starting from the current time
const bufferedWidth =
((bufferedEnd - startBuffer) / (playerMaxKnownTime - startBuffer)) * 100;
const currentSliderPercentage = (playerCurrentTime / playerMaxKnownTime) * 100;
const effectiveWidth = Math.min(Math.max(bufferedWidth, 0), currentSliderPercentage);
bufferBar.style.width = effectiveWidth + '%';
bufferBar.style.left = Math.min(currentSliderPercentage, 100) + '%';
}
});
@@ -898,26 +941,40 @@
if (
event.target &&
event.target instanceof HTMLElement &&
event.target.id === 'player-center' &&
event.target.id === 'player-tap-controls-area' &&
parseFloat(getComputedStyle(event.target).opacity) > 0 &&
playerElement
) {
clickCount++;
const container = event.currentTarget;
const rect = container.getBoundingClientRect();
const clickX = event.clientX - rect.left;
const width = rect.width;
if (clickCounterTimeout) clearTimeout(clickCounterTimeout);
clickCounterTimeout = setTimeout(() => {
if (clickCount == 1) {
toggleVideoPlaybackStatus();
}
clickCount = 0;
}, 300);
if (clickCount < 2) return;
if (clickX < width / 3) {
// Left third, back 10s
playerElement.currentTime = Math.max(0, playerElement.currentTime - 10);
seekDirection = 'backwards';
playerElement.currentTime = Math.max(0, playerElement.currentTime - playerDoubleTapSeek);
} else if (clickX < (2 * width) / 3) {
// Middle third, toggle play/pause
toggleVideoPlaybackStatus();
toggleFullscreen();
} else {
// Right third, forward 10s
seekDirection = 'forwards';
playerElement.currentTime = Math.min(
playerElement.duration,
playerElement.currentTime + 10
playerElement.currentTime + playerDoubleTapSeek
);
}
}
@@ -935,6 +992,8 @@
}
}
await setStatusBarColor();
await CapacitorMusicControls.destroy();
}
@@ -948,10 +1007,12 @@
Mousetrap.unbind(['left', 'right', 'space', 'c', 'f', 'shift+left', 'shift+right']);
if (watchProgressTimeout) clearTimeout(watchProgressTimeout);
if (watchProgressInterval) clearTimeout(watchProgressInterval);
if (sabrAdapter) sabrAdapter.dispose();
if (clickCounterTimeout) clearTimeout(clickCounterTimeout);
if (player) {
player.unload();
player.destroy();
@@ -979,42 +1040,75 @@
</div>
{/if}
{#if !hideControls}
<p id="mobile-time" class="chip secondary s">
{#if data.video.liveNow}
{$_('thumbnail.live')}
{:else}
{videoLength(playerCurrentTime)} / {videoLength(data.video.lengthSeconds)}
{/if}
</p>
<div id="mobile-time">
<p class="chip inverse-primary s">
{#if data.video.liveNow}
{$_('thumbnail.live')}
{:else}
{videoLength(playerCurrentTime)} / {videoLength(playerMaxKnownTime)}
{/if}
</p>
<p class="chip inverse-primary">
{playerVideoEndTimePretty}
</p>
</div>
{/if}
<div id="player-center">
{#if playerIsBuffering}
<progress class="circle large indeterminate" value="50" max="100"></progress>
{:else if !playerCurrentPlaybackState}
<button class="extra secondary" onclick={toggleVideoPlaybackStatus}>
<i>play_arrow</i>
</button>
{/if}
<div class="grid">
<div class="s4 m4 l4" id="player-tap-controls-area">
{#if clickCount > 1 && seekDirection === 'backwards'}
<div
class="seek-double-click"
class:buffer-left={seekDirection === 'backwards'}
id="player-tap-controls-area"
>
<h4 id="player-tap-controls-area">-{(clickCount - 1) * playerDoubleTapSeek}</h4>
</div>
{/if}
</div>
<div class="s4 m4 l4">
<div class="player-status" id="player-tap-controls-area">
{#if playerIsBuffering}
<progress class="circle large indeterminate" value="50" max="100"></progress>
{:else if !playerCurrentPlaybackState}
<button class="extra inverse-primary" onclick={toggleVideoPlaybackStatus}>
<i>play_arrow</i>
</button>
{/if}
</div>
</div>
<div class="s4 m4 l4" id="player-tap-controls-area">
{#if clickCount > 1 && seekDirection === 'forwards'}
<div
class="seek-double-click"
class:buffer-right={seekDirection === 'forwards'}
id="player-tap-controls-area"
>
<h4 id="player-tap-controls-area">+{(clickCount - 1) * playerDoubleTapSeek}</h4>
</div>
{/if}
</div>
</div>
</div>
{#if !hideControls}
<div id="player-controls">
<article class="round" style="width: 100%;padding: 0;height: 10px;">
<label id="progress-slider" class="slider max">
{#key playerCurrentTime}
<input
oninput={handleTimeChange}
type="range"
min={0}
step={0.1}
bind:value={playerCurrentTime}
max={data.video.liveNow ? playerMaxKnownTime : data.video.lengthSeconds}
onmousemove={handleMouseMove}
onmouseleave={handleMouseLeave}
/>
{/key}
<span></span>
</label>
</article>
<label class="slider" id="progress-slider">
{#key playerCurrentTime}
<input
style="width: 100%;"
type="range"
oninput={handleTimeChange}
min={0}
step={0.1}
bind:value={playerCurrentTime}
max={playerMaxKnownTime}
onmousemove={handleMouseMove}
onmouseleave={handleMouseLeave}
/>
{/key}
<span></span>
<div bind:this={bufferBar} class="buffered-bar"></div>
</label>
{#if playerTimelineTooltipVisible}
<div class="tooltip" style="position: absolute; left: {playerTimelineMouseX}px;">
@@ -1024,7 +1118,7 @@
<nav>
<nav class="no-wrap">
<button class="secondary" onclick={toggleVideoPlaybackStatus}>
<button class="inverse-primary" onclick={toggleVideoPlaybackStatus}>
<i>
{#if playerCurrentPlaybackState}
pause
@@ -1034,35 +1128,28 @@
</i>
</button>
{#if Capacitor.getPlatform() !== 'android'}
<article
id="volume-slider"
class="round m l"
style="padding: 0;height: 10px;width: 150px;"
>
<label class="slider max">
{#key playerVolume}
<input
oninput={() => {
if (!playerElement) return;
<label class="slider round m l" id="volume-slider">
{#key playerVolume}
<input
oninput={() => {
if (!playerElement) return;
playerElement.volume = playerVolume;
}}
bind:value={playerVolume}
type="range"
step="0.1"
max="1"
/>
{/key}
<span></span>
</label>
</article>
playerElement.volume = playerVolume;
}}
bind:value={playerVolume}
type="range"
step="0.1"
max="1"
/>
{/key} <span></span>
</label>
{/if}
</nav>
<div class="max"></div>
<nav class="no-wrap">
<p class="chip secondary m l" style="border: none;">
<p class="chip m l" style="height: 100%;">
{#if data.video.liveNow}
{$_('thumbnail.live')}
{:else}
@@ -1070,7 +1157,7 @@
{/if}
</p>
{#if playerTextTracks && playerTextTracks.length > 0 && !data.video.liveNow}
<button class="secondary">
<button class="inverse-primary">
<i>closed_caption</i>
<menu class="no-wrap mobile" id="cc-menu" data-ui="#cc-menu">
<li
@@ -1095,7 +1182,7 @@
</menu>
</button>
{/if}
<button class="secondary">
<button class="inverse-primary">
<i>settings</i>
<menu class="no-wrap mobile" id="settings-menu">
{#if playerSettings !== 'root'}
@@ -1231,7 +1318,7 @@
</button>
{#if document.pictureInPictureEnabled}
<button
class="secondary"
class="inverse-primary"
onclick={() => {
(playerElement as HTMLVideoElement).requestPictureInPicture();
}}
@@ -1240,7 +1327,7 @@
</button>
{/if}
<button
class="secondary"
class="inverse-primary"
onclick={() => {
if (document.fullscreenElement) {
document.exitFullscreen();
@@ -1305,13 +1392,21 @@
padding: 10px;
}
#player-controls span {
clip-path: none;
}
#player-center {
position: absolute;
width: 100%;
height: 100%;
}
.player-status {
display: flex;
align-items: center;
justify-content: center;
align-items: center;
height: var(--video-player-height);
}
#mobile-time {
@@ -1319,15 +1414,24 @@
top: 0;
right: 0;
padding: 10px;
border: none;
opacity: 0;
transition: opacity 2s ease;
}
#mobile-time .chip {
border: none;
margin: 0;
}
#progress-slider > span {
transition: 0.25s;
}
#progress-slider {
block-size: 1.5em;
margin: 0;
}
#player-container:focus-within #player-controls,
#player-container:active #player-controls,
#player-container:hover #player-controls {
@@ -1342,6 +1446,42 @@
transition: opacity 0.3s ease;
}
.seek-double-click {
background-color: var(--secondary-container);
height: var(--video-player-height);
color: var(--secondary);
width: 100%;
opacity: 0.8;
padding: 1em;
display: flex;
justify-content: center;
align-items: center;
user-select: none;
}
.seek-double-click.buffer-right {
border-top-left-radius: 2rem;
border-bottom-left-radius: 2rem;
}
.seek-double-click.buffer-left {
border-top-right-radius: 2rem;
border-bottom-right-radius: 2rem;
}
.buffered-bar {
position: absolute;
height: 1rem;
background: var(--secondary);
top: 50%;
left: 0;
transform: translateY(-50%);
z-index: 0;
pointer-events: none;
border-top-right-radius: 2rem;
border-bottom-right-radius: 2rem;
}
menu.mobile {
transform: scale(1) translateY(-40%) translateX(0);
width: 300px;
@@ -1389,4 +1529,25 @@
.hide {
display: none;
}
.chip {
background-color: var(--inverse-primary);
color: var(--primary);
border: none;
}
.slider > span {
background-color: var(--inverse-primary);
color: var(--primary);
}
.slider > input::-webkit-slider-thumb {
background-color: var(--inverse-primary);
color: var(--primary);
}
.slider > input::-moz-range-thumb {
background-color: var(--inverse-primary);
color: var(--primary);
}
</style>
@@ -4,7 +4,7 @@
let ghContributors: { login: string; avatar_url: string; type: 'Bot' | 'User' }[] = $state([]);
onMount(() => {
fetch('https://api.github.com/repos/Materialious/Materialious/contributors', {
fetch(`${document.location.origin}/localApi/ghContributors.json`, {
priority: 'low'
}).then(async (resp) => {
if (!resp.ok) {
@@ -172,7 +172,15 @@
</dialog>
<style>
.tabbed {
padding: 0 1em;
}
.tabbed > a {
flex: none;
}
#dialog-settings {
width: fit-content;
}
</style>
-9
View File
@@ -41,23 +41,14 @@ button,
}
nav.top {
padding-top: calc(var(--safe-area-inset-top) + 1rem) !important;
padding-bottom: 1em;
}
nav.bottom {
padding-bottom: var(--safe-area-inset-bottom) !important;
}
main.root {
max-height: 100vh;
overflow-y: auto;
}
dialog {
padding-top: calc(var(--safe-area-inset-top) + 1rem) !important;
}
.row {
padding: 1em !important;
}
+12 -2
View File
@@ -117,7 +117,7 @@
"defaultPage": "Alapértelmezett oldal",
"player": {
"youtubeJsFallBack": "Helyi videó visszatérés van használatban",
"preferredQuality": "Előnyben részesített minőség",
"preferredQuality": "Előnyben részesített felbontás",
"share": {
"includeTimestamp": "Időbélyeggel ellátott",
"title": "Megosztás",
@@ -150,7 +150,17 @@
"enableYoutubejsPerm": "Mindig engedélyezze a helyi videó visszatérést",
"retryText": "A videó betöltése siketelen, hogyan szeretné folytatni?",
"youtubeJsLoading": "A videó betöltése sikertelen, újraprobálkozás a helyi videó visszatéréssel.",
"premiere": "A videó elérhető lesz ekkor"
"premiere": "A videó elérhető lesz ekkor",
"controls": {
"back": "Vissza",
"quality": "Felbontás",
"auto": "Automatikus",
"playbackSpeed": "Lejátszási sebesség",
"loop": "Folyamatos lejátszás",
"on": "Be",
"off": "Ki",
"language": "Nyelv"
}
},
"title": "Cím",
"subscriptions": {
+40 -7
View File
@@ -7,7 +7,7 @@
"cancel": "İptal",
"create": "Oluştur",
"title": "Başlık",
"searchPlaceholder": "Ara...",
"searchPlaceholder": "Ara",
"delete": "Sil",
"deleteAllHistory": "Tüm geçmişi sil",
"skipping": "Atla",
@@ -44,7 +44,9 @@
"private": "Gizli",
"playVideos": "Videoları oynat",
"shuffleVideos": "Videoları karışık oynat",
"loopPlaylist": "Çalma listesini tekrarla"
"loopPlaylist": "Çalma listesini tekrarla",
"next": "Sıradaki",
"previous": "Önceki"
},
"thumbnail": {
"live": "CANLI",
@@ -67,7 +69,8 @@
"title": "Paylaş",
"materialiousLink": "Bağlantıyı Materialious a kopyala",
"invidiousRedirect": "Invidious yönlendirme bağlantısını kopyala",
"youtubeLink": "Youtube bağlantısını kopyala"
"youtubeLink": "Youtube bağlantısını kopyala",
"copiedSuccess": "Panoya kopyalandı"
},
"download": "İndir",
"downloadSteps": {
@@ -82,7 +85,19 @@
},
"noPlaylists": "Oynatma listesi yok",
"unableToLoadComments": "Yorumlar yüklenemiyor",
"addToPlaylist": "Oynatma listesine ekle"
"addToPlaylist": "Oynatma listesine ekle",
"pauseTimer": "Zamanlayıcıyı durdur",
"preferredQuality": "Tercih edilen kalite",
"chapters": "Bölümler",
"defaultLanguage": "Varsayılan Dil",
"retryText": "Video yüklenemedi, nasıl devam etmek istersiniz?",
"controls": {
"quality": "Kalite",
"auto": "Otomatik",
"playbackSpeed": "Oynatma hızı",
"loop": "Döngü",
"language": "Dil"
}
},
"layout": {
"interface": "Arayüz",
@@ -101,7 +116,8 @@
"theme": "Tema",
"darkMode": "Koyu mod",
"lightMode": "Açık Mod",
"color": "Renk"
"color": "Renk",
"AmoledTheme": "Amoled tema"
},
"searchSuggestions": "Önerileri ara",
"previewVideoOnHover": "Fareyle üzerine gelindiğinde videoyu ön izleyin",
@@ -141,7 +157,11 @@
"thumbnailInstanceUrl": "Küçük resim görünüm URL'si",
"titleOnly": "Sadece başlık"
},
"allowInsecureRequests": "Güvenli olmayan isteklere izin ver"
"allowInsecureRequests": "Güvenli olmayan isteklere izin ver",
"about": "Hakkında",
"lowBandwidthMode": "Düşük bant genişliği modu",
"searchHistory": "Arama geçmişi",
"disableAutoUpdate": "Otomatik güncellemeleri devre dışı bırak"
},
"subscribe": "Abone Ol",
"premium": "Premium YouTube içeriği Materialious ile izlenemez.",
@@ -157,5 +177,18 @@
"comments": "yorumlar",
"reply": "cevapla",
"login": "Giriş",
"invidiousLogin": "Lütfen Invidious hesabınız ile giriş yapınız"
"invidiousLogin": "Lütfen Invidious hesabınız ile giriş yapınız",
"popularPageDisabled": "Popüler sayfası Yöneticiler tarafından devre dışı bırakıldı",
"filters": {
"filters": "Filtreler",
"type": "Tür",
"uploadDate": "Yüklenme tarihi",
"duration": "Uzunluk",
"features": "Özellikler"
},
"contributors": "Katkıda bulunanlar",
"version": "Sürüm",
"newest": "En yeni",
"oldest": "En eski",
"popular": "Popüler"
}
+1
View File
@@ -25,6 +25,7 @@ export interface PlayerEvents {
}
export const playbackRates = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3];
export const playerDoubleTapSeek = 10.0;
export function goToPreviousVideo(playlistId: string | null) {
const previousVideos = get(playerPlaylistHistory);
+9 -14
View File
@@ -2,7 +2,7 @@ import { Capacitor } from '@capacitor/core';
import ui from 'beercss';
import { tick } from 'svelte';
import { get } from 'svelte/store';
import { SafeArea } from '@capacitor-community/safe-area';
import { SafeArea, SystemBarsStyle, SystemBarsType } from '@capacitor-community/safe-area';
import { darkModeStore, interfaceAmoledTheme } from './store';
export async function getDynamicTheme(mode?: string): Promise<Record<string, string>> {
@@ -23,20 +23,15 @@ export async function setStatusBarColor() {
await tick();
const surfaceColor = get(interfaceAmoledTheme)
? '#000000'
: (await getDynamicTheme())['--surface-container'];
await SafeArea.setSystemBarsStyle({
style: get(darkModeStore) ? SystemBarsStyle.Dark : SystemBarsStyle.Light
});
const contentColor = !get(darkModeStore) ? 'dark' : 'light';
await SafeArea.enable({
config: {
customColorsForSystemBars: true,
statusBarColor: surfaceColor,
statusBarContent: contentColor,
navigationBarColor: surfaceColor,
navigationBarContent: contentColor
}
await SafeArea.showSystemBars({
type: SystemBarsType.NavigationBar
});
await SafeArea.showSystemBars({
type: SystemBarsType.StatusBar
});
}
@@ -525,11 +525,6 @@
width: 150px;
}
.pip > .pip-info > nav {
align-items: start;
padding-right: 0.5em;
}
.pip > .pip-info {
display: flex;
justify-content: space-between;
+7
View File
@@ -12,6 +12,13 @@
box-shadow: none !important;
}
</style>
{:else}
<style>
:focus {
outline: none !important;
box-shadow: none !important;
}
</style>
{/if}
</svelte:head>
File diff suppressed because one or more lines are too long
-23
View File
@@ -1,23 +0,0 @@
{
"sourceLanguageTag": "en",
"languageTags": [
"en",
"de",
"es",
"nl",
"pt-BR",
"ru",
"sh",
"tr"
],
"modules": [
"https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@latest/dist/index.js"
],
"plugin.inlang.i18next": {
"pathPattern": "./materialious/src/lib/i18n/locales/{languageTag}.json",
"variableReferencePattern": [
"{{",
"}}"
]
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ import os
import re
from datetime import datetime
LATEST_VERSION = "1.12.8"
LATEST_VERSION = "1.12.9"
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")