7 Commits
Author SHA1 Message Date
Sansan b3ffef9fd9 Use /api/v1 with token authentication
Build and Release / build (push) Successful in 50s
Build and Release / release (push) Successful in 3m18s
All server calls go through a shared axios instance (src/api.js) that gets a token
with API_PASSWORD from the runtime config, renews it before it expires and retries
once on 401. Character images are loaded with the token as blobs. Removes the
unused express and cors dependencies and bumps the version to 1.2.0.

Closes #2
2026-10-06 19:01:13 +02:00
Sansan d061e02d81 Always search with /find, which now includes fuzzy matches
Closes #3
2026-10-06 18:57:02 +02:00
Sansan 2e7be01487 Start sync with POST and show progress when a sync is running
Closes #1
2026-10-06 18:57:02 +02:00
Sansan f972b579fc Release automatically when package.json version changes
Build and Release / build (push) Successful in 23s
Build and Release / release (push) Successful in 6s
Build and release now run in one workflow on push to main: after the
build passes, if no tag exists for the package.json version, the image
is pushed and the tag and Gitea release are created. Running them in
sequence avoids the action-cache race between the separate build and
tag-triggered release workflows.
2026-10-04 21:56:15 +02:00
Sansan 71de395a20 Create Gitea release on tag and bump version to 1.1.2
Build and Test / build (push) Failing after 5s
Release Docker Image / build-and-push (push) Successful in 3m18s
2026-10-04 21:49:11 +02:00
Sansan e6d71fc77c Give modals the dark app theme and close them on outside click
Build and Test / build (push) Successful in 58s
Release Docker Image / build-and-push (push) Successful in 3m49s
- Restyle the shared modal styles to the dark app palette with orange accents
- Remove light-theme overrides in Search, Sync and Settings modals
- Add outside-click close and fade transition to the Sync modal
- Use @click.self for outside-click close in all modals
- Let tall modals scroll instead of being cut off
- Darken the mobile menu to match
2026-10-04 21:35:57 +02:00
Sansan 8b5aa3adf0 Replace game with soundtrack terminology and add fuzzy/exact search
Build and Test / build (push) Successful in 24s
Release Docker Image / build-and-push (push) Successful in 12m29s
- Replace all 'game' references with 'soundtrack' throughout frontend (112 occurrences)
- Add radio buttons for fuzzy/exact search in SearchModal
- Fuzzy search is default and uses /findfuzzy endpoint
- Exact search uses existing /find endpoint
- Fix syntax errors in SyncProgressModal from previous refactoring

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-07-03 23:06:30 +02:00
24 changed files with 472 additions and 372 deletions

No files matched your search

+76 -1
View File
@@ -1,4 +1,10 @@
name: Build and Test
name: Build and Release
# Releases are driven by the version in package.json: bump it (e.g.
# `npm version patch --no-git-tag-version`) and push to main. If no tag
# v<version> exists yet, the release job pushes the Docker image and creates
# the tag and Gitea release. Build and release run in sequence in one
# workflow, so they never race for the runner's action cache.
on:
push:
@@ -31,3 +37,72 @@ jobs:
push: false
cache-from: type=gha
cache-to: type=gha,mode=max
release:
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: https://github.com/actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Check version
id: version
run: |
VERSION="v$(node -p "require('./package.json').version")"
echo "tag=$VERSION" >> "$GITHUB_OUTPUT"
if git rev-parse -q --verify "refs/tags/$VERSION" >/dev/null; then
echo "$VERSION already released, nothing to do"
echo "release=false" >> "$GITHUB_OUTPUT"
else
echo "Releasing $VERSION"
echo "release=true" >> "$GITHUB_OUTPUT"
fi
- name: Set up QEMU
if: steps.version.outputs.release == 'true'
uses: https://github.com/docker/setup-qemu-action@v3
- name: Set up Docker Buildx
if: steps.version.outputs.release == 'true'
uses: https://github.com/docker/setup-buildx-action@v3
with:
config-inline: |
[registry."gitea.sanplex.xyz/sansan"]
http = true
insecure = true
- name: Login to Gitea Container Registry
if: steps.version.outputs.release == 'true'
uses: docker/login-action@v3
with:
registry: gitea.sanplex.xyz
username: sansan
password: ${{ secrets.TOKEN }}
- name: Build and push multi-arch image
if: steps.version.outputs.release == 'true'
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
gitea.sanplex.xyz/sansan/musicfrontend:${{ steps.version.outputs.tag }}
gitea.sanplex.xyz/sansan/musicfrontend:latest
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Create Release
if: steps.version.outputs.release == 'true'
uses: akkuman/gitea-release-action@v1
env:
NODE_OPTIONS: '--experimental-fetch' # if nodejs < 18
with:
tag_name: ${{ steps.version.outputs.tag }}
target_commitish: ${{ github.sha }}
body: |
Docker image: `gitea.sanplex.xyz/sansan/musicfrontend:${{ steps.version.outputs.tag }}`
-32
View File
@@ -1,32 +0,0 @@
name: Publish
on:
push:
branches:
- '*.*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: https://github.com/actions/checkout@v4
- name: Set up Docker Buildx
uses: https://github.com/docker/setup-buildx-action@v3
with:
config-inline: |
[registry."gitea.sanplex.xyz/sansan"]
http = true
insecure = true
- name: Login to Gitea
uses: docker/login-action@v2
with:
registry: gitea.sanplex.xyz
username: ${{ github.repository_owner }}
password: ${{ secrets.TOKEN }}
- name: Build and push Docker image
uses: https://github.com/docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: "gitea.sanplex.xyz/sansan/musicfrontend:${{gitea.ref_name}}, gitea.sanplex.xyz/sansan/musicfrontend:latest"
-43
View File
@@ -1,43 +0,0 @@
name: Release Docker Image
on:
push:
tags:
- 'v*'
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- uses: https://github.com/actions/checkout@v4
- name: Set up QEMU
uses: https://github.com/docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: https://github.com/docker/setup-buildx-action@v3
with:
config-inline: |
[registry."gitea.sanplex.xyz/sansan"]
http = true
insecure = true
- name: Login to Gitea Container Registry
uses: docker/login-action@v3
with:
registry: gitea.sanplex.xyz
username: sansan
password: ${{ secrets.TOKEN }}
- name: Build and push multi-arch image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
gitea.sanplex.xyz/sansan/musicfrontend:${{ gitea.ref_name }}
gitea.sanplex.xyz/sansan/musicfrontend:latest
cache-from: type=gha
cache-to: type=gha,mode=max
+3
View File
@@ -19,3 +19,6 @@ pnpm-debug.log*
*.njsproj
*.sln
*.sw?
# Local runtime config for npm run serve
/public/config.js
+15
View File
@@ -20,5 +20,20 @@ npm run build
npm run lint
```
## Configuration
The Docker image writes `config.js` from `public/config.template.js` when the container starts, using these environment variables:
| Variable | Description |
|---|---|
| `API_HOSTNAME` | URL of the MusicServer, for example `https://music.example.com`. Empty to use the same host as the frontend. |
| `API_PASSWORD` | The MusicServer's `API_PASSWORD`, used to get an API token. Empty if the server has no password. Anyone who can open the frontend can read it. |
For `npm run serve`, create `public/config.js` (ignored by git):
```js
window.__RUNTIME_CONFIG__ = { API_HOSTNAME: 'http://localhost:8080', API_PASSWORD: '' };
```
When the frontend is served by the MusicServer image, the server provides `config.js` with its own `API_PASSWORD`.
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
+62 -21
View File
@@ -1,17 +1,15 @@
{
"name": "music-randomizer-vue",
"version": "0.1.0",
"version": "1.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "music-randomizer-vue",
"version": "0.1.0",
"version": "1.2.0",
"dependencies": {
"axios": "^1.7.2",
"core-js": "^3.37.1",
"cors": "^2.8.5",
"express": "^4.19.2",
"vue": "^3.4.31",
"vue-axios": "^3.5.2",
"vuex": "^4.1.0"
@@ -3185,6 +3183,7 @@
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dev": true,
"license": "MIT",
"dependencies": {
"mime-types": "~2.1.34",
@@ -3436,6 +3435,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"dev": true,
"license": "MIT"
},
"node_modules/array-union": {
@@ -3701,6 +3701,7 @@
"version": "1.20.5",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
"integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
"dev": true,
"license": "MIT",
"dependencies": {
"bytes": "~3.1.2",
@@ -3725,6 +3726,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
@@ -3734,6 +3736,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true,
"license": "MIT"
},
"node_modules/bonjour-service": {
@@ -3848,6 +3851,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -3889,6 +3893,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
@@ -4332,6 +4337,7 @@
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
@@ -4344,6 +4350,7 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -4360,6 +4367,7 @@
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -4369,6 +4377,7 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
"dev": true,
"license": "MIT"
},
"node_modules/copy-webpack-plugin": {
@@ -4447,23 +4456,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/cors": {
"version": "2.8.6",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
"integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/cosmiconfig": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
@@ -4998,6 +4990,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -5007,6 +5000,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8",
@@ -5201,6 +5195,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"dev": true,
"license": "MIT"
},
"node_modules/electron-to-chromium": {
@@ -5231,6 +5226,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -5358,6 +5354,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"dev": true,
"license": "MIT"
},
"node_modules/escape-string-regexp": {
@@ -5767,6 +5764,7 @@
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -5895,6 +5893,7 @@
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
"integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
@@ -5941,6 +5940,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
@@ -5950,6 +5950,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-deep-equal": {
@@ -6096,6 +6097,7 @@
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
"integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
@@ -6114,6 +6116,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
@@ -6123,6 +6126,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true,
"license": "MIT"
},
"node_modules/find-cache-dir": {
@@ -6232,6 +6236,7 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -6255,6 +6260,7 @@
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -6784,6 +6790,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"depd": "~2.0.0",
@@ -6874,6 +6881,7 @@
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
@@ -6969,12 +6977,14 @@
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true,
"license": "ISC"
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.10"
@@ -7781,6 +7791,7 @@
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -7803,6 +7814,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -7839,6 +7851,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -7862,6 +7875,7 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dev": true,
"license": "MIT",
"bin": {
"mime": "cli.js"
@@ -8107,6 +8121,7 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -8264,6 +8279,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -8273,6 +8289,7 @@
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -8323,6 +8340,7 @@
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dev": true,
"license": "MIT",
"dependencies": {
"ee-first": "1.1.1"
@@ -8591,6 +8609,7 @@
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -8648,6 +8667,7 @@
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
"integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
"dev": true,
"license": "MIT"
},
"node_modules/path-type": {
@@ -9515,6 +9535,7 @@
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"dev": true,
"license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
@@ -9565,6 +9586,7 @@
"version": "6.15.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
"integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
@@ -9611,6 +9633,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -9620,6 +9643,7 @@
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
"integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
"dev": true,
"license": "MIT",
"dependencies": {
"bytes": "~3.1.2",
@@ -9990,6 +10014,7 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
@@ -10010,6 +10035,7 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"license": "MIT"
},
"node_modules/sax": {
@@ -10076,6 +10102,7 @@
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
"integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
@@ -10100,6 +10127,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
@@ -10109,6 +10137,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true,
"license": "MIT"
},
"node_modules/serialize-javascript": {
@@ -10202,6 +10231,7 @@
"version": "1.16.3",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
"integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"encodeurl": "~2.0.0",
@@ -10235,6 +10265,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"dev": true,
"license": "ISC"
},
"node_modules/shallow-clone": {
@@ -10290,6 +10321,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -10309,6 +10341,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
"integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -10325,6 +10358,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
@@ -10343,6 +10377,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
@@ -10532,6 +10567,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -10969,6 +11005,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.6"
@@ -11028,6 +11065,7 @@
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
@@ -11102,6 +11140,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -11166,6 +11205,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
@@ -11197,6 +11237,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
+1 -3
View File
@@ -1,6 +1,6 @@
{
"name": "music-randomizer-vue",
"version": "0.1.0",
"version": "1.2.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@@ -10,8 +10,6 @@
"dependencies": {
"axios": "^1.7.2",
"core-js": "^3.37.1",
"cors": "^2.8.5",
"express": "^4.19.2",
"vue": "^3.4.31",
"vue-axios": "^3.5.2",
"vuex": "^4.1.0"
+2 -1
View File
@@ -1,3 +1,4 @@
window.__RUNTIME_CONFIG__ = {
API_HOSTNAME: '${API_HOSTNAME}'
API_HOSTNAME: '${API_HOSTNAME}',
API_PASSWORD: '${API_PASSWORD}'
};
+21 -6
View File
@@ -87,14 +87,16 @@ body {
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: hidden;
overflow-y: auto; /* Scroll tall modals instead of cutting them off */
background-color: rgb(0, 0, 0); /* Fallback color */
background-color: rgba(0, 0, 0, 0.5); /* Black w/ opacity */
background-color: rgba(0, 0, 0, 0.6); /* Black w/ opacity */
}
.modalContainer {
background-color: #eeeeee;
background-color: #333;
color: white;
margin: 10% auto; /* 15% from the top and centered */
border: 1px solid #888;
border: 1px solid #ff9c00;
border-radius: 10px;
width: 50%; /* Could be more or less, depending on screen size */
}
.modalWrapper {
@@ -114,16 +116,29 @@ body {
top: 8px;
cursor: pointer;
opacity: 70%;
font-size: 2rem;
line-height: 1;
color: #ff9c00;
}
/* The close icon is a black svg, recolor it to the app's orange */
img.closeModalImg {
filter: invert(66%) sepia(96%) saturate(2783%) hue-rotate(1deg) brightness(105%) contrast(103%);
}
.closeModalImg:hover {
opacity: 100%;
}
.modal h1 {
color: black;
color: white;
margin-left: auto;
margin-right: auto;
padding-top: 10px;
font-size: 1.9rem;
}
.modal p {
color: black;
color: white;
}
.modal a {
color: #ff9c00;
}
@media only screen and (max-width: 1000px) {
+61
View File
@@ -0,0 +1,61 @@
import axios from "axios";
const config = window.__RUNTIME_CONFIG__ || {};
const hostname = config.API_HOSTNAME || "";
// Request a new token this long before the current one expires
const EXPIRY_MARGIN_MS = 5 * 60 * 1000;
// Client for the MusicServer /api/v1 endpoints, with token authentication.
export const api = axios.create({ baseURL: `${hostname}/api/v1` });
// Client for the server routes outside /api/v1 (the /find search).
export const legacyApi = axios.create({ baseURL: hostname });
let token = null;
let expiresAt = 0;
let pendingToken = null;
function isTokenRequest(request) {
return request.url === "/token";
}
// getToken returns a valid token, requesting a new one when needed.
// Concurrent callers share one request, since token creation is rate limited.
function getToken() {
if (token && Date.now() < expiresAt - EXPIRY_MARGIN_MS) {
return Promise.resolve(token);
}
if (!pendingToken) {
pendingToken = api
.post("/token", { password: config.API_PASSWORD || "", client_type: "web" })
.then((response) => {
token = response.data.token;
expiresAt = new Date(response.data.expires_at).getTime();
return token;
})
.finally(() => {
pendingToken = null;
});
}
return pendingToken;
}
api.interceptors.request.use(async (request) => {
if (!isTokenRequest(request)) {
request.headers.Authorization = `Bearer ${await getToken()}`;
}
return request;
});
// On 401 the token is no longer valid (for example after a server restart):
// get a new one and retry the request once.
api.interceptors.response.use(undefined, (error) => {
const request = error.config;
if (error.response?.status !== 401 || !request || isTokenRequest(request) || request.retried) {
return Promise.reject(error);
}
request.retried = true;
token = null;
return api(request);
});
+3 -3
View File
@@ -3,7 +3,7 @@
<div class="textWrapper">
<transition-group name="displayingInfo" appear>
<p v-if="currentTrackHidden" :key="1">????????</p>
<p v-else :key="2">{{ currentGame }}</p>
<p v-else :key="2">{{ currentSoundtrack }}</p>
<p v-if="currentTrackHidden" :key="3">??????</p>
<p v-else :key="4">{{ currentTrack }}</p>
</transition-group>
@@ -16,7 +16,7 @@
<p>??????</p>
</div>
<div v-else class="textWrapper">
<p>{{ currentGame }}</p>
<p>{{ currentSoundtrack }}</p>
<p>{{ currentTrack }}</p>
</div>
</transition>
@@ -27,7 +27,7 @@
import { mapState } from "vuex";
export default {
computed: {
...mapState(["currentGame", "currentTrack", "currentTrackHidden"]),
...mapState(["currentSoundtrack", "currentTrack", "currentTrackHidden"]),
},
};
</script>
+22 -22
View File
@@ -8,7 +8,7 @@
<input
v-model="searchInputText"
type="text"
@input="searchGame()"
@input="searchSoundtrack()"
ref="inputField"
/>
</div>
@@ -20,8 +20,8 @@
class="inspirationList"
ref="inspirationList"
>
<li v-for="game in showingGamesList" class="inspirationEntry" :key="game">
{{ game }}
<li v-for="soundtrack in showingSoundtracksList" class="inspirationEntry" :key="soundtrack">
{{ soundtrack }}
</li>
</transition-group>
</div>
@@ -32,39 +32,39 @@ import {mapState} from "vuex";
export default {
data() {
return {
allGamesList: [],
showingGamesList: [],
allSoundtracksList: [],
showingSoundtracksList: [],
scrollDown: true,
searchInputText: "",
};
},
computed: {
...mapState(["reloadGamesList"]),
...mapState(["reloadSoundtracksList"]),
},
watch: {
reloadGamesList(newValue) {
reloadSoundtracksList(newValue) {
if (newValue) {
this.reloadGames();
this.$store.dispatch("reloadGamesList", false);
this.reloadSoundtracks();
this.$store.dispatch("reloadSoundtracksList", false);
}
}
},
methods: {
clearSearch() {
this.searchInputText = "";
this.searchGame();
this.searchSoundtrack();
},
searchGame() {
this.showingGamesList = [];
for (const game of this.allGamesList) {
searchSoundtrack() {
this.showingSoundtracksList = [];
for (const soundtrack of this.allSoundtracksList) {
if (this.searchInputText === "" ||
game.toLowerCase().replace(/\s/g, "")
soundtrack.toLowerCase().replace(/\s/g, "")
.includes(this.searchInputText.toLowerCase().replace(/\s/g, ""))) {
this.showingGamesList.push(game);
this.showingSoundtracksList.push(soundtrack);
}
}
if (this.searchInputText.replace(/\s/g, "") !== "") {
this.showingGamesList.sort((n1, n2) => {
this.showingSoundtracksList.sort((n1, n2) => {
if (n1 > n2) {
return 1;
}
@@ -89,16 +89,16 @@ export default {
this.scrollDown = !this.scrollDown;
}
},
reloadGames() {
reloadSoundtracks() {
this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/all`,
url: `/music/all`,
})
.then((response) => {
this.allGamesList = response.data;
this.allSoundtracksList = response.data;
this.searchInputText = "";
this.searchGame();
this.$store.dispatch("updateHowManyGames", this.allGamesList.length);
this.searchSoundtrack();
this.$store.dispatch("updateHowManySoundtracks", this.allSoundtracksList.length);
})
.catch(function(error) {
console.log(error);
@@ -106,7 +106,7 @@ export default {
}
},
mounted() {
this.reloadGames();
this.reloadSoundtracks();
window.setInterval(() => {
this.scrollInspiration();
}, 40);
+28 -31
View File
@@ -1,23 +1,23 @@
<template>
<transition name="modalAni">
<div v-if="show" class="modal" @click="checkIfClickShouldCloseModal">
<div v-if="show" class="modal" @click.self="closeModal">
<div class="modalContainer">
<div class="modalWrapper">
<span class="closeModalImg" @click="closeModal">&times;</span>
<h1>Search Games</h1>
<h1>Search Soundtracks</h1>
<div class="searchContainer">
<input
type="text"
v-model="searchTerm"
@input="debouncedSearch"
placeholder="Search for games..."
placeholder="Search for soundtracks..."
class="searchInput"
/>
<div v-if="searchTerm.length > 0" class="searchResults">
<div v-if="loading" class="loading">Searching...</div>
<div v-else-if="results.length > 0" class="resultsList">
<div v-for="game in results" :key="game" class="resultItem">
{{ game }}
<div v-for="soundtrack in results" :key="soundtrack" class="resultItem">
{{ soundtrack }}
</div>
</div>
<div v-else class="noResults">No results found</div>
@@ -56,11 +56,6 @@ export default {
this.searchTimeout = null;
}
},
checkIfClickShouldCloseModal(event) {
if (event.target.classList[0] === "modal") {
this.closeModal();
}
},
debouncedSearch() {
// Clear existing timeout
if (this.searchTimeout) {
@@ -78,40 +73,40 @@ export default {
}
this.loading = true;
try {
const response = await this.axios({
const response = await this.legacyAxios({
method: "post",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/find`,
url: "/find",
data: new URLSearchParams({ search_term: this.searchTerm }),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
// Parse games from HTML response
this.results = this.parseGamesFromResponse(response.data);
// Parse soundtracks from HTML response
this.results = this.parseSoundtracksFromResponse(response.data);
} catch (error) {
console.error("Error searching games:", error);
console.error("Error searching soundtracks:", error);
this.results = [];
} finally {
this.loading = false;
}
},
parseGamesFromResponse(htmlData) {
// The server returns HTML from FoundGames templ component
// It generates divs containing game names in <p> tags
const games = [];
parseSoundtracksFromResponse(htmlData) {
// The server returns HTML from FoundSoundtracks templ component
// It generates divs containing soundtrack names in <p> tags
const soundtracks = [];
const parser = new DOMParser();
const doc = parser.parseFromString(htmlData, "text/html");
// Find all p tags inside divs (the templ generates <div><p>{game}</p></div>)
// Find all p tags inside divs (the templ generates <div><p>{soundtrack}</p></div>)
const pTags = doc.querySelectorAll("div p");
for (const p of pTags) {
const gameName = p.textContent.trim();
if (gameName) {
games.push(gameName);
const soundtrackName = p.textContent.trim();
if (soundtrackName) {
soundtracks.push(soundtrackName);
}
}
return games;
return soundtracks;
},
},
};
@@ -120,14 +115,16 @@ export default {
<style scoped>
.searchContainer {
width: 100%;
margin-top: 20px;
margin-top: 10px;
}
.searchInput {
width: 100%;
padding: 12px;
font-size: 1.1rem;
border: 1px solid #ccc;
border: 1px solid #777;
background-color: rgb(66, 66, 66);
color: white;
border-radius: 5px;
box-sizing: border-box;
}
@@ -145,7 +142,7 @@ export default {
}
.loading {
color: #666;
color: #bbb;
font-style: italic;
text-align: center;
padding: 20px;
@@ -158,19 +155,19 @@ export default {
.resultItem {
padding: 12px;
margin-bottom: 8px;
background-color: #f8f8f8;
border: 1px solid #ddd;
background-color: rgb(66, 66, 66);
border: 1px solid #555;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.2s;
}
.resultItem:hover {
background-color: #e8e8e8;
background-color: #555;
}
.noResults {
color: #666;
color: #bbb;
text-align: center;
padding: 20px;
font-style: italic;
+90 -110
View File
@@ -1,67 +1,69 @@
<template>
<div class="modal" v-if="isOpen">
<div class="modalContainer syncProgressContainer">
<span class="closeModalImg" @click="closeModal">&times;</span>
<h1>Sync in Progress</h1>
<transition name="modalAni">
<div class="modal" v-if="isOpen" @click.self="closeModal">
<div class="modalContainer syncProgressContainer">
<span class="closeModalImg" @click="closeModal">&times;</span>
<h1>Sync in Progress</h1>
<div v-if="!syncComplete" class="progressSection">
<div class="progressBarContainer">
<div class="progressBar" :style="{ width: progress + '%' }"></div>
<div v-if="!syncComplete" class="progressSection">
<div class="progressBarContainer">
<div class="progressBar" :style="{ width: progress + '%' }"></div>
</div>
<p class="progressText">{{ progress }}% complete</p>
<p class="timeText">Time spent: {{ timeSpent }}</p>
</div>
<p class="progressText">{{ progress }}% complete</p>
<p class="timeText">Time spent: {{ timeSpent }}</p>
</div>
<div v-else class="resultsSection">
<h2>Sync Complete!</h2>
<p>Total time: {{ syncData.total_time }}</p>
<div v-else class="resultsSection">
<h2>Sync Complete!</h2>
<p>Total time: {{ syncData.total_time }}</p>
<div v-if="syncData.games_added && syncData.games_added.length > 0" class="resultItem">
<h3>Games Added: {{ syncData.games_added.length }}</h3>
<ul class="gameList">
<li v-for="game in syncData.games_added" :key="game">{{ game }}</li>
</ul>
<div v-if="syncData.soundtracks_added && syncData.soundtracks_added.length > 0" class="resultItem">
<h3>Soundtracks Added: {{ syncData.soundtracks_added.length }}</h3>
<ul class="soundtrackList">
<li v-for="soundtrack in syncData.soundtracks_added" :key="soundtrack">{{ soundtrack }}</li>
</ul>
</div>
<div v-if="syncData.soundtracks_re_added && syncData.soundtracks_re_added.length > 0" class="resultItem">
<h3>Soundtracks Re-added: {{ syncData.soundtracks_re_added.length }}</h3>
<ul class="soundtrackList">
<li v-for="soundtrack in syncData.soundtracks_re_added" :key="soundtrack">{{ soundtrack }}</li>
</ul>
</div>
<div v-if="syncData.soundtracks_removed && syncData.soundtracks_removed.length > 0" class="resultItem">
<h3>Soundtracks Removed: {{ syncData.soundtracks_removed.length }}</h3>
<ul class="soundtrackList">
<li v-for="soundtrack in syncData.soundtracks_removed" :key="soundtrack">{{ soundtrack }}</li>
</ul>
</div>
<div v-if="Object.keys(syncData.soundtracks_changed_title || {}).length > 0" class="resultItem">
<h3>Soundtracks with Changed Title: {{ Object.keys(syncData.soundtracks_changed_title || {}).length }}</h3>
<ul class="soundtrackList">
<li v-for="(newTitle, oldTitle) in syncData.soundtracks_changed_title" :key="oldTitle">{{ oldTitle }} → {{ newTitle }}</li>
</ul>
</div>
<div v-if="syncData.soundtracks_changed_content && syncData.soundtracks_changed_content.length > 0" class="resultItem">
<h3>Soundtracks with Changed Content: {{ syncData.soundtracks_changed_content.length }}</h3>
<ul class="soundtrackList">
<li v-for="soundtrack in syncData.soundtracks_changed_content" :key="soundtrack">{{ soundtrack }}</li>
</ul>
</div>
<div v-if="syncData.catched_errors && syncData.catched_errors.length > 0" class="resultItem errors">
<h3>Errors: {{ syncData.catched_errors.length }}</h3>
<ul class="soundtrackList">
<li v-for="error in syncData.catched_errors" :key="error">{{ error }}</li>
</ul>
</div>
<p v-if="noChanges" class="noChanges">No changes detected.</p>
</div>
<div v-if="syncData.games_re_added && syncData.games_re_added.length > 0" class="resultItem">
<h3>Games Re-added: {{ syncData.games_re_added.length }}</h3>
<ul class="gameList">
<li v-for="game in syncData.games_re_added" :key="game">{{ game }}</li>
</ul>
</div>
<div v-if="syncData.games_removed && syncData.games_removed.length > 0" class="resultItem">
<h3>Games Removed: {{ syncData.games_removed.length }}</h3>
<ul class="gameList">
<li v-for="game in syncData.games_removed" :key="game">{{ game }}</li>
</ul>
</div>
<div v-if="Object.keys(syncData.games_changed_title || {}).length > 0" class="resultItem">
<h3>Games with Changed Title: {{ Object.keys(syncData.games_changed_title || {}).length }}</h3>
<ul class="gameList">
<li v-for="(newTitle, oldTitle) in syncData.games_changed_title" :key="oldTitle">{{ oldTitle }} → {{ newTitle }}</li>
</ul>
</div>
<div v-if="syncData.games_changed_content && syncData.games_changed_content.length > 0" class="resultItem">
<h3>Games with Changed Content: {{ syncData.games_changed_content.length }}</h3>
<ul class="gameList">
<li v-for="game in syncData.games_changed_content" :key="game">{{ game }}</li>
</ul>
</div>
<div v-if="syncData.catched_errors && syncData.catched_errors.length > 0" class="resultItem errors">
<h3>Errors: {{ syncData.catched_errors.length }}</h3>
<ul class="gameList">
<li v-for="error in syncData.catched_errors" :key="error">{{ error }}</li>
</ul>
</div>
<p v-if="noChanges" class="noChanges">No changes detected.</p>
</div>
</div>
</div>
</transition>
</template>
<script>
@@ -82,11 +84,11 @@ export default {
timeSpent: "00:00:00",
syncComplete: false,
syncData: {
games_added: [],
games_re_added: [],
games_changed_title: {},
games_changed_content: [],
games_removed: [],
soundtracks_added: [],
soundtracks_re_added: [],
soundtracks_changed_title: {},
soundtracks_changed_content: [],
soundtracks_removed: [],
catched_errors: [],
total_time: "",
},
@@ -97,11 +99,11 @@ export default {
computed: {
noChanges() {
return (
this.syncData.games_added.length === 0 &&
this.syncData.games_re_added.length === 0 &&
Object.keys(this.syncData.games_changed_title).length === 0 &&
this.syncData.games_changed_content.length === 0 &&
this.syncData.games_removed.length === 0 &&
this.syncData.soundtracks_added.length === 0 &&
this.syncData.soundtracks_re_added.length === 0 &&
Object.keys(this.syncData.soundtracks_changed_title).length === 0 &&
this.syncData.soundtracks_changed_content.length === 0 &&
this.syncData.soundtracks_removed.length === 0 &&
this.syncData.catched_errors.length === 0
);
},
@@ -128,7 +130,7 @@ export default {
try {
const response = await this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/sync/progress`,
url: "/sync/progress",
});
const data = response.data;
@@ -147,11 +149,11 @@ export default {
// Sync complete - show results
this.syncComplete = true;
this.syncData = {
games_added: data.games_added || [],
games_re_added: data.games_re_added || [],
games_changed_title: data.games_changed_title || {},
games_changed_content: data.games_changed_content || [],
games_removed: data.games_removed || [],
soundtracks_added: data.soundtracks_added || [],
soundtracks_re_added: data.soundtracks_re_added || [],
soundtracks_changed_title: data.soundtracks_changed_title || {},
soundtracks_changed_content: data.soundtracks_changed_content || [],
soundtracks_removed: data.soundtracks_removed || [],
catched_errors: data.catched_errors || [],
total_time: data.total_time || "",
};
@@ -182,11 +184,11 @@ export default {
this.timeSpent = "00:00:00";
this.syncComplete = false;
this.syncData = {
games_added: [],
games_re_added: [],
games_changed_title: {},
games_changed_content: [],
games_removed: [],
soundtracks_added: [],
soundtracks_re_added: [],
soundtracks_changed_title: {},
soundtracks_changed_content: [],
soundtracks_removed: [],
catched_errors: [],
total_time: "",
};
@@ -211,48 +213,26 @@ export default {
<style scoped>
.syncProgressContainer {
background-color: #eeeeee;
margin: 10% auto;
border: 1px solid #888;
position: relative;
width: 70%;
max-width: 800px;
padding: 20px;
border-radius: 10px;
}
.closeModalImg {
position: absolute;
right: 8px;
top: 8px;
cursor: pointer;
opacity: 70%;
font-size: 2rem;
color: #555;
}
.closeModalImg:hover {
opacity: 100%;
box-sizing: border-box;
}
.syncProgressContainer h1 {
color: black;
margin-left: auto;
margin-right: auto;
padding-top: 10px;
font-size: 1.9rem;
text-align: center;
margin-bottom: 20px;
}
.syncProgressContainer h2 {
color: black;
color: white;
text-align: center;
font-size: 1.5rem;
margin-bottom: 15px;
}
.syncProgressContainer p {
color: black;
text-align: center;
}
@@ -266,7 +246,7 @@ export default {
.progressBarContainer {
width: 100%;
height: 30px;
background-color: #ddd;
background-color: rgb(66, 66, 66);
border-radius: 15px;
overflow: hidden;
margin-bottom: 10px;
@@ -290,7 +270,7 @@ export default {
.timeText {
font-size: 1rem;
color: #666;
color: #bbb;
}
.resultsSection {
@@ -301,7 +281,7 @@ export default {
.resultItem {
margin-bottom: 20px;
padding: 10px;
background-color: #f8f8f8;
background-color: rgb(66, 66, 66);
border-radius: 8px;
border-left: 4px solid #4CAF50;
}
@@ -311,25 +291,25 @@ export default {
}
.resultItem h3 {
color: #333;
color: white;
font-size: 1.1rem;
margin-bottom: 8px;
}
.gameList {
.soundtrackList {
list-style-type: none;
padding: 0;
margin: 5px 0 0 0;
}
.gameList li {
.soundtrackList li {
padding: 3px 0;
color: #555;
color: #ddd;
}
.noChanges {
text-align: center;
color: #666;
color: #bbb;
font-style: italic;
padding: 20px;
}
+2 -7
View File
@@ -1,6 +1,6 @@
<template>
<transition name="modalAni">
<div v-if="show" class="modal" @click="checkIfClickShouldCloseModal">
<div v-if="show" class="modal" @click.self="closeModal">
<div class="modalContainer">
<div class="modalWrapper">
<img
@@ -11,7 +11,7 @@
/>
<h1>Music Player Randomizer v0.1</h1>
<p class="descriptionText">
Try your video game music knowledge with this VGM randomizer,
Try your video game soundtrack knowledge with this VGM randomizer,
invite your friends and see who is the best.
</p>
<p class="creditText">
@@ -46,11 +46,6 @@ export default {
openModal() {
this.show = true;
},
checkIfClickShouldCloseModal(event) {
if (event.target.classList[0] === "modal") {
this.closeModal();
}
},
},
};
</script>
+33 -14
View File
@@ -1,6 +1,6 @@
<template>
<transition name="modalAni">
<div v-if="show" class="modal" @click="checkIfClickShouldCloseModal">
<div v-if="show" class="modal" @click.self="closeModal">
<div class="modalContainer">
<div class="modalWrapper">
<img
@@ -38,6 +38,8 @@ export default {
show: false,
playerName: "",
characters: [],
// Object URLs of the character images, by file name
imageUrls: {},
isLoading: false,
};
},
@@ -50,22 +52,18 @@ export default {
this.show = true;
await this.fetchCharacters();
},
checkIfClickShouldCloseModal(event) {
if (event.target.classList[0] === "modal") {
this.closeModal();
}
},
async fetchCharacters() {
this.isLoading = true;
try {
const response = await this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/characters`,
url: "/character/list",
});
this.characters = response.data || [];
await this.fetchImages();
} catch (error) {
console.error("Failed to fetch characters:", error);
// Fallback to hardcoded list if API fails
// Fall back to the characters bundled with the frontend
this.characters = [
"Adol.png",
"Link.png",
@@ -81,24 +79,45 @@ export default {
"Raiden.png",
"PaperMario.png",
];
this.imageUrls = Object.fromEntries(
this.characters.map((character) => [character, `characters/${character}`])
);
} finally {
this.isLoading = false;
}
},
// Images need the token, which <img src> can't send, so they are
// downloaded once and shown through object URLs.
async fetchImages() {
await Promise.all(
this.characters
.filter((character) => !this.imageUrls[character])
.map(async (character) => {
try {
const response = await this.axios({
method: "get",
url: "/character",
params: { name: character },
responseType: "blob",
});
this.imageUrls[character] = URL.createObjectURL(response.data);
} catch (error) {
console.error(`Failed to fetch character ${character}:`, error);
}
})
);
},
getCharacterImageUrl(character) {
return `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/character?name=${encodeURIComponent(character)}`;
return this.imageUrls[character] || "characters/noCharacter.png";
},
getCharacterName(character) {
// Remove file extension
return character.replace(/\.(png|jpg|jpeg|gif)$/i, "");
},
chooseFighter(characterFilename) {
const apiHostname = window.__RUNTIME_CONFIG__.API_HOSTNAME;
const profile = `${apiHostname}/character?name=${encodeURIComponent(characterFilename)}`;
let payload = {
playerName: this.playerName,
profile: profile,
profile: this.getCharacterImageUrl(characterFilename),
};
this.$store.dispatch("changePlayerProfile", payload);
this.closeModal();
@@ -150,7 +169,7 @@ export default {
text-align: center;
padding: 40px;
font-size: 1.2rem;
color: #666;
color: #bbb;
}
@media only screen and (max-width: 1000px) {
+14 -23
View File
@@ -2,7 +2,7 @@
<div class="extraButtonsDiv">
<button @click="resetPlaylist">Reset playlist</button>
<button @click="resetPoints">Reset points</button>
<button @click="handleSyncButtonClick">Sync games</button>
<button @click="handleSyncButtonClick">Sync soundtracks</button>
<button @click="startSoundTest">Sound test</button>
<button @click="showSearchModal">Search</button>
<sync-progress-modal
@@ -46,25 +46,17 @@ export default {
},
async startSync() {
try {
// Start the sync
const response = await this.APIsyncGames();
// Check if sync was actually started or if one is in progress
if (response && (response.status === 423 || (response.data && response.data.includes("in progress")))) {
// Sync is already in progress - show modal with polling
this.syncInProgress = true;
} else {
this.syncInProgress = true;
}
// Show the modal which will poll for progress
this.showSyncModal = true;
await this.APIsyncSoundtracks();
} catch (error) {
console.error("Failed to start sync:", error);
// If error is 423, sync is already in progress
if (error.response && error.response.status === 423) {
this.syncInProgress = true;
this.showSyncModal = true;
// 423 means a sync is already running: show its progress
if (!error.response || error.response.status !== 423) {
console.error("Failed to start sync:", error);
return;
}
}
// Show the modal which will poll for progress
this.syncInProgress = true;
this.showSyncModal = true;
},
handleSyncButtonClick() {
// If we have a completed sync result, just show it
@@ -93,7 +85,7 @@ export default {
this.$store.dispatch("updatePlaylistHistory", this.emptyPlaylist);
this.$store.dispatch("setCurrentlyLoadingTrack", "N/A");
this.$store.dispatch("setCurrentTrackHidden", false);
this.$store.dispatch("reloadGamesList", true);
this.$store.dispatch("reloadSoundtracksList", true);
},
startSoundTest() {
this.$emit("start-sound-test");
@@ -105,7 +97,7 @@ export default {
return new Promise((resolve, reject) => {
this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/reset`,
url: `/music/reset`,
})
.then(() => {
resolve();
@@ -116,17 +108,16 @@ export default {
});
});
},
APIsyncGames() {
APIsyncSoundtracks() {
return new Promise((resolve, reject) => {
this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/sync`,
method: "post",
url: "/sync",
})
.then((response) => {
resolve(response);
})
.catch(function(error) {
console.log(error);
reject(error);
});
});
+2 -1
View File
@@ -109,7 +109,8 @@ export default {
}
.mobileMenuContainer {
background-color: #c4c4c4;
background-color: #333;
color: white;
width: 80vw;
height: 100%;
margin-left: 20%;
+2 -2
View File
@@ -24,8 +24,8 @@
>
??? - ???
</p>
<p v-else-if="track.Game !== ''" :class="{ activeTrack: track.CurrentlyPlaying }">
{{ track.SongNo + 1 }}. {{ track.Game }} -
<p v-else-if="track.Soundtrack !== ''" :class="{ activeTrack: track.CurrentlyPlaying }">
{{ track.SongNo + 1 }}. {{ track.Soundtrack }} -
{{ displayTrack(track.Song) }}
</p>
<span v-if="currentlyLoadingTrack === track.SongNo" class="loadingTrack">
+2 -9
View File
@@ -1,7 +1,7 @@
<template>
<!-- Round Settings, Stop after current, Don't hide title -->
<transition name="modalAni">
<div v-if="show" class="modal" @click="checkIfClickShouldCloseModal">
<div v-if="show" class="modal" @click.self="closeModal">
<div class="modalContainer">
<div class="modalWrapper">
<img
@@ -98,11 +98,6 @@ export default {
openModal() {
this.show = true;
},
checkIfClickShouldCloseModal(event) {
if (event.target.classList[0] === "modal") {
this.closeModal();
}
},
updateOptionHideTitle() {
if (this.checkboxHideTitle) {
this.$store.dispatch("updateHideNextTitle", true);
@@ -165,6 +160,7 @@ export default {
margin-top: 20px;
}
.checkboxDiv input {
accent-color: #ff9c00;
transform: scale(1.3);
}
.checkboxDiv label {
@@ -181,9 +177,6 @@ export default {
display: flex;
width: 50px;
height: 33px;
background: rgb(16, 99, 194);
color: rgb(235, 235, 235);
border-color: rgb(161, 161, 161);
justify-content: center;
align-items: center;
line-height: 0;
+3 -8
View File
@@ -1,6 +1,6 @@
<template>
<transition name="modalAni">
<div v-if="show" class="modal" @click="checkIfClickShouldCloseModal">
<div v-if="show" class="modal" @click.self="closeModal">
<div class="modalContainer">
<div class="modalWrapper">
<img
@@ -10,7 +10,7 @@
@click="closeModal"
/>
<h1>Statistics</h1>
<p>Total amount of games in the playlist: {{ howManyGames }}</p>
<p>Total amount of soundtracks in the playlist: {{ howManySoundtracks }}</p>
</div>
</div>
</div>
@@ -26,7 +26,7 @@ export default {
};
},
computed: {
...mapState(["howManyGames"]),
...mapState(["howManySoundtracks"]),
},
methods: {
closeModal() {
@@ -35,11 +35,6 @@ export default {
openModal() {
this.show = true;
},
checkIfClickShouldCloseModal(event) {
if (event.target.classList[0] === "modal") {
this.closeModal();
}
},
},
};
</script>
+1 -6
View File
@@ -1,6 +1,6 @@
<template>
<transition name="modalAni">
<div v-if="show" class="modal" @click="checkIfClickShouldCloseModal">
<div v-if="show" class="modal" @click.self="closeModal">
<div class="modalContainer">
<div class="modalWrapper">
<img
@@ -38,11 +38,6 @@ export default {
this.playerName = playerName;
this.show = true;
},
checkIfClickShouldCloseModal(event) {
if (event.target.classList[0] === "modal") {
this.closeModal();
}
},
},
};
</script>
+11 -11
View File
@@ -167,7 +167,7 @@ export default {
return new Promise((resolve, reject) => {
this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/rand`,
url: `/music/rand`,
responseType: "blob",
onDownloadProgress: (progressEvent) => {
let percentCompleted = Math.round(
@@ -197,7 +197,7 @@ export default {
return new Promise((resolve, reject) => {
this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/rand/low`,
url: `/music/rand/low`,
responseType: "blob",
onDownloadProgress: (progressEvent) => {
let percentCompleted = Math.round(
@@ -227,7 +227,7 @@ export default {
return new Promise((resolve, reject) => {
this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/addPlayed`,
url: `/music/addPlayed`,
})
.then(() => {
resolve(false);
@@ -242,7 +242,7 @@ export default {
return new Promise((resolve, reject) => {
this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/addQue`,
url: `/music/addQue`,
})
.then(() => {
resolve(false);
@@ -257,16 +257,16 @@ export default {
return new Promise((resolve, reject) => {
this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/info`,
url: `/music/info`,
})
.then((response) => {
let gameInfoObject = {
game: response.data.Game,
let soundtrackInfoObject = {
soundtrack: response.data.Soundtrack,
track: response.data.Song.replace(".mp3", ""),
songNo: response.data.SongNo,
};
this.$store.dispatch("setCurrentGame", gameInfoObject.game);
this.$store.dispatch("setCurrentTrack", gameInfoObject.track);
this.$store.dispatch("setCurrentSoundtrack", soundtrackInfoObject.soundtrack);
this.$store.dispatch("setCurrentTrack", soundtrackInfoObject.track);
resolve(false);
})
.catch(function(error) {
@@ -279,7 +279,7 @@ export default {
return new Promise((resolve, reject) => {
this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/list`,
url: `/music/list`,
})
.then((response) => {
let tracklistArray = response.data;
@@ -296,7 +296,7 @@ export default {
return new Promise((resolve, reject) => {
this.axios({
method: "get",
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music?song=${trackNumber}`,
url: `/music?song=${trackNumber}`,
responseType: "blob",
})
.then((response) => {
+18 -18
View File
@@ -1,13 +1,13 @@
import { createApp } from "vue";
import { createStore } from "vuex";
import App from "./App.vue";
import axios from "axios";
import VueAxios from "vue-axios";
import { api, legacyApi } from "./api";
const store = createStore({
state() {
return {
currentGame: "",
currentSoundtrack: "",
currentTrack: ``,
currentTrackHidden: false,
currentlyLoadingTrack: "",
@@ -15,18 +15,18 @@ const store = createStore({
someoneHasWon: false,
winningScore: 20,
roundStarted: false,
reloadGamesList: false,
reloadSoundtracksList: false,
listOfPlayers: [],
localPlaylist: [],
playlistHistory: [
{
SongNo: "",
Game: "",
Soundtrack: "",
Song: "",
},
],
/* Stats */
howManyGames: 0,
howManySoundtracks: 0,
/* Options */
stopAfterCurrent: true,
hideNextTrack: true,
@@ -35,8 +35,8 @@ const store = createStore({
};
},
mutations: {
setCurrentGame(state, payload) {
state.currentGame = payload;
setCurrentSoundtrack(state, payload) {
state.currentSoundtrack = payload;
},
setCurrentTrack(state, payload) {
state.currentTrack = payload;
@@ -89,11 +89,11 @@ const store = createStore({
updatePlaylistHistory(state, payload) {
state.playlistHistory = payload;
},
updateHowManyGames(state, payload) {
state.howManyGames = payload;
updateHowManySoundtracks(state, payload) {
state.howManySoundtracks = payload;
},
reloadGamesList(state, payload) {
state.reloadGamesList = payload;
reloadSoundtracksList(state, payload) {
state.reloadSoundtracksList = payload;
},
updateStopAfterCurrent(state, payload) {
state.stopAfterCurrent = payload;
@@ -109,8 +109,8 @@ const store = createStore({
},
},
actions: {
setCurrentGame(context, payload) {
context.commit("setCurrentGame", payload);
setCurrentSoundtrack(context, payload) {
context.commit("setCurrentSoundtrack", payload);
},
setCurrentTrack(context, payload) {
context.commit("setCurrentTrack", payload);
@@ -225,11 +225,11 @@ const store = createStore({
updatePlaylistHistory(context, payload) {
context.commit("updatePlaylistHistory", payload);
},
updateHowManyGames(context, payload) {
context.commit("updateHowManyGames", payload);
updateHowManySoundtracks(context, payload) {
context.commit("updateHowManySoundtracks", payload);
},
reloadGamesList(context, payload) {
context.commit("reloadGamesList", payload);
reloadSoundtracksList(context, payload) {
context.commit("reloadSoundtracksList", payload);
},
updateStopAfterCurrent(context, payload) {
context.commit("updateStopAfterCurrent", payload);
@@ -256,6 +256,6 @@ const store = createStore({
const app = createApp(App);
app.use(store);
app.use(VueAxios, axios);
app.use(VueAxios, { axios: api, legacyAxios: legacyApi });
app.mount("#app");