diff --git a/.github/workflows/prod-desktop.yml b/.github/workflows/prod-desktop.yml index 9a8c1405..2c43d2cf 100644 --- a/.github/workflows/prod-desktop.yml +++ b/.github/workflows/prod-desktop.yml @@ -20,15 +20,12 @@ jobs: node-version: [latest] runtime: - linux-x64 - - linux-arm64 - win-x64 - osx-x64 include: - runtime: linux-x64 os: ubuntu-latest - - runtime: linux-arm64 - os: ubuntu-latest - runtime: osx-x64 os: macOS-latest - runtime: win-x64 diff --git a/.github/workflows/pull-desktop.yml b/.github/workflows/pull-desktop.yml index b5fb836b..6b2dfd7a 100644 --- a/.github/workflows/pull-desktop.yml +++ b/.github/workflows/pull-desktop.yml @@ -1,59 +1,50 @@ name: Check desktop on: - pull_request: - branches: ["main"] + pull_request: + branches: ["main"] - workflow_dispatch: + workflow_dispatch: permissions: - contents: write + contents: write jobs: - desktop-check: - strategy: - matrix: - node-version: [latest] - runtime: - - linux-x64 - - linux-arm64 - - win-x64 - - osx-x64 + desktop-check: + strategy: + matrix: + node-version: [latest] + runtime: + - osx-x64 - include: - - runtime: linux-x64 - os: ubuntu-latest - - runtime: linux-arm64 - os: ubuntu-latest - - runtime: osx-x64 - os: macOS-latest - - runtime: win-x64 - os: windows-latest + include: + - runtime: osx-x64 + os: macOS-latest - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v3 + steps: + - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} - - name: Install modules - run: cd ./materialious && npm install + - name: Install modules + run: cd ./materialious && npm install - - name: Build package - run: cd ./materialious && npm run build + - name: Build package + run: cd ./materialious && npm run build - - name: Sync electron package - run: cd ./materialious && npx cap sync @capacitor-community/electron + - name: Sync electron package + run: cd ./materialious && npx cap sync @capacitor-community/electron - - name: Patch capacitor plugin - run: cd ./materialious/electron && python patch_capacitor_plugin.py + - name: Patch capacitor plugin + run: cd ./materialious/electron && python patch_capacitor_plugin.py - - name: Install electron modules - run: cd ./materialious/electron && npm install + - name: Install electron modules + run: cd ./materialious/electron && npm install - - name: Build desktop packages - run: cd ./materialious/electron && npm run electron:make-no-publish + - name: Build desktop packages + run: cd ./materialious/electron && npm run electron:make-no-publish diff --git a/README.md b/README.md index 9a06857b..3437397b 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ - Dash support. - Chapters. - Audio only mode. +- Mini player (PIP.) - Playlists. - PWA support. - YT path redirects (So your redirect plugins should still work!) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 94daa4ac..abc221d1 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -106,6 +106,15 @@ http: ### Invidious Companion support The `VITE_DEFAULT_COMPANION_INSTANCE` environment variable allows you to specify a custom [Invidious Companion](https://github.com/iv-org/invidious-companion) instance. +`public_url` **MUST** be set in Invidious under **invidious_companion** for companion to work with Materialious. + +e.g. +```yml +invidious_companion: + - private_url: "http://companion:8282/companion" + public_url: "http://companion.example.com/companion" +``` + To use this with Materialious, your Invidious Companion instance must be accessible with proper CORS headers. Fortunately, you can reuse the same reverse proxy configuration (with CORS modifications) that you applied to your Invidious instance—just apply it to your companion domain as well. For example, if you're using Caddy, you can configure your companion domain like this: diff --git a/docs/INSTANCES.md b/docs/INSTANCES.md index e45af722..66ecedce 100644 --- a/docs/INSTANCES.md +++ b/docs/INSTANCES.md @@ -1,10 +1,2 @@ # Public instances -- [app.materialio.us](https://app.materialio.us) - - **Location:** New Zealand - - **Threads:** 6 - - **IPV6 rotation:** Yes - - **Man in the Middle:** No - - **Accounts:** Yes - - **RYD-Proxy:** Yes - - **API Extended integration:** Yes - - **Self-hosted PeerJS:** No +N/A diff --git a/materialious/android/app/build.gradle b/materialious/android/app/build.gradle index 6ae2853d..02332b07 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 159 - versionName "1.10.18" + versionCode 165 + versionName "1.11.5" 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 f945386d..058be182 100644 --- a/materialious/electron/materialious.metainfo.xml +++ b/materialious/electron/materialious.metainfo.xml @@ -69,7 +69,31 @@ - + + + + + + + + https://github.com/Materialious/Materialious/releases/tag/1.11.5 + + + https://github.com/Materialious/Materialious/releases/tag/1.11.4 + + + https://github.com/Materialious/Materialious/releases/tag/1.11.3 + + + https://github.com/Materialious/Materialious/releases/tag/1.11.2 + + + https://github.com/Materialious/Materialious/releases/tag/1.11.1 + + + https://github.com/Materialious/Materialious/releases/tag/1.11.0 + + https://github.com/Materialious/Materialious/releases/tag/1.10.18 diff --git a/materialious/electron/package-lock.json b/materialious/electron/package-lock.json index 601e6e53..2b1650c8 100644 --- a/materialious/electron/package-lock.json +++ b/materialious/electron/package-lock.json @@ -1,12 +1,12 @@ { "name": "Materialious", - "version": "1.10.18", + "version": "1.11.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "Materialious", - "version": "1.10.18", + "version": "1.11.5", "license": "MIT", "dependencies": { "@capacitor-community/electron": "^5.0.0", diff --git a/materialious/electron/package.json b/materialious/electron/package.json index e7534f19..1238f92a 100644 --- a/materialious/electron/package.json +++ b/materialious/electron/package.json @@ -1,6 +1,6 @@ { "name": "Materialious", - "version": "1.10.18", + "version": "1.11.5", "description": "Modern material design for Invidious.", "author": { "name": "Ward Pearce", @@ -44,4 +44,4 @@ "capacitor", "electron" ] -} +} \ No newline at end of file diff --git a/materialious/electron/src/setup.ts b/materialious/electron/src/setup.ts index ae6fa10d..6af16323 100644 --- a/materialious/electron/src/setup.ts +++ b/materialious/electron/src/setup.ts @@ -5,6 +5,7 @@ import { setupCapacitorElectronPlugins } from '@capacitor-community/electron'; import { USER_AGENT } from 'bgutils-js'; +import path from 'node:path'; import chokidar from 'chokidar'; import type { MenuItemConstructorOptions } from 'electron'; import { @@ -219,6 +220,34 @@ export class ElectronCapacitorApp { CapElectronEventEmitter.emit('CAPELECTRON_DeeplinkListenerInitialized', ''); }, 400); }); + + // remove so we can register each time as we run the app. + app.removeAsDefaultProtocolClient('materialious'); + + // If we are running a non-packaged version of the app && on windows + if (process.env.NODE_ENV === 'development' && process.platform === 'win32') { + // Set the path of electron.exe and your app. + // These two additional parameters are only available on windows. + app.setAsDefaultProtocolClient('materialious', process.execPath, [ + path.resolve(process.argv[1]) + ]); + } else { + app.setAsDefaultProtocolClient('materialious'); + } + + const gotTheLock = app.requestSingleInstanceLock(); + + if (!gotTheLock) { + app.quit(); + } else { + app.on('second-instance', (event, commandLine, workingDirectory) => { + // Someone tried to run a second instance, we should focus our window. + if (this.MainWindow) { + if (this.MainWindow.isMinimized()) this.MainWindow.restore(); + this.MainWindow.focus(); + } + }); + } } } diff --git a/materialious/package-lock.json b/materialious/package-lock.json index 4d04842a..8a3bb2d6 100644 --- a/materialious/package-lock.json +++ b/materialious/package-lock.json @@ -1,12 +1,12 @@ { "name": "materialious", - "version": "1.10.18", + "version": "1.11.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "materialious", - "version": "1.10.18", + "version": "1.11.5", "hasInstallScript": true, "dependencies": { "@capacitor-community/electron": "^5.0.1", @@ -17,9 +17,10 @@ "@capacitor/clipboard": "^7.0.0", "@capacitor/core": "^7.4.3", "@capacitor/screen-orientation": "^7.0.0", - "beercss": "^3.12.11", + "beercss": "^3.12.13", "bgutils-js": "^3.2.0", "capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz", + "dayjs": "^1.11.19", "fuse.js": "^7.0.0", "googlevideo": "^4.0.4", "he": "^1.2.0", @@ -41,9 +42,9 @@ "@capacitor/assets": "^3.0.5", "@capacitor/cli": "^7.4.3", "@sveltejs/adapter-auto": "^6.0.1", - "@sveltejs/adapter-static": "^3.0.8", + "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.46.5", - "@sveltejs/vite-plugin-svelte": "^6.1.4", + "@sveltejs/vite-plugin-svelte": "^6.2.1", "@types/eslint": "^8.56.7", "@types/event-source-polyfill": "^1.0.5", "@types/he": "^1.2.3", @@ -55,13 +56,13 @@ "eslint": "^8.56.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.11.0", - "patch-package": "^8.0.0", + "patch-package": "^8.0.1", "prettier": "^3.6.2", "prettier-plugin-svelte": "^3.4.0", "svelte": "^5.43.2", "svelte-check": "^4.3.3", "tslib": "^2.7.0", - "typescript": "^5.6.3", + "typescript": "^5.9.3", "vite": "^7.1.12" } }, @@ -3248,9 +3249,9 @@ } }, "node_modules/@sveltejs/adapter-static": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz", - "integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.10.tgz", + "integrity": "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3297,9 +3298,9 @@ } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.1.4.tgz", - "integrity": "sha512-4jfkfvsGI+U2OhHX8OPCKtMCf7g7ledXhs3E6UcA4EY0jQWsiVbe83pTAHp9XTifzYNOiD4AJieJUsI0qqxsbw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz", + "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4293,9 +4294,9 @@ } }, "node_modules/beercss": { - "version": "3.12.11", - "resolved": "https://registry.npmjs.org/beercss/-/beercss-3.12.11.tgz", - "integrity": "sha512-9NQWL3kxSOb5RPWAV5BPVs64CS72F5Z4HgxusqD07Ho4asaTJB33/SkJBMclQeUQs7hFH2CJOgGgDnIsRk2RyQ==", + "version": "3.12.13", + "resolved": "https://registry.npmjs.org/beercss/-/beercss-3.12.13.tgz", + "integrity": "sha512-zsg15us8pSLTupIOzFFJqpJ7eJDLV6whsxT6GcRYGnYaF0ka45+HQ/u3+ldSUkCeyA5J24g6csEuMW8tqrI/0w==", "license": "MIT", "dependencies": { "material-dynamic-colors": "^1.1.2" @@ -5235,6 +5236,12 @@ "node": "*" } }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -9152,16 +9159,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -9272,9 +9269,9 @@ } }, "node_modules/patch-package": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", - "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", + "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", "dev": true, "license": "MIT", "dependencies": { @@ -9283,15 +9280,14 @@ "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", + "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", - "rimraf": "^2.6.3", "semver": "^7.5.3", "slash": "^2.0.0", - "tmp": "^0.0.33", + "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { @@ -9302,66 +9298,19 @@ "npm": ">5" } }, - "node_modules/patch-package/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/patch-package/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/patch-package/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/patch-package/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "node": ">=12" } }, "node_modules/patch-package/node_modules/open": { @@ -9381,20 +9330,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/patch-package/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/patch-package/node_modules/slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", @@ -9405,19 +9340,6 @@ "node": ">=6" } }, - "node_modules/patch-package/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -12018,9 +11940,9 @@ } }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, "license": "MIT", "engines": { @@ -12267,9 +12189,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", "bin": { diff --git a/materialious/package.json b/materialious/package.json index 2ef59008..0dcbfde6 100644 --- a/materialious/package.json +++ b/materialious/package.json @@ -1,6 +1,6 @@ { "name": "materialious", - "version": "1.10.18", + "version": "1.11.5", "private": true, "scripts": { "dev": "vite dev", @@ -17,9 +17,9 @@ "@capacitor/assets": "^3.0.5", "@capacitor/cli": "^7.4.3", "@sveltejs/adapter-auto": "^6.0.1", - "@sveltejs/adapter-static": "^3.0.8", + "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.46.5", - "@sveltejs/vite-plugin-svelte": "^6.1.4", + "@sveltejs/vite-plugin-svelte": "^6.2.1", "@types/eslint": "^8.56.7", "@types/event-source-polyfill": "^1.0.5", "@types/he": "^1.2.3", @@ -31,13 +31,13 @@ "eslint": "^8.56.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.11.0", - "patch-package": "^8.0.0", + "patch-package": "^8.0.1", "prettier": "^3.6.2", "prettier-plugin-svelte": "^3.4.0", "svelte": "^5.43.2", "svelte-check": "^4.3.3", "tslib": "^2.7.0", - "typescript": "^5.6.3", + "typescript": "^5.9.3", "vite": "^7.1.12" }, "type": "module", @@ -50,9 +50,10 @@ "@capacitor/clipboard": "^7.0.0", "@capacitor/core": "^7.4.3", "@capacitor/screen-orientation": "^7.0.0", - "beercss": "^3.12.11", + "beercss": "^3.12.13", "bgutils-js": "^3.2.0", "capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz", + "dayjs": "^1.11.19", "fuse.js": "^7.0.0", "googlevideo": "^4.0.4", "he": "^1.2.0", @@ -70,4 +71,4 @@ "svelte-persisted-store": "^0.12.0", "youtubei.js": "^16.0.1" } -} +} \ No newline at end of file diff --git a/materialious/src/lib/api/index.ts b/materialious/src/lib/api/index.ts index c3e40d9d..b1971860 100644 --- a/materialious/src/lib/api/index.ts +++ b/materialious/src/lib/api/index.ts @@ -145,11 +145,15 @@ export async function getChannel( return await resp.json(); } +export type channelSortBy = 'oldest' | 'newest' | 'popular'; +export type channelContentTypes = 'videos' | 'playlists' | 'streams' | 'shorts'; + export async function getChannelContent( channelId: string, parameters: { - type?: 'videos' | 'playlists' | 'streams' | 'shorts'; + type?: channelContentTypes; continuation?: string; + sortBy?: channelSortBy; }, fetchOptions?: RequestInit ): Promise { @@ -160,10 +164,23 @@ export async function getChannelContent( if (typeof parameters.continuation !== 'undefined') url.searchParams.set('continuation', parameters.continuation); + if (typeof parameters.sortBy !== 'undefined') url.searchParams.set('sort_by', parameters.sortBy); + const resp = await fetchErrorHandle(await fetch(url.toString(), fetchOptions)); return await resp.json(); } +export async function searchChannelContent( + channelId: string, + search: string, + fetchOptions?: RequestInit +) { + const path = buildPath(`channel/${channelId}/search`); + path.search = new URLSearchParams({ q: search }).toString(); + const resp = await fetchErrorHandle(await fetch(path, fetchOptions)); + return await resp.json(); +} + export async function getSearchSuggestions( search: string, fetchOptions?: RequestInit @@ -179,13 +196,18 @@ export async function getHashtag(tag: string, page: number = 0): Promise<{ resul return await resp.json(); } +export interface SearchOptions { + sort_by?: 'relevance' | 'rating' | 'upload_date' | 'view_count'; + type?: 'video' | 'playlist' | 'channel' | 'all'; + duration?: 'short' | 'medium' | 'long'; + date?: 'hour' | 'today' | 'week' | 'month' | 'year'; + features?: string; + page?: string; +} + export async function getSearch( search: string, - options: { - sort_by?: 'relevance' | 'rating' | 'upload_date' | 'view_count'; - type?: 'video' | 'playlist' | 'channel' | 'all'; - page?: string; - }, + options: SearchOptions, fetchOptions?: RequestInit ): Promise<(Channel | Video | Playlist | HashTag)[]> { if (typeof options.sort_by === 'undefined') { @@ -193,7 +215,7 @@ export async function getSearch( } if (typeof options.type === 'undefined') { - options.type = 'video'; + options.type = 'all'; } if (typeof options.page === 'undefined') { diff --git a/materialious/src/lib/api/model.ts b/materialious/src/lib/api/model.ts index 3dd8672a..a3cf0683 100644 --- a/materialious/src/lib/api/model.ts +++ b/materialious/src/lib/api/model.ts @@ -38,7 +38,7 @@ export interface Video extends VideoBase { descriptionHtml: string; published: number; publishedText: string; - premiereTimestamp: number; + premiereTimestamp?: number; liveNow: boolean; premium: boolean; isUpcoming: boolean; diff --git a/materialious/src/lib/components/ItemsList.svelte b/materialious/src/lib/components/ItemsList.svelte index 15fb6f89..d4e102ba 100644 --- a/materialious/src/lib/components/ItemsList.svelte +++ b/materialious/src/lib/components/ItemsList.svelte @@ -162,14 +162,6 @@ } onMount(async () => { - if ($feedLastItemId && !$isAndroidTvStore) { - document - .getElementById($feedLastItemId) - ?.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' }); - - feedLastItemId.set(undefined); - } - if ($isAndroidTvStore) { await tick(); // Setup Android TV navigation @@ -200,6 +192,16 @@ // Watch for focus changes to detect when we're active document.addEventListener('focusin', checkComponentFocus); document.addEventListener('focusout', checkComponentFocus); + } else if ($feedLastItemId) { + const element = document.getElementById($feedLastItemId); + + if (element) { + element.scrollIntoView({ + behavior: 'instant', + block: 'start', + inline: 'nearest' + }); + } } }); @@ -221,6 +223,18 @@
+ {#if items.length === 0} +
+
+ +

{$_('noResult')}

+
+ {/if}
{#each items as item, index} @@ -251,7 +265,9 @@ }} > {#if item.type === 'video' || item.type === 'shortVideo' || item.type === 'stream'} - + {#key item.videoId} + + {/key} {#if $authStore && decodeURIComponent($authStore.username) === playlistAuthor && 'indexId' in item}
{#if showVideoRetry} -
+
{#if $playerYouTubeJsFallback}

{$_('player.youtubeJsLoading')}

@@ -931,13 +908,4 @@ .hide { display: none; } - - .fallback { - height: 30vh; - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - } diff --git a/materialious/src/lib/components/PlaylistThumbnail.svelte b/materialious/src/lib/components/PlaylistThumbnail.svelte index 404c4473..9a2f5789 100644 --- a/materialious/src/lib/components/PlaylistThumbnail.svelte +++ b/materialious/src/lib/components/PlaylistThumbnail.svelte @@ -26,10 +26,9 @@ if (get(interfaceLowBandwidthMode)) return; let imgSrc = ''; - if (playlist.videos.length > 0) { imgSrc = getBestThumbnail(playlist.videos[0].videoThumbnails) || ''; - } else if (playlist.playlistThumbnail) { + } else if (playlist.playlistThumbnail.length > 0) { imgSrc = playlist.playlistThumbnail; } else { imgSrc = ''; diff --git a/materialious/src/lib/components/Search.svelte b/materialious/src/lib/components/Search.svelte index 48fd9154..3b586df9 100644 --- a/materialious/src/lib/components/Search.svelte +++ b/materialious/src/lib/components/Search.svelte @@ -11,6 +11,8 @@ isAndroidTvStore, searchHistoryStore } from '../store'; + import { isVideoID } from '$lib/misc'; + import { resolve } from '$app/paths'; const dispatch = createEventDispatcher(); @@ -38,22 +40,30 @@ } function handleSubmit(event: Event | undefined = undefined) { - if (event) event.preventDefault(); + event?.preventDefault(); - if (search.trim() === '') return; + const searchTrimed = search.trim(); + + if (!searchTrimed) return; + + if (isVideoID(searchTrimed)) { + // Go directly to video if Video ID provided + goto(resolve('/watch/[videoId]', { videoId: searchTrimed })); + return; + } selectedSuggestionIndex = -1; - goto(resolve(`/search/${encodeURIComponent(search)}`)); + goto(resolve(`/search/${encodeURIComponent(searchTrimed)}`)); suggestionsForSearch = []; showSearchBox = false; - if ($interfaceSearchHistoryEnabled && !$searchHistoryStore.includes(search)) { + if ($interfaceSearchHistoryEnabled && !$searchHistoryStore.includes(searchTrimed)) { let pastHistory = $searchHistoryStore; if (pastHistory.length > 15) { pastHistory.pop(); } - searchHistoryStore.set([search, ...pastHistory]); + searchHistoryStore.set([searchTrimed, ...pastHistory]); } } diff --git a/materialious/src/lib/components/Thumbnail.svelte b/materialious/src/lib/components/Thumbnail.svelte index 6d2da79d..df8b780b 100644 --- a/materialious/src/lib/components/Thumbnail.svelte +++ b/materialious/src/lib/components/Thumbnail.svelte @@ -21,9 +21,7 @@ synciousInstanceStore, synciousStore } from '../store'; - import { goto } from '$app/navigation'; import { queueSyncious } from '$lib/api/apiExtended'; - import { page } from '$app/state'; interface Props { video: VideoBase | Video | Notification | PlaylistPageVideo; diff --git a/materialious/src/lib/components/settings/Player.svelte b/materialious/src/lib/components/settings/Player.svelte index 8492a3b0..467c1707 100644 --- a/materialious/src/lib/components/settings/Player.svelte +++ b/materialious/src/lib/components/settings/Player.svelte @@ -13,6 +13,7 @@ playerDefaultLanguage, playerDefaultPlaybackSpeed, playerDefaultQualityStore, + playerMiniplayerEnabled, playerProxyVideosStore, playerSavePlaybackPositionStore, playerStatisticsByDefault, @@ -71,7 +72,7 @@ bind:value={defaultLanguage} onchange={() => playerDefaultLanguage.set(defaultLanguage)} > - + {#each languageNames as language}
+
+ +
+
- {#if $authStore} - + {:else} + - {:else} - +
+ {$_('loginRequired')} +
+ + {/if} {/if} diff --git a/materialious/src/lib/components/watch/Description.svelte b/materialious/src/lib/components/watch/Description.svelte index 746f0792..d15324d0 100644 --- a/materialious/src/lib/components/watch/Description.svelte +++ b/materialious/src/lib/components/watch/Description.svelte @@ -31,7 +31,7 @@ {@html description}
- {#if video.keywords} + {#if video.keywords && video.keywords.length > 0}
+
    {#each data.content.timestamps as timestamp} @@ -483,8 +562,8 @@
{/if}
- {#if !theatreMode} -
+ {#if !$playertheatreModeIsActive} + + +
@@ -617,38 +711,26 @@ overflow-x: hidden; } + .recommended { + margin-top: calc(var(--video-player-height) * -1); + } + .video-actions { display: flex; align-items: center; justify-content: flex-end; } - .video-actions button:not(.left-round):not(.right-round) { - margin: 0.3em; - } - @media screen and (max-width: 1000px) { .video-actions { align-items: flex-start; flex-direction: column; } - - .video-actions > div { - margin-top: 1em; - } - - nav.group { - flex-direction: column; - } - - nav.group button { - border-radius: 0.5rem !important; - } } - @media screen and (max-width: 1646px) { - .grid { - padding: 0; + @media only screen and (max-width: 993px) { + .recommended { + margin-top: 0; } } diff --git a/materialious/src/routes/(no-layout)/tv/[slug]/+layout.svelte b/materialious/src/routes/(no-layout)/tv/[slug]/+layout.svelte index 675a39d7..b78a5686 100644 --- a/materialious/src/routes/(no-layout)/tv/[slug]/+layout.svelte +++ b/materialious/src/routes/(no-layout)/tv/[slug]/+layout.svelte @@ -1,7 +1,6 @@ @@ -15,7 +14,7 @@ -{#if $isTvRouteLoading || $navigating} +{#if $navigating} {:else} {@render children?.()} diff --git a/materialious/src/routes/(no-layout)/tv/[slug]/+page.ts b/materialious/src/routes/(no-layout)/tv/[slug]/+page.ts index 57c51522..05e9da2e 100644 --- a/materialious/src/routes/(no-layout)/tv/[slug]/+page.ts +++ b/materialious/src/routes/(no-layout)/tv/[slug]/+page.ts @@ -1,9 +1,5 @@ -import { isTvRouteLoading } from '$lib/store'; import { getWatchDetails } from '$lib/watch'; export async function load({ params, url }) { - isTvRouteLoading.set(true); - const details = await getWatchDetails(params.slug, url); - isTvRouteLoading.set(false); - return details; + return await getWatchDetails(params.slug, url); } diff --git a/update_versions.py b/update_versions.py index 5f7b70db..e12c3358 100644 --- a/update_versions.py +++ b/update_versions.py @@ -3,7 +3,7 @@ import os import re from datetime import datetime -LATEST_VERSION = "1.10.18" +LATEST_VERSION = "1.11.5" RELEASE_DATE = datetime.now().strftime("%Y-%-m-%d") # Format: YYYY-M-D WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious")