@@ -1,13 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
@@ -1,31 +0,0 @@
|
||||
/** @type { import("eslint").Linter.Config } */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:svelte/recommended',
|
||||
'prettier'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
extraFileExtensions: ['.svelte']
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
parser: 'svelte-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "us.materialio.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 166
|
||||
versionName "1.11.6"
|
||||
versionCode 167
|
||||
versionName "1.11.7"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -76,7 +76,11 @@
|
||||
|
||||
|
||||
|
||||
<release version="1.11.6" date="2025-11-13">
|
||||
|
||||
<release version="1.11.7" date="2025-11-20">
|
||||
<url>https://github.com/Materialious/Materialious/releases/tag/1.11.7</url>
|
||||
</release>
|
||||
<release version="1.11.6" date="2025-11-13">
|
||||
<url>https://github.com/Materialious/Materialious/releases/tag/1.11.6</url>
|
||||
</release>
|
||||
<release version="1.11.5" date="2025-11-08">
|
||||
|
||||
Generated
+46
-33
@@ -1,17 +1,16 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.11.6",
|
||||
"version": "1.11.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "Materialious",
|
||||
"version": "1.11.6",
|
||||
"version": "1.11.7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@capacitor-community/electron": "^5.0.0",
|
||||
"bgutils-js": "^3.2.0",
|
||||
"capacitor-nodejs": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz",
|
||||
"chokidar": "~4.0.3",
|
||||
"electron-is-dev": "~2.0.0",
|
||||
"electron-serve": "~3.0.0",
|
||||
@@ -841,6 +840,27 @@
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/balanced-match": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
|
||||
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/brace-expansion": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
|
||||
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@isaacs/balanced-match": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
@@ -1486,6 +1506,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
@@ -1571,6 +1592,7 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
@@ -1863,15 +1885,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/capacitor-nodejs": {
|
||||
"version": "1.0.0-beta.9",
|
||||
"resolved": "https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.9/capacitor-nodejs.tgz",
|
||||
"integrity": "sha512-4EU/QBolfuyHiMtwd1nxZ4wsK243tGE4VttKvVBG+x1yQuK9EIlEf3lbRjFXfjrJMTZNCqaP6glmdfj9Zo7KVg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@capacitor/core": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -2118,9 +2131,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/config-file-ts/node_modules/glob": {
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
@@ -3913,9 +3926,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
@@ -4295,12 +4308,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
|
||||
"integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
|
||||
"license": "ISC",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
|
||||
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
@@ -5088,14 +5101,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf/node_modules/glob": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz",
|
||||
"integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==",
|
||||
"license": "ISC",
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz",
|
||||
"integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^4.0.1",
|
||||
"minimatch": "^10.0.0",
|
||||
"foreground-child": "^3.3.1",
|
||||
"jackspeak": "^4.1.1",
|
||||
"minimatch": "^10.1.1",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^2.0.0"
|
||||
@@ -5111,9 +5124,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf/node_modules/jackspeak": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz",
|
||||
"integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz",
|
||||
"integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.11.6",
|
||||
"version": "1.11.7",
|
||||
"description": "Modern material design for Invidious.",
|
||||
"author": {
|
||||
"name": "Ward Pearce",
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// based on eslint-plugin-svelte README.md
|
||||
import js from '@eslint/js';
|
||||
import svelte from 'eslint-plugin-svelte';
|
||||
import globals from 'globals';
|
||||
import ts from 'typescript-eslint';
|
||||
import svelteConfig from './svelte.config.js';
|
||||
|
||||
export default ts.config(
|
||||
js.configs.recommended,
|
||||
...ts.configs.recommended,
|
||||
...svelte.configs.recommended,
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.svelte'],
|
||||
// See more details at: https://typescript-eslint.io/packages/parser/
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
extraFileExtensions: ['.svelte'], // Add support for additional file extensions, such as .svelte
|
||||
parser: ts.parser,
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
// Specify a parser for each language, if needed:
|
||||
// parser: {
|
||||
// ts: ts.parser,
|
||||
// js: espree, // Use espree for .js files (add: import espree from 'espree')
|
||||
// typescript: ts.parser
|
||||
// },
|
||||
|
||||
// We recommend importing and specifying svelte.config.js.
|
||||
// By doing so, some rules in eslint-plugin-svelte will automatically read the configuration and adjust their behavior accordingly.
|
||||
// While certain Svelte settings may be statically loaded from svelte.config.js even if you don’t specify it,
|
||||
// explicitly specifying it ensures better compatibility and functionality.
|
||||
//
|
||||
// If non-serializable properties are included, running ESLint with the --cache flag will fail.
|
||||
// In that case, please remove the non-serializable properties. (e.g. `svelteConfig: { ...svelteConfig, kit: { ...svelteConfig.kit, typescript: undefined }}`)
|
||||
svelteConfig
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
// Override or add rule settings here, such as:
|
||||
// 'svelte/rule-name': 'error'
|
||||
}
|
||||
},
|
||||
{
|
||||
ignores: [
|
||||
'.DS_Store',
|
||||
'node_modules',
|
||||
'/build',
|
||||
'/.svelte-kit',
|
||||
'/package',
|
||||
'.env',
|
||||
'.env.*',
|
||||
'!.env.example',
|
||||
// Ignore files for PNPM, NPM and YARN
|
||||
'pnpm-lock.yaml',
|
||||
'package-lock.json',
|
||||
'yarn.lock'
|
||||
],
|
||||
}
|
||||
);
|
||||
Generated
+710
-662
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.11.6",
|
||||
"version": "1.11.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
@@ -9,6 +9,7 @@
|
||||
"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"
|
||||
@@ -20,15 +21,12 @@
|
||||
"@sveltejs/adapter-static": "^3.0.10",
|
||||
"@sveltejs/kit": "^2.46.5",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
||||
"@types/eslint": "^8.56.7",
|
||||
"@types/event-source-polyfill": "^1.0.5",
|
||||
"@types/he": "^1.2.3",
|
||||
"@types/human-number": "^1.0.2",
|
||||
"@types/mousetrap": "^1.6.15",
|
||||
"@typescript-eslint/eslint-plugin": "^7.16.0",
|
||||
"@typescript-eslint/parser": "^7.18.0",
|
||||
"@vite-pwa/sveltekit": "^1.0.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-svelte": "^3.13.0",
|
||||
"patch-package": "^8.0.1",
|
||||
@@ -38,6 +36,7 @@
|
||||
"svelte-check": "^4.3.3",
|
||||
"tslib": "^2.7.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.3",
|
||||
"vite": "^7.1.12"
|
||||
},
|
||||
"type": "module",
|
||||
@@ -71,4 +70,4 @@
|
||||
"svelte-persisted-store": "^0.12.0",
|
||||
"youtubei.js": "^16.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { getBestThumbnail, proxyGoogleImage } from '$lib/images';
|
||||
import { cleanNumber } from '$lib/numbers';
|
||||
import { onMount } from 'svelte';
|
||||
@@ -28,7 +29,11 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<a href={`/channel/${channel.authorId}`} class="wave" style="min-width: 100%;min-height: 100%;">
|
||||
<a
|
||||
href={resolve(`/channel/[authorId]`, { authorId: channel.authorId })}
|
||||
class="wave"
|
||||
style="min-width: 100%;min-height: 100%;"
|
||||
>
|
||||
<div class="padding">
|
||||
{#if !$interfaceLowBandwidthMode}
|
||||
<div class="center-align">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { cleanNumber } from '$lib/numbers';
|
||||
import { _ } from '$lib/i18n';
|
||||
import type { HashTag } from '../api/model';
|
||||
@@ -11,7 +12,7 @@
|
||||
let { hashtag }: Props = $props();
|
||||
</script>
|
||||
|
||||
<a href={hashtag.url} class="wave" style="min-width: 100%;min-height: 100%;">
|
||||
<a href={resolve(hashtag.url, {})} class="wave" style="min-width: 100%;min-height: 100%;">
|
||||
<div class="padding">
|
||||
<h5 class="center-align">{truncate(hashtag.title, 14)}</h5>
|
||||
<h6 style="margin-top: 0;" class="center-align grey-text medium-text">
|
||||
|
||||
@@ -347,7 +347,7 @@
|
||||
if (!import.meta.env.VITE_DEFAULT_COMPANION_INSTANCE) {
|
||||
captionUrl = caption.url.startsWith('http')
|
||||
? caption.url
|
||||
: `${get(instanceStore)}${caption.url}`;
|
||||
: `${new URL(get(instanceStore)).origin}${caption.url}`;
|
||||
} else {
|
||||
captionUrl = `${import.meta.env.VITE_DEFAULT_COMPANION_INSTANCE}${caption.url}`;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { getBestThumbnail } from '$lib/images';
|
||||
import { resolve } from '$app/paths';
|
||||
import { letterCase } from '$lib/letterCasing';
|
||||
import { onMount } from 'svelte';
|
||||
import { _ } from '$lib/i18n';
|
||||
@@ -19,7 +20,7 @@
|
||||
|
||||
let img: HTMLImageElement | undefined = $state();
|
||||
|
||||
const playlistLink = `/playlist/${playlist.playlistId}`;
|
||||
const playlistLink = resolve('/playlist/[playlistId]', { playlistId: playlist.playlistId });
|
||||
|
||||
onMount(async () => {
|
||||
if (get(interfaceLowBandwidthMode)) return;
|
||||
@@ -80,9 +81,13 @@
|
||||
><div class="bold">{letterCase(truncate(playlist.title))}</div></a
|
||||
>
|
||||
<div>
|
||||
<a class:link-disabled={playlist.authorId === null} href={`/channel/${playlist.authorId}`}
|
||||
>{playlist.author}</a
|
||||
>
|
||||
{#if playlist.authorId}
|
||||
<a href={resolve('/channel/[authorId]', { authorId: playlist.authorId })}
|
||||
>{playlist.author}</a
|
||||
>
|
||||
{:else}
|
||||
<p>{playlist.author}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { goto } from '$app/navigation';
|
||||
import Mousetrap from 'mousetrap';
|
||||
import { createEventDispatcher, onMount, tick } from 'svelte';
|
||||
@@ -11,7 +12,6 @@
|
||||
searchHistoryStore
|
||||
} from '../store';
|
||||
import { isVideoID } from '$lib/misc';
|
||||
import { resolve } from '$app/paths';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
}
|
||||
|
||||
selectedSuggestionIndex = -1;
|
||||
goto(`/search/${encodeURIComponent(searchTrimed)}`);
|
||||
goto(resolve(`/search/[search]`, { search: encodeURIComponent(searchTrimed) }));
|
||||
|
||||
suggestionsForSearch = [];
|
||||
showSearchBox = false;
|
||||
@@ -156,7 +156,7 @@
|
||||
<i class="front" role="presentation" onclick={resetSearch}>close</i>
|
||||
</div>
|
||||
{#if searchSuggestions}
|
||||
{#each suggestionsForSearch as suggestion, index}
|
||||
{#each suggestionsForSearch as suggestion, index (index)}
|
||||
<li>
|
||||
<a
|
||||
onclick={() => {
|
||||
@@ -164,7 +164,7 @@
|
||||
handleSubmit();
|
||||
}}
|
||||
class:selected={index === selectedSuggestionIndex}
|
||||
href={`/search/${encodeURIComponent(suggestion)}`}
|
||||
href={resolve(`/search/[search]`, { search: encodeURIComponent(suggestion) })}
|
||||
>
|
||||
<div>{suggestion}</div>
|
||||
</a>
|
||||
@@ -172,13 +172,13 @@
|
||||
{/each}
|
||||
{/if}
|
||||
{#if !suggestionsForSearch.length && $interfaceSearchHistoryEnabled}
|
||||
{#each $searchHistoryStore as history}
|
||||
{#each $searchHistoryStore as history (history)}
|
||||
<li>
|
||||
<a
|
||||
onclick={() => {
|
||||
search = history;
|
||||
}}
|
||||
href={`/search/${encodeURIComponent(history)}`}
|
||||
href={resolve(`/search/[search]`, { search: encodeURIComponent(history) })}
|
||||
>
|
||||
<div>{history}</div>
|
||||
</a>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { instanceStore } from '$lib/store';
|
||||
import { Clipboard } from '@capacitor/clipboard';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
@@ -33,7 +34,7 @@
|
||||
if (Capacitor.isNativePlatform()) {
|
||||
url = `${get(instanceStore)}/watch/${video.videoId}`;
|
||||
} else {
|
||||
url = `${location.origin}/watch/${video.videoId}`;
|
||||
url = `${location.origin}${resolve('/watch/[videoId]', { videoId: video.videoId })}`;
|
||||
}
|
||||
if (includeTimestamp && currentTime !== undefined) {
|
||||
url += `?time=${Math.floor(currentTime)}`;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { peerJs, removeWindowQueryFlag, setWindowQueryFlag } from '$lib/misc';
|
||||
import type { PlayerEvents } from '$lib/player';
|
||||
@@ -20,7 +21,7 @@
|
||||
|
||||
events.events.forEach((event) => {
|
||||
if (event.type === 'change-video' && event.videoId) {
|
||||
currentUrl.pathname = `/watch/${event.videoId}`;
|
||||
currentUrl.pathname = resolve(`/watch/[videoId]`, { videoId: event.videoId });
|
||||
goto(currentUrl);
|
||||
} else if (event.type === 'goto' && event.path && event.path !== $page.url.pathname) {
|
||||
if (blockedPages.includes(event.path.replace('/', ''))) {
|
||||
@@ -72,7 +73,7 @@
|
||||
}
|
||||
|
||||
page.subscribe((newPage) => {
|
||||
if (!newPage.url.pathname.startsWith('/watch') && $syncPartyPeerStore) {
|
||||
if (!newPage.url.pathname.startsWith(resolve('/watch', {})) && $syncPartyPeerStore) {
|
||||
$syncPartyConnectionsStore?.forEach((conn) => {
|
||||
conn.send({
|
||||
events: [
|
||||
@@ -124,14 +125,16 @@
|
||||
<input
|
||||
name="sync-share"
|
||||
readonly
|
||||
value={`${location.origin}?sync=${$syncPartyPeerStore.id}`}
|
||||
value={`${location.origin}${resolve(`/?sync=${$syncPartyPeerStore?.id}`, {})}`}
|
||||
type="text"
|
||||
/>
|
||||
<label class="active" for="sync-share">Share URL</label>
|
||||
</div>
|
||||
<button
|
||||
onclick={async () => {
|
||||
await Clipboard.write({ string: `${location.origin}?sync=${$syncPartyPeerStore?.id}` });
|
||||
await Clipboard.write({
|
||||
string: `${location.origin}${resolve(`/?sync=${$syncPartyPeerStore?.id}`, {})}`
|
||||
});
|
||||
}}
|
||||
class="square round"
|
||||
>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { getBestThumbnail } from '$lib/images';
|
||||
import { letterCase } from '$lib/letterCasing';
|
||||
import { videoLength } from '$lib/numbers';
|
||||
@@ -212,7 +213,10 @@
|
||||
</a>
|
||||
|
||||
<div>
|
||||
<a tabindex="-1" class:author={!sideways} href={`/channel/${video.authorId}`}
|
||||
<a
|
||||
tabindex="-1"
|
||||
class:author={!sideways}
|
||||
href={resolve(`/channel/[authorId]`, { authorId: video.authorId })}
|
||||
>{video.author}
|
||||
</a>
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
isLoading = true;
|
||||
transcript = null;
|
||||
const resp = await fetch(`${!video.fallbackPatch ? get(instanceStore) : ''}${url}`);
|
||||
const resp = await fetch(`${!video.fallbackPatch ? new URL(get(instanceStore)).origin : ''}${url}`);
|
||||
transcript = await parseText(await resp.text(), { strict: false });
|
||||
|
||||
// Group cues by Math.ceil(startTime)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import { bookmarkletSaveToUrl } from '$lib/externalSettings';
|
||||
import { letterCase, titleCases } from '$lib/letterCasing';
|
||||
import { setAmoledTheme } from '$lib/theme';
|
||||
@@ -89,7 +90,7 @@
|
||||
|
||||
instanceStore.set(instance);
|
||||
authStore.set(null);
|
||||
goto('/', { replaceState: true });
|
||||
goto(resolve('/', {}), { replaceState: true });
|
||||
ui('#dialog-settings');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { deleteUnsubscribe, postSubscribe } from '$lib/api';
|
||||
import type { VideoPlay } from '$lib/api/model';
|
||||
import { getBestThumbnail, proxyGoogleImage } from '$lib/images';
|
||||
@@ -24,7 +25,7 @@
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
<a href={`/channel/${video.authorId}`}>
|
||||
<a href={resolve(`/channel/[authorId]`, { authorId: video.authorId })}>
|
||||
<nav style="gap: 0.5em;">
|
||||
{#if !$interfaceLowBandwidthMode}
|
||||
<img
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { getComments } from '$lib/api';
|
||||
import { type Comment, type Comments } from '$lib/api/model';
|
||||
import { getBestThumbnail, proxyGoogleImage } from '$lib/images';
|
||||
@@ -34,7 +35,7 @@
|
||||
function commentTimestamps(html: string): string {
|
||||
const regex =
|
||||
/<a href="([^"]+)" data-onclick="jump_to_time" data-jump-time="(\d+)">([^<]+)<\/a>\s*(.+)/g;
|
||||
const replacement = `<a href="/watch/${videoId}?time=$2" data-sveltekit-preload-data="off" class="link">$3 $4</a>`;
|
||||
const replacement = `<a href=${resolve(`/watch/[videoId]?time=$2`, { videoId: videoId })} data-sveltekit-preload-data="off" class="link">$3 $4</a>`;
|
||||
|
||||
const processedHtml = html.replace(regex, replacement);
|
||||
|
||||
@@ -63,7 +64,7 @@
|
||||
<progress class="circle"></progress>
|
||||
{/if}
|
||||
<div class="comment-info">
|
||||
<a href={`/channel/${comment.authorId}`} class="author">
|
||||
<a href={resolve(`/channel/[authorId]`, { authorId: comment.authorId })} class="author">
|
||||
<span class="bold" class:channel-owner={comment.authorIsChannelOwner}>
|
||||
{comment.author}
|
||||
</span>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { cleanNumber } from '$lib/numbers';
|
||||
import { _ } from '$lib/i18n';
|
||||
import type { VideoPlay } from '$lib/api/model';
|
||||
@@ -33,8 +34,11 @@
|
||||
{#if video.keywords && video.keywords.length > 0}
|
||||
<article class="border">
|
||||
<nav class="scroll">
|
||||
{#each video.keywords as keyword}
|
||||
<a href={`/search/${encodeURIComponent(keyword)}`} class="chip">{keyword}</a>
|
||||
{#each video.keywords as keyword (keyword)}
|
||||
<a
|
||||
href={resolve(`/search/[search]`, { search: encodeURIComponent(keyword) })}
|
||||
class="chip">{keyword}</a
|
||||
>
|
||||
{/each}
|
||||
</nav>
|
||||
</article>
|
||||
|
||||
@@ -147,7 +147,8 @@
|
||||
"enableYoutubejsTemp": "Helyi videó visszatérés használata egyszer",
|
||||
"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."
|
||||
"youtubeJsLoading": "A videó betöltése sikertelen, újraprobálkozás a helyi videó visszatéréssel.",
|
||||
"premiere": "A videó elérhető lesz ekkor"
|
||||
},
|
||||
"title": "Cím",
|
||||
"subscriptions": {
|
||||
@@ -163,7 +164,9 @@
|
||||
"private": "Privát",
|
||||
"playVideos": "Videók lejátszása",
|
||||
"loopPlaylist": "Lejátszási lista folyamatos lejátszása",
|
||||
"shuffleVideos": "Videók lejátszása keverten"
|
||||
"shuffleVideos": "Videók lejátszása keverten",
|
||||
"next": "Következő",
|
||||
"previous": "Előző"
|
||||
},
|
||||
"thumbnail": {
|
||||
"live": "ÉLŐ",
|
||||
@@ -188,5 +191,17 @@
|
||||
"subscribe": "Feliratkozás",
|
||||
"invidiousBlockWarning": "Az Invidious jelenleg le van tiltva a Google által. Ha ebben az esetben a videók nem töltődnek be, akkor használja ezt a példányt a Materialious-on {android} vagy {desktop} platformon, hogy helyi videó visszatéréssel megkerülje ezt a problémát.",
|
||||
"version": "Verzió",
|
||||
"contributors": "Közreműködők"
|
||||
"contributors": "Közreműködők",
|
||||
"popularPageDisabled": "A népszerű oldal letiltva az adminok által",
|
||||
"filters": {
|
||||
"filters": "Szűrők",
|
||||
"type": "Típus",
|
||||
"uploadDate": "Feltöltés dátuma",
|
||||
"duration": "Időtartam",
|
||||
"sortBy": "Rendezés",
|
||||
"features": "Jellemzők"
|
||||
},
|
||||
"newest": "Legújabb",
|
||||
"oldest": "Legrégebbi",
|
||||
"popular": "Népszerű"
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
"delete": "Удалить",
|
||||
"donate": "Пожертвовать",
|
||||
"deleteAllHistory": "Удалить всю историю",
|
||||
"skipping": "Пропускаю",
|
||||
"replies": "ответ(а/ов)",
|
||||
"skipping": "Пропуск",
|
||||
"replies": "ответа(ов)",
|
||||
"region": "Регион",
|
||||
"noResult": "Без результатов",
|
||||
"language": "Язык",
|
||||
@@ -32,7 +32,7 @@
|
||||
"videoTabs": {
|
||||
"all": "Всё",
|
||||
"videos": "Видео",
|
||||
"playlists": "Списки воспроизведения",
|
||||
"playlists": "Плейлисты",
|
||||
"channels": "Каналы",
|
||||
"shorts": "Shorts",
|
||||
"streams": "Трансляции"
|
||||
@@ -41,13 +41,15 @@
|
||||
"manageSubscriptions": "Управление подписками"
|
||||
},
|
||||
"playlist": {
|
||||
"createPlaylist": "Создать список воспроизведения",
|
||||
"createPlaylist": "Создать плейлист",
|
||||
"public": "Общедоступно",
|
||||
"unlisted": "Недоступно",
|
||||
"private": "Ограниченно",
|
||||
"playVideos": "Воспроизвести видео",
|
||||
"shuffleVideos": "Воспроизвести видео в случайном порядке",
|
||||
"loopPlaylist": "Повторять список воспроизведения"
|
||||
"loopPlaylist": "Зациклить плейлист",
|
||||
"next": "Следующее",
|
||||
"previous": "Предыдущее"
|
||||
},
|
||||
"thumbnail": {
|
||||
"live": "В ЭФИРЕ",
|
||||
@@ -64,7 +66,7 @@
|
||||
"unsubscribe": "Отписаться",
|
||||
"loginRequired": "Нужно войти в учётную запись",
|
||||
"player": {
|
||||
"audioOnly": "Только звук",
|
||||
"audioOnly": "Только аудио",
|
||||
"theatreMode": "Широкий экран",
|
||||
"pauseTimer": "Таймер паузы",
|
||||
"pauseVideoIn": "Приостановить видео на ",
|
||||
@@ -86,7 +88,7 @@
|
||||
"wasm": "Скачивание ffmpeg wasm",
|
||||
"worker": "Скачивание ffmpeg worker"
|
||||
},
|
||||
"noPlaylists": "Нет списков воспроизведения",
|
||||
"noPlaylists": "Нет плейлистов",
|
||||
"unableToLoadComments": "Невозможно загрузить комментарии",
|
||||
"addToPlaylist": "Добавить в плейлист",
|
||||
"defaultLanguage": "Язык по умолчанию",
|
||||
@@ -96,7 +98,8 @@
|
||||
"youtubeJsLoading": "Не удалось скачать видео, попытка перезагрузки с локальным резервным видео.",
|
||||
"chapters": "Главы",
|
||||
"enableYoutubejsPerm": "Всегда использовать резервное локальное видео",
|
||||
"preferredQuality": "Желаемое качество"
|
||||
"preferredQuality": "Желаемое качество",
|
||||
"premiere": "Видео будет доступно"
|
||||
},
|
||||
"layout": {
|
||||
"interface": "Интерфейс",
|
||||
@@ -129,10 +132,10 @@
|
||||
"player": {
|
||||
"title": "Плеер",
|
||||
"autoPlay": "Авто-воспроизведение видео",
|
||||
"alwaysLoopVideo": "Зацикливать видео",
|
||||
"alwaysLoopVideo": "Всегда зацикливать видео",
|
||||
"savePlaybackPosition": "Сохранять позицию",
|
||||
"listenByDefault": "Слушать по умолчанию",
|
||||
"theatreModeByDefault": "Широкий формат по умолчанию",
|
||||
"theatreModeByDefault": "Широкий экран по умолчанию",
|
||||
"autoPlayNextByDefault": "Авто-воспроизведение по умолчанию",
|
||||
"miniPlayer": "Мини-плеер",
|
||||
"proxyVideos": "Прокси видео",
|
||||
@@ -153,7 +156,7 @@
|
||||
"sponsor": "Спонсор",
|
||||
"Catagories": "Категории",
|
||||
"unpaidSelfPromotion": "Самореклама/рекомендация",
|
||||
"interactionReminder": "Напоминание о взаимодействии (Подписка)",
|
||||
"interactionReminder": "Напоминание о взаимодействии (Подписаться)",
|
||||
"intermissionIntroAnimation": "Анимация концовки/вступления",
|
||||
"credits": "Конечные заставки/титры",
|
||||
"preViewRecapHook": "Предпросмотр/краткое содержание/отсылка",
|
||||
@@ -186,6 +189,19 @@
|
||||
"premium": "Премиум-контент YouTube нельзя просматривать в Materialious.",
|
||||
"isUpcoming": "Видео ещё не готово к просмотру.",
|
||||
"comments": "комментарии",
|
||||
"reply": "ответить",
|
||||
"version": "Версия"
|
||||
"reply": "ответ",
|
||||
"version": "Версия",
|
||||
"popularPageDisabled": "Страница «Популярные» отключена администраторами",
|
||||
"filters": {
|
||||
"filters": "Фильтры",
|
||||
"type": "Тип",
|
||||
"uploadDate": "Дата загрузки",
|
||||
"duration": "Длительность",
|
||||
"sortBy": "Сортировать по",
|
||||
"features": "Функции"
|
||||
},
|
||||
"contributors": "Участники",
|
||||
"newest": "Сначала новые",
|
||||
"oldest": "Сначала старые",
|
||||
"popular": "Популярные"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { pushState } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import he from 'he';
|
||||
import type Peer from 'peerjs';
|
||||
@@ -128,9 +129,10 @@ export function expandSummery(id: string) {
|
||||
}
|
||||
|
||||
export function createVideoUrl(videoId: string, playlistId: string): URL {
|
||||
const watchUrl = new URL(
|
||||
`${location.origin}/${get(isAndroidTvStore) ? 'tv' : 'watch'}/${videoId}`
|
||||
);
|
||||
const watchPath = resolve(`/${get(isAndroidTvStore) ? 'tv' : 'watch'}/[videoId]`, {
|
||||
videoId: videoId
|
||||
});
|
||||
const watchUrl = new URL(`${location.origin}${watchPath}`);
|
||||
|
||||
if (playlistId !== '') {
|
||||
watchUrl.searchParams.set('playlist', playlistId);
|
||||
|
||||
@@ -10,9 +10,9 @@ import type {
|
||||
} from '$lib/api/model';
|
||||
import { interfaceRegionStore, poTokenCacheStore } from '$lib/store';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import BG, { USER_AGENT } from 'bgutils-js';
|
||||
import { USER_AGENT } from 'bgutils-js';
|
||||
import { get } from 'svelte/store';
|
||||
import type { Types } from 'youtubei.js';
|
||||
import type { Types, Misc } from 'youtubei.js';
|
||||
import { Innertube, UniversalCache, Utils, YT, YTNodes, Platform } from 'youtubei.js';
|
||||
|
||||
Platform.shim.eval = async (
|
||||
@@ -85,6 +85,29 @@ export async function patchYoutubeJs(videoId: string): Promise<VideoPlay> {
|
||||
|
||||
let dashUri: string | undefined;
|
||||
|
||||
// Unsupported format fix
|
||||
// https://github.com/LuanRT/googlevideo/issues/42
|
||||
if (video.streaming_data)
|
||||
video.streaming_data.adaptive_formats = video.streaming_data.adaptive_formats.filter(
|
||||
(format) => format.xtags !== 'CgcKAnZiEgEx'
|
||||
);
|
||||
|
||||
const adaptiveFormats: AdaptiveFormats[] = [];
|
||||
video.streaming_data?.adaptive_formats.forEach((format) => {
|
||||
adaptiveFormats.push({
|
||||
index: format.index_range?.start?.toString() || '',
|
||||
bitrate: format.bitrate?.toString() || '',
|
||||
init: format.init_range?.start?.toString() || '',
|
||||
url: format.url || '',
|
||||
itag: format.itag?.toString() || '',
|
||||
type: format.mime_type,
|
||||
clen: '',
|
||||
lmt: '',
|
||||
projectionType: 0,
|
||||
resolution: format.width ? `${format.width}x${format.height}` : undefined
|
||||
});
|
||||
});
|
||||
|
||||
const isPostLiveDVR =
|
||||
!!video.basic_info.is_post_live_dvr ||
|
||||
(video.basic_info.is_live_content &&
|
||||
@@ -100,14 +123,13 @@ export async function patchYoutubeJs(videoId: string): Promise<VideoPlay> {
|
||||
? video.streaming_data.hls_manifest_url // HLS is preferred for DVR streams.
|
||||
: `${video.streaming_data.dash_manifest_url}/mpd_version/7`;
|
||||
} else {
|
||||
dashUri = `data:application/dash+xml;base64,${btoa(
|
||||
await video.toDash({
|
||||
manifest_options: {
|
||||
is_sabr: true,
|
||||
include_thumbnails: false
|
||||
}
|
||||
})
|
||||
)}`;
|
||||
const manifest = await video.toDash({
|
||||
manifest_options: {
|
||||
is_sabr: true,
|
||||
include_thumbnails: false
|
||||
}
|
||||
});
|
||||
dashUri = `data:application/dash+xml;base64,${btoa(manifest)}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,22 +194,6 @@ export async function patchYoutubeJs(videoId: string): Promise<VideoPlay> {
|
||||
});
|
||||
});
|
||||
|
||||
const adaptiveFormats: AdaptiveFormats[] = [];
|
||||
video.streaming_data?.adaptive_formats.forEach((format) => {
|
||||
adaptiveFormats.push({
|
||||
index: format.index_range?.start?.toString() || '',
|
||||
bitrate: format.bitrate?.toString() || '',
|
||||
init: format.init_range?.start?.toString() || '',
|
||||
url: format.url || '',
|
||||
itag: format.itag?.toString() || '',
|
||||
type: format.mime_type,
|
||||
clen: '',
|
||||
lmt: '',
|
||||
projectionType: 0,
|
||||
resolution: format.width ? `${format.width}x${format.height}` : undefined
|
||||
});
|
||||
});
|
||||
|
||||
const storyboard: StoryBoard[] = [];
|
||||
if (video.storyboards && 'boards' in video.storyboards) {
|
||||
video.storyboards.boards.forEach((board) => {
|
||||
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
syncPartyConnectionsStore
|
||||
} from './store';
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import { loadEntirePlaylist } from './playlist';
|
||||
import { unsafeRandomItem } from './misc';
|
||||
import { resolve } from '$app/paths';
|
||||
|
||||
export interface PlayerEvent {
|
||||
type: 'pause' | 'seek' | 'change-video' | 'play' | 'playlist' | 'goto';
|
||||
@@ -44,9 +44,14 @@ export async function goToNextVideo(video: VideoPlay, playlistId: string | null)
|
||||
|
||||
if (!playlistId) {
|
||||
if (get(playerAutoplayNextByDefaultStore)) {
|
||||
goto(`/${isAndroidTv ? 'tv' : 'watch'}/${video.recommendedVideos[0].videoId}`, {
|
||||
replaceState: isAndroidTv
|
||||
});
|
||||
goto(
|
||||
resolve(`/${isAndroidTv ? 'tv' : 'watch'}/[videoId]`, {
|
||||
videoId: video.recommendedVideos[0].videoId
|
||||
}),
|
||||
{
|
||||
replaceState: isAndroidTv
|
||||
}
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -86,8 +91,13 @@ export async function goToNextVideo(video: VideoPlay, playlistId: string | null)
|
||||
} as PlayerEvents);
|
||||
});
|
||||
|
||||
goto(`/${isAndroidTv ? 'tv' : 'watch'}/${goToVideo.videoId}?playlist=${playlistId}`, {
|
||||
replaceState: isAndroidTv
|
||||
});
|
||||
goto(
|
||||
resolve(`/${isAndroidTv ? 'tv' : 'watch'}/[videoId]?playlist=${playlistId}`, {
|
||||
videoId: goToVideo.videoId
|
||||
}),
|
||||
{
|
||||
replaceState: isAndroidTv
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { navigating, page } from '$app/stores';
|
||||
@@ -102,7 +103,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
goto(`/watch/${videoId}`);
|
||||
goto(resolve(`/watch/[videoId]`, { videoId: videoId }));
|
||||
} else {
|
||||
// Auth response handling for Mobile
|
||||
const username = url.searchParams.get('username');
|
||||
@@ -128,7 +129,7 @@
|
||||
path.search = searchParams.toString();
|
||||
await Browser.open({ url: path.toString() });
|
||||
} else {
|
||||
searchParams.set('callback_url', `${location.origin}/auth`);
|
||||
searchParams.set('callback_url', `${location.origin}${resolve('/auth', {})}`);
|
||||
path.search = searchParams.toString();
|
||||
document.location.href = path.toString();
|
||||
}
|
||||
@@ -173,7 +174,7 @@
|
||||
console.log(sid);
|
||||
authStore.set({ username: rawUsername, token: sid });
|
||||
await ui('#tv-login');
|
||||
goto('/', { replaceState: true });
|
||||
goto(resolve('/', {}), { replaceState: true });
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -187,7 +188,7 @@
|
||||
feedCacheStore.set({});
|
||||
searchCacheStore.set({});
|
||||
playlistCacheStore.set({});
|
||||
goto('/');
|
||||
goto(resolve('/', {}));
|
||||
}
|
||||
|
||||
async function loadNotifications() {
|
||||
@@ -277,7 +278,7 @@
|
||||
<nav class="left m l surface-container" class:tv-nav={$isAndroidTvStore}>
|
||||
<header
|
||||
role="presentation"
|
||||
onclick={() => goto($interfaceDefaultPage)}
|
||||
onclick={() => goto(resolve($interfaceDefaultPage, {}))}
|
||||
style="cursor: pointer;"
|
||||
tabindex="-1"
|
||||
class="small-padding"
|
||||
@@ -286,7 +287,7 @@
|
||||
</header>
|
||||
{#each getPages() as navPage}
|
||||
{#if !navPage.requiresAuth || isLoggedIn}
|
||||
<a href={navPage.href} class:active={$page.url.href.endsWith(navPage.href)}
|
||||
<a href={resolve(navPage.href, {})} class:active={$page.url.href.endsWith(navPage.href)}
|
||||
><i>{navPage.icon}</i>
|
||||
<div>{navPage.name}</div>
|
||||
</a>
|
||||
@@ -365,7 +366,7 @@
|
||||
{#if !navPage.requiresAuth || isLoggedIn}
|
||||
<a
|
||||
class="round"
|
||||
href={navPage.href}
|
||||
href={resolve(navPage.href, {})}
|
||||
class:active={$page.url.href.endsWith(navPage.href)}
|
||||
data-sveltekit-preload-data="off"
|
||||
><i>{navPage.icon}</i>
|
||||
@@ -421,7 +422,9 @@
|
||||
</div>
|
||||
{#if playerIsPip}
|
||||
<nav class="s">
|
||||
<a class="button border" href={`/watch/${$playerState.data.video.videoId}`}
|
||||
<a
|
||||
class="button border"
|
||||
href={resolve(`/watch/[videoId]`, { videoId: $playerState.data.video.videoId })}
|
||||
><i>keyboard_arrow_right</i></a
|
||||
>
|
||||
<button class="border" onclick={() => playerState.set(undefined)}>
|
||||
@@ -432,7 +435,9 @@
|
||||
<nav class="m l">
|
||||
<Author video={$playerState.data.video} hideSubscribe={true} />
|
||||
<div class="max"></div>
|
||||
<a class="button border" href={`/watch/${$playerState.data.video.videoId}`}
|
||||
<a
|
||||
class="button border"
|
||||
href={resolve(`/watch/[videoId]`, { videoId: $playerState.data.video.videoId })}
|
||||
><i>keyboard_arrow_right</i></a
|
||||
>
|
||||
</nav>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import PageLoading from '$lib/components/PageLoading.svelte';
|
||||
import { authStore } from '$lib/store';
|
||||
@@ -17,7 +18,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
goto('/');
|
||||
goto(resolve('/', {}));
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@
|
||||
<div class="grid">
|
||||
<div class="s12 m6 l6">
|
||||
<nav class="group">
|
||||
{#each sortByOptions as sortingOption}
|
||||
{#each sortByOptions as sortingOption (sortingOption)}
|
||||
<button
|
||||
class="no-round"
|
||||
onclick={async () => {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export async function load({ url }) {
|
||||
const playlistId = url.searchParams.get('list');
|
||||
if (playlistId) {
|
||||
goto(`/playlist/${playlistId}`);
|
||||
goto(resolve(`/playlist/[playlistId]`, { playlistId }));
|
||||
} else {
|
||||
error(404);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { unsafeRandomItem } from '$lib/misc';
|
||||
import { cleanNumber } from '$lib/numbers';
|
||||
import { isAndroidTvStore, playlistSettingsStore } from '$lib/store';
|
||||
@@ -14,9 +15,14 @@
|
||||
{#if data.playlist.videos}
|
||||
<nav>
|
||||
<a
|
||||
href={!$isAndroidTvStore
|
||||
? `/watch/${data.playlist.videos[0].videoId}?playlist=${data.playlist.info.playlistId}`
|
||||
: `/tv/${data.playlist.videos[0].videoId}?playlist=${data.playlist.info.playlistId}`}
|
||||
href={resolve(
|
||||
$isAndroidTvStore
|
||||
? `/tv/[playlistId]?playlist=${data.playlist.info.playlistId}`
|
||||
: `/watch/[playlistId]?playlist=${data.playlist.info.playlistId}`,
|
||||
{
|
||||
playlistId: data.playlist.videos[0].videoId
|
||||
}
|
||||
)}
|
||||
class="button circle extra no-margin"
|
||||
>
|
||||
<i>play_arrow</i>
|
||||
@@ -26,9 +32,14 @@
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={!$isAndroidTvStore
|
||||
? `/watch/${unsafeRandomItem(data.playlist.videos).videoId}?playlist=${data.playlist.info.playlistId}`
|
||||
: `/tv/${unsafeRandomItem(data.playlist.videos).videoId}?playlist=${data.playlist.info.playlistId}`}
|
||||
href={resolve(
|
||||
$isAndroidTvStore
|
||||
? `/tv/[playlistId]?playlist=${data.playlist.info.playlistId}`
|
||||
: `/watch/[playlistId]?playlist=${data.playlist.info.playlistId}`,
|
||||
{
|
||||
playlistId: unsafeRandomItem(data.playlist.videos).videoId
|
||||
}
|
||||
)}
|
||||
onclick={() =>
|
||||
playlistSettingsStore.set({
|
||||
[data.playlist.info.playlistId]: { shuffle: true, loop: false }
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<div class="page right active">
|
||||
<div class="space"></div>
|
||||
<div class="grid">
|
||||
{#each playlists.playlists as playlist}
|
||||
{#each playlists.playlists as playlist (playlist.playlistId)}
|
||||
<ContentColumn>
|
||||
<article class="no-padding" style="height: 100%;">
|
||||
{#key playlist.playlistId}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { deleteUnsubscribe } from '$lib/api';
|
||||
import Fuse from 'fuse.js';
|
||||
import { _ } from '$lib/i18n';
|
||||
@@ -42,10 +43,10 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#each subscriptions as sub}
|
||||
{#each subscriptions as sub (sub.authorId)}
|
||||
<article>
|
||||
<nav>
|
||||
<a href={`/channel/${sub.authorId}`}
|
||||
<a href={resolve(`/channel/[authorId]`, { authorId: sub.authorId })}
|
||||
><h6>
|
||||
{sub.author}
|
||||
</h6></a
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export async function load({ url }) {
|
||||
@@ -7,7 +8,7 @@ export async function load({ url }) {
|
||||
const timestamp = url.searchParams.get('t');
|
||||
|
||||
if (videoId) {
|
||||
let goToUrl = new URL(`${location.origin}/watch/${videoId}`);
|
||||
const goToUrl = new URL(`${location.origin}${resolve(`/watch/[videoId]`, { videoId })}`);
|
||||
|
||||
if (playlistId) {
|
||||
goToUrl.searchParams.set('playlist', playlistId);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import {
|
||||
addPlaylistVideo,
|
||||
getComments,
|
||||
@@ -453,7 +454,7 @@
|
||||
<i>add</i>
|
||||
<div class="tooltip">{$_('player.addToPlaylist')}</div>
|
||||
<menu class="no-wrap mobile">
|
||||
{#each personalPlaylists as personalPlaylist}
|
||||
{#each personalPlaylists as personalPlaylist (personalPlaylist.playlistId)}
|
||||
<li
|
||||
role="presentation"
|
||||
class="row"
|
||||
@@ -509,7 +510,7 @@
|
||||
<div class="space"></div>
|
||||
<div class="chapter-list" id="chapters">
|
||||
<ul class="list">
|
||||
{#each data.content.timestamps as timestamp}
|
||||
{#each data.content.timestamps as timestamp (timestamp)}
|
||||
<li
|
||||
role="presentation"
|
||||
onclick={() => {
|
||||
@@ -550,7 +551,7 @@
|
||||
<div class="space"></div>
|
||||
|
||||
<div class="medium scroll">
|
||||
{#each comments.comments as comment}
|
||||
{#each comments.comments as comment (comment)}
|
||||
<Comment {comment} videoId={data.video.videoId}></Comment>
|
||||
{/each}
|
||||
{#if comments.continuation}
|
||||
@@ -580,8 +581,10 @@
|
||||
{$_('videos')}
|
||||
</p>
|
||||
<p>
|
||||
<a href={`/channel/${$playlistCacheStore[data.playlistId].info.authorId}`}
|
||||
>{$playlistCacheStore[data.playlistId].info.author}</a
|
||||
<a
|
||||
href={resolve(`/channel/[authorId]`, {
|
||||
authorId: $playlistCacheStore[data.playlistId].info.authorId
|
||||
})}>{$playlistCacheStore[data.playlistId].info.author}</a
|
||||
>
|
||||
</p>
|
||||
<nav>
|
||||
@@ -638,7 +641,7 @@
|
||||
|
||||
<div class="space"></div>
|
||||
|
||||
{#each $playlistCacheStore[data.playlistId].videos as playlistVideo}
|
||||
{#each $playlistCacheStore[data.playlistId].videos as playlistVideo (playlistVideo.videoId)}
|
||||
<article
|
||||
class="no-padding border"
|
||||
style="margin: .7em;"
|
||||
@@ -656,7 +659,7 @@
|
||||
{/each}
|
||||
</article>
|
||||
{:else if data.video.recommendedVideos}
|
||||
{#each data.video.recommendedVideos as recommendedVideo}
|
||||
{#each data.video.recommendedVideos as recommendedVideo (recommendedVideo.videoId)}
|
||||
<article class="no-padding border">
|
||||
{#key recommendedVideo.videoId}
|
||||
<Thumbnail video={recommendedVideo} sideways={true} />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { browser } from '$app/environment';
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
import androidTv from '$lib/android/plugins/androidTv';
|
||||
import { getResolveUrl } from '$lib/api';
|
||||
import '$lib/i18n'; // Import to initialize. Important :)
|
||||
@@ -17,13 +18,14 @@ export async function load({ url }) {
|
||||
|
||||
isAndroidTvStore.set((await androidTv.isAndroidTv()).value);
|
||||
|
||||
if (url.pathname.startsWith('/@')) {
|
||||
const username = url.pathname.split('/')[1];
|
||||
const resolvedRoot = resolve('/', {});
|
||||
if (url.pathname.startsWith(resolvedRoot + '@')) {
|
||||
const username = url.pathname.substring(resolvedRoot.length).split('/')[0];
|
||||
|
||||
try {
|
||||
const resolvedUrl = await getResolveUrl(`www.youtube.com/${username}`);
|
||||
if (resolvedUrl.pageType === 'WEB_PAGE_TYPE_CHANNEL') {
|
||||
goto(`/channel/${resolvedUrl.ucid}`);
|
||||
goto(resolve(`/channel/[authorId]`, { authorId: resolvedUrl.ucid }));
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
@@ -34,12 +36,12 @@ export async function load({ url }) {
|
||||
defaultPage &&
|
||||
defaultPage !== '/' &&
|
||||
defaultPage.startsWith('/') &&
|
||||
url.pathname === '/' &&
|
||||
url.pathname === resolvedRoot &&
|
||||
window.history.length < 3
|
||||
) {
|
||||
getPages().forEach((page) => {
|
||||
if (page.href === defaultPage && (!page.requiresAuth || get(authStore))) {
|
||||
goto(defaultPage);
|
||||
goto(resolve(defaultPage, {}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ const config = {
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
kit: {
|
||||
// Optional: Set base path for hosted Materialious here
|
||||
//paths: {base: '/materialious'},
|
||||
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
LATEST_VERSION = "1.11.6"
|
||||
LATEST_VERSION = "1.11.7"
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user