diff --git a/frontend/src/components/common/Game/Card/Sources.vue b/frontend/src/components/common/Game/Card/Sources.vue
index c06427368..ec897aa61 100644
--- a/frontend/src/components/common/Game/Card/Sources.vue
+++ b/frontend/src/components/common/Game/Card/Sources.vue
@@ -14,7 +14,7 @@ defineProps<{ rom: SearchRomSchema }>();
open-delay="500"
>
-
();
size="30"
rounded="1"
>
-
+
+
+
diff --git a/frontend/src/components/common/Game/Dialog/EditRom.vue b/frontend/src/components/common/Game/Dialog/EditRom.vue
index 20179a567..4e9c0f778 100644
--- a/frontend/src/components/common/Game/Dialog/EditRom.vue
+++ b/frontend/src/components/common/Game/Dialog/EditRom.vue
@@ -6,6 +6,7 @@ import storeGalleryView from "@/stores/galleryView";
import storeHeartbeat from "@/stores/heartbeat";
import storePlatforms from "@/stores/platforms";
import storeRoms, { type SimpleRom } from "@/stores/roms";
+import storeUpload from "@/stores/upload";
import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { computed, inject, ref } from "vue";
@@ -24,8 +25,10 @@ const rom = ref
();
const romsStore = storeRoms();
const imagePreviewUrl = ref("");
const removeCover = ref(false);
+const manualFiles = ref([]);
const platfotmsStore = storePlatforms();
const galleryViewStore = storeGalleryView();
+const uploadStore = storeUpload();
const emitter = inject>("emitter");
emitter?.on("showEditRomDialog", (romToEdit: UpdateRom | undefined) => {
show.value = true;
@@ -78,7 +81,7 @@ async function removeArtwork() {
}
const noMetadataMatch = computed(() => {
- return !rom.value?.igdb_id && !rom.value?.moby_id && !rom.value?.sgdb_id;
+ return !rom.value?.igdb_id && !rom.value?.moby_id && !rom.value?.ss_id;
});
async function handleRomUpdate(
@@ -120,6 +123,51 @@ async function handleRomUpdate(
});
}
+async function uploadManuals() {
+ if (!rom.value) return;
+
+ await romApi
+ .uploadManuals({
+ romId: rom.value.id,
+ filesToUpload: manualFiles.value,
+ })
+ .then((responses: PromiseSettledResult[]) => {
+ const successfulUploads = responses.filter(
+ (d) => d.status == "fulfilled",
+ );
+ const failedUploads = responses.filter((d) => d.status == "rejected");
+
+ if (failedUploads.length == 0) {
+ uploadStore.reset();
+ }
+
+ if (successfulUploads.length == 0) {
+ return emitter?.emit("snackbarShow", {
+ msg: `All manuals skipped, nothing to upload.`,
+ icon: "mdi-close-circle",
+ color: "orange",
+ timeout: 5000,
+ });
+ }
+
+ emitter?.emit("snackbarShow", {
+ msg: `${successfulUploads.length} manuals uploaded successfully (and ${failedUploads.length} skipped/failed).`,
+ icon: "mdi-check-bold",
+ color: "green",
+ timeout: 3000,
+ });
+ })
+ .catch(({ response, message }) => {
+ emitter?.emit("snackbarShow", {
+ msg: `Unable to upload manuals: ${response?.data?.detail || response?.statusText || message}`,
+ icon: "mdi-close-circle",
+ color: "red",
+ timeout: 4000,
+ });
+ });
+ manualFiles.value = [];
+}
+
async function unmatchRom() {
if (!rom.value) return;
await handleRomUpdate(
@@ -213,6 +261,54 @@ function closeDialog() {
/>
+
+
+
+ {{ t("rom.manual")
+ }}{{
+ rom.has_manual ? "mdi-check" : "mdi-close"
+ }}
+
+ mdi-upload
+
+
+
+
+
+
+ mdi-folder-file-outline
+
+ /romm/resources/{{ rom.path_manual }}
+
+
+
+
(null);
const romsStore = storeRoms();
const galleryViewStore = storeGalleryView();
+const platfotmsStore = storePlatforms();
const searching = ref(false);
const route = useRoute();
const searchTerm = ref("");
@@ -42,9 +44,17 @@ const sources = ref([]);
const heartbeat = storeHeartbeat();
const isIGDBFiltered = ref(true);
const isMobyFiltered = ref(true);
+const isSSFiltered = ref(true);
+const computedAspectRatio = computed(() => {
+ const ratio =
+ platfotmsStore.getAspectRatio(rom.value?.platform_id ?? -1) ||
+ galleryViewStore.defaultAspectRatioCover;
+ return parseFloat(ratio.toString());
+});
emitter?.on("showMatchRomDialog", (romToSearch) => {
rom.value = romToSearch;
show.value = true;
+ matchedRoms.value = [];
// Use name as search term, only when it's matched
// Otherwise use the filename without tags and extensions
@@ -70,11 +80,17 @@ function toggleSourceFilter(source: MatchedSource["name"]) {
heartbeat.value.METADATA_SOURCES.MOBY_API_ENABLED
) {
isMobyFiltered.value = !isMobyFiltered.value;
+ } else if (
+ source == "Screenscraper" &&
+ heartbeat.value.METADATA_SOURCES.SS_API_ENABLED
+ ) {
+ isSSFiltered.value = !isSSFiltered.value;
}
filteredMatchedRoms.value = matchedRoms.value.filter((rom) => {
if (
(rom.igdb_id && isIGDBFiltered.value) ||
- (rom.moby_id && isMobyFiltered.value)
+ (rom.moby_id && isMobyFiltered.value) ||
+ (rom.ss_id && isSSFiltered.value)
) {
return true;
}
@@ -103,7 +119,8 @@ async function searchRom() {
filteredMatchedRoms.value = matchedRoms.value.filter((rom) => {
if (
(rom.igdb_id && isIGDBFiltered.value) ||
- (rom.moby_id && isMobyFiltered.value)
+ (rom.moby_id && isMobyFiltered.value) ||
+ (rom.ss_id && isSSFiltered.value)
) {
return true;
}
@@ -131,16 +148,31 @@ function showSources(matchedRom: SearchRomSchema) {
}
showSelectSource.value = true;
selectedMatchRom.value = matchedRom;
- sources.value.push({
- url_cover: matchedRom.igdb_url_cover,
- name: "IGDB",
- logo_path: "/assets/scrappers/igdb.png",
- });
- sources.value.push({
- url_cover: matchedRom.moby_url_cover,
- name: "Mobygames",
- logo_path: "/assets/scrappers/moby.png",
- });
+ sources.value = [];
+ if (matchedRom.igdb_url_cover || matchedRom.igdb_id) {
+ sources.value.push({
+ url_cover: matchedRom.igdb_url_cover,
+ name: "IGDB",
+ logo_path: "/assets/scrappers/igdb.png",
+ });
+ }
+ if (matchedRom.moby_url_cover || matchedRom.moby_id) {
+ sources.value.push({
+ url_cover: matchedRom.moby_url_cover,
+ name: "Mobygames",
+ logo_path: "/assets/scrappers/moby.png",
+ });
+ }
+ if (matchedRom.ss_url_cover || matchedRom.ss_id) {
+ sources.value.push({
+ url_cover: matchedRom.ss_url_cover,
+ name: "Screenscraper",
+ logo_path: "/assets/scrappers/ss.png",
+ });
+ }
+ if (sources.value.length == 1) {
+ selectedCover.value = sources.value[0];
+ }
}
function selectCover(source: MatchedSource) {
@@ -213,7 +245,6 @@ function closeDialog() {
selectedCover.value = undefined;
selectedMatchRom.value = undefined;
renameAsSource.value = false;
- matchedRoms.value = [];
}
onBeforeUnmount(() => {
@@ -291,6 +322,34 @@ onBeforeUnmount(() => {
>
+
+
+
+
+
+
@@ -324,6 +383,7 @@ onBeforeUnmount(() => {
@click="searchRom()"
class="bg-toplayer"
variant="text"
+ rounded="0"
icon="mdi-search-web"
block
:disabled="searching"
@@ -371,7 +431,7 @@ onBeforeUnmount(() => {
-
+
{{
@@ -397,7 +457,7 @@ onBeforeUnmount(() => {
>
diff --git a/frontend/src/components/common/Navigation/CollectionsDrawer.vue b/frontend/src/components/common/Navigation/CollectionsDrawer.vue
index 1225ad6aa..275b4ca40 100644
--- a/frontend/src/components/common/Navigation/CollectionsDrawer.vue
+++ b/frontend/src/components/common/Navigation/CollectionsDrawer.vue
@@ -76,6 +76,7 @@ function clear() {
+
{{
t("common.virtual-collections").toUpperCase()
}}
diff --git a/frontend/src/components/common/Navigation/SettingsDrawer.vue b/frontend/src/components/common/Navigation/SettingsDrawer.vue
index 8f14c6345..b4356ab0b 100644
--- a/frontend/src/components/common/Navigation/SettingsDrawer.vue
+++ b/frontend/src/components/common/Navigation/SettingsDrawer.vue
@@ -57,7 +57,6 @@ async function logout() {
'mx-2': smAndDown || activeSettingsDrawer,
'my-2': !smAndDown || activeSettingsDrawer,
'drawer-mobile': smAndDown,
- 'drawer-desktop': !smAndDown,
}"
class="bg-surface pa-1"
style="height: unset"
@@ -115,26 +114,15 @@ async function logout() {
append-icon="mdi-security"
>{{ t("common.administration") }}
-
- {{ t("common.logout") }}
-
-
-
- {{ t("common.logout") }}
-
+
+ {{ t("common.logout") }}
diff --git a/frontend/src/components/common/Notifications/UploadProgress.vue b/frontend/src/components/common/Notifications/UploadProgress.vue
index 3c0628c08..6dbef6efa 100644
--- a/frontend/src/components/common/Notifications/UploadProgress.vue
+++ b/frontend/src/components/common/Notifications/UploadProgress.vue
@@ -30,12 +30,12 @@ watch(files, (newList) => {
absolute
:location="xs ? 'bottom' : 'bottom right'"
class="mb-4 mr-4"
- color="tooltip"
+ color="toplayer"
>
-
+
@@ -43,7 +43,7 @@ watch(files, (newList) => {
{{ file.filename }}
-
+
{{ file.failureReason }}
@@ -82,12 +82,12 @@ watch(files, (newList) => {
-
+
diff --git a/frontend/src/layouts/Auth.vue b/frontend/src/layouts/Auth.vue
index ff1ec4b5f..83f9af008 100644
--- a/frontend/src/layouts/Auth.vue
+++ b/frontend/src/layouts/Auth.vue
@@ -30,6 +30,7 @@ const heartbeatStore = storeHeartbeat();
#container {
background-image: url("/assets/auth_background.svg");
background-size: cover;
+ background-position: center;
max-width: 100vw;
}
diff --git a/frontend/src/locales/de_DE/rom.json b/frontend/src/locales/de_DE/rom.json
index 145122a5c..0fbf60739 100644
--- a/frontend/src/locales/de_DE/rom.json
+++ b/frontend/src/locales/de_DE/rom.json
@@ -28,6 +28,7 @@
"copy-link": "Download-Link kopieren",
"cant-copy-link": "Link kann nicht in Zwischenablage kopiert werden. Bitte manuell kopieren.",
"details": "Details",
+ "manual": "Benutzerhandbuch",
"personal": "Persönlich",
"version": "Version",
"default": "Standard",
diff --git a/frontend/src/locales/en_GB/rom.json b/frontend/src/locales/en_GB/rom.json
index 948731324..4640dc061 100644
--- a/frontend/src/locales/en_GB/rom.json
+++ b/frontend/src/locales/en_GB/rom.json
@@ -28,6 +28,7 @@
"copy-link": "Copy download link",
"cant-copy-link": "Can't copy link to clipboard, copy it manually",
"details": "Details",
+ "manual": "User manual",
"personal": "Personal",
"version": "Version",
"default": "Default",
diff --git a/frontend/src/locales/en_US/rom.json b/frontend/src/locales/en_US/rom.json
index 948731324..4640dc061 100644
--- a/frontend/src/locales/en_US/rom.json
+++ b/frontend/src/locales/en_US/rom.json
@@ -28,6 +28,7 @@
"copy-link": "Copy download link",
"cant-copy-link": "Can't copy link to clipboard, copy it manually",
"details": "Details",
+ "manual": "User manual",
"personal": "Personal",
"version": "Version",
"default": "Default",
diff --git a/frontend/src/locales/es_ES/rom.json b/frontend/src/locales/es_ES/rom.json
index 0c25d8854..06144fa67 100644
--- a/frontend/src/locales/es_ES/rom.json
+++ b/frontend/src/locales/es_ES/rom.json
@@ -28,6 +28,7 @@
"copy-link": "Copiar link de descarga",
"cant-copy-link": "No se pudo copiar el link al portapapeles, copialo manualmente",
"details": "Detalles",
+ "manual": "Manual de usuario",
"personal": "Personal",
"version": "Versión",
"default": "Por defecto",
diff --git a/frontend/src/locales/fr_FR/rom.json b/frontend/src/locales/fr_FR/rom.json
index 45adbf277..9106a502f 100644
--- a/frontend/src/locales/fr_FR/rom.json
+++ b/frontend/src/locales/fr_FR/rom.json
@@ -28,6 +28,7 @@
"copy-link": "Copier le lien de téléchargement",
"cant-copy-link": "Impossible de copier le lien dans le presse-papiers, copiez-le manuellement",
"details": "Détails",
+ "manual": "Manuel utilisateur",
"personal": "Personnel",
"version": "Version",
"default": "Par défaut",
diff --git a/frontend/src/locales/ko_KR/rom.json b/frontend/src/locales/ko_KR/rom.json
index b64860456..313059873 100644
--- a/frontend/src/locales/ko_KR/rom.json
+++ b/frontend/src/locales/ko_KR/rom.json
@@ -28,6 +28,7 @@
"copy-link": "다운로드 링크 복사",
"cant-copy-link": "링크를 클립보드에 복사할 수 없습니다. 수동으로 복사합니다",
"details": "자세히",
+ "manual": "사용자 설명서",
"personal": "개인",
"version": "버전",
"default": "기본으로",
diff --git a/frontend/src/locales/pt_BR/rom.json b/frontend/src/locales/pt_BR/rom.json
index 8434c85fd..43e1ec40a 100644
--- a/frontend/src/locales/pt_BR/rom.json
+++ b/frontend/src/locales/pt_BR/rom.json
@@ -28,6 +28,7 @@
"copy-link": "Copiar link de download",
"cant-copy-link": "Não é possível copiar o link para a área de transferência, copie manualmente",
"details": "Detalhes",
+ "manual": "Manual do usuário",
"personal": "Pessoal",
"version": "Versão",
"default": "Padrão",
diff --git a/frontend/src/locales/ru_RU/rom.json b/frontend/src/locales/ru_RU/rom.json
index 7e9a38431..f0579a822 100644
--- a/frontend/src/locales/ru_RU/rom.json
+++ b/frontend/src/locales/ru_RU/rom.json
@@ -28,6 +28,7 @@
"copy-link": "Скопировать ссылку для скачивания",
"cant-copy-link": "Не удается скопировать ссылку в буфер обмена, скопируйте ее вручную",
"details": "Детали",
+ "manual": "Руководство пользователя",
"personal": "Личное",
"version": "Версия",
"default": "По умолчанию",
diff --git a/frontend/src/locales/zh_CN/rom.json b/frontend/src/locales/zh_CN/rom.json
index 8d8cacd40..19e77ba49 100644
--- a/frontend/src/locales/zh_CN/rom.json
+++ b/frontend/src/locales/zh_CN/rom.json
@@ -29,6 +29,7 @@
"copy-link": "复制下载链接",
"cant-copy-link": "无法将链接复制到剪贴板,请手动复制",
"details": "详情",
+ "manual": "用户手册",
"personal": "私人",
"version": "版本",
"default": "默认",
diff --git a/frontend/src/services/api/rom.ts b/frontend/src/services/api/rom.ts
index 074725921..d25f15071 100644
--- a/frontend/src/services/api/rom.ts
+++ b/frontend/src/services/api/rom.ts
@@ -155,6 +155,7 @@ async function updateRom({
const formData = new FormData();
if (rom.igdb_id) formData.append("igdb_id", rom.igdb_id.toString());
if (rom.moby_id) formData.append("moby_id", rom.moby_id.toString());
+ if (rom.ss_id) formData.append("ss_id", rom.ss_id.toString());
formData.append("name", rom.name || "");
formData.append("fs_name", rom.fs_name);
formData.append("summary", rom.summary || "");
@@ -170,17 +171,44 @@ async function updateRom({
});
}
-async function deleteRoms({
- roms,
- deleteFromFs = [],
+async function uploadManuals({
+ romId,
+ filesToUpload,
}: {
- roms: SimpleRom[];
- deleteFromFs: number[];
-}): Promise<{ data: MessageResponse }> {
- return api.post("/roms/delete", {
- roms: roms.map((r) => r.id),
- delete_from_fs: deleteFromFs,
+ romId: number;
+ filesToUpload: File[];
+}): Promise[]> {
+ const heartbeat = storeHeartbeat();
+ const uploadStore = storeUpload();
+
+ console.log(filesToUpload);
+ const promises = filesToUpload.map((file) => {
+ const formData = new FormData();
+ formData.append(file.name, file);
+
+ uploadStore.start(file.name);
+ return new Promise((resolve, reject) => {
+ api
+ .post(`/roms/${romId}/manuals`, formData, {
+ headers: {
+ "Content-Type": "multipart/form-data",
+ "X-Upload-Filename": file.name,
+ },
+ timeout: heartbeat.value.FRONTEND.UPLOAD_TIMEOUT * 1000,
+ params: {},
+ onUploadProgress: (progressEvent: AxiosProgressEvent) => {
+ uploadStore.update(file.name, progressEvent);
+ },
+ })
+ .then(resolve)
+ .catch((error) => {
+ uploadStore.fail(file.name, error.response?.data?.detail);
+ reject(error);
+ });
+ });
});
+
+ return Promise.allSettled(promises);
}
async function updateUserRomProps({
@@ -201,6 +229,19 @@ async function updateUserRomProps({
});
}
+async function deleteRoms({
+ roms,
+ deleteFromFs = [],
+}: {
+ roms: SimpleRom[];
+ deleteFromFs: number[];
+}): Promise<{ data: MessageResponse }> {
+ return api.post("/roms/delete", {
+ roms: roms.map((r) => r.id),
+ delete_from_fs: deleteFromFs,
+ });
+}
+
export default {
uploadRoms,
getRoms,
@@ -210,6 +251,7 @@ export default {
downloadRom,
searchRom,
updateRom,
- deleteRoms,
+ uploadManuals,
updateUserRomProps,
+ deleteRoms,
};
diff --git a/frontend/src/stores/heartbeat.ts b/frontend/src/stores/heartbeat.ts
index 10f6d1dbc..fe29bfecd 100644
--- a/frontend/src/stores/heartbeat.ts
+++ b/frontend/src/stores/heartbeat.ts
@@ -25,6 +25,11 @@ export default defineStore("heartbeat", {
value: "moby",
disabled: !this.value.METADATA_SOURCES?.MOBY_API_ENABLED,
},
+ {
+ name: "Screenscraper",
+ value: "ss",
+ disabled: !this.value.METADATA_SOURCES?.SS_API_ENABLED,
+ },
]).value.filter((s) => !s.disabled);
},
reset() {
diff --git a/frontend/src/styles/fonts.css b/frontend/src/styles/fonts.css
index 135945204..930ac263a 100644
--- a/frontend/src/styles/fonts.css
+++ b/frontend/src/styles/fonts.css
@@ -7,8 +7,8 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaSTbQWt4N.woff2)
format("woff2");
- unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
- U+FE2E-FE2F;
+ unicode-range:
+ U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
@@ -41,8 +41,8 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaSTbQWt4N.woff2)
format("woff2");
- unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
- U+03A3-03FF;
+ unicode-range:
+ U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* math */
@font-face {
@@ -53,9 +53,10 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaSTbQWt4N.woff2)
format("woff2");
- unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315,
- U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A,
- U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6,
+ unicode-range:
+ U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A,
+ U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346,
+ U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6,
U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043,
U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C,
U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121,
@@ -75,20 +76,21 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaSTbQWt4N.woff2)
format("woff2");
- unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0,
- U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0,
- U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A,
- U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB,
- U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C,
- U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3,
- U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F,
- U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D,
- U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2,
- U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3,
- U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442,
- U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3,
- U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6,
- U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB,
+ unicode-range:
+ U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4,
+ U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3,
+ U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF,
+ U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF,
+ U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0,
+ U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F,
+ U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315,
+ U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382,
+ U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6,
+ U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7,
+ U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444,
+ U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0,
+ U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA,
+ U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB,
U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513,
U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D,
U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC,
@@ -108,9 +110,10 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaSTbQWt4N.woff2)
format("woff2");
- unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169,
- U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323,
- U+0329, U+1EA0-1EF9, U+20AB;
+ unicode-range:
+ U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
+ U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
+ U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
@@ -121,9 +124,10 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaSTbQWt4N.woff2)
format("woff2");
- unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
- U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
- U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+ unicode-range:
+ U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
+ U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
+ U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
@@ -134,9 +138,10 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnkaSTbQWg.woff2)
format("woff2");
- unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
- U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
- U+2212, U+2215, U+FEFF, U+FFFD;
+ unicode-range:
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
+ U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
+ U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
@@ -147,8 +152,8 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBHMdazTgWw.woff2)
format("woff2");
- unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
- U+FE2E-FE2F;
+ unicode-range:
+ U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
@@ -181,8 +186,8 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBHMdazTgWw.woff2)
format("woff2");
- unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
- U+03A3-03FF;
+ unicode-range:
+ U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* math */
@font-face {
@@ -193,9 +198,10 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBHMdazTgWw.woff2)
format("woff2");
- unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315,
- U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A,
- U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6,
+ unicode-range:
+ U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A,
+ U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346,
+ U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6,
U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043,
U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C,
U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121,
@@ -215,20 +221,21 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBHMdazTgWw.woff2)
format("woff2");
- unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0,
- U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0,
- U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A,
- U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB,
- U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C,
- U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3,
- U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F,
- U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D,
- U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2,
- U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3,
- U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442,
- U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3,
- U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6,
- U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB,
+ unicode-range:
+ U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4,
+ U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3,
+ U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF,
+ U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF,
+ U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0,
+ U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F,
+ U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315,
+ U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382,
+ U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6,
+ U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7,
+ U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444,
+ U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0,
+ U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA,
+ U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB,
U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513,
U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D,
U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC,
@@ -248,9 +255,10 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBHMdazTgWw.woff2)
format("woff2");
- unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169,
- U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323,
- U+0329, U+1EA0-1EF9, U+20AB;
+ unicode-range:
+ U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
+ U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
+ U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
@@ -261,9 +269,10 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBHMdazTgWw.woff2)
format("woff2");
- unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
- U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
- U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+ unicode-range:
+ U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
+ U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
+ U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
@@ -274,7 +283,8 @@
font-display: swap;
src: url(/assets/fonts/roboto/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBHMdazQ.woff2)
format("woff2");
- unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
- U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
- U+2212, U+2215, U+FEFF, U+FFFD;
+ unicode-range:
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
+ U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
+ U+2215, U+FEFF, U+FFFD;
}
diff --git a/frontend/src/views/Auth/Setup.vue b/frontend/src/views/Auth/Setup.vue
index d7f0597e7..753ab0ef8 100644
--- a/frontend/src/views/Auth/Setup.vue
+++ b/frontend/src/views/Auth/Setup.vue
@@ -29,6 +29,12 @@ const metadataOptions = computed(() => [
logo_path: "/assets/scrappers/moby.png",
disabled: !heartbeat.value.METADATA_SOURCES?.MOBY_API_ENABLED,
},
+ {
+ name: "ScreenScrapper",
+ value: "ss",
+ logo_path: "/assets/scrappers/ss.png",
+ disabled: !heartbeat.value.METADATA_SOURCES?.SS_API_ENABLED,
+ },
{
name: "SteamgridDB",
value: "sgdb",
diff --git a/frontend/src/views/GameDetails.vue b/frontend/src/views/GameDetails.vue
index d73bd6a20..1d98e49be 100644
--- a/frontend/src/views/GameDetails.vue
+++ b/frontend/src/views/GameDetails.vue
@@ -5,6 +5,7 @@ import BackgroundHeader from "@/components/Details/BackgroundHeader.vue";
import FileInfo from "@/components/Details/Info/FileInfo.vue";
import GameInfo from "@/components/Details/Info/GameInfo.vue";
import Personal from "@/components/Details/Personal.vue";
+import PdfViewer from "@/components/Details/PDFViewer.vue";
import RelatedGames from "@/components/Details/RelatedGames.vue";
import Saves from "@/components/Details/Saves.vue";
import States from "@/components/Details/States.vue";
@@ -27,6 +28,7 @@ const { t } = useI18n();
const route = useRoute();
const tab = ref<
| "details"
+ | "manual"
| "saves"
| "states"
| "personal"
@@ -40,6 +42,7 @@ const noRomError = ref(false);
const romsStore = storeRoms();
const { currentRom, gettingRoms } = storeToRefs(romsStore);
+// Functions
async function fetchDetails() {
gettingRoms.value = true;
await romApi
@@ -115,6 +118,9 @@ watch(
:class="{ 'mt-4': smAndDown }"
>
{{ t("rom.details") }}
+
+ {{ t("rom.manual") }}
+
{{ t("common.saves") }}
{{ t("common.states") }}
@@ -152,6 +158,9 @@ watch(
+
+
+
diff --git a/frontend/src/views/Scan.vue b/frontend/src/views/Scan.vue
index fc25beb7a..7a9d015d9 100644
--- a/frontend/src/views/Scan.vue
+++ b/frontend/src/views/Scan.vue
@@ -30,11 +30,17 @@ const metadataOptions = computed(() => [
disabled: !heartbeat.value.METADATA_SOURCES?.IGDB_API_ENABLED,
},
{
- name: "MobyGames",
+ name: "Mobygames",
value: "moby",
logo_path: "/assets/scrappers/moby.png",
disabled: !heartbeat.value.METADATA_SOURCES?.MOBY_API_ENABLED,
},
+ {
+ name: "Screenscraper",
+ value: "ss",
+ logo_path: "/assets/scrappers/ss.png",
+ disabled: !heartbeat.value.METADATA_SOURCES?.SS_API_ENABLED,
+ },
]);
// Use the computed metadataOptions to filter out disabled sources
const metadataSources = ref(metadataOptions.value.filter((s) => !s.disabled));
@@ -199,11 +205,7 @@ async function stopScan() {
-
+
{{ item.raw.name }}
@@ -296,76 +298,85 @@ async function stopScan() {
/>
-
-
-
-
-
-
-
-
-
-
-
- {{ platform.name }}
-
- {{
- platform.roms.length
- }}
-
-
-
-
-
+
+
+
+
+
-
- Not identifiedmdi-close
+
+
+
+
+
+
+ {{ platform.name }}
+
+ {{
+ platform.roms.length
+ }}
+
+
+
+
+
-
-
-
- {{ t("scan.no-new-roms") }}
-
-
-
-
-
-
+
+ Not identifiedmdi-close
+
+
+
+ {{ t("scan.no-new-roms") }}
+
+
+
+
+
+
+
+
-
+
mdi-controller
{{
t("scan.platforms-scanned-n", scanningPlatforms.length)