Merge pull request #796 from rommapp/792-feature-remove-cover-art

Improved custom art management
This commit is contained in:
Zurdi
2024-04-08 08:49:47 +02:00
committed by GitHub
14 changed files with 330 additions and 158 deletions

View File

@@ -250,6 +250,7 @@ async def update_rom(
request: Request,
id: int,
rename_as_igdb: bool = False,
remove_cover: bool = False,
artwork: Optional[UploadFile] = File(None),
) -> RomSchema:
"""Update rom endpoint
@@ -317,22 +318,34 @@ async def update_rom(
cleaned_data["file_name_no_ext"] = fs_rom_handler.get_file_name_with_no_extension(
fs_safe_file_name
)
cleaned_data.update(
fs_resource_handler.get_rom_cover(
overwrite=True,
platform_fs_slug=platform_fs_slug,
rom_name=cleaned_data["name"],
url_cover=cleaned_data.get("url_cover", ""),
)
)
cleaned_data.update(
fs_resource_handler.get_rom_screenshots(
platform_fs_slug=platform_fs_slug,
rom_name=cleaned_data["name"],
url_screenshots=cleaned_data.get("url_screenshots", []),
),
)
if remove_cover:
cleaned_data.update(
fs_resource_handler.remove_cover(
rom_name=cleaned_data["name"], platform_fs_slug=platform_fs_slug
)
)
else:
cleaned_data.update(
fs_resource_handler.get_rom_cover(
overwrite=True,
platform_fs_slug=platform_fs_slug,
rom_name=cleaned_data["name"],
url_cover=cleaned_data.get("url_cover", ""),
)
)
if (
cleaned_data["igdb_id"] != db_rom.igdb_id
or cleaned_data["moby_id"] != db_rom.moby_id
):
cleaned_data.update(
fs_resource_handler.get_rom_screenshots(
platform_fs_slug=platform_fs_slug,
rom_name=cleaned_data["name"],
url_screenshots=cleaned_data.get("url_screenshots", []),
),
)
if artwork is not None:
file_ext = artwork.filename.split(".")[-1]

View File

@@ -23,7 +23,8 @@ class FSResourceHandler(FSHandler):
def __init__(self) -> None:
pass
def _cover_exists(self, fs_slug: str, rom_name: str, size: CoverSize):
@staticmethod
def _cover_exists(fs_slug: str, rom_name: str, size: CoverSize):
"""Check if rom cover exists in filesystem
Args:
@@ -39,7 +40,8 @@ class FSResourceHandler(FSHandler):
)
)
def resize_cover(self, cover_path: str, size: CoverSize = CoverSize.BIG) -> None:
@staticmethod
def resize_cover(cover_path: str, size: CoverSize = CoverSize.BIG) -> None:
"""Resizes the cover image to the standard size
Args:
@@ -48,7 +50,6 @@ class FSResourceHandler(FSHandler):
"""
cover = Image.open(cover_path)
if size == CoverSize.BIG and cover.size[1] > DEFAULT_HEIGHT_COVER_L:
big_dimensions = (DEFAULT_WIDTH_COVER_L, DEFAULT_HEIGHT_COVER_L)
background = Image.new("RGBA", big_dimensions, (0, 0, 0, 0))
cover.thumbnail(big_dimensions)
@@ -86,7 +87,7 @@ class FSResourceHandler(FSHandler):
"""
cover_file = f"{size.value}.png"
cover_path = f"{RESOURCES_BASE_PATH}/{fs_slug}/{rom_name}/cover"
try:
res = requests.get(
url_cover.replace("t_thumb", f"t_cover_{size.value}"),
@@ -99,14 +100,15 @@ class FSResourceHandler(FSHandler):
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Can't connect to IGDB, check your internet connection.",
)
if res.status_code == 200:
Path(cover_path).mkdir(parents=True, exist_ok=True)
with open(f"{cover_path}/{cover_file}", "wb") as f:
shutil.copyfileobj(res.raw, f)
self.resize_cover(f"{cover_path}/{cover_file}", size)
def _get_cover_path(self, fs_slug: str, rom_name: str, size: CoverSize):
@staticmethod
def _get_cover_path(fs_slug: str, rom_name: str, size: CoverSize):
"""Returns rom cover filesystem path adapted to frontend folder structure
Args:
@@ -147,18 +149,35 @@ class FSResourceHandler(FSHandler):
"path_cover_l": path_cover_l,
}
def build_artwork_path(self, rom_name: str, fs_slug: str, file_ext: str):
@staticmethod
def remove_cover(
rom_name: str,
platform_fs_slug: str,
):
try:
shutil.rmtree(
os.path.join(RESOURCES_BASE_PATH, platform_fs_slug, rom_name, "cover")
)
except FileNotFoundError:
log.warning(f"Couldn't remove {rom_name} cover")
return {"path_cover_s": "", "path_cover_l": ""}
@staticmethod
def build_artwork_path(rom_name: str, platform_fs_slug: str, file_ext: str):
q_rom_name = quote(rom_name)
path_cover_l = f"{fs_slug}/{q_rom_name}/cover/{CoverSize.BIG.value}.{file_ext}"
path_cover_s = (
f"{fs_slug}/{q_rom_name}/cover/{CoverSize.SMALL.value}.{file_ext}"
path_cover_l = (
f"{platform_fs_slug}/{q_rom_name}/cover/{CoverSize.BIG.value}.{file_ext}"
)
artwork_path = f"{RESOURCES_BASE_PATH}/{fs_slug}/{rom_name}/cover"
path_cover_s = (
f"{platform_fs_slug}/{q_rom_name}/cover/{CoverSize.SMALL.value}.{file_ext}"
)
artwork_path = f"{RESOURCES_BASE_PATH}/{platform_fs_slug}/{rom_name}/cover"
Path(artwork_path).mkdir(parents=True, exist_ok=True)
return path_cover_l, path_cover_s, artwork_path
def _store_screenshot(self, fs_slug: str, rom_name: str, url: str, idx: int):
@staticmethod
def _store_screenshot(fs_slug: str, rom_name: str, url: str, idx: int):
"""Store roms resources in filesystem
Args:
@@ -168,7 +187,7 @@ class FSResourceHandler(FSHandler):
"""
screenshot_file = f"{idx}.jpg"
screenshot_path = f"{RESOURCES_BASE_PATH}/{fs_slug}/{rom_name}/screenshots"
try:
res = requests.get(url, stream=True, timeout=120)
except requests.exceptions.ConnectionError:
@@ -177,7 +196,7 @@ class FSResourceHandler(FSHandler):
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Can't connect to IGDB, check your internet connection.",
)
if res.status_code == 200:
Path(screenshot_path).mkdir(parents=True, exist_ok=True)
with open(f"{screenshot_path}/{screenshot_file}", "wb") as f:
@@ -188,7 +207,8 @@ class FSResourceHandler(FSHandler):
f"Failure writing screenshot {url} to file (ProtocolError)"
)
def _get_screenshot_path(self, fs_slug: str, rom_name: str, idx: str):
@staticmethod
def _get_screenshot_path(fs_slug: str, rom_name: str, idx: str):
"""Returns rom cover filesystem path adapted to frontend folder structure
Args:

View File

@@ -5,13 +5,13 @@ const theme = useTheme();
const props = defineProps<{ rom: RomSchema }>();
const imgSrc =
!props.rom.igdb_id && !props.rom.has_cover
!props.rom.igdb_id && !props.rom.moby_id && !props.rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_unmatched.png`
: !props.rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_missing_cover.png`
: `/assets/romm/resources/${props.rom.path_cover_s}`;
const imgSrcLazy =
!props.rom.igdb_id && !props.rom.has_cover
!props.rom.igdb_id && !props.rom.moby_id && !props.rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_unmatched.png`
: !props.rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_missing_cover.png`

View File

@@ -1,36 +1,22 @@
<script setup lang="ts">
import storeDownload from "@/stores/download";
import type { Rom } from "@/stores/roms";
import { useTheme } from "vuetify";
const theme = useTheme();
defineProps<{ romId: number; src: string; lazySrc: string }>();
const downloadStore = storeDownload();
defineProps<{ rom: Rom }>();
</script>
<template>
<v-card
elevation="2"
:loading="downloadStore.value.includes(rom.id) ? 'romm-accent-1' : false"
:loading="downloadStore.value.includes(romId) ? 'romm-accent-1' : false"
>
<v-img
:value="rom.id"
:key="rom.id"
:src="
!rom.igdb_id && !rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_unmatched.png`
: !rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_missing_cover.png`
: `/assets/romm/resources/${rom.path_cover_l}`
"
:lazy-src="
!rom.igdb_id && !rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_unmatched.png`
: !rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_missing_cover.png`
: `/assets/romm/resources/${rom.path_cover_s}`
"
:value="romId"
:key="romId"
:src="src"
:lazy-src="lazySrc"
:aspect-ratio="3 / 4"
>
<slot name="editable"></slot>
<template v-slot:placeholder>
<div class="d-flex align-center justify-center fill-height">
<v-progress-circular

View File

@@ -7,7 +7,7 @@ defineProps<{ rom: Rom }>();
<template>
<div rounded="0" class="table">
<v-row
v-if="rom.igdb_id"
v-if="rom.igdb_id || rom.moby_id"
class="align-center justify-center pa-2"
no-gutters
>

View File

@@ -1,21 +1,24 @@
<script setup lang="ts">
import { ref, inject } from "vue";
import { useDisplay } from "vuetify";
import type { Emitter } from "mitt";
import type { Events } from "@/types/emitter";
import Cover from "@/components/Details/Cover.vue";
import romApi, { type UpdateRom } from "@/services/api/rom";
import storeRoms from "@/stores/roms";
import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject, ref } from "vue";
import { useDisplay, useTheme } from "vuetify";
const { xs, mdAndDown, lgAndUp } = useDisplay();
// Props
const theme = useTheme();
const { xs, mdAndDown, smAndDown, md, lgAndUp } = useDisplay();
const show = ref(false);
const rom = ref<UpdateRom>();
const romsStore = storeRoms();
const imagePreviewUrl = ref<string | undefined>("");
const removeCover = ref(false);
const fileNameInputRules = {
required: (value: string) => !!value || "Required",
newFileName: (value: string) => !value.includes("/") || "Invalid characters",
};
const emitter = inject<Emitter<Events>>("emitter");
emitter?.on("showEditRomDialog", (romToEdit) => {
show.value = true;
@@ -23,6 +26,27 @@ emitter?.on("showEditRomDialog", (romToEdit) => {
});
// Functions
function triggerFileInput() {
const fileInput = document.getElementById("file-input");
fileInput?.click();
}
function previewImage(event: { target: { files: any[] } }) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = () => {
imagePreviewUrl.value = reader.result?.toString();
};
if (file) {
reader.readAsDataURL(file);
}
}
async function removeArtwork() {
imagePreviewUrl.value = `/assets/default/cover/big_${theme.global.name.value}_missing_cover.png`;
removeCover.value = true;
}
async function updateRom() {
if (!rom.value) return;
@@ -44,8 +68,9 @@ async function updateRom() {
show.value = false;
emitter?.emit("showLoadingDialog", { loading: true, scrim: true });
await romApi
.updateRom({ rom: rom.value })
.updateRom({ rom: rom.value, removeCover: removeCover.value })
.then(({ data }) => {
emitter?.emit("snackbarShow", {
msg: "Rom updated successfully!",
@@ -70,6 +95,7 @@ async function updateRom() {
function closeDialog() {
show.value = false;
imagePreviewUrl.value = "";
}
</script>
@@ -113,51 +139,94 @@ function closeDialog() {
<v-divider class="border-opacity-25" :thickness="1" />
<v-card-text>
<v-row class="pa-2" no-gutters>
<v-text-field
@keyup.enter="updateRom()"
v-model="rom.name"
label="Name"
variant="outlined"
required
hide-details
/>
</v-row>
<v-row class="pa-2" no-gutters>
<v-text-field
@keyup.enter="updateRom()"
v-model="rom.file_name"
:rules="[
fileNameInputRules.newFileName,
fileNameInputRules.required,
]"
label="File name"
variant="outlined"
required
hide-details
/>
</v-row>
<v-row class="pa-2" no-gutters>
<v-textarea
@keyup.enter="updateRom()"
v-model="rom.summary"
label="Summary"
variant="outlined"
required
hide-details
/>
</v-row>
<v-row class="pa-2" no-gutters>
<v-file-input
@keyup.enter="updateRom()"
v-model="rom.artwork"
label="Custom artwork"
accept="image/*"
prepend-inner-icon="mdi-image"
prepend-icon=""
variant="outlined"
hide-details
/>
<v-row class="align-center" no-gutters>
<v-col cols="12" md="8" lg="9">
<v-text-field
class="py-2"
:class="{ 'pr-4': lgAndUp }"
@keyup.enter="updateRom()"
v-model="rom.name"
label="Name"
variant="outlined"
required
hide-details
/>
<v-text-field
class="py-2"
:class="{ 'pr-4': lgAndUp }"
@keyup.enter="updateRom()"
v-model="rom.file_name"
:rules="[
fileNameInputRules.newFileName,
fileNameInputRules.required,
]"
label="File name"
variant="outlined"
required
hide-details
/>
<v-textarea
class="py-2"
:class="{ 'pr-4': lgAndUp }"
@keyup.enter="updateRom()"
v-model="rom.summary"
label="Summary"
variant="outlined"
required
hide-details
/>
</v-col>
<v-col cols="12" md="4" lg="3">
<cover
:class="{ 'mx-16': smAndDown, 'ml-2': md, 'my-4': smAndDown }"
:romId="rom.id"
:src="
imagePreviewUrl
? imagePreviewUrl
: !rom.igdb_id && !rom.moby_id && !rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_unmatched.png`
: !rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_missing_cover.png`
: `/assets/romm/resources/${rom.path_cover_l}`
"
:lazy-src="
imagePreviewUrl
? imagePreviewUrl
: !rom.igdb_id && !rom.moby_id && !rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_unmatched.png`
: !rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_missing_cover.png`
: `/assets/romm/resources/${rom.path_cover_s}`
"
>
<template v-slot:editable>
<v-chip-group class="position-absolute edit-cover pa-0">
<v-chip
class="translucent"
size="small"
@click="triggerFileInput"
label
><v-icon>mdi-pencil</v-icon>
<v-file-input
id="file-input"
v-model="rom.artwork"
accept="image/*"
hide-details
class="file-input"
@change="previewImage"
/>
</v-chip>
<v-chip
class="translucent"
size="small"
@click="removeArtwork"
label
><v-icon class="text-red">mdi-delete</v-icon></v-chip
>
</v-chip-group>
</template>
</cover>
</v-col>
</v-row>
<v-row class="justify-center pa-2" no-gutters>
<v-btn @click="closeDialog" class="bg-terciary">Cancel</v-btn>
@@ -174,12 +243,21 @@ function closeDialog() {
.edit-content {
width: 900px;
}
.edit-content-tablet {
width: 570px;
width: 620px;
}
.edit-content-mobile {
width: 85vw;
}
.edit-cover {
bottom: -0.1rem;
right: -0.3rem;
}
.file-input {
display: none;
}
.translucent {
background: rgba(0, 0, 0, 0.35);
backdrop-filter: blur(10px);
}
</style>

View File

@@ -219,14 +219,14 @@ onBeforeUnmount(() => {
<v-img
v-bind="props"
:src="
!rom.igdb_id && !rom.has_cover
!rom.igdb_id && !rom.moby_id && !rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_unmatched.png`
: !rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_missing_cover.png`
: `/assets/romm/resources/${rom.path_cover_l}`
"
:lazy-src="
!rom.igdb_id && !rom.has_cover
!rom.igdb_id && !rom.moby_id && !rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_unmatched.png`
: !rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_missing_cover.png`

View File

@@ -1,16 +1,16 @@
<script setup lang="ts">
import { ref, inject } from "vue";
import type { Emitter } from "mitt";
import type { Events, UserItem } from "@/types/emitter";
import userApi from "@/services/api/user";
import { defaultAvatarPath } from "@/utils";
import storeUsers from "@/stores/users";
import type { Events, UserItem } from "@/types/emitter";
import { defaultAvatarPath } from "@/utils";
import type { Emitter } from "mitt";
import { inject, ref } from "vue";
// Props
const user = ref<UserItem | null>(null);
const show = ref(false);
const usersStore = storeUsers();
const imagePreviewUrl = ref<string | undefined>("");
const emitter = inject<Emitter<Events>>("emitter");
emitter?.on("showEditUserDialog", (userToEdit) => {
user.value = { ...userToEdit, avatar: undefined };
@@ -18,6 +18,22 @@ emitter?.on("showEditUserDialog", (userToEdit) => {
});
// Functions
function triggerFileInput() {
const fileInput = document.getElementById("file-input");
fileInput?.click();
}
function previewImage(event: { target: { files: any[] } }) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = () => {
imagePreviewUrl.value = reader.result?.toString();
};
if (file) {
reader.readAsDataURL(file);
}
}
function editUser() {
if (!user.value) return;
@@ -49,10 +65,21 @@ function editUser() {
function closeDialog() {
show.value = false;
imagePreviewUrl.value = "";
}
</script>
<template>
<v-dialog v-if="user" v-model="show" max-width="700px" :scrim="false">
<v-dialog
v-if="user"
v-model="show"
max-width="700px"
:scrim="true"
@click:outside="closeDialog"
@keydown.esc="closeDialog"
scroll-strategy="none"
no-click-animation
persistent
>
<v-card>
<v-toolbar density="compact" class="bg-terciary">
<v-row class="align-center" no-gutters>
@@ -74,8 +101,8 @@ function closeDialog() {
<v-divider class="border-opacity-25" :thickness="1" />
<v-card-text>
<v-row no-gutters>
<v-col cols="12" lg="9">
<v-row class="align-center" no-gutters>
<v-col cols="12" sm="8">
<v-row class="pa-2" no-gutters>
<v-col>
<v-text-field
@@ -116,30 +143,42 @@ function closeDialog() {
</v-col>
</v-row>
</v-col>
<v-col cols="12" lg="3">
<v-col cols="12" sm="4">
<v-row class="pa-2 justify-center" no-gutters>
<v-avatar size="128" class="">
<v-img
:src="
user.avatar_path
? `/assets/romm/assets/${user.avatar_path}`
: defaultAvatarPath
"
/>
</v-avatar>
</v-row>
<v-row class="pa-2" no-gutters>
<v-col>
<v-file-input
class="text-truncate"
v-model="user.avatar"
label="Avatar"
prepend-inner-icon="mdi-image"
prepend-icon=""
variant="outlined"
hide-details
/>
</v-col>
<v-hover v-slot="{ isHovering, props }">
<v-avatar size="190" class="ml-4" v-bind="props">
<v-img
:src="
imagePreviewUrl
? imagePreviewUrl
: user.avatar_path
? `/assets/romm/assets/${user.avatar_path}`
: defaultAvatarPath
"
>
<v-fade-transition>
<div
v-if="isHovering"
class="d-flex translucent v-card--reveal text-h4"
@click="triggerFileInput"
>
<v-icon>mdi-pencil</v-icon>
</div>
</v-fade-transition>
<v-file-input
id="file-input"
class="file-input text-truncate"
v-model="user.avatar"
label="Avatar"
prepend-inner-icon="mdi-image"
prepend-icon=""
variant="outlined"
hide-details
@change="previewImage"
/>
</v-img>
</v-avatar>
</v-hover>
</v-row>
</v-col>
</v-row>
@@ -153,3 +192,22 @@ function closeDialog() {
</v-card>
</v-dialog>
</template>
<style scoped>
.file-input {
display: none;
}
.translucent {
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(2px);
}
.v-card--reveal {
align-items: center;
bottom: 0;
justify-content: center;
position: absolute;
width: 100%;
height: 100%;
cursor: pointer;
}
</style>

View File

@@ -6,7 +6,6 @@ import FilterTextField from "@/components/Gallery/AppBar/FilterTextField.vue";
import SelectingBtn from "@/components/Gallery/AppBar/SelectingBtn.vue";
import GalleryViewBtn from "@/components/Gallery/AppBar/GalleryViewBtn.vue";
import SortBar from "@/components/Gallery/AppBar/SortBar.vue";
import SortBtn from "@/components/Gallery/AppBar/SortBtn.vue";
import storeAuth from "@/stores/auth";
// Props

View File

@@ -87,14 +87,14 @@ function onTouchEnd() {
:key="rom.id"
v-bind="props"
:src="
!rom.igdb_id && !rom.has_cover
!rom.igdb_id && !rom.moby_id && !rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_unmatched.png`
: !rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_missing_cover.png`
: `/assets/romm/resources/${rom.path_cover_l}`
"
:lazy-src="
!rom.igdb_id && !rom.has_cover
!rom.igdb_id && !rom.moby_id && !rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_unmatched.png`
: !rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_missing_cover.png`

View File

@@ -144,9 +144,11 @@ export type UpdateRom = Rom & {
async function updateRom({
rom,
renameAsIGDB = false,
removeCover = false
}: {
rom: UpdateRom;
renameAsIGDB?: boolean;
removeCover?: boolean;
}): Promise<{ data: RomSchema }> {
var formData = new FormData();
if (rom.igdb_id) formData.append("igdb_id", rom.igdb_id.toString());
@@ -157,7 +159,7 @@ async function updateRom({
if (rom.artwork) formData.append("artwork", rom.artwork[0]);
return api.put(`/roms/${rom.id}`, formData, {
params: { rename_as_igdb: renameAsIGDB },
params: { rename_as_igdb: renameAsIGDB, remove_cover: removeCover },
});
}

View File

@@ -132,7 +132,7 @@ export default defineStore("roms", {
},
filterUnmatched() {
this._filteredIDs = this.filteredRoms
.filter((rom) => !rom.igdb_id)
.filter((rom) => !rom.igdb_id && !rom.moby_id)
.map((roms) => roms.id);
},
filterGenre(genreToFilter: string) {

View File

@@ -98,7 +98,7 @@ export function formatTimestamp(timestamp: string | null) {
if (!timestamp) return "-";
const date = new Date(timestamp);
return date.toLocaleString();
return date.toLocaleString("en-GB");
}
export function regionToEmoji(region: string) {

View File

@@ -1,11 +1,4 @@
<script setup lang="ts">
import storeDownload from "@/stores/download";
import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject, onBeforeMount, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useDisplay } from "vuetify";
import type { PlatformSchema } from "@/__generated__";
import ActionBar from "@/components/Details/ActionBar.vue";
import AdditionalContent from "@/components/Details/AdditionalContent.vue";
@@ -24,7 +17,13 @@ import EditRomDialog from "@/components/Dialog/Rom/EditRom.vue";
import SearchRomDialog from "@/components/Dialog/Rom/SearchRom.vue";
import platformApi from "@/services/api/platform";
import romApi from "@/services/api/rom";
import storeDownload from "@/stores/download";
import type { Rom } from "@/stores/roms";
import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject, onBeforeMount, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useDisplay, useTheme } from "vuetify";
const route = useRoute();
const rom = ref<Rom>();
@@ -38,6 +37,7 @@ const tab = ref<
| "relatedgames"
| "emulation"
>("details");
const theme = useTheme();
const { smAndDown, mdAndDown, mdAndUp, lgAndUp } = useDisplay();
const emitter = inject<Emitter<Events>>("emitter");
const showEmulation = ref(false);
@@ -122,7 +122,23 @@ watch(
'cover-xs': smAndDown,
}"
>
<cover :rom="rom" />
<cover
:romId="rom.id"
:src="
!rom.igdb_id && !rom.moby_id && !rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_unmatched.png`
: !rom.has_cover
? `/assets/default/cover/big_${theme.global.name.value}_missing_cover.png`
: `/assets/romm/resources/${rom.path_cover_l}`
"
:lazy-src="
!rom.igdb_id && !rom.moby_id && !rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_unmatched.png`
: !rom.has_cover
? `/assets/default/cover/small_${theme.global.name.value}_missing_cover.png`
: `/assets/romm/resources/${rom.path_cover_s}`
"
/>
<action-bar class="mt-2" :rom="rom" />
<related-games class="mt-3 px-2" v-if="mdAndUp" :rom="rom" />
</v-col>