commit e68a8b162294dec011df02fd2a84fb7c780cb76e Author: ponzischeme89 Date: Sat Apr 18 23:08:52 2026 +1200 v1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f231590 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +node_modules/ + +.svelte-kit/ +.vite/ +dist/ +build/ + +.DS_Store +Thumbs.db + +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +.env +.env.* +!.env.example + +config.json +users.db diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..e69de29 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e49d42f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1464 @@ +{ + "name": "emby-homescreen-editor", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "emby-homescreen-editor", + "version": "1.0.0", + "devDependencies": { + "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/kit": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0", + "vite": "^5.0.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", + "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-auto": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.3.1.tgz", + "integrity": "sha512-5Sc7WAxYdL6q9j/+D0jJKjGREGlfIevDyHSQ2eNETHcB1TKlQWHcAo8AS8H1QdjNvSXpvOwNjykDUHPEAyGgdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-meta-resolve": "^4.1.0" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.0.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.57.1", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.57.1.tgz", + "integrity": "sha512-VRdSbB96cI1EnRh09CqmnQqP/YJvET5buj8S6k7CxaJqBJD4bw4fRKDjcarAj/eX9k2eHifQfDH8NtOh+ZxxPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", + "cookie": "^0.6.0", + "devalue": "^5.6.4", + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "set-cookie-parser": "^3.0.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": "^5.3.3 || ^6.0.0", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz", + "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", + "debug": "^4.3.4", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.10", + "svelte-hmr": "^0.16.0", + "vitefu": "^0.2.5" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", + "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/code-red": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", + "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1", + "acorn": "^8.10.0", + "estree-walker": "^3.0.3", + "periscopic": "^3.1.0" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/devalue": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz", + "integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/set-cookie-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svelte": { + "version": "4.2.20", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.20.tgz", + "integrity": "sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/estree": "^1.0.1", + "acorn": "^8.9.0", + "aria-query": "^5.3.0", + "axobject-query": "^4.0.0", + "code-red": "^1.0.3", + "css-tree": "^2.3.1", + "estree-walker": "^3.0.3", + "is-reference": "^3.0.1", + "locate-character": "^3.0.0", + "magic-string": "^0.30.4", + "periscopic": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/svelte-hmr": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", + "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.20 || ^14.13.1 || >= 16" + }, + "peerDependencies": { + "svelte": "^3.19.0 || ^4.0.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", + "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..01f83af --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "emby-homescreen-editor", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/kit": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0", + "vite": "^5.0.0" + } +} diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000..1377588 --- /dev/null +++ b/src/app.html @@ -0,0 +1,12 @@ + + + + + + Emby Home Screen Editor + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/lib/SectionCard.svelte b/src/lib/SectionCard.svelte new file mode 100644 index 0000000..8bbdeb9 --- /dev/null +++ b/src/lib/SectionCard.svelte @@ -0,0 +1,649 @@ + + +
+ + + +
+ + {index + 1} + +
+
{section.CustomName || section.Name || '(unnamed)'}
+ +
+ + + {expanded ? '▾' : '▸'} + + + + {#if expanded} +
+ + +
+ + + + + + + + + + + {#if showFilters} + + {/if} + + {#if section.SectionType === 'userviews'} + + {/if} + + +
+ + +
+ {#if section.SectionType === 'resume'} + + {/if} + + {#if showFilters} + + + {/if} +
+ + {#if showFilters} + +
+ Item types +
+ {#each ITEM_TYPES as type} + + {/each} +
+
+ + +
+ Genres +
+ {#each Object.entries(GENRES).sort((a, b) => a[1].localeCompare(b[1])) as [id, name]} + + {/each} +
+
+ + +
+ Tag IDs (comma-separated Emby tag IDs) + + {#if section.Query?.TagIds?.length} +
+ {#each section.Query.TagIds as id} + + {id} + + + {/each} +
+ {/if} +
+ {/if} + + + {#if section.SectionType === 'boxset'} +
+ Linked box set + {#if section.ParentItem} +
+ {section.ParentItem.Name} + ID: {section.ParentItem.Id} +
+ {:else} +
+ + +
+ {/if} +
+ {/if} + + +
+ + Excluded folders + {#if section.ExcludedFolders?.length} + {section.ExcludedFolders.length} + {/if} + (comma-separated folder IDs) + + + {#if section.ExcludedFolders?.length} +
+ {#each section.ExcludedFolders as id} + + {id} + + + {/each} +
+ {/if} +
+ +
+ {/if} + + + diff --git a/src/lib/SettingsPanel.svelte b/src/lib/SettingsPanel.svelte new file mode 100644 index 0000000..390fecc --- /dev/null +++ b/src/lib/SettingsPanel.svelte @@ -0,0 +1,295 @@ + + +
+

Settings

+ +
+

Emby connection

+ + +

+ Get your API key from Emby: Dashboard → Advanced → API Keys → New API Key. +

+
+ + + +
+
+ +
+

Database file

+ +

+ Stop Emby before loading or writing to avoid corruption. Typical path: + C:\ProgramData\Emby-Server\data\users.db +

+
+ + +
+
+ + {#if status} +
+ {status} +
+ {/if} +
+ + diff --git a/src/lib/SqlModal.svelte b/src/lib/SqlModal.svelte new file mode 100644 index 0000000..15baab7 --- /dev/null +++ b/src/lib/SqlModal.svelte @@ -0,0 +1,134 @@ + + +
dispatch('close')} on:keydown={() => {}}> + +
+ + diff --git a/src/lib/SyncPanel.svelte b/src/lib/SyncPanel.svelte new file mode 100644 index 0000000..e5c2cc2 --- /dev/null +++ b/src/lib/SyncPanel.svelte @@ -0,0 +1,325 @@ + + +
+

Sync sections between users

+ +
+ 1. Source user + +
+ + {#if sourceUser} +
+ 2. Sync mode +
+ + + +
+
+ + {#if syncMode === 'selected'} +
+ Sections to sync +
+ {#each sourceSections as section, idx} + + {/each} +
+
+ {/if} + +
+
+ 3. Target users +
+ + +
+
+
+ {#each availableTargets as user} + + {/each} +
+
+ + + {/if} +
+ + diff --git a/src/lib/constants.js b/src/lib/constants.js new file mode 100644 index 0000000..3b46f16 --- /dev/null +++ b/src/lib/constants.js @@ -0,0 +1,201 @@ +export const GENRES = { + '2042': 'Action', + '1212': 'Sci-Fi', + '4910': 'Crime', + '62': 'Drama', + '82': 'Comedy', + '293': 'Animation', + '36': 'Documentary', + '5024': 'Horror', + '4835': 'Romance', + '4428': 'Thriller', + '5709': 'War', + '14124': 'Western', + '218': 'Food', + '396654': 'Reality', + '19618': 'Travel', + '17565': 'Mini Series', + '2008': 'Mystery', + '235': 'Family', + '480': 'Fantasy' +}; + +export const SECTION_TYPES = [ + { value: 'resume', label: 'Resume / Next Up' }, + { value: 'items', label: 'Items (filtered)' }, + { value: 'userviews', label: 'Libraries' }, + { value: 'boxset', label: 'Box Set' }, + { value: 'collections', label: 'Collections' }, + { value: 'latestepisodereleases', label: 'Latest episode releases' }, + { value: 'latestmoviereleases', label: 'Latest movie releases' }, + { value: 'latestmediablock', label: 'Latest media' } +]; + +export const COLLECTION_TYPES = [ + { value: '', label: '(none)' }, + { value: 'movies', label: 'Movies' }, + { value: 'tvshows', label: 'TV Shows' }, + { value: 'boxsets', label: 'Box Sets' } +]; + +export const ITEM_TYPES = ['Movie', 'Series', 'Episode', 'BoxSet']; + +export const SORT_OPTIONS = [ + { value: '', label: '(none)' }, + { value: 'default', label: 'Default (boxset)' }, + { value: 'DateLastContentAdded,SortName', label: 'Date added' }, + { value: 'ProductionYear,PremiereDate,SortName', label: 'Release year' }, + { value: 'CommunityRating', label: 'Community rating' }, + { value: 'CriticRating,SortName', label: 'Critic rating' }, + { value: 'DateCreated,SortName', label: 'Date created' }, + { value: 'Random', label: 'Random' }, + { value: 'SortName', label: 'Name' } +]; + +export const IMAGE_TYPES = [ + { value: '', label: 'Default' }, + { value: 'Thumb', label: 'Thumb' }, + { value: 'Primary', label: 'Primary / Poster' } +]; + +export function genId() { + return crypto.randomUUID().replace(/-/g, '').slice(0, 32); +} + +export function createEmptySection(userId) { + return { + UserId: userId, + Name: 'New Section', + CustomName: 'New Section', + Id: genId(), + SectionType: 'items', + ImageType: 'Thumb', + CollectionType: 'movies', + SortBy: 'DateLastContentAdded,SortName', + SortOrder: 'Descending', + Monitor: [], + ItemTypes: ['Movie'], + ExcludedFolders: [], + CardSizeOffset: 0, + IncludeNextUpInResume: true, + Query: { + StudioIds: [], + TagIds: [], + GenreIds: [], + CollectionTypes: [], + IsPlayed: false + } + }; +} + +export function getSectionTypeLabel(type) { + const found = SECTION_TYPES.find((t) => t.value === type); + return found ? found.label : type; +} + +export function getGenreNames(genreIds) { + if (!genreIds || genreIds.length === 0) return ''; + return genreIds.map((id) => GENRES[id] || id).join(', '); +} + +export function extractUserName(sections) { + for (const s of sections) { + const name = s.CustomName || s.Name || ''; + const lower = name.toLowerCase(); + if (lower.includes('watchlist')) { + const extracted = name + .replace(/['']s\s*Watchlist/i, '') + .replace(/\s*Watchlist/i, '') + .trim(); + if (extracted && extracted !== name) return extracted; + } + } + return null; +} + +export function isWatchlistSection(section) { + if (!section) return false; + + const name = `${section.CustomName || ''} ${section.Name || ''}`.toLowerCase(); + return !!section.Query?.IsFavorite || name.includes('watchlist') || name.includes('watch list'); +} + +function renameWatchlistLabel(label, targetName) { + if (!label || !targetName) return label; + if (/^\s*watch\s*list\s*$/i.test(label)) return 'Watch List'; + if (/^\s*watchlist\s*$/i.test(label)) return 'Watchlist'; + + if (/watch\s*list/i.test(label)) { + return label.replace(/^.*?watch\s*list/i, `${targetName}'s Watch List`); + } + + if (/watchlist/i.test(label)) { + return label.replace(/^.*?watchlist/i, `${targetName}'s Watchlist`); + } + + return label; +} + +export function getWatchlistLabelsForTarget(sourceSection, targetUser) { + const existingTargetWatchlist = targetUser?.sections?.find((section) => isWatchlistSection(section)); + + if (existingTargetWatchlist) { + return { + Name: existingTargetWatchlist.Name || sourceSection.Name, + CustomName: existingTargetWatchlist.CustomName || existingTargetWatchlist.Name || sourceSection.CustomName || sourceSection.Name + }; + } + + const targetName = targetUser?.name || ''; + return { + Name: renameWatchlistLabel(sourceSection.Name, targetName), + CustomName: renameWatchlistLabel(sourceSection.CustomName, targetName) + }; +} + +/** + * Build SQL UPDATE statements from modified user data. + * Each user's entire homescreensettings JSON is replaced. + */ +export function generateSQL(users, originalUsers) { + const statements = []; + + for (const user of users) { + if (!user.sections || user.sections.length === 0) continue; + + const original = originalUsers.find((u) => u.id === user.id); + if (!original) continue; + + const origJSON = JSON.stringify({ Sections: original.sections }); + const newJSON = JSON.stringify({ Sections: user.sections }); + + if (origJSON === newJSON) continue; + + const escapedValue = newJSON.replace(/'/g, "''"); + statements.push( + `-- User: ${user.name} (DB ID: ${user.id})`, + `UPDATE UserSettings SET Value = '${escapedValue}' WHERE UserId = ${user.id} AND UserSettingsKeyId = (SELECT UserSettingsKeyId FROM UserSettingsKeys WHERE Name = 'homescreensettings');`, + '' + ); + } + + if (statements.length === 0) { + return '-- No changes detected'; + } + + return [ + '-- ===========================================', + '-- Emby Home Screen Settings Update', + `-- Generated: ${new Date().toISOString()}`, + '-- ===========================================', + '-- IMPORTANT: Stop Emby before running this!', + '-- sqlite3 /path/to/users.db < this_file.sql', + '-- Then restart Emby.', + '-- ===========================================', + '', + 'BEGIN TRANSACTION;', + '', + ...statements, + 'COMMIT;' + ].join('\n'); +} diff --git a/src/lib/server/emby-user-db.js b/src/lib/server/emby-user-db.js new file mode 100644 index 0000000..5b644c0 --- /dev/null +++ b/src/lib/server/emby-user-db.js @@ -0,0 +1,204 @@ +function parseJsonBlob(blob) { + if (!blob) return null; + try { + const text = typeof blob === 'string' ? blob : Buffer.from(blob).toString('utf8'); + return JSON.parse(text); + } catch { + return null; + } +} + +/** + * Emby stores GUIDs in SQLite as 16-byte blobs using Microsoft mixed-endian ordering. + * Components 1-3 are little-endian; components 4-5 are big-endian. + */ +export function blobToEmbyGuid(blob) { + if (!blob) return ''; + const b = blob instanceof Uint8Array ? blob : new Uint8Array(blob); + if (b.length !== 16) return Buffer.from(b).toString('hex').toLowerCase(); + const out = new Uint8Array([ + b[3], b[2], b[1], b[0], + b[5], b[4], + b[7], b[6], + b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15] + ]); + return Buffer.from(out).toString('hex').toLowerCase(); +} + +function hasTable(db, tableName) { + const row = db + .prepare("SELECT 1 AS found FROM sqlite_master WHERE type = 'table' AND name = ?") + .get(tableName); + return !!row?.found; +} + +function normalizeSectionUserId(sectionUserId) { + return typeof sectionUserId === 'string' ? sectionUserId.trim().toLowerCase() : ''; +} + +export function normalizeSectionsForUser(sections, expectedEmbyGuid) { + if (!Array.isArray(sections)) return []; + if (!expectedEmbyGuid) return sections; + + return sections.map((section) => ({ + ...section, + UserId: expectedEmbyGuid + })); +} + +function loadUsersTableUsers(db) { + const cols = db.prepare('PRAGMA table_info(Users)').all().map((c) => c.name); + const nameCol = cols.find((c) => /^username$/i.test(c)) || cols.find((c) => /^name$/i.test(c)); + const guidCol = cols.find((c) => /^guid$/i.test(c)); + const idCol = cols.find((c) => /^id$/i.test(c)) || 'Id'; + + if (!nameCol) { + throw new Error(`Cannot find a name column in Users table. Columns found: ${cols.join(', ')}`); + } + + const selectCols = [idCol, nameCol, guidCol].filter(Boolean).join(', '); + const rows = db.prepare(`SELECT ${selectCols} FROM Users`).all(); + + return rows.map((row) => { + const rawId = row[idCol] ?? row.Id ?? row.id; + const rawGuid = guidCol ? row[guidCol] : null; + let embyGuid = ''; + let guid = ''; + + if (rawGuid) { + if (rawGuid instanceof Uint8Array || rawGuid instanceof Buffer) { + const buf = Buffer.from(rawGuid); + guid = buf.toString('hex').toUpperCase(); + embyGuid = blobToEmbyGuid(rawGuid); + } else if (typeof rawGuid === 'string') { + const clean = rawGuid.replace(/-/g, '').toLowerCase(); + embyGuid = clean; + guid = clean.toUpperCase(); + } + } + + return { + id: rawId, + name: row[nameCol] || `User ${rawId}`, + guid, + embyGuid, + sourceTable: 'Users' + }; + }); +} + +function loadLocalUsers(db) { + const rows = db.prepare('SELECT Id, guid, data FROM LocalUsersv2').all(); + + return rows.map((row) => { + const parsed = parseJsonBlob(row.data); + const guid = row.guid ? Buffer.from(row.guid).toString('hex').toUpperCase() : ''; + const embyGuidFromBlob = blobToEmbyGuid(row.guid); + const embyGuidFromJson = normalizeSectionUserId(parsed?.IdString); + const embyGuid = embyGuidFromJson || embyGuidFromBlob; + + return { + id: row.Id, + name: parsed?.Name || `User ${row.Id}`, + guid, + embyGuid, + sourceTable: 'LocalUsersv2', + profile: parsed || null + }; + }); +} + +export function loadCanonicalUsers(db) { + if (hasTable(db, 'LocalUsersv2')) { + return loadLocalUsers(db); + } + if (hasTable(db, 'Users')) { + return loadUsersTableUsers(db); + } + throw new Error('No supported user table found. Expected LocalUsersv2 or Users.'); +} + +export function loadHomeScreenUsers(db) { + const users = loadCanonicalUsers(db); + const settingsRows = db + .prepare( + `SELECT us.UserId, us.Value + FROM UserSettings us + JOIN UserSettingsKeys usk ON us.UserSettingsKeyId = usk.UserSettingsKeyId + WHERE usk.Name = 'homescreensettings'` + ) + .all(); + + const settingsMap = new Map(settingsRows.map((row) => [String(row.UserId), row.Value])); + const userIds = new Set(users.map((user) => String(user.id))); + + let matchedUsers = 0; + let mismatchedUsers = 0; + let normalizedUsers = 0; + let missingSectionUserIds = 0; + + const hydratedUsers = users.map((user) => { + const rawValue = settingsMap.get(String(user.id)); + let sections = []; + + try { + if (rawValue) sections = JSON.parse(rawValue).Sections || []; + } catch { + sections = []; + } + + const actualUserIds = [...new Set(sections.map((section) => normalizeSectionUserId(section?.UserId)).filter(Boolean))]; + const mismatchedSectionUserIds = user.embyGuid + ? actualUserIds.filter((id) => id !== user.embyGuid) + : actualUserIds; + const missingIdsForUser = sections.filter((section) => !normalizeSectionUserId(section?.UserId)).length; + const normalizedSections = normalizeSectionsForUser(sections, user.embyGuid); + const sectionsWereNormalized = + user.embyGuid && + JSON.stringify(sections) !== JSON.stringify(normalizedSections); + + if (mismatchedSectionUserIds.length === 0) matchedUsers++; + else mismatchedUsers++; + if (sectionsWereNormalized) normalizedUsers++; + missingSectionUserIds += missingIdsForUser; + + return { + id: user.id, + name: user.name, + guid: user.guid, + embyGuid: user.embyGuid, + sections: normalizedSections, + match: { + sourceTable: user.sourceTable, + settingsUserId: user.id, + expectedSectionUserId: user.embyGuid, + actualSectionUserIds: actualUserIds, + mismatchedSectionUserIds, + missingSectionUserIds: missingIdsForUser, + ok: mismatchedSectionUserIds.length === 0 + } + }; + }); + + const orphanedSettingsUserIds = settingsRows + .map((row) => String(row.UserId)) + .filter((userId, index, all) => all.indexOf(userId) === index && !userIds.has(userId)); + + return { + users: hydratedUsers, + validation: { + userSource: users[0]?.sourceTable || null, + userCount: hydratedUsers.length, + settingsCount: settingsRows.length, + matchedUsers, + mismatchedUsers, + normalizedUsers, + missingSectionUserIds, + orphanedSettingsUserIds + } + }; +} + +export function loadUserLookup(db) { + return new Map(loadCanonicalUsers(db).map((user) => [String(user.id), user])); +} diff --git a/src/routes/+page.server.js b/src/routes/+page.server.js new file mode 100644 index 0000000..4817ac5 --- /dev/null +++ b/src/routes/+page.server.js @@ -0,0 +1,24 @@ +import { readFileSync, existsSync } from 'fs'; +import { resolve } from 'path'; + +/** @type {import('./$types').PageServerLoad} */ +export async function load() { + const dataPath = resolve('static/db_export.json'); + const raw = readFileSync(dataPath, 'utf-8'); + const data = JSON.parse(raw); + + const configPath = resolve('config.json'); + let config = { embyUrl: '', apiKey: '', dbPath: '' }; + if (existsSync(configPath)) { + try { + config = JSON.parse(readFileSync(configPath, 'utf-8')); + } catch { /* use defaults */ } + } + + return { + users: data.users, + genres: data.genres, + enums: data.enums, + config + }; +} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..f066726 --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,678 @@ + + +
+
+
+

Emby home screen editor

+ {users.length} users · {users.reduce((n, u) => n + (u.sections?.length || 0), 0)} total sections +
+
+ {#if writeStatus === 'ok'} + {writeMessage} + {:else if writeStatus === 'error'} + Write failed + {/if} + {#if dbValidation && dbValidation.userSource} + + {dbValidation.userSource} linked + + {/if} + {#if changeCount > 0} + + {/if} + + {#if dbConfigured} + + {/if} + +
+
+ +
+ + + + +
+ {#if activeTab === 'edit' && selectedUser} +
+

{selectedUser.name}'s home screen

+ +
+ + {#if sections.length === 0} +
+

No sections configured for this user.

+ +
+ {:else} +
+ {#each sections as section, i (section.Id + '-' + i)} + (expandedIndex = expandedIndex === i ? -1 : i)} + on:move={(e) => moveSection(i, e.detail)} + on:remove={() => removeSection(i)} + on:change={onSectionChange} + /> + {/each} +
+ {/if} + {:else if activeTab === 'sync'} +
+

Section sync

+

+ Use the panel on the left to copy home screen sections from one user to others. + You can replace their entire layout, append sections, or pick specific ones. +

+

After syncing, switch to the Edit tab to review changes per user.

+ +
+ {:else if activeTab === 'settings'} +
+

Settings

+

Configure your Emby server connection and database path in the panel on the left.

+
+
+
Emby API
+
+ Connect to your Emby server to fetch real user names. Without this, users + without a recognisable name pattern will show as "User 11", "User 14" etc. +
+
+
+
Load from DB
+
+ Read the live users.db directly instead of using the static + db_export.json snapshot. Stop Emby first to avoid a locked database. +
+
+
+
Write to DB
+
+ Apply your changes directly to the database — only the + homescreensettings rows for users you modified are touched. + Stop Emby first. The "Generate SQL" button is still available as a fallback. +
+
+
+
+ {:else} +
+

Select a user from the sidebar to edit their home screen.

+
+ {/if} +
+
+
+ +{#if showSqlModal} + (showSqlModal = false)} /> +{/if} + + diff --git a/src/routes/api/config/+server.js b/src/routes/api/config/+server.js new file mode 100644 index 0000000..aa3bc70 --- /dev/null +++ b/src/routes/api/config/+server.js @@ -0,0 +1,29 @@ +import { readFileSync, writeFileSync, existsSync } from 'fs'; +import { resolve } from 'path'; +import { json } from '@sveltejs/kit'; + +const CONFIG_PATH = resolve('config.json'); + +function loadConfig() { + if (!existsSync(CONFIG_PATH)) return { embyUrl: '', apiKey: '', dbPath: '' }; + try { + return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); + } catch { + return { embyUrl: '', apiKey: '', dbPath: '' }; + } +} + +export async function GET() { + return json(loadConfig()); +} + +export async function POST({ request }) { + const body = await request.json(); + const config = { + embyUrl: String(body.embyUrl || '').trim(), + apiKey: String(body.apiKey || '').trim(), + dbPath: String(body.dbPath || '').trim() + }; + writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2)); + return json({ ok: true }); +} diff --git a/src/routes/api/db-read/+server.js b/src/routes/api/db-read/+server.js new file mode 100644 index 0000000..331ec26 --- /dev/null +++ b/src/routes/api/db-read/+server.js @@ -0,0 +1,21 @@ +import { json, error } from '@sveltejs/kit'; +import { existsSync } from 'fs'; +import { DatabaseSync } from 'node:sqlite'; +import { loadHomeScreenUsers } from '../../../lib/server/emby-user-db.js'; + +export async function POST({ request }) { + const { dbPath } = await request.json(); + if (!dbPath) throw error(400, 'No dbPath provided'); + if (!existsSync(dbPath)) throw error(404, `Database file not found: ${dbPath}`); + + let db; + try { + db = new DatabaseSync(dbPath, { readOnly: true }); + const result = loadHomeScreenUsers(db); + db.close(); + return json(result); + } catch (err) { + if (db) try { db.close(); } catch { /* ignore */ } + throw error(500, err.message); + } +} diff --git a/src/routes/api/db-write/+server.js b/src/routes/api/db-write/+server.js new file mode 100644 index 0000000..d974f28 --- /dev/null +++ b/src/routes/api/db-write/+server.js @@ -0,0 +1,72 @@ +import { json, error } from '@sveltejs/kit'; +import { existsSync } from 'fs'; +import { DatabaseSync } from 'node:sqlite'; +import { loadUserLookup, normalizeSectionsForUser } from '../../../lib/server/emby-user-db.js'; + +export async function POST({ request }) { + const { dbPath, changes } = await request.json(); + if (!dbPath) throw error(400, 'No dbPath provided'); + if (!existsSync(dbPath)) throw error(404, `Database file not found: ${dbPath}`); + if (!changes?.length) return json({ ok: true, count: 0 }); + + let db; + try { + db = new DatabaseSync(dbPath); + const userLookup = loadUserLookup(db); + + const keyRow = db + .prepare("SELECT UserSettingsKeyId FROM UserSettingsKeys WHERE Name = 'homescreensettings'") + .get(); + if (!keyRow) throw new Error("'homescreensettings' key not found in UserSettingsKeys table"); + const keyId = keyRow.UserSettingsKeyId; + + const checkStmt = db.prepare( + 'SELECT 1 FROM UserSettings WHERE UserId = ? AND UserSettingsKeyId = ?' + ); + const updateStmt = db.prepare( + 'UPDATE UserSettings SET Value = ? WHERE UserId = ? AND UserSettingsKeyId = ?' + ); + const insertStmt = db.prepare( + 'INSERT INTO UserSettings (UserId, UserSettingsKeyId, Value) VALUES (?, ?, ?)' + ); + + let count = 0; + let normalizedSections = 0; + + // node:sqlite transactions: use db.exec('BEGIN') / db.exec('COMMIT') manually + // or wrap in a function with db.transaction() if supported + db.exec('BEGIN'); + try { + for (const { userId, sections } of changes) { + const user = userLookup.get(String(userId)); + if (!user) { + throw new Error(`UserId ${userId} does not exist in ${dbPath}`); + } + + const nextSections = normalizeSectionsForUser(sections, user.embyGuid); + if (JSON.stringify(nextSections) !== JSON.stringify(sections)) { + normalizedSections += nextSections.length; + } + + const value = JSON.stringify({ Sections: nextSections }); + const exists = checkStmt.get(userId, keyId); + if (exists) { + updateStmt.run(value, userId, keyId); + } else { + insertStmt.run(userId, keyId, value); + } + count++; + } + db.exec('COMMIT'); + } catch (err) { + db.exec('ROLLBACK'); + throw err; + } + + db.close(); + return json({ ok: true, count, normalizedSections }); + } catch (err) { + if (db) try { db.close(); } catch { /* ignore */ } + throw error(500, err.message); + } +} diff --git a/src/routes/api/emby-users/+server.js b/src/routes/api/emby-users/+server.js new file mode 100644 index 0000000..c4ef70f --- /dev/null +++ b/src/routes/api/emby-users/+server.js @@ -0,0 +1,42 @@ +import { readFileSync, existsSync } from 'fs'; +import { resolve } from 'path'; +import { json, error } from '@sveltejs/kit'; + +const CONFIG_PATH = resolve('config.json'); + +function loadConfig() { + if (!existsSync(CONFIG_PATH)) return {}; + try { + return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); + } catch { + return {}; + } +} + +export async function GET() { + const { embyUrl, apiKey } = loadConfig(); + if (!embyUrl || !apiKey) { + throw error(400, 'Emby URL and API key not configured'); + } + + const base = embyUrl.replace(/\/+$/, ''); + let res; + try { + res = await fetch(`${base}/Users?api_key=${encodeURIComponent(apiKey)}`); + } catch (e) { + throw error(502, `Could not reach Emby server: ${e.message}`); + } + + if (!res.ok) { + throw error(res.status, `Emby API error: ${res.status} ${res.statusText}`); + } + + const users = await res.json(); + // Return embyGuid (no dashes, lowercase) -> name mapping + return json( + users.map((u) => ({ + embyGuid: u.Id.replace(/-/g, '').toLowerCase(), + name: u.Name + })) + ); +} diff --git a/static/db_export.json b/static/db_export.json new file mode 100644 index 0000000..594d799 --- /dev/null +++ b/static/db_export.json @@ -0,0 +1 @@ +{"users":[{"id":2,"guid":"F2F854C43962954C928441C40099FD12","embyGuid":"c454f8f262394c95928441c40099fd12","name":"Alessandra","sections":[{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Next Up","CustomName":"Next Up","Id":"resume","SectionType":"resume","ImageType":"Thumb","ItemTypes":[],"Monitor":["videoplayback","markplayed"],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Alessandra's Watchlist","CustomName":"Alessandra's Watchlist","Id":"ba5e2015adf8460fa5e0b48f236b613f","SectionType":"items","ImageType":"Primary","ItemTypes":["Movie","Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsFavorite":true}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Libraries","CustomName":"Libraries","Id":"librarybuttons","SectionType":"userviews","ViewType":"buttons","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"New to Emby","CustomName":"New to Emby","Id":"3b23e19202e74390a2bf9fd05100d66f","SectionType":"latestmoviereleases","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Action","CustomName":"Action","Id":"ab55b7b4849a4d2ca7f3021c8c18dff6","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["2042"],"IsPlayed":false}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Crime & Drama","CustomName":"Crime & Drama","Id":"5038ad0dfc9c45818e1871215e4bc50c","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["4910","36"]}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Comedy","CustomName":"Comedy","Id":"50a13f081ee74ebe8ad50189ae78fafb","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["82"],"IsPlayed":false}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Animation Station","CustomName":"Animation Station","Id":"fa6aab975ba641a0819183547875db62","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["293"]}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Academy Award Winners","CustomName":"Academy Award Winners","Id":"72bb693a517647f98d563afb9ae977ac","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"TagIds":["1336097"]}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Romcoms","CustomName":"Romcoms","Id":"00e87d65ebae45a7bea24975099fe66f","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"SortBy":"Random","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["4835"],"IsPlayed":false}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Apple TV","Id":"0506e503427f4176ab3a6e6b87145232","SectionType":"boxset","ImageType":"Thumb","ItemTypes":[],"SortBy":"default","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Apple TV","Id":"1317648"}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"HBO","Id":"e3f039f530b54b8bba61da7ce2e6094d","SectionType":"boxset","ImageType":"Thumb","ItemTypes":[],"SortBy":"default","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"HBO","Id":"1317563"}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Netflix","Id":"8ec31791f07949f384058d06212e55f2","SectionType":"boxset","ImageType":"Thumb","ItemTypes":[],"SortBy":"default","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Netflix","Id":"1336929"}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Paramount+","Id":"81feb1781ed2475c88bc36c3c944fdb0","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Paramount+","Id":"1320468"}},{"UserId":"c454f8f262394c95928441c40099fd12","Name":"Pixar","Id":"c25e951c2df2436e93d4cee1eae317fa","SectionType":"boxset","ImageType":"Thumb","ItemTypes":[],"SortBy":"ProductionYear,PremiereDate,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Pixar","Id":"1336096"}}]},{"id":5,"guid":"57825AFFC2609A4FB6F63B04513909A4","embyGuid":"ff5a825760c24f9ab6f63b04513909a4","name":"Dave","sections":[{"UserId":"ff5a825760c24f9ab6f63b04513909a4","Name":"Up Next","CustomName":"Up Next","Id":"nextup","SectionType":"resume","ImageType":"Thumb","ItemTypes":[],"Monitor":["videoplayback","markplayed"],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"ff5a825760c24f9ab6f63b04513909a4","Name":"Dave's Watchlist","CustomName":"Dave's Watchlist","Id":"45e9a68249a748e485134dd7d93251fb","SectionType":"items","ImageType":"Thumb","ItemTypes":["Movie","Series"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsFavorite":true,"IsPlayed":false}},{"UserId":"ff5a825760c24f9ab6f63b04513909a4","Name":"New to Emby","CustomName":"New to Emby","Id":"9eb76e920cd94bb7a8823be1ca241f88","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}},{"UserId":"ff5a825760c24f9ab6f63b04513909a4","Name":"Libraries","CustomName":"Libraries","Id":"8dc2deadc532483698a674f85b9d5545","SectionType":"userviews","ViewType":"buttons","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true}]},{"id":11,"guid":"233358283693CD44869605F51ACCF76C","embyGuid":"e1a4868aa0284c30b7b99a174abd276d","sections":[{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"Next Up","CustomName":"Next Up","Id":"resume","SectionType":"resume","ImageType":"Thumb","ItemTypes":[],"Monitor":["videoplayback","markplayed"],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"Watchlist","CustomName":"Watchlist","Id":"b48dbb8627cc41ada43d62a2ce4beab5","SectionType":"items","ImageType":"Thumb","ItemTypes":["Movie","Series"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsFavorite":true,"IsPlayed":false}},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"Libraries","CustomName":"Libraries","Id":"bfc241c4fbbc438c8655589602028c9a","SectionType":"userviews","ViewType":"buttons","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"New to Emby","CustomName":"New to Emby","Id":"3b2d719e1237463da993e04a2c1534b5","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"SortBy":"ProductionYear,PremiereDate,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"Action / Sci Fi","CustomName":"Action / Sci Fi","Id":"5ff93a7cc99d4a99a2ce6b16dcfc8614","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["2042","1212"],"IsPlayed":false}},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"Crime / Drama","CustomName":"Crime / Drama","Id":"477edbd0be9c41c486530ebb0ba3d6fe","SectionType":"items","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["4910","62"],"IsPlayed":false}},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","CustomName":"Recently Released TV","Id":"e72e3422f80541e3bd6dc53897b5d560","SectionType":"latestepisodereleases","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}}],"name":"User 11"},{"id":13,"guid":"FC90BF23E01FCE49898DB7ADDA1ED7BF","embyGuid":"23bf90fc1fe049ce898db7adda1ed7bf","name":"Matt","sections":[{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Up Next","CustomName":"Up Next","Id":"resume","SectionType":"resume","ImageType":"Thumb","ItemTypes":[],"Monitor":["videoplayback","markplayed"],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Matt's Watchlist","CustomName":"Matt's Watchlist","Id":"b0eb077b069e40d88bf5157799bf8471","SectionType":"items","ImageType":"Primary","ItemTypes":["Movie","Series"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsFavorite":true,"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Libraries","CustomName":"Libraries","Id":"3aed0697e1fb4514a4d7bae6faebd5f9","SectionType":"userviews","ViewType":"buttons","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Popular This Week","Id":"fc0f3af0760c46ba9704a479c7c37ec6","SectionType":"boxset","ItemTypes":[],"SortBy":"CommunityRating","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Popular This Week","Id":"1318882"}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"New to Emby","CustomName":"New to Emby","Id":"1341fc9baf814e54b0a51441b9b1bd82","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"SortBy":"Random","SortOrder":"Ascending","Monitor":[],"ExcludedFolders":["39975","462878","95362","320534","3","38276","112374","118103","228590","451346","469353","527247","1143376","1242097","1191239"],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Action Shows","CustomName":"Action Shows","Id":"cef8043b9e904521b07e352bcd9652b2","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":["4309","39975","462878","3","112374","118103","228590","451346","469353","527247","1242097","1191239"],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["2042"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Crime / Drama Shows","CustomName":"Crime / Drama Shows","Id":"d3cbd44d6f9e41d0a2eda10c146540ff","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"ProductionYear,PremiereDate,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["4910","62"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Comedy Shows","CustomName":"Comedy Shows","Id":"2d3c696cafb44421968c610897666e5e","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["82"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Horror Shows","CustomName":"Horror Shows","Id":"721361b724c34ee2842d2badd1c74525","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["5024"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Science Fiction Shows","CustomName":"Science Fiction Shows","Id":"f5102f1b132441598a777af47ceba75a","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["1212"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Recently Released TV","CustomName":"Recently Released TV","Id":"66429b3128404d999d0fc3babf10d396","SectionType":"latestepisodereleases","ImageType":"Primary","CollectionType":"tvshows","ItemTypes":["Episode"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Collections","CustomName":"Collections","Id":"b6321fcec77d48eb9692abb9462e6ddf","SectionType":"collections","ImageType":"Thumb","CollectionType":"boxsets","ItemTypes":["BoxSet"],"SortBy":"Random","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Animation Station","CustomName":"Animation Station","Id":"a23747b738694f82a96093e07eec1acc","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"CriticRating,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":["4309","39975","462878","3","112374","118103","228590","451346","469353","527247","1242097","1191239"],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["293"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Action","CustomName":"Action","Id":"fc8603051fc64da085dd16323305ed02","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"SortBy":"Random","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["2042"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Drama","CustomName":"Drama","Id":"5e65196c99924329a8944ee44d04ac50","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":["320534","4309","462878","3","112374","118103","228590","451346","469353","527247","1242097","1191239"],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["62"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Horror & Mysteries","CustomName":"Horror & Mysteries","Id":"1385e69593ce4df583cdc95ede9daf06","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["5024","2008"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Thrillers","CustomName":"Thrillers","Id":"94fe8e29b25945aba0bc2b340be04cc0","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"SortBy":"ProductionYear,PremiereDate,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["4428"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Romcoms","CustomName":"Romcoms","Id":"db4b2e3c8d0945859f87e7e4301104bb","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"SortBy":"ProductionYear,PremiereDate,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["4835"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"War & Westerns","CustomName":"War & Westerns","Id":"5845672c045e4d96b02d7b90e4e32123","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":["462878","95362","320534","3","38276","112374","118103","228590","451346","469353","527247","1242097","1191239"],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["5709","14124"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Food, Reality & Travel Shows","CustomName":"Food, Reality & Travel Shows","Id":"323675305d214b2ea462aede52afdb95","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["218","396654","19618"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Mini Series","CustomName":"Mini Series","Id":"5a9bdce07efd4b7ab016961363cc3e5a","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["17565"]}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Documentaries","CustomName":"Documentaries","Id":"3ad7bb6eadfa41ae951a8149f5517b64","SectionType":"items","ImageType":"Primary","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["36"],"IsPlayed":false}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Ended Shows","CustomName":"Ended Shows","Id":"c27e338d8650498f8cecb29f6d074260","SectionType":"items","CollectionType":"tvshows","ItemTypes":["Series"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"TagIds":["1317339"]}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"HBO","CustomName":"HBO","Id":"e3da09352fe9482e9bd2c53d1fbc63ad","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"HBO","Id":"1317563"}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Apple TV","CustomName":"Apple TV","Id":"eed1b7e2e5b04f6aa66e6e93515fc90f","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Apple TV","Id":"1317648"}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Hulu","Id":"7539650ab3dc4247949848155adc45ab","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Hulu","Id":"1317805"}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Paramount+","Id":"08253e8074e845ac889215ad1b8a1d71","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Paramount+","Id":"1320468"}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Pixar","Id":"9f36b67e3472457aa5ebe58f036b7517","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Pixar","Id":"1336096"}},{"UserId":"23bf90fc1fe049ce898db7adda1ed7bf","Name":"Academy Award for Best Picture","CustomName":"Academy Award for Best Picture","Id":"1d17267211f047eeb1de76e9c08fd5f3","SectionType":"items","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"TagIds":["1336097"]}}]},{"id":14,"guid":"8A86A4E128A0304CB7B99A174ABD276D","embyGuid":"e1a4868aa0284c30b7b99a174abd276d","sections":[{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"Next Up","CustomName":"Next Up","Id":"resume","SectionType":"resume","ImageType":"Thumb","ItemTypes":[],"Monitor":["videoplayback","markplayed"],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"Watchlist","CustomName":"Watchlist","Id":"b48dbb8627cc41ada43d62a2ce4beab5","SectionType":"items","ImageType":"Thumb","ItemTypes":["Movie","Series"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsFavorite":true,"IsPlayed":false}},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"Libraries","CustomName":"Libraries","Id":"bfc241c4fbbc438c8655589602028c9a","SectionType":"userviews","ViewType":"buttons","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"New to Emby","CustomName":"New to Emby","Id":"3b2d719e1237463da993e04a2c1534b5","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"SortBy":"ProductionYear,PremiereDate,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"Action / Sci Fi","CustomName":"Action / Sci Fi","Id":"5ff93a7cc99d4a99a2ce6b16dcfc8614","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["2042","1212"],"IsPlayed":false}},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","Name":"Crime / Drama","CustomName":"Crime / Drama","Id":"477edbd0be9c41c486530ebb0ba3d6fe","SectionType":"items","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["4910","62"],"IsPlayed":false}},{"UserId":"e1a4868aa0284c30b7b99a174abd276d","CustomName":"Recently Released TV","Id":"e72e3422f80541e3bd6dc53897b5d560","SectionType":"latestepisodereleases","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}}],"name":"User 14"},{"id":15,"guid":"F19BE80982410D40BADE53595113D1BE","embyGuid":"09e89bf14182400dbade53595113d1be","name":"Peter","sections":[{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Up Next","CustomName":"Up Next","Id":"resume","SectionType":"resume","ImageType":"Thumb","ItemTypes":[],"Monitor":["videoplayback","markplayed"],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Peter's Watchlist","CustomName":"Peter's Watchlist","Id":"e9ee3524bf784ab28d2cd72ce7c74cf6","SectionType":"items","ImageType":"Primary","ItemTypes":["Movie","Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsFavorite":true,"IsPlayed":false}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"New to Emby","CustomName":"New to Emby","Id":"1d410535a5c747ee8000c8baae9fcffa","SectionType":"latestmoviereleases","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Libraries","CustomName":"Libraries","Id":"smalllibrarytiles","SectionType":"userviews","ViewType":"buttons","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":-1,"IncludeNextUpInResume":true},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Popular This Week","Id":"77486c8ecef8432b8c233a716daab504","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Popular This Week","Id":"1318882"}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Action Shows","CustomName":"Action","Id":"d5a4052ef627490c928eab7b53ced879","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["2042"],"IsPlayed":false}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Comedy","CustomName":"Comedy","Id":"d7463dd9a4de455a913b19c43cff9875","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["82"],"IsPlayed":false}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Crime & Drama ","CustomName":"Crime & Drama ","Id":"c89faee4f4534663a5a06e5724e95e0c","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["4910","62"]}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Sci-Fi & Fantasty","CustomName":"Sci-Fi & Fantasty","Id":"33b9abee3d354b0f9879713a1d874b60","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":["4309","39975","3","112374","118103","228590","462878","469353","451346","527247","1191239"],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["1212"]}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Academy Award Winners","CustomName":"Academy Award Winners","Id":"d47523545f8f41e8b4a2a9b142703bd3","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"SortBy":"Random","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"TagIds":["1336097"]}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"AMC","CustomName":"AMC","Id":"69cab14baf1845fda30c068a663b9331","SectionType":"boxset","ImageType":"Thumb","ItemTypes":[],"SortBy":"default","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"AMC","Id":"1318265"}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Apple TV","CustomName":"Apple TV","Id":"c44b4243e7f24a9885e813247606cf67","SectionType":"boxset","ImageType":"Thumb","ItemTypes":[],"SortBy":"default","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Apple TV","Id":"1317648"}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"HBO","Id":"e294241fec97466e85861b1c708f1a86","SectionType":"boxset","ImageType":"Thumb","ItemTypes":[],"SortBy":"default","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"HBO","Id":"1317563"}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Hulu","Id":"36bcd965f289409ba401f939244cbdc9","SectionType":"boxset","ImageType":"Thumb","ItemTypes":[],"SortBy":"default","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Hulu","Id":"1317805"}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Paramount+","Id":"9cfa826d2d804ea4a42880f40d70f08c","SectionType":"boxset","ImageType":"Thumb","ItemTypes":[],"SortBy":"default","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Paramount+","Id":"1320468"}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Pixar","Id":"391f9dd4cefc43ada8ca179c80e60c3e","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Pixar","Id":"1336096"}},{"UserId":"09e89bf14182400dbade53595113d1be","Name":"Netflix","Id":"eeda24a573d74bdfb1efa20370ab2391","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Netflix","Id":"1336929"}}]},{"id":16,"guid":"FF4763AF2BAAE94A9380A2F07AF2BDB8","name":"User 16","sections":[]},{"id":22,"guid":"D8DBFF905C6B0C4C8C4E53D4A8765175","embyGuid":"90ffdbd86b5c4c0c8c4e53d4a8765175","sections":[{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"Next Up","CustomName":"Next Up","Id":"resume","SectionType":"resume","ImageType":"Thumb","ItemTypes":[],"Monitor":["videoplayback","markplayed"],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"Watchlist","CustomName":"Watchlist","Id":"88693197611749dc99afb64eaebb903c","SectionType":"items","ImageType":"Primary","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsFavorite":true,"IsPlayed":false}},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"New to Emby","CustomName":"New to Emby","Id":"682326ee0a1548b296421527f480a413","SectionType":"latestmoviereleases","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"Libraries","CustomName":"Libraries","Id":"smalllibrarytiles","SectionType":"userviews","ViewType":"buttons","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":-1,"IncludeNextUpInResume":true},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"Action","CustomName":"Action","Id":"879f57e82fda4f6988fffb3c06f8e7aa","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["2042"]}},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"Comedy Shows","CustomName":"Comedy Shows","Id":"af1ad65e572b45dab50d298abf7fc9aa","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["82"],"IsPlayed":false}},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"Recently Released Episodes","Id":"ad50042e7e4941489dbcf5bcc3664b3a","SectionType":"latestepisodereleases","CollectionType":"tvshows","ItemTypes":["Episode"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"Action / Drama Shows","CustomName":"Action / Drama Shows","Id":"0b2f856461af41be8aaeafd9f81b2a82","SectionType":"items","ImageType":"Thumb","CollectionType":"tvshows","ItemTypes":["Series"],"SortBy":"DateLastContentAdded,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["2042","62"]}},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"Academy Award Winners","CustomName":"Academy Award Winners","Id":"533a324626234845bc1f0577d1504444","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"TagIds":["1336097"]}},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"Romcoms","CustomName":"Romcoms","Id":"c5a76615c5524db39be8f94f68fbe501","SectionType":"items","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["4835"],"IsPlayed":false}},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"AMC","CustomName":"AMC","Id":"2dd96de7f26c4f80a28223e86bfe9d0a","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"AMC","Id":"1318265"}},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"Apple TV","Id":"1bf6587da3054444bb9e8732ea3a8a4a","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"Apple TV","Id":"1317648"}},{"UserId":"90ffdbd86b5c4c0c8c4e53d4a8765175","Name":"HBO","Id":"7a281f4b2266435c8aeaf993b1da3aef","SectionType":"boxset","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"ParentItem":{"Name":"HBO","Id":"1317563"}}],"name":"User 22"},{"id":26,"guid":"8FC40635FBC88B469C17C76A9164F6E5","embyGuid":"3506c48fc8fb468b9c17c76a9164f6e5","sections":[{"UserId":"3506c48fc8fb468b9c17c76a9164f6e5","Id":"smalllibrarytiles","SectionType":"userviews","ViewType":"buttons","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":-1,"IncludeNextUpInResume":true}],"name":"User 26"},{"id":29,"guid":"D471D466476E484FAECAFF9CED3A2378","embyGuid":"66d471d46e474f48aecaff9ced3a2378","name":"Rob","sections":[{"UserId":"66d471d46e474f48aecaff9ced3a2378","Name":"Resume / Up Next","CustomName":"Resume / Up Next","Id":"resume","SectionType":"resume","ImageType":"Thumb","ItemTypes":[],"Monitor":["videoplayback","markplayed"],"ExcludedFolders":["228590"],"CardSizeOffset":0,"IncludeNextUpInResume":false},{"UserId":"66d471d46e474f48aecaff9ced3a2378","Name":"Rob's Watchlist","CustomName":"Rob's Watchlist","Id":"970f788dadd04e009bab95e0f8787170","SectionType":"items","ImageType":"Thumb","ItemTypes":["Movie","Series"],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsFavorite":true,"IsPlayed":false}},{"Name":"Libraries","CustomName":"Libraries","Id":"smalllibrarytiles","SectionType":"userviews","ItemTypes":[],"Monitor":[],"ExcludedFolders":["527247"],"CardSizeOffset":-1,"IncludeNextUpInResume":true,"Query":{}},{"Name":"Latest Media","Id":"latestmediablock","SectionType":"latestmediablock","ItemTypes":[],"Monitor":[],"ExcludedFolders":["79a2726d3c50e769a8af1e4184e4fccf"],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}}]},{"id":30,"guid":"19C5507D70F5D54F9DB63EA3DE1EC249","name":"User 30","sections":[]},{"id":31,"guid":"AC090E549112834C97D535D02FCBF352","embyGuid":"540e09ac12914c8397d535d02fcbf352","sections":[{"UserId":"540e09ac12914c8397d535d02fcbf352","Name":"Up Next","CustomName":"Up Next","Id":"nextup","SectionType":"resume","ImageType":"Thumb","ItemTypes":[],"Monitor":["videoplayback","markplayed"],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"540e09ac12914c8397d535d02fcbf352","Name":"Watch List","CustomName":"Watch List","Id":"a7b4af3f139c476ab76f7bedda5dcb54","SectionType":"items","ImageType":"Thumb","ItemTypes":["Movie","Series"],"SortBy":"DateCreated,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsFavorite":true,"IsPlayed":false}},{"UserId":"540e09ac12914c8397d535d02fcbf352","Name":"My Media","Id":"librarybuttons","SectionType":"userviews","ViewType":"buttons","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"Name":"Latest Media","Id":"latestmediablock","SectionType":"latestmediablock","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}}],"name":"User 31"},{"id":35,"guid":"21B22B9C01F6B144B775163A55BB740C","embyGuid":"9c2bb221f60144b1b775163a55bb740c","sections":[{"UserId":"9c2bb221f60144b1b775163a55bb740c","Name":"Up Next","CustomName":"Up Next","Id":"resume","SectionType":"resume","ImageType":"Thumb","ItemTypes":[],"Monitor":["videoplayback","markplayed"],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"UserId":"9c2bb221f60144b1b775163a55bb740c","Name":"New to Emby","CustomName":"New to Emby","Id":"0d3119c10ddb4c47bee8de014a1a7dcf","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"SortBy":"DateCreated,SortName","SortOrder":"Descending","Monitor":[],"ExcludedFolders":["462878","95362","320534","112374","118103","39975","469353","38276","451346","228590","527247","1143376"],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}},{"UserId":"9c2bb221f60144b1b775163a55bb740c","Name":"Libraries","CustomName":"Libraries","Id":"smalllibrarytiles","SectionType":"userviews","ViewType":"buttons","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":-1,"IncludeNextUpInResume":true},{"Name":"Latest Media","Id":"latestmediablock","SectionType":"latestmediablock","ItemTypes":[],"Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsPlayed":false}}],"name":"User 35"},{"id":37,"guid":"C4A1F9C2DBC05642B7F67FCC63AEC2B2","name":"User 37","sections":[]},{"id":39,"guid":"3EE5C02FD251A642AF04F7F107187D91","name":"User 39","sections":[]},{"id":43,"guid":"1152D7D3E34A9440AB9C7015A80B93AC","embyGuid":"d3d752114ae34094ab9c7015a80b93ac","name":"FamilyTV","sections":[{"UserId":"d3d752114ae34094ab9c7015a80b93ac","Name":"Next Up","CustomName":"Next Up","Id":"resume","SectionType":"resume","ImageType":"Thumb","ItemTypes":[],"Monitor":["videoplayback","markplayed"],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true},{"Name":"FamilyTV's Watchlist","CustomName":"FamilyTV's Watchlist","Id":"fc7e5a39112542cdb0e8a734feb7986b","SectionType":"items","ImageType":"Thumb","CollectionType":"boxsets","ItemTypes":["Movie","Series"],"SortBy":"DateCreated,SortName","SortOrder":"Ascending","Monitor":[],"ExcludedFolders":[],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"IsFavorite":true,"IsPlayed":false}},{"UserId":"d3d752114ae34094ab9c7015a80b93ac","Name":"Libraries","CustomName":"Libraries","Id":"smalllibrarytiles","SectionType":"userviews","ViewType":"buttons","ItemTypes":[],"Monitor":[],"ExcludedFolders":["451346","527247"],"CardSizeOffset":-1,"IncludeNextUpInResume":true},{"UserId":"d3d752114ae34094ab9c7015a80b93ac","Name":"Suggested Watching for FamilyTV","CustomName":"Suggested Watching","Id":"947bdf6809324dca8acaea0be1e78f43","SectionType":"items","ImageType":"Thumb","CollectionType":"movies","ItemTypes":["Movie"],"SortBy":"Random","SortOrder":"Descending","Monitor":[],"ExcludedFolders":["4309","118103","462878","95362","320534","3","38276","112374","228590","451346","469353","527247","1143376","1191239"],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{"GenreIds":["235","82","480","4835"],"IsPlayed":false}},{"Name":"Latest Media","Id":"latestmediablock","SectionType":"latestmediablock","ItemTypes":[],"Monitor":[],"ExcludedFolders":["28b5d5b8bef4bd1f1914503b5c11ac91"],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{}},{"Name":"Recently Released Movies","Id":"latestmoviereleases","SectionType":"latestmoviereleases","CollectionType":"movies","ItemTypes":["Movie"],"Monitor":[],"ExcludedFolders":["28b5d5b8bef4bd1f1914503b5c11ac91"],"CardSizeOffset":0,"IncludeNextUpInResume":true,"Query":{}}]}],"genres":{"2042":"Action","1212":"Sci-Fi","4910":"Crime","62":"Drama","82":"Comedy","293":"Animation","36":"Documentary","5024":"Horror","4835":"Romance","4428":"Thriller","5709":"War","14124":"Western","218":"Food","396654":"Reality","19618":"Travel","17565":"Mini Series","2008":"Mystery","235":"Family","480":"Fantasy"},"enums":{"sectionTypes":["resume","items","userviews","boxset","collections","latestepisodereleases","latestmoviereleases","latestmediablock"],"collectionTypes":["movies","tvshows","boxsets"],"itemTypes":["Movie","Series","Episode","BoxSet"],"sortOptions":["DateLastContentAdded,SortName","ProductionYear,PremiereDate,SortName","CommunityRating","CriticRating,SortName","DateCreated,SortName","Random","SortName"],"imageTypes":["Thumb","Primary"]}} \ No newline at end of file diff --git a/svelte.config.js b/svelte.config.js new file mode 100644 index 0000000..301e785 --- /dev/null +++ b/svelte.config.js @@ -0,0 +1,10 @@ +import adapter from '@sveltejs/adapter-auto'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + kit: { + adapter: adapter() + } +}; + +export default config; diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..bbf8c7d --- /dev/null +++ b/vite.config.js @@ -0,0 +1,6 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [sveltekit()] +});