diff --git a/materialious/android/app/build.gradle b/materialious/android/app/build.gradle
index 77d92978..4e421263 100644
--- a/materialious/android/app/build.gradle
+++ b/materialious/android/app/build.gradle
@@ -7,8 +7,8 @@ android {
applicationId "us.materialio.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 138
- versionName "1.9.21"
+ versionCode 139
+ versionName "1.9.22"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
diff --git a/materialious/electron/materialious.metainfo.xml b/materialious/electron/materialious.metainfo.xml
index d82b5c68..decdd37d 100644
--- a/materialious/electron/materialious.metainfo.xml
+++ b/materialious/electron/materialious.metainfo.xml
@@ -55,7 +55,11 @@
intense
-
+
+
+ https://github.com/Materialious/Materialious/releases/tag/1.9.22
+
+
https://github.com/Materialious/Materialious/releases/tag/1.9.21
diff --git a/materialious/electron/package-lock.json b/materialious/electron/package-lock.json
index e7b06988..9425e8d8 100644
--- a/materialious/electron/package-lock.json
+++ b/materialious/electron/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "Materialious",
- "version": "1.9.21",
+ "version": "1.9.22",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "Materialious",
- "version": "1.9.21",
+ "version": "1.9.22",
"license": "MIT",
"dependencies": {
"@capacitor-community/electron": "^5.0.0",
@@ -22,7 +22,7 @@
"jsonfile": "^6.1.0"
},
"devDependencies": {
- "electron": "^37.2.1",
+ "electron": "^37.2.3",
"electron-builder": "^26.0.12",
"electron-rebuild": "^3.2.9",
"typescript": "^5.7.3"
@@ -2495,9 +2495,9 @@
}
},
"node_modules/electron": {
- "version": "37.2.1",
- "resolved": "https://registry.npmjs.org/electron/-/electron-37.2.1.tgz",
- "integrity": "sha512-ae2EbzRNqIAHlftfCHtbbt6EgJUW8+zxWLONqNnn2iSrLF0O/pbxbff3xcpZYPpmFBs4uqjoi+s4QS7DQ+zZ/w==",
+ "version": "37.2.3",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-37.2.3.tgz",
+ "integrity": "sha512-JRKKn8cRDXDfkC+oWISbYs+c+L6RA776JM0NiB9bn2yV8H/LnBUlVPzKKfsXgrUIokN4YcbCw694vfAdEJwtGw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -3093,15 +3093,16 @@
}
},
"node_modules/form-data": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
- "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
+ "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"dev": true,
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
diff --git a/materialious/electron/package.json b/materialious/electron/package.json
index 70c81c57..6b4e98e8 100644
--- a/materialious/electron/package.json
+++ b/materialious/electron/package.json
@@ -1,6 +1,6 @@
{
"name": "Materialious",
- "version": "1.9.21",
+ "version": "1.9.22",
"description": "Modern material design for Invidious.",
"author": {
"name": "Ward Pearce",
@@ -35,7 +35,7 @@
"jsonfile": "^6.1.0"
},
"devDependencies": {
- "electron": "^37.2.1",
+ "electron": "^37.2.3",
"electron-builder": "^26.0.12",
"electron-rebuild": "^3.2.9",
"typescript": "^5.7.3"
@@ -44,4 +44,4 @@
"capacitor",
"electron"
]
-}
+}
\ No newline at end of file
diff --git a/materialious/electron/src/index.ts b/materialious/electron/src/index.ts
index b1a2e6a4..642bc1e3 100644
--- a/materialious/electron/src/index.ts
+++ b/materialious/electron/src/index.ts
@@ -3,8 +3,7 @@ import {
getCapacitorElectronConfig,
setupElectronDeepLinking
} from '@capacitor-community/electron';
-import type { MenuItemConstructorOptions } from 'electron';
-import { app, ipcMain, MenuItem, session } from 'electron';
+import { app, ipcMain, session } from 'electron';
import electronIsDev from 'electron-is-dev';
import unhandled from 'electron-unhandled';
import { autoUpdater } from 'electron-updater';
@@ -16,25 +15,12 @@ import { ElectronCapacitorApp, setupContentSecurityPolicy, setupReloadWatcher }
// Graceful handling of unhandled errors.
unhandled();
-// Define our menu templates (these are optional)
-const trayMenuTemplate: (MenuItemConstructorOptions | MenuItem)[] = [
- new MenuItem({ label: 'Quit App', role: 'quit' })
-];
-const appMenuBarMenuTemplate: (MenuItemConstructorOptions | MenuItem)[] = [
- { role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' },
- { role: 'viewMenu' }
-];
-
// Get Config options from capacitor.config
const capacitorFileConfig: CapacitorElectronConfig = getCapacitorElectronConfig();
// Initialize our app. You can pass menu templates into the app here.
// const myCapacitorApp = new ElectronCapacitorApp(capacitorFileConfig);
-const myCapacitorApp = new ElectronCapacitorApp(
- capacitorFileConfig,
- trayMenuTemplate,
- appMenuBarMenuTemplate
-);
+const myCapacitorApp = new ElectronCapacitorApp(capacitorFileConfig);
// If deeplinking is enabled then we will set it up here.
if (capacitorFileConfig.electron?.deepLinkingEnabled) {
diff --git a/materialious/electron/src/setup.ts b/materialious/electron/src/setup.ts
index d6169790..7f026b1f 100644
--- a/materialious/electron/src/setup.ts
+++ b/materialious/electron/src/setup.ts
@@ -1,13 +1,23 @@
import type { CapacitorElectronConfig } from '@capacitor-community/electron';
import {
- CapacitorSplashScreen,
- CapElectronEventEmitter,
- setupCapacitorElectronPlugins,
+ CapacitorSplashScreen,
+ CapElectronEventEmitter,
+ setupCapacitorElectronPlugins
} from '@capacitor-community/electron';
import { USER_AGENT } from 'bgutils-js';
import chokidar from 'chokidar';
import type { MenuItemConstructorOptions } from 'electron';
-import { app, BrowserWindow, Menu, MenuItem, nativeImage, session, shell, Tray } from 'electron';
+import {
+ app,
+ BrowserWindow,
+ Menu,
+ MenuItem,
+ nativeImage,
+ session,
+ shell,
+ Tray,
+ globalShortcut
+} from 'electron';
import electronIsDev from 'electron-is-dev';
import electronServe from 'electron-serve';
import windowStateKeeper from 'electron-window-state';
@@ -15,222 +25,236 @@ import { join } from 'path';
// Define components for a watcher to detect when the webapp is changed so we can reload in Dev mode.
const reloadWatcher = {
- debouncer: null,
- ready: false,
- watcher: null,
+ debouncer: null,
+ ready: false,
+ watcher: null
};
export function setupReloadWatcher(electronCapacitorApp: ElectronCapacitorApp): void {
- reloadWatcher.watcher = chokidar
- .watch(join(app.getAppPath(), 'app'), {
- ignored: /[/\\]\./,
- persistent: true,
- })
- .on('ready', () => {
- reloadWatcher.ready = true;
- })
- .on('all', (_event, _path) => {
- if (reloadWatcher.ready) {
- clearTimeout(reloadWatcher.debouncer);
- reloadWatcher.debouncer = setTimeout(async () => {
- electronCapacitorApp.getMainWindow().webContents.reload();
- reloadWatcher.ready = false;
- clearTimeout(reloadWatcher.debouncer);
- reloadWatcher.debouncer = null;
- reloadWatcher.watcher = null;
- setupReloadWatcher(electronCapacitorApp);
- }, 1500);
- }
- });
+ reloadWatcher.watcher = chokidar
+ .watch(join(app.getAppPath(), 'app'), {
+ ignored: /[/\\]\./,
+ persistent: true
+ })
+ .on('ready', () => {
+ reloadWatcher.ready = true;
+ })
+ .on('all', (_event, _path) => {
+ if (reloadWatcher.ready) {
+ clearTimeout(reloadWatcher.debouncer);
+ reloadWatcher.debouncer = setTimeout(async () => {
+ electronCapacitorApp.getMainWindow().webContents.reload();
+ reloadWatcher.ready = false;
+ clearTimeout(reloadWatcher.debouncer);
+ reloadWatcher.debouncer = null;
+ reloadWatcher.watcher = null;
+ setupReloadWatcher(electronCapacitorApp);
+ }, 1500);
+ }
+ });
}
// Define our class to manage our app.
export class ElectronCapacitorApp {
- private MainWindow: BrowserWindow | null = null;
- private SplashScreen: CapacitorSplashScreen | null = null;
- private TrayIcon: Tray | null = null;
- private CapacitorFileConfig: CapacitorElectronConfig;
- private TrayMenuTemplate: (MenuItem | MenuItemConstructorOptions)[] = [
- new MenuItem({ label: 'Quit App', role: 'quit' }),
- ];
- private AppMenuBarMenuTemplate: (MenuItem | MenuItemConstructorOptions)[] = [
- { role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' },
- { role: 'viewMenu' },
- ];
- private mainWindowState;
- private loadWebApp;
- private customScheme: string;
+ private MainWindow: BrowserWindow | null = null;
+ private SplashScreen: CapacitorSplashScreen | null = null;
+ private TrayIcon: Tray | null = null;
+ private CapacitorFileConfig: CapacitorElectronConfig;
+ private TrayMenuTemplate: (MenuItem | MenuItemConstructorOptions)[] = [
+ new MenuItem({ label: 'Quit App', role: 'quit' })
+ ];
+ private mainWindowState;
+ private loadWebApp;
+ private customScheme: string;
- constructor(
- capacitorFileConfig: CapacitorElectronConfig,
- trayMenuTemplate?: (MenuItemConstructorOptions | MenuItem)[],
- appMenuBarMenuTemplate?: (MenuItemConstructorOptions | MenuItem)[]
- ) {
- this.CapacitorFileConfig = capacitorFileConfig;
+ constructor(
+ capacitorFileConfig: CapacitorElectronConfig,
+ trayMenuTemplate?: (MenuItemConstructorOptions | MenuItem)[]
+ ) {
+ this.CapacitorFileConfig = capacitorFileConfig;
- this.customScheme = this.CapacitorFileConfig.electron?.customUrlScheme ?? 'capacitor-electron';
+ this.customScheme = this.CapacitorFileConfig.electron?.customUrlScheme ?? 'capacitor-electron';
- if (trayMenuTemplate) {
- this.TrayMenuTemplate = trayMenuTemplate;
- }
+ if (trayMenuTemplate) {
+ this.TrayMenuTemplate = trayMenuTemplate;
+ }
- if (appMenuBarMenuTemplate) {
- this.AppMenuBarMenuTemplate = appMenuBarMenuTemplate;
- }
+ // Setup our web app loader, this lets us load apps like react, vue, and angular without changing their build chains.
+ this.loadWebApp = electronServe({
+ directory: join(app.getAppPath(), 'app'),
+ scheme: this.customScheme
+ });
+ }
- // Setup our web app loader, this lets us load apps like react, vue, and angular without changing their build chains.
- this.loadWebApp = electronServe({
- directory: join(app.getAppPath(), 'app'),
- scheme: this.customScheme,
- });
- }
+ // Helper function to load in the app.
+ private async loadMainWindow(thisRef: any) {
+ await thisRef.loadWebApp(thisRef.MainWindow);
+ }
- // Helper function to load in the app.
- private async loadMainWindow(thisRef: any) {
- await thisRef.loadWebApp(thisRef.MainWindow);
- }
+ // Expose the mainWindow ref for use outside of the class.
+ getMainWindow(): BrowserWindow {
+ return this.MainWindow;
+ }
- // Expose the mainWindow ref for use outside of the class.
- getMainWindow(): BrowserWindow {
- return this.MainWindow;
- }
+ getCustomURLScheme(): string {
+ return this.customScheme;
+ }
- getCustomURLScheme(): string {
- return this.customScheme;
- }
+ async init(): Promise {
+ const icon = nativeImage.createFromPath(
+ join(app.getAppPath(), 'assets', process.platform === 'win32' ? 'appIcon.ico' : 'appIcon.png')
+ );
+ this.mainWindowState = windowStateKeeper({
+ defaultWidth: 1000,
+ defaultHeight: 800
+ });
+ // Setup preload script path and construct our main window.
+ const preloadPath = join(app.getAppPath(), 'build', 'src', 'preload.js');
+ this.MainWindow = new BrowserWindow({
+ icon,
+ show: false,
+ x: this.mainWindowState.x,
+ y: this.mainWindowState.y,
+ width: this.mainWindowState.width,
+ height: this.mainWindowState.height,
+ webPreferences: {
+ nodeIntegration: true,
+ contextIsolation: true,
+ webSecurity: false,
+ // Use preload to inject the electron varriant overrides for capacitor plugins.
+ // preload: join(app.getAppPath(), "node_modules", "@capacitor-community", "electron", "dist", "runtime", "electron-rt.js"),
+ preload: preloadPath
+ }
+ });
+ this.mainWindowState.manage(this.MainWindow);
- async init(): Promise {
- const icon = nativeImage.createFromPath(
- join(app.getAppPath(), 'assets', process.platform === 'win32' ? 'appIcon.ico' : 'appIcon.png')
- );
- this.mainWindowState = windowStateKeeper({
- defaultWidth: 1000,
- defaultHeight: 800,
- });
- // Setup preload script path and construct our main window.
- const preloadPath = join(app.getAppPath(), 'build', 'src', 'preload.js');
- this.MainWindow = new BrowserWindow({
- icon,
- show: false,
- x: this.mainWindowState.x,
- y: this.mainWindowState.y,
- width: this.mainWindowState.width,
- height: this.mainWindowState.height,
- webPreferences: {
- nodeIntegration: true,
- contextIsolation: true,
- webSecurity: false,
- // Use preload to inject the electron varriant overrides for capacitor plugins.
- // preload: join(app.getAppPath(), "node_modules", "@capacitor-community", "electron", "dist", "runtime", "electron-rt.js"),
- preload: preloadPath,
- },
- });
- this.mainWindowState.manage(this.MainWindow);
+ if (this.CapacitorFileConfig.backgroundColor) {
+ this.MainWindow.setBackgroundColor(this.CapacitorFileConfig.electron.backgroundColor);
+ }
- if (this.CapacitorFileConfig.backgroundColor) {
- this.MainWindow.setBackgroundColor(this.CapacitorFileConfig.electron.backgroundColor);
- }
+ // If we close the main window with the splashscreen enabled we need to destory the ref.
+ this.MainWindow.on('closed', () => {
+ if (
+ this.SplashScreen?.getSplashWindow() &&
+ !this.SplashScreen.getSplashWindow().isDestroyed()
+ ) {
+ this.SplashScreen.getSplashWindow().close();
+ }
+ });
- // If we close the main window with the splashscreen enabled we need to destory the ref.
- this.MainWindow.on('closed', () => {
- if (this.SplashScreen?.getSplashWindow() && !this.SplashScreen.getSplashWindow().isDestroyed()) {
- this.SplashScreen.getSplashWindow().close();
- }
- });
+ // When the tray icon is enabled, setup the options.
+ if (this.CapacitorFileConfig.electron?.trayIconAndMenuEnabled) {
+ this.TrayIcon = new Tray(icon);
+ this.TrayIcon.on('double-click', () => {
+ if (this.MainWindow) {
+ if (this.MainWindow.isVisible()) {
+ this.MainWindow.hide();
+ } else {
+ this.MainWindow.show();
+ this.MainWindow.focus();
+ }
+ }
+ });
+ this.TrayIcon.on('click', () => {
+ if (this.MainWindow) {
+ if (this.MainWindow.isVisible()) {
+ this.MainWindow.hide();
+ } else {
+ this.MainWindow.show();
+ this.MainWindow.focus();
+ }
+ }
+ });
+ this.TrayIcon.setToolTip(app.getName());
+ this.TrayIcon.setContextMenu(Menu.buildFromTemplate(this.TrayMenuTemplate));
+ }
- // When the tray icon is enabled, setup the options.
- if (this.CapacitorFileConfig.electron?.trayIconAndMenuEnabled) {
- this.TrayIcon = new Tray(icon);
- this.TrayIcon.on('double-click', () => {
- if (this.MainWindow) {
- if (this.MainWindow.isVisible()) {
- this.MainWindow.hide();
- } else {
- this.MainWindow.show();
- this.MainWindow.focus();
- }
- }
- });
- this.TrayIcon.on('click', () => {
- if (this.MainWindow) {
- if (this.MainWindow.isVisible()) {
- this.MainWindow.hide();
- } else {
- this.MainWindow.show();
- this.MainWindow.focus();
- }
- }
- });
- this.TrayIcon.setToolTip(app.getName());
- this.TrayIcon.setContextMenu(Menu.buildFromTemplate(this.TrayMenuTemplate));
- }
+ // Disable Menu Bar at the top.
+ Menu.setApplicationMenu(null);
- // Setup the main manu bar at the top of our window.
- Menu.setApplicationMenu(Menu.buildFromTemplate(this.AppMenuBarMenuTemplate));
+ // If the splashscreen is enabled, show it first while the main window loads then switch it out for the main window, or just load the main window from the start.
+ if (this.CapacitorFileConfig.electron?.splashScreenEnabled) {
+ this.SplashScreen = new CapacitorSplashScreen({
+ imageFilePath: join(
+ app.getAppPath(),
+ 'assets',
+ this.CapacitorFileConfig.electron?.splashScreenImageName ?? 'splash.png'
+ ),
+ windowWidth: 400,
+ windowHeight: 400
+ });
+ this.SplashScreen.init(this.loadMainWindow, this);
+ } else {
+ this.loadMainWindow(this);
+ }
- // If the splashscreen is enabled, show it first while the main window loads then switch it out for the main window, or just load the main window from the start.
- if (this.CapacitorFileConfig.electron?.splashScreenEnabled) {
- this.SplashScreen = new CapacitorSplashScreen({
- imageFilePath: join(
- app.getAppPath(),
- 'assets',
- this.CapacitorFileConfig.electron?.splashScreenImageName ?? 'splash.png'
- ),
- windowWidth: 400,
- windowHeight: 400,
- });
- this.SplashScreen.init(this.loadMainWindow, this);
- } else {
- this.loadMainWindow(this);
- }
+ globalShortcut.register('Control+Shift+I', () => {
+ this.MainWindow.webContents.toggleDevTools();
+ });
- // Security
- this.MainWindow.webContents.setWindowOpenHandler((details) => {
- shell.openExternal(details.url);
- return { action: 'deny' };
- });
- this.MainWindow.webContents.on('will-navigate', (event, _newURL) => {
- if (!this.MainWindow.webContents.getURL().includes(this.customScheme)) {
- event.preventDefault();
- }
- });
- // Link electron plugins into the system.
- setupCapacitorElectronPlugins();
+ let zoomFactor = 1.0;
+ globalShortcut.register('Control+Plus', () => {
+ zoomFactor += 0.1;
+ this.MainWindow.webContents.setZoomFactor(zoomFactor);
+ });
- // When the web app is loaded we hide the splashscreen if needed and show the mainwindow.
- this.MainWindow.webContents.on('dom-ready', () => {
- if (this.CapacitorFileConfig.electron?.splashScreenEnabled) {
- this.SplashScreen.getSplashWindow().hide();
- }
- if (!this.CapacitorFileConfig.electron?.hideMainWindowOnLaunch) {
- this.MainWindow.show();
- }
- setTimeout(() => {
- if (electronIsDev) {
- this.MainWindow.webContents.openDevTools();
- }
- CapElectronEventEmitter.emit('CAPELECTRON_DeeplinkListenerInitialized', '');
- }, 400);
- });
- }
+ globalShortcut.register('Control+Minus', () => {
+ zoomFactor = Math.max(0.2, zoomFactor - 0.1);
+ this.MainWindow.webContents.setZoomFactor(zoomFactor);
+ });
+
+ globalShortcut.register('Control+0', () => {
+ zoomFactor = 1.0;
+ this.MainWindow.webContents.setZoomFactor(zoomFactor);
+ });
+
+ // Security
+ this.MainWindow.webContents.setWindowOpenHandler((details) => {
+ shell.openExternal(details.url);
+ return { action: 'deny' };
+ });
+ this.MainWindow.webContents.on('will-navigate', (event, _newURL) => {
+ if (!this.MainWindow.webContents.getURL().includes(this.customScheme)) {
+ event.preventDefault();
+ }
+ });
+ // Link electron plugins into the system.
+ setupCapacitorElectronPlugins();
+
+ // When the web app is loaded we hide the splashscreen if needed and show the mainwindow.
+ this.MainWindow.webContents.on('dom-ready', () => {
+ if (this.CapacitorFileConfig.electron?.splashScreenEnabled) {
+ this.SplashScreen.getSplashWindow().hide();
+ }
+ if (!this.CapacitorFileConfig.electron?.hideMainWindowOnLaunch) {
+ this.MainWindow.show();
+ }
+ setTimeout(() => {
+ if (electronIsDev) {
+ this.MainWindow.webContents.openDevTools();
+ }
+ CapElectronEventEmitter.emit('CAPELECTRON_DeeplinkListenerInitialized', '');
+ }, 400);
+ });
+ }
}
// Set a CSP up for our application based on the custom scheme
export function setupContentSecurityPolicy(customScheme: string): void {
- session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
- callback({
- responseHeaders: {
- ...details.responseHeaders,
- },
- });
- });
+ session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
+ callback({
+ responseHeaders: {
+ ...details.responseHeaders
+ }
+ });
+ });
- session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
- const uri = new URL(details.url);
+ session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
+ const uri = new URL(details.url);
- details.requestHeaders['User-Agent'] = USER_AGENT;
- details.requestHeaders['origin'] = uri.origin;
- details.requestHeaders['host'] = uri.host;
+ details.requestHeaders['User-Agent'] = USER_AGENT;
+ details.requestHeaders['origin'] = uri.origin;
+ details.requestHeaders['host'] = uri.host;
- callback({ requestHeaders: details.requestHeaders });
- });
+ callback({ requestHeaders: details.requestHeaders });
+ });
}
diff --git a/materialious/package-lock.json b/materialious/package-lock.json
index 66614d57..36c6332d 100644
--- a/materialious/package-lock.json
+++ b/materialious/package-lock.json
@@ -55,7 +55,7 @@
"@vite-pwa/sveltekit": "^0.6.6",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
- "eslint-plugin-svelte": "^2.45.1",
+ "eslint-plugin-svelte": "^3.11.0",
"patch-package": "^8.0.0",
"prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.7",
@@ -3598,9 +3598,9 @@
"license": "BSD-2-Clause"
},
"node_modules/acorn": {
- "version": "8.14.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
- "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -5699,22 +5699,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint-compat-utils": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz",
- "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.5.4"
- },
- "engines": {
- "node": ">=12"
- },
- "peerDependencies": {
- "eslint": ">=6.0.0"
- }
- },
"node_modules/eslint-config-prettier": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
@@ -5729,32 +5713,31 @@
}
},
"node_modules/eslint-plugin-svelte": {
- "version": "2.46.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.46.1.tgz",
- "integrity": "sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==",
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.11.0.tgz",
+ "integrity": "sha512-KliWlkieHyEa65aQIkRwUFfHzT5Cn4u3BQQsu3KlkJOs7c1u7ryn84EWaOjEzilbKgttT4OfBURA8Uc4JBSQIw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
- "@jridgewell/sourcemap-codec": "^1.4.15",
- "eslint-compat-utils": "^0.5.1",
+ "@eslint-community/eslint-utils": "^4.6.1",
+ "@jridgewell/sourcemap-codec": "^1.5.0",
"esutils": "^2.0.3",
- "known-css-properties": "^0.35.0",
- "postcss": "^8.4.38",
+ "globals": "^16.0.0",
+ "known-css-properties": "^0.37.0",
+ "postcss": "^8.4.49",
"postcss-load-config": "^3.1.4",
- "postcss-safe-parser": "^6.0.0",
- "postcss-selector-parser": "^6.1.0",
- "semver": "^7.6.2",
- "svelte-eslint-parser": "^0.43.0"
+ "postcss-safe-parser": "^7.0.0",
+ "semver": "^7.6.3",
+ "svelte-eslint-parser": "^1.3.0"
},
"engines": {
- "node": "^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://github.com/sponsors/ota-meshi"
},
"peerDependencies": {
- "eslint": "^7.0.0 || ^8.0.0-0 || ^9.0.0-0",
+ "eslint": "^8.57.1 || ^9.0.0",
"svelte": "^3.37.0 || ^4.0.0 || ^5.0.0"
},
"peerDependenciesMeta": {
@@ -5763,6 +5746,19 @@
}
}
},
+ "node_modules/eslint-plugin-svelte/node_modules/globals": {
+ "version": "16.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
+ "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/eslint-scope": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
@@ -6225,14 +6221,15 @@
}
},
"node_modules/form-data": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
- "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
+ "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@@ -7966,9 +7963,9 @@
}
},
"node_modules/known-css-properties": {
- "version": "0.35.0",
- "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz",
- "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==",
+ "version": "0.37.0",
+ "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz",
+ "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==",
"dev": true,
"license": "MIT"
},
@@ -9438,20 +9435,30 @@
}
},
"node_modules/postcss-safe-parser": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz",
- "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz",
+ "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==",
"dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
"license": "MIT",
"engines": {
- "node": ">=12.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
+ "node": ">=18.0"
},
"peerDependencies": {
- "postcss": "^8.3.3"
+ "postcss": "^8.4.31"
}
},
"node_modules/postcss-scss": {
@@ -9482,9 +9489,9 @@
}
},
"node_modules/postcss-selector-parser": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
- "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
+ "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11469,20 +11476,21 @@
}
},
"node_modules/svelte-eslint-parser": {
- "version": "0.43.0",
- "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.43.0.tgz",
- "integrity": "sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.3.0.tgz",
+ "integrity": "sha512-VCgMHKV7UtOGcGLGNFSbmdm6kEKjtzo5nnpGU/mnx4OsFY6bZ7QwRF5DUx+Hokw5Lvdyo8dpk8B1m8mliomrNg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "eslint-scope": "^7.2.2",
- "eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1",
- "postcss": "^8.4.39",
- "postcss-scss": "^4.0.9"
+ "eslint-scope": "^8.2.0",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.0.0",
+ "postcss": "^8.4.49",
+ "postcss-scss": "^4.0.9",
+ "postcss-selector-parser": "^7.0.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://github.com/sponsors/ota-meshi"
@@ -11496,6 +11504,54 @@
}
}
},
+ "node_modules/svelte-eslint-parser/node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/svelte-eslint-parser/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/svelte-eslint-parser/node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/svelte-infinite-loading": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/svelte-infinite-loading/-/svelte-infinite-loading-1.4.0.tgz",
diff --git a/materialious/package.json b/materialious/package.json
index 840cbc4b..3ba92222 100644
--- a/materialious/package.json
+++ b/materialious/package.json
@@ -1,6 +1,6 @@
{
"name": "materialious",
- "version": "1.9.21",
+ "version": "1.9.22",
"private": true,
"scripts": {
"dev": "vite dev",
@@ -28,7 +28,7 @@
"@vite-pwa/sveltekit": "^0.6.6",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
- "eslint-plugin-svelte": "^2.45.1",
+ "eslint-plugin-svelte": "^3.11.0",
"patch-package": "^8.0.0",
"prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.7",
@@ -70,4 +70,4 @@
"svelte-persisted-store": "^0.12.0",
"youtubei.js": "^15.0.0"
}
-}
+}
\ No newline at end of file
diff --git a/materialious/src/lib/api/model.ts b/materialious/src/lib/api/model.ts
index 55feb8dc..bd1cdc60 100644
--- a/materialious/src/lib/api/model.ts
+++ b/materialious/src/lib/api/model.ts
@@ -100,7 +100,7 @@ export interface VideoPlay extends Video {
genre: string;
genreUrl: string;
hlsUrl?: string;
- dashUrl: string;
+ dashUrl?: string;
adaptiveFormats: AdaptiveFormats[];
formatStreams: FormatStreams[];
recommendedVideos: VideoBase[];
diff --git a/materialious/src/lib/components/ErrorMsg.svelte b/materialious/src/lib/components/ErrorMsg.svelte
new file mode 100644
index 00000000..48abe3b0
--- /dev/null
+++ b/materialious/src/lib/components/ErrorMsg.svelte
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+ {#if page.error}
+
+ {page.error.message}
+
+ {/if}
+
diff --git a/materialious/src/lib/components/Player.svelte b/materialious/src/lib/components/Player.svelte
index 3144cab9..7ab39c19 100644
--- a/materialious/src/lib/components/Player.svelte
+++ b/materialious/src/lib/components/Player.svelte
@@ -8,7 +8,7 @@
import { ScreenOrientation, type ScreenOrientationResult } from '@capacitor/screen-orientation';
import { StatusBar, Style } from '@capacitor/status-bar';
import { NavigationBar } from '@hugotomazi/capacitor-navigation-bar';
- import { type Page } from '@sveltejs/kit';
+ import { error, type Page } from '@sveltejs/kit';
import { HttpFetchPlugin } from '$lib/sabr/shakaHttpPlugin';
import ui from 'beercss';
import ISO6391 from 'iso-639-1';
@@ -385,7 +385,7 @@
watchProgressTimeout = setInterval(() => savePlayerPos(), 60000);
setupSponsorSkip();
- let dashUrl: string = '';
+ let dashUrl: string;
// Due to CORs issues with redirects, hosted instances of Materialious
// dirctly provide the companion instance
@@ -393,6 +393,10 @@
if (import.meta.env.VITE_DEFAULT_COMPANION_INSTANCE && Capacitor.getPlatform() === 'web') {
dashUrl = `${import.meta.env.VITE_DEFAULT_COMPANION_INSTANCE}/api/manifest/dash/id/${data.video.videoId}`;
} else {
+ if (!data.video.dashUrl) {
+ error(500, 'No dash manifest found');
+ return;
+ }
dashUrl = data.video.dashUrl;
}
@@ -403,6 +407,10 @@
await player.load(dashUrl, await getLastPlayPos());
} else {
if (data.video.fallbackPatch === 'youtubejs') {
+ if (!data.video.dashUrl) {
+ error(500, 'No dash manifest found');
+ return;
+ }
await player.load(data.video.dashUrl);
} else {
await player.load(data.video.hlsUrl + '?local=true');
@@ -590,22 +598,22 @@
playerElement.playbackRate = playerElement.playbackRate + 0.25;
return false;
});
-
+
Mousetrap.bind(',', () => {
- if (!playerElement) return
-
- const currentTrack = player.getVariantTracks().find(track => track.active)
- const frameTime = 1/(currentTrack?.frameRate || 30)
- playerElement.currentTime -= frameTime
- })
+ if (!playerElement) return;
+
+ const currentTrack = player.getVariantTracks().find((track) => track.active);
+ const frameTime = 1 / (currentTrack?.frameRate || 30);
+ playerElement.currentTime -= frameTime;
+ });
Mousetrap.bind('.', () => {
- if (!playerElement) return
-
- const currentTrack = player.getVariantTracks().find(track => track.active)
- const frameTime = 1/(currentTrack?.frameRate || 30)
- playerElement.currentTime += frameTime
- })
+ if (!playerElement) return;
+
+ const currentTrack = player.getVariantTracks().find((track) => track.active);
+ const frameTime = 1 / (currentTrack?.frameRate || 30);
+ playerElement.currentTime += frameTime;
+ });
playerElement.addEventListener('pause', async () => {
savePlayerPos();
diff --git a/materialious/src/lib/components/Thumbnail.svelte b/materialious/src/lib/components/Thumbnail.svelte
index 9d3c5de7..b0de978a 100644
--- a/materialious/src/lib/components/Thumbnail.svelte
+++ b/materialious/src/lib/components/Thumbnail.svelte
@@ -270,6 +270,7 @@
.video-title {
word-wrap: break-word;
overflow: hidden;
+ border-radius: 0px;
}
.thumbnail-details {
diff --git a/materialious/src/lib/i18n/index.ts b/materialious/src/lib/i18n/index.ts
index e62b5313..e6e27826 100644
--- a/materialious/src/lib/i18n/index.ts
+++ b/materialious/src/lib/i18n/index.ts
@@ -7,6 +7,7 @@ export const locale: Writable = writable(defaultLocale);
export const _: Writable<(key: string, options?: any) => string> = writable(() => '');
const resources: Record Promise>> = {
+ ar: () => import('./locales/ar.json'),
en: () => import('./locales/en.json'),
ru: () => import('./locales/ru.json'),
'zh-CN': () => import('./locales/zh-CN.json'),
@@ -14,6 +15,7 @@ const resources: Record Promise>> = {
nl: () => import('./locales/nl.json'),
de: () => import('./locales/de.json'),
es: () => import('./locales/es.json'),
+ gsw: () => import('./locales/gsw.json'),
sh: () => import('./locales/sh.json'),
'pt-BR': () => import('./locales/pt-BR.json'),
lv: () => import('./locales/lv.json'),
diff --git a/materialious/src/lib/i18n/locales/de.json b/materialious/src/lib/i18n/locales/de.json
index 599bb757..21b3ada6 100644
--- a/materialious/src/lib/i18n/locales/de.json
+++ b/materialious/src/lib/i18n/locales/de.json
@@ -124,7 +124,7 @@
"previewVideoOnHover": "Video-Vorschau beim Hovern",
"expandDescription": "Beschreibung standardmäßig erweitern",
"expandComments": "Kommentare standardmäßig erweitern",
- "disableAutoUpdate": "Automatische Installation von Updates deaktivieren",
+ "disableAutoUpdate": "Automatische Updates deaktivieren",
"dataPreferences": {
"content": "Du möchtest Abonnements importieren/exportieren, dein Passwort ändern oder dein Konto löschen? Klicke hier und scrolle bis zum Ende der Seite.",
"dataPreferences": "Dateneinstellungen"
diff --git a/materialious/src/lib/i18n/locales/en.json b/materialious/src/lib/i18n/locales/en.json
index e081e293..7f697f11 100644
--- a/materialious/src/lib/i18n/locales/en.json
+++ b/materialious/src/lib/i18n/locales/en.json
@@ -1,6 +1,8 @@
{
"enabled": "Enabled",
"disabled": "Disabled",
+ "premium": "Premium YouTube content can't be watched on Materialious.",
+ "isUpcoming": "Video is not in a watchable state yet.",
"copyUrl": "Copy URL",
"loadMore": "Load more",
"views": "views",
diff --git a/materialious/src/lib/i18n/locales/gsw.json b/materialious/src/lib/i18n/locales/gsw.json
index df5e4678..25f71674 100644
--- a/materialious/src/lib/i18n/locales/gsw.json
+++ b/materialious/src/lib/i18n/locales/gsw.json
@@ -85,7 +85,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"
+ "expandDescription": "Beschriibig standardmässig erwiitere",
+ "disableAutoUpdate": "Automatischi Updates deaktiviere"
},
"videos": "Videos",
"cancel": "Abbreche",
diff --git a/materialious/src/lib/patches/youtubejs.ts b/materialious/src/lib/patches/youtubejs.ts
index c77086db..135b6824 100644
--- a/materialious/src/lib/patches/youtubejs.ts
+++ b/materialious/src/lib/patches/youtubejs.ts
@@ -7,7 +7,6 @@ import type {
VideoBase,
VideoPlay
} from '$lib/api/model';
-import { numberWithCommas } from '$lib/numbers';
import { fromFormat } from '$lib/sabr/formatKeyUtils';
import { interfaceRegionStore, poTokenCacheStore } from '$lib/store';
import { Capacitor } from '@capacitor/core';
@@ -77,8 +76,6 @@ export async function patchYoutubeJs(videoId: string): Promise {
throw new Error('Unable to pull video info from youtube.js');
}
- console.log(video);
-
let dashUri: string | undefined;
if (video.streaming_data) {
@@ -103,8 +100,6 @@ export async function patchYoutubeJs(videoId: string): Promise {
}
}
- if (!dashUri) throw Error('Unable to find suitable dash manifest');
-
const descString = video.secondary_info.description?.toString() || '';
let authorThumbnails: Image[];
@@ -206,9 +201,10 @@ export async function patchYoutubeJs(videoId: string): Promise {
premiereTimestamp: 0,
hlsUrl: video.streaming_data?.hls_manifest_url || undefined,
liveNow: video.basic_info.is_live || false,
- premium: false,
+ // @ts-expect-error Type does have offer_id
+ premium: video?.playability_status?.error_screen?.offer_id === 'sponsors_only_video',
storyboards: storyboard,
- isUpcoming: false,
+ isUpcoming: video?.playability_status?.status !== 'OK',
videoId: videoId,
videoThumbnails: video.basic_info.thumbnail as Thumbnail[],
author: video.basic_info.author || 'Unknown',
diff --git a/materialious/src/lib/store.ts b/materialious/src/lib/store.ts
index 48b32947..27a0b60c 100644
--- a/materialious/src/lib/store.ts
+++ b/materialious/src/lib/store.ts
@@ -67,7 +67,7 @@ export const returnYTDislikesInstanceStore: Writable
)
);
-export const synciousStore = persisted('syncious', true);
+export const synciousStore = persisted('syncious', false);
export const synciousInstanceStore: Writable = persisted(
'synciousInstance',
platformDependentDefault(
@@ -92,7 +92,7 @@ export const interfaceDisplayThumbnailAvatars = persisted('disableThumbnailAvata
export const interfaceDefaultPage = persisted('defaultPage', '/');
export const interfaceSearchHistoryEnabled = persisted('searchHistoryEnabled', false);
export const interfaceAllowInsecureRequests = persisted('allowInsecureRequests', false);
-export const interfaceDisableAutoUpdate = persisted('disableAutoUpdate', false)
+export const interfaceDisableAutoUpdate = persisted('disableAutoUpdate', false);
export const sponsorBlockStore = persisted('sponsorBlock', true);
export const sponsorBlockUrlStore: Writable = persisted(
diff --git a/materialious/src/lib/watch.ts b/materialious/src/lib/watch.ts
index d0979319..d9ff42d8 100644
--- a/materialious/src/lib/watch.ts
+++ b/materialious/src/lib/watch.ts
@@ -16,6 +16,7 @@ import {
import { phaseDescription } from '$lib/timestamps';
import { error } from '@sveltejs/kit';
import { get } from 'svelte/store';
+import { _ } from './i18n';
export async function getWatchDetails(videoId: string, url: URL) {
let video;
@@ -25,6 +26,14 @@ export async function getWatchDetails(videoId: string, url: URL) {
error(500, errorMessage);
}
+ if (video.premium) {
+ error(400, get(_)('premium'));
+ }
+
+ if (video.isUpcoming) {
+ error(400, get(_)('isUpcoming'));
+ }
+
let personalPlaylists;
if (get(authStore)) {
postHistory(video.videoId);
diff --git a/materialious/src/routes/(app)/+error.svelte b/materialious/src/routes/(app)/+error.svelte
index 9ebfd8a5..bee7c6b6 100644
--- a/materialious/src/routes/(app)/+error.svelte
+++ b/materialious/src/routes/(app)/+error.svelte
@@ -1,29 +1,5 @@
-
-
-
-
-
Oh no! You've encountered an error.
-
-
Report it here
-
- {#if $page.error}
-
- {$page.error.message}
-
- {/if}
-
+
diff --git a/materialious/src/routes/(no-layout)/tv/[slug]/+error.svelte b/materialious/src/routes/(no-layout)/tv/[slug]/+error.svelte
new file mode 100644
index 00000000..bee7c6b6
--- /dev/null
+++ b/materialious/src/routes/(no-layout)/tv/[slug]/+error.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/materialious/src/routes/+error.svelte b/materialious/src/routes/+error.svelte
new file mode 100644
index 00000000..bee7c6b6
--- /dev/null
+++ b/materialious/src/routes/+error.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/update_versions.py b/update_versions.py
index 34ce3310..831a58de 100644
--- a/update_versions.py
+++ b/update_versions.py
@@ -3,7 +3,7 @@ import os
import re
from datetime import datetime
-LATEST_VERSION = "1.9.21"
+LATEST_VERSION = "1.9.22"
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")