Merge branch 'master' into scheduled-tasks

This commit is contained in:
Georges-Antoine Assi
2023-10-30 11:19:47 -04:00
17 changed files with 86 additions and 27 deletions

View File

@@ -75,16 +75,14 @@ Inspired by [Jellyfin](https://jellyfin.org/), allows you to manage all your gam
Docker should be installed and set up before running the [image](https://hub.docker.com/r/zurdi15/romm/tags).
1. Generate an API key for [IGDB](https://www.igdb.com/), and set the `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` variables. _This is required to run a library scan._ Instructions on generating the ID and Secret are [here](https://api-docs.igdb.com/#about). Note that IDGB requires a Twitch account with 2FA enabled to generate the ID and Secret.
1. Generate an API key for [IGDB](https://www.igdb.com/), and set the `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` variables. _This is required to run a library scan._ Instructions on generating the ID and Secret are [here](https://api-docs.igdb.com/#about). Note that IGDB requires a Twitch account with 2FA enabled to generate the ID and Secret.
2. Verify that your library folder structure matches one of the options listed in the [following section](#folder-structure).
3. Create a docker-compose file. See the following example [docker-compose.yml](https://github.com/zurdi15/romm/blob/master/examples/docker-compose.example.yml) file for reference. Customize for your setup and include the `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` vareiables where indicated in the environment section of the file.
3. Create a docker-compose file. See the following example [docker-compose.yml](https://github.com/zurdi15/romm/blob/master/examples/docker-compose.example.yml) file for reference. Customize for your setup and include the `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` variables where indicated in the environment section of the file.
4. Launch the container:
```bash
docker-compose up -d
```
If configured correctly, Romm will automatically run an initial scan on your library.
# Configuration
@@ -143,7 +141,7 @@ If you want to enable the user management system, a redis container and some env
- `ROMM_AUTH_ENABLED` and `ENABLE_EXPERIMENTAL_REDIS` must be set as `true`
- `ROMM_AUTH_SECRET_KEY` must be generated with `openssl rand -hex 32`
- `ROMM_AUTH_USERNAME` and `ROMM_AUTH_PASSWORD` can be set as wanted, being both `admin` by default.
- `REDIS_HOST` and `REDIS_PORT` must point to your redis instance
- `REDIS_HOST` and `REDIS_PORT` must point to your redis instance. Aditionally, if your redis is secured you can set `REDIS_PASSWORD`
<h2 id="configuration-file">⚙️ Configuration file</h2>

View File

@@ -43,6 +43,7 @@ ENABLE_EXPERIMENTAL_REDIS: Final = (
)
REDIS_HOST: Final = os.environ.get("REDIS_HOST", "localhost")
REDIS_PORT: Final = os.environ.get("REDIS_PORT", "6379")
REDIS_PASSWORD: Final = os.environ.get("REDIS_PASSWORD")
# IGDB
IGDB_CLIENT_ID: Final = os.environ.get(

View File

@@ -1,6 +1,16 @@
from redis import Redis
from config import REDIS_HOST, REDIS_PORT, ENABLE_EXPERIMENTAL_REDIS
from config import REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, ENABLE_EXPERIMENTAL_REDIS
redis_client = Redis(
host=REDIS_HOST, port=int(REDIS_PORT), password=REDIS_PASSWORD, db=0
)
redis_url = (
f"redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}"
if REDIS_PASSWORD
else f"redis://{REDIS_HOST}:{REDIS_PORT}"
)
class FallbackCache:
def __init__(self) -> None:
@@ -30,7 +40,11 @@ class FallbackCache:
# A seperate client that auto-decodes responses is needed
_cache_client = Redis(
host=REDIS_HOST, port=int(REDIS_PORT), db=0, decode_responses=True
host=REDIS_HOST,
port=int(REDIS_PORT),
password=REDIS_PASSWORD,
db=0,
decode_responses=True,
)
_fallback_cache = FallbackCache()
cache = _cache_client if ENABLE_EXPERIMENTAL_REDIS else _fallback_cache

View File

@@ -15,6 +15,16 @@ services:
redis:
image: redis:alpine
container_name: redis
command:
# - /bin/sh
# - -c
# # - Double dollars, so that the variable is not expanded by Docker Compose
# # - Surround by quotes, so that the shell does not split the password
# # - The ${variable:?message} syntax causes shell to exit with a non-zero
# # code and print a message, when the variable is not set or empty
# - redis-server --requirepass "$${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}"
restart: unless-stopped
ports:
- ${REDIS_PORT}:6379
env_file:
- .env

View File

@@ -24,6 +24,7 @@ ENABLE_EXPERIMENTAL_REDIS=true
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
ENABLE_EXPERIMENTAL_REDIS=true
#REDIS_PASSWORD=admin_redis
# Authentication (optional)
ROMM_AUTH_ENABLED=true

View File

@@ -27,6 +27,7 @@ services:
- ENABLE_EXPERIMENTAL_REDIS=true # default: false
- REDIS_HOST=redis # default: localhost
- REDIS_PORT=6379 # default: 6379
- REDIS_PASSWORD=<redis password> # [Optional] Support for secured redis
volumes:
- "/path/to/library:/romm/library"
- "/path/to/resources:/romm/resources" # [Optional] Path where roms metadata (covers) are stored
@@ -40,9 +41,9 @@ services:
restart: "unless-stopped"
# [Optional] Only required if using MariaDB as the database
mariadb:
romm_db:
image: mariadb:latest
container_name: mariadb
container_name: romm_db
environment:
- MYSQL_ROOT_PASSWORD=<root password>
- MYSQL_DATABASE=romm

View File

@@ -1,6 +1,6 @@
{
"name": "romm",
"version": "2.0.0",
"version": "2.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {

View File

@@ -1,6 +1,6 @@
{
"name": "romm",
"version": "2.0.0",
"version": "2.0.1",
"private": true,
"scripts": {
"dev": "vite --host",

View File

@@ -50,7 +50,7 @@ emitter.on("toggleDrawerRail", () => {
</v-list-item>
</template>
<platform-list-item
v-for="platform in platforms.value"
v-for="platform in platforms.filledPlatforms"
:platform="platform"
:rail="rail"
:key="platform.slug"

View File

@@ -8,6 +8,7 @@ export default defineStore("platforms", {
},
getters: {
totalGames: ({ value }) => value.reduce((count, p) => count + p.n_roms, 0),
filledPlatforms: ({ value }) => value.filter((p) => p.n_roms > 0),
},
actions: {
set(platforms) {

View File

@@ -3,6 +3,7 @@ import { defineStore } from "pinia";
export default defineStore("roms", {
state: () => ({
_platform: "",
_all: [],
_filteredIDs: [],
_searchIDs: [],
@@ -14,6 +15,7 @@ export default defineStore("roms", {
}),
getters: {
platform: (state) => state._platform,
allRoms: (state) => state._all,
filteredRoms: (state) =>
state._all.filter((rom) => state._filteredIDs.includes(rom.id)),
@@ -33,6 +35,9 @@ export default defineStore("roms", {
"id"
);
},
setPlatform(platform) {
this._platform = platform;
},
// All roms
set(roms) {
this._all = roms;

View File

@@ -17,7 +17,7 @@ const platforms = storePlatforms();
<v-card-text>
<v-row>
<v-col
v-for="platform in platforms.value"
v-for="platform in platforms.filledPlatforms"
class="pa-1"
:key="platform.slug"
:cols="views[0]['size-cols']"

View File

@@ -10,7 +10,7 @@ const platforms = storePlatforms();
<v-chip-group>
<v-chip class="text-overline" variant="text" label>
<v-icon class="mr-2">mdi-controller</v-icon
>{{ platforms.value.length }} Platforms
>{{ platforms.filledPlatforms.length }} Platforms
</v-chip>
<v-chip class="text-overline" variant="text" label>
<v-icon class="mr-2">mdi-disc</v-icon>{{ platforms.totalGames }} Games

View File

@@ -68,10 +68,11 @@ async function fetchRoms(platform) {
const allRomsSet = [...allRoms.value, ...response.data.items];
romsStore.set(allRomsSet);
romsStore.setFiltered(allRomsSet);
romsStore.setPlatform(platform);
if (isFiltered) {
searchCursor.value = response.data.next_page;
const serchedRomsSet = [...searchRoms.value, ...response.data.items];
romsStore.setSearch(serchedRomsSet);
romsStore.setFiltered(serchedRomsSet);
@@ -143,7 +144,23 @@ function selectRom({ event, index, selected }) {
}
}
function resetGallery() {
cursor.value = "";
searchCursor.value = "";
romsStore.reset();
scrolledToTop.value = true;
}
onMounted(() => {
const platform = route.params.platform;
// If platform is different, reset store and fetch roms
if (platform != romsStore.platform) {
resetGallery();
fetchRoms(route.params.platform);
}
// If platform is the same but there are no roms, fetch them
if (filteredRoms.value.length == 0) {
fetchRoms(route.params.platform);
}
@@ -154,14 +171,12 @@ onBeforeUnmount(() => {
});
onBeforeRouteLeave((to, from, next) => {
// Only reset selection if platform is the same
// Reset only the selection if platform is the same
if (to.fullPath.includes(from.path)) {
romsStore.resetSelection();
// Otherwise reset store
// Otherwise reset the entire store
} else {
cursor.value = "";
searchCursor.value = "";
romsStore.reset();
resetGallery();
}
next();
@@ -169,11 +184,8 @@ onBeforeRouteLeave((to, from, next) => {
onBeforeRouteUpdate((to, _) => {
// Reset store if switching to another platform
cursor.value = "";
searchCursor.value = "";
romsStore.reset();
resetGallery();
fetchRoms(to.params.platform);
scrolledToTop.value = true;
});
</script>

View File

@@ -11,8 +11,10 @@ const router = useRouter();
const username = ref();
const password = ref();
const visiblePassword = ref(false);
const logging = ref(false);
function login() {
logging.value = true;
api
.post(
"/login",
@@ -36,6 +38,9 @@ function login() {
icon: "mdi-close-circle",
color: "red",
});
})
.finally(() => {
logging.value = false;
});
}
@@ -88,10 +93,20 @@ onBeforeMount(async () => {
<v-col cols="10" md="8">
<v-btn
@click="login()"
:disabled="logging"
append-icon="mdi-chevron-right-circle-outline"
block
>Login</v-btn
>
:loading="logging"
>Login
<template v-slot:loader>
<v-progress-circular
color="romm-accent-1"
:width="2"
:size="20"
indeterminate
/>
</template>
</v-btn>
</v-col>
</v-row>
</v-col>

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "RomM"
version = "2.0.0"
version = "2.0.1"
description = "RomM (Rom Manager) is a web based retro roms manager integrated with IGDB."
authors = ["zurdi <zurdizurdo25@gmail.com>"]
readme = "README.md"

View File

@@ -44,6 +44,7 @@
<Config Name="ENABLE_EXPERIMENTAL_REDIS" Target="ENABLE_EXPERIMENTAL_REDIS" Default="false" Mode="" Description="Enable Redis (experimental)" Type="Variable" Display="advanced" Required="false" Mask="false"/>
<Config Name="REDIS_HOST" Target="REDIS_HOST" Default="127.0.0.1" Mode="" Description="Redis host" Type="Variable" Display="advanced" Required="false" Mask="false"/>
<Config Name="REDIS_PORT" Target="REDIS_PORT" Default="6379" Mode="" Description="Redis port" Type="Variable" Display="advanced" Required="false" Mask="false"/>
<Config Name="REDIS_PASSWORD" Target="REDIS_PASSWORD" Default="" Mode="" Description="Redis password" Type="Variable" Display="advanced" Required="false" Mask="false"/>
<Config Name="ROMM_AUTH_ENABLED" Target="ROMM_AUTH_ENABLED" Default="false" Mode="" Description="Enable authentication" Type="Variable" Display="advanced" Required="false" Mask="false"/>
<Config Name="ROMM_AUTH_USERNAME" Target="ROMM_AUTH_USERNAME" Default="admin" Mode="" Description="Default admin username" Type="Variable" Display="advanced" Required="false" Mask="false"/>
<Config Name="ROMM_AUTH_PASSWORD" Target="ROMM_AUTH_PASSWORD" Default="" Mode="" Description="Default admin password" Type="Variable" Display="advanced" Required="false" Mask="true"/>