Refactor socket connections

This commit is contained in:
Georges-Antoine Assi
2023-05-18 17:34:45 -04:00
parent c5161e0541
commit 99afbc8f3f
14 changed files with 334 additions and 228 deletions

View File

@@ -35,9 +35,8 @@ async def updateRom(req: Request, p_slug: str, id: int) -> dict:
try:
fs.rename_rom(platform.fs_slug, db_rom.file_name, updated_rom['file_name'])
except RomAlreadyExistsException as e:
error: str = f"{e}"
log.error(error)
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=error)
log.error(str(e))
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
updated_rom['file_name_no_tags'] = get_file_name_with_no_tags(updated_rom['file_name'])
updated_rom.update(fs.get_cover(True, p_slug, updated_rom['file_name'], updated_rom['url_cover']))
updated_rom.update(fs.get_screenshots(p_slug, updated_rom['file_name'], updated_rom['url_screenshots']))
@@ -58,7 +57,7 @@ def delete_rom(p_slug: str, id: int, filesystem: bool=False) -> dict:
platform: Platform = dbh.get_platform(p_slug)
fs.remove_rom(platform.fs_slug, rom.file_name)
except RomNotFoundError as e:
error: str = f"{e}. Couldn't delete from filesystem."
error = f"Couldn't delete from filesystem: {str(e)}"
log.error(error)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=error)
return {'msg': f'{rom.file_name} deleted successfully!'}

View File

@@ -9,7 +9,7 @@ from models.platform import Platform
from models.rom import Rom
async def scan(sid: str, platforms: str, complete_rescan: bool=True, sm=None):
async def scan(_sid: str, platforms: str, complete_rescan: bool=True, sm=None):
"""Scan platforms and roms and write them in database."""
log.info(emoji.emojize(":magnifying_glass_tilted_right: Scanning "))
@@ -18,7 +18,7 @@ async def scan(sid: str, platforms: str, complete_rescan: bool=True, sm=None):
try: # Scanning platforms
fs_platforms: list[str] = fs.get_platforms()
except PlatformsNotFoundException as e:
log.error(e); await sm.emit('done_ko', e.message); return
log.error(e); await sm.emit('scan:done_ko', e.message); return
platforms: list[str] = json.loads(platforms) if len(json.loads(platforms)) > 0 else fs_platforms
log.info(f"Platforms to be scanned: {', '.join(platforms)}")
@@ -28,7 +28,7 @@ async def scan(sid: str, platforms: str, complete_rescan: bool=True, sm=None):
scanned_platform: Platform = fastapi.scan_platform(platform)
except RomsNotFoundException as e:
log.error(e); continue
await sm.emit('scanning_platform', [scanned_platform.name, scanned_platform.slug]); await sm.emit('') # Workaround to emit in real-time
await sm.emit('scan:scanning_platform', [scanned_platform.name, scanned_platform.slug]); await sm.emit('') # Workaround to emit in real-time
if platform != str(scanned_platform): log.info(f"Identified as {COLORS['blue']}{scanned_platform}{COLORS['reset']}")
dbh.add_platform(scanned_platform)
@@ -37,7 +37,7 @@ async def scan(sid: str, platforms: str, complete_rescan: bool=True, sm=None):
for rom in fs_roms:
rom_id: int = dbh.rom_exists(scanned_platform.slug, rom['file_name'])
if rom_id and not complete_rescan: continue
await sm.emit('scanning_rom', rom['file_name']); await sm.emit('') # Workaround to emit in real-time
await sm.emit('scan:scanning_rom', rom['file_name']); await sm.emit('') # Workaround to emit in real-time
log.info(f"Scanning {COLORS['orange']}{rom['file_name']}{COLORS['reset']}")
if rom['multi']: [log.info(f"\t - {COLORS['orange_i']}{file}{COLORS['reset']}") for file in rom['files']]
scanned_rom: Rom = fastapi.scan_rom(scanned_platform, rom)
@@ -45,4 +45,4 @@ async def scan(sid: str, platforms: str, complete_rescan: bool=True, sm=None):
dbh.add_rom(scanned_rom)
dbh.purge_roms(scanned_platform.slug, [rom['file_name'] for rom in fs_roms])
dbh.purge_platforms(fs_platforms)
await sm.emit('done')
await sm.emit('scan:done')

View File

@@ -27,9 +27,8 @@ class DBHandler:
@staticmethod
def raise_error(e: Exception) -> None:
error: str = f"{e}"
log.critical(error)
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=error)
log.critical(str(e))
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
# ========= Platforms =========

View File

@@ -21,8 +21,12 @@ app.include_router(rom.router)
sm = SocketManager()
sm.mount_to("/ws", app)
async def scan_handler(*args): await scan.scan(*args, sm)
sm.on('scan', handler=scan_handler)
async def scan_handler(*args):
await scan.scan(*args, sm)
sm.on("scan", handler=scan_handler)
@app.on_event("startup")
@@ -31,6 +35,6 @@ def startup() -> None:
pass
if __name__ == '__main__':
if __name__ == "__main__":
uvicorn.run("main:app", host=DEV_HOST, port=DEV_PORT, reload=True)
# uvicorn.run("main:app", host=DEV_HOST, port=DEV_PORT, reload=False, workers=2)

View File

@@ -1,26 +1,85 @@
import re
LANGUAGES = [
"Ar",
"Da",
"De",
"En",
"En-US",
"Es",
"Fi",
"Fr",
"It",
"Ja",
"Ko",
"Nl",
"Pl",
"Pt",
"Pt-BR",
"Ru",
"Sv",
"Zh",
"Zh-Hans",
"Zh-Hant",
"nolang",
]
REGIONS = [
("U", "USA"),
("E", "Europe"),
("J", "Japan"),
("K", "Korea"),
("T", "Taiwan"),
("G", "Germany"),
("B", "Brazil"),
("A", "Australia"),
("CH", "China"),
("NL", "Netherlands"),
("PD", "Public Domain"),
("F", "France"),
("S", "Spain"),
("W", "World"),
("C", "Canada"),
("SW", "Sweden"),
("FN", "Finland"),
("UK", "England"),
("GR", "Greece"),
("UNK", "Unknown"),
("HK", "Hong Kong"),
("I", "Italy"),
("H", "Holland"),
("UNL", "Unlicensed"),
("AS", "Asia"),
("R", "Russia"),
("NO", "Norway"),
]
REGIONS_BY_SHORTCODE = {region[0].lower(): region[1] for region in REGIONS}
REGIONS_NAME_KEYS = [region[1].lower() for region in REGIONS]
def parse_tags(file_name: str) -> tuple:
reg: str = ''
rev: str = ''
other_tags: list = []
tags: list = re.findall('\(([^)]+)', file_name)
reg = ""
rev = ""
other_tags = []
tags = re.findall("\(([^)]+)", file_name)
for tag in tags:
if tag.split('-')[0].lower() == 'reg':
try: reg = tag.split('-', 1)[1]
except IndexError: pass
elif tag.split('-')[0].lower() == 'rev':
try: rev = tag.split('-', 1)[1]
except IndexError: pass
if tag.lower() in REGIONS_BY_SHORTCODE.keys():
reg = REGIONS_BY_SHORTCODE[tag.lower()]
elif tag.lower() in REGIONS_NAME_KEYS:
reg = tag
elif "rev" in tag.lower():
rev = tag.split(" ")[1]
else:
other_tags.append(tag)
return reg, rev, other_tags
def get_file_name_with_no_tags(file_name: str) -> str:
return re.sub('[\(\[].*?[\)\]]', '', file_name.split('.')[0])
return re.sub("[\(\[].*?[\)\]]", "", file_name.split(".")[0])
def get_file_extension(rom: dict) -> str:
return rom['file_name'].split('.')[-1] if not rom['multi'] else ''
return rom["file_name"].split(".")[-1] if not rom["multi"] else ""

15
frontend/.eslintignore Normal file
View File

@@ -0,0 +1,15 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local

10
frontend/.eslintrc.json Normal file
View File

@@ -0,0 +1,10 @@
{
"env": {
"node": true,
"browser": true
},
"extends": [
"eslint:recommended",
"plugin:vue/vue3-recommended"
]
}

View File

@@ -6,7 +6,7 @@
"dev": "vite --host",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --fix --ignore-path .gitignore"
"lint": "eslint . --fix"
},
"dependencies": {
"@mdi/font": "7.0.96",

View File

@@ -7,7 +7,7 @@ const snackbarStatus = ref({})
// Event listeners bus
const emitter = inject('emitter')
emitter.on('snackbarScan', (snackbar) => {
emitter.on('snackbarShow', (snackbar) => {
snackbarShow.value = true
snackbarStatus.value = snackbar
})
@@ -21,4 +21,4 @@ emitter.on('snackbarScan', (snackbar) => {
<v-btn @click="snackbarShow=false" variant="text"><v-icon icon="mdi-close"/></v-btn>
</template>
</v-snackbar>
</template>
</template>

View File

@@ -6,7 +6,7 @@ const downloading = storeDownloading()
export async function downloadRom(rom, emitter, filesToDownload=[]) {
downloading.add(rom.file_name)
emitter.emit('snackbarScan', {'msg': `Downloading ${rom.file_name}...`, 'icon': 'mdi-download', 'color': 'green'})
emitter.emit('snackbarShow', {'msg': `Downloading ${rom.file_name}...`, 'icon': 'mdi-download', 'color': 'green'})
if(rom.multi){
const zip = new JSZip()
var zipFilename = `${rom.file_name}.zip`
@@ -33,4 +33,4 @@ export async function downloadRom(rom, emitter, filesToDownload=[]) {
downloading.remove(rom.file_name)
}
export async function downloadSave(rom) { console.log(`Downloading ${rom.file_name} save file`) }
export async function downloadSave(rom) { console.log(`Downloading ${rom.file_name} save file`) }

View File

@@ -0,0 +1,9 @@
import { io } from "socket.io-client";
const socket = io({
path: "/ws/socket.io/",
transports: ["websocket", "polling"],
autoConnect: false,
});
export default socket;

View File

@@ -59,10 +59,10 @@ async function updateRom(updatedData={...updatedRom.value}) {
.then((response) => {
rom.value = response.data.data
updatedRom.value = {...response.data.data}
emitter.emit('snackbarScan', {'msg': response.data.msg, 'icon': 'mdi-check-bold', 'color': 'green'})
emitter.emit('snackbarShow', {'msg': response.data.msg, 'icon': 'mdi-check-bold', 'color': 'green'})
emitter.emit('refreshGallery')
}).catch((error) => {
emitter.emit('snackbarScan', {'msg': error.response.data.detail, 'icon': 'mdi-close-circle', 'color': 'red'})
emitter.emit('snackbarShow', {'msg': error.response.data.detail, 'icon': 'mdi-close-circle', 'color': 'red'})
})
renameAsIGDB.value = false
updating.value = false
@@ -72,11 +72,11 @@ async function updateRom(updatedData={...updatedRom.value}) {
async function deleteRom() {
await deleteRomApi(rom.value.p_slug, deleteFromFs.value)
.then((response) => {
emitter.emit('snackbarScan', {'msg': response.data.msg, 'icon': 'mdi-check-bold', 'color': 'green'})
emitter.emit('snackbarShow', {'msg': response.data.msg, 'icon': 'mdi-check-bold', 'color': 'green'})
router.push(`/platform/${rom.value.p_slug}`)
}).catch((error) => {
console.log(error)
emitter.emit('snackbarScan', {'msg': error.response.data.detail, 'icon': 'mdi-close-circle', 'color': 'red'})
emitter.emit('snackbarShow', {'msg': error.response.data.detail, 'icon': 'mdi-close-circle', 'color': 'red'})
if (error.response.status == 404) { router.push(`/platform/${rom.value.p_slug}`) }
})
dialogDeleteRom.value = false

View File

@@ -1,142 +1,154 @@
<script setup>
import { ref, inject, onMounted } from 'vue'
import { onBeforeRouteUpdate, useRoute } from 'vue-router'
import { io } from "socket.io-client";
import { views } from '@/utils/utils.js'
import { fetchRomsApi } from '@/services/api.js'
import { storeFilter } from '@/stores/filter.js'
import { storeGalleryView } from '@/stores/galleryView.js'
import { normalizeString } from '@/utils/utils.js'
import FilterBar from '@/components/GameGallery/FilterBar.vue'
import GalleryViewBtn from '@/components/GameGallery/GalleryViewBtn.vue'
import GameCard from '@/components/GameGallery/Card/Base.vue'
import GameListHeader from '@/components/GameGallery/ListItem/Header.vue'
import GameListItem from '@/components/GameGallery/ListItem/Item.vue'
import { storeScanning } from '@/stores/scanning.js'
import { useDisplay } from "vuetify"
import { downloadRom, downloadSave } from '@/services/download.js'
import { storeDownloading } from '@/stores/downloading.js'
import BackgroundHeader from '@/components/GameDetails/BackgroundHeader.vue'
const { xs, mdAndDown, lgAndUp } = useDisplay()
import { ref, inject, onMounted } from "vue";
import { onBeforeRouteUpdate, useRoute } from "vue-router";
import { views } from "@/utils/utils.js";
import { fetchRomsApi } from "@/services/api.js";
import { storeFilter } from "@/stores/filter.js";
import { storeGalleryView } from "@/stores/galleryView.js";
import { normalizeString } from "@/utils/utils.js";
import FilterBar from "@/components/GameGallery/FilterBar.vue";
import GalleryViewBtn from "@/components/GameGallery/GalleryViewBtn.vue";
import GameCard from "@/components/GameGallery/Card/Base.vue";
import GameListHeader from "@/components/GameGallery/ListItem/Header.vue";
import GameListItem from "@/components/GameGallery/ListItem/Item.vue";
import { storeScanning } from "@/stores/scanning.js";
import socket from "@/utils/socket";
import { useDisplay } from "vuetify";
import { downloadRom, downloadSave } from "@/services/download.js";
import { storeDownloading } from "@/stores/downloading.js";
import BackgroundHeader from "@/components/GameDetails/BackgroundHeader.vue";
const { xs, mdAndDown, lgAndUp } = useDisplay();
// Props
const route = useRoute()
const route = useRoute();
// const sections = ['roms', 'firmwares']
const currentSection = ref('roms')
const roms = ref([])
const gettingRoms = ref(false)
const filter = storeFilter()
const romsFiltered = ref([])
const currentSection = ref("roms");
const roms = ref([]);
const gettingRoms = ref(false);
const filter = storeFilter();
const romsFiltered = ref([]);
// const firmwares = ["firmware_base", "firmware_bios"]
const galleryView = storeGalleryView()
const scanning = storeScanning()
const galleryView = storeGalleryView();
const scanning = storeScanning();
// Event listeners bus
const emitter = inject('emitter')
emitter.on('filter', () => { filterRoms() })
const emitter = inject("emitter");
emitter.on("filter", () => {
filterRoms();
});
// Functions
async function scan() {
scanning.set(true);
emitter.emit('snackbarScan', {'msg': `Scanning ${route.params.platform}...`, 'icon': 'mdi-check-bold', 'color': 'green'})
const socket = io({ path: '/ws/socket.io/', transports: ['websocket', 'polling'] })
socket.on("done", () => {
scanning.set(false)
emitter.emit('refreshGallery')
emitter.emit('snackbarScan', {'msg': "Scan completed successfully!", 'icon': 'mdi-check-bold', 'color': 'green'})
socket.close()
})
socket.on("done_ko", (msg) => {
scanning.set(false)
emitter.emit('snackbarScan', {'msg': `Scan couldn't be completed. Something went wrong: ${msg}`, 'icon': 'mdi-close-circle', 'color': 'red'})
socket.close()
})
socket.emit("scan", JSON.stringify([route.params.platform]), false)
scanning.set(true);
emitter.emit("snackbarShow", {
msg: `Scanning ${route.params.platform}...`,
icon: "mdi-loading mdi-spin",
color: "yellow",
});
if (!socket.connected) socket.connect();
socket.on("scan:done", () => {
scanning.set(false);
emitter.emit("refreshGallery");
emitter.emit("snackbarShow", {
msg: "Scan completed successfully!",
icon: "mdi-check-bold",
color: "green",
});
socket.disconnect();
});
socket.on("scan:done_ko", (msg) => {
scanning.set(false);
emitter.emit("snackbarShow", {
msg: `Scan couldn't be completed. Something went wrong: ${msg}`,
icon: "mdi-close-circle",
color: "red",
});
socket.disconnect();
});
socket.emit("scan", JSON.stringify([route.params.platform]), false);
}
function filterRoms() {
romsFiltered.value = roms.value.filter(rom => {
return normalizeString(rom.file_name).includes(filter.value)
})
romsFiltered.value = roms.value.filter((rom) => {
return normalizeString(rom.file_name).includes(filter.value);
});
}
async function fetchRoms(platform) {
gettingRoms.value = true
await fetchRomsApi(platform)
.then((response) => {
roms.value = response.data.data
filterRoms()
})
.catch((error) => {console.log(error);console.log(`Couldn't fetch roms for ${platform}`)})
gettingRoms.value = false
gettingRoms.value = true;
await fetchRomsApi(platform)
.then((response) => {
roms.value = response.data.data;
filterRoms();
})
.catch((error) => {
console.log(error);
console.log(`Couldn't fetch roms for ${platform}`);
})
.finally(() => {
gettingRoms.value = false;
});
}
onMounted(async () => { fetchRoms(route.params.platform)})
onBeforeRouteUpdate(async (to, _) => { fetchRoms(to.params.platform) })
onMounted(async () => {
fetchRoms(route.params.platform);
});
onBeforeRouteUpdate(async (to, _) => {
fetchRoms(to.params.platform);
});
</script>
<template>
<v-app-bar id="gallery-app-bar" elevation="0" density="compact">
<!-- <v-select item-title="name" :items="sections" v-model="currentSection" hide-details/> -->
<filter-bar/>
<gallery-view-btn/>
<v-btn @click="scan" rounded="0" variant="text" class="mr-0" icon="mdi-magnify-scan"/>
</v-app-bar>
<template v-if="gettingRoms">
<v-row class="fill-height justify-center align-center" no-gutters>
<v-progress-circular color="rommAccent1" :width="3" :size="70" indeterminate/>
</v-row>
<v-app-bar id="gallery-app-bar" elevation="0" density="compact">
<!-- <v-select item-title="name" :items="sections" v-model="currentSection" hide-details/> -->
<filter-bar />
<gallery-view-btn />
<v-btn @click="scan" rounded="0" variant="text" class="mr-0" icon="mdi-magnify-scan" />
</v-app-bar>
<template v-if="gettingRoms">
<v-row class="fill-height justify-center align-center" no-gutters>
<v-progress-circular color="rommAccent1" :width="3" :size="70" indeterminate />
</v-row>
</template>
<template v-else>
<template v-if="roms.length > 0">
<v-row v-show="galleryView.value != 2" id="card-view" no-gutters>
<v-col v-for="rom in romsFiltered" class="pa-1" :key="rom.file_name" :cols="views[galleryView.value]['size-cols']"
:xs="views[galleryView.value]['size-xs']" :sm="views[galleryView.value]['size-sm']"
:md="views[galleryView.value]['size-md']" :lg="views[galleryView.value]['size-lg']">
<game-card :rom="rom" />
</v-col>
</v-row>
<v-row v-show="galleryView.value == 2" id="list-view" no-gutters>
<v-col :cols="views[galleryView.value]['size-cols']" :xs="views[galleryView.value]['size-xs']"
:sm="views[galleryView.value]['size-sm']" :md="views[galleryView.value]['size-md']"
:lg="views[galleryView.value]['size-lg']">
<v-table class="bg-secondary">
<game-list-header />
<v-divider class="border-opacity-100 mb-4 ml-2 mr-2" color="rommAccent1" :thickness="1" />
<tbody>
<game-list-item v-for="rom in romsFiltered" :key="rom.file_name" :rom="rom" />
</tbody>
</v-table>
</v-col>
</v-row>
</template>
<template v-else>
<template v-if="roms.length>0">
<v-row v-show="galleryView.value != 2" id="card-view" no-gutters>
<v-col v-for="rom in romsFiltered" class="pa-1" :key="rom.file_name"
:cols="views[galleryView.value]['size-cols']"
:xs="views[galleryView.value]['size-xs']"
:sm="views[galleryView.value]['size-sm']"
:md="views[galleryView.value]['size-md']"
:lg="views[galleryView.value]['size-lg']">
<game-card :rom="rom"/>
</v-col>
</v-row>
<v-row v-show="galleryView.value == 2" id="list-view" no-gutters>
<v-col
:cols="views[galleryView.value]['size-cols']"
:xs="views[galleryView.value]['size-xs']"
:sm="views[galleryView.value]['size-sm']"
:md="views[galleryView.value]['size-md']"
:lg="views[galleryView.value]['size-lg']">
<v-table class="bg-secondary">
<game-list-header/>
<v-divider class="border-opacity-100 mb-4 ml-2 mr-2" color="rommAccent1" :thickness="1"/>
<tbody>
<game-list-item v-for="rom in romsFiltered" :key="rom.file_name" :rom="rom"/>
</tbody>
</v-table>
</v-col>
</v-row>
</template>
<template v-else>
<v-row class="fill-height justify-center align-center" no-gutters>
<div class="text-h6">Feels cold here... <v-icon>mdi-emoticon-sad</v-icon></div>
</v-row>
</template>
<v-row class="fill-height justify-center align-center" no-gutters>
<div class="text-h6">
Feels cold here... <v-icon>mdi-emoticon-sad</v-icon>
</div>
</v-row>
</template>
</template>
</template>
<style scoped>
#gallery-app-bar { z-index: 999 !important; }
#gallery-app-bar {
z-index: 999 !important;
}
</style>

View File

@@ -1,95 +1,94 @@
<script setup>
import { ref, inject } from "vue"
import { io } from "socket.io-client";
import { storePlatforms } from '@/stores/platforms.js'
import { storeScanning } from '@/stores/scanning.js'
import { ref, inject } from "vue";
import { storePlatforms } from "@/stores/platforms.js";
import { storeScanning } from "@/stores/scanning.js";
import socket from "@/utils/socket";
// Props
const platforms = storePlatforms()
const platformsToScan = ref([])
const scanning = storeScanning()
const scanningPlatform = ref("")
const scannedPlatforms = ref([])
const completeRescan = ref(false)
const platforms = storePlatforms();
const platformsToScan = ref([]);
const scanning = storeScanning();
const scanningPlatform = ref("");
const scannedPlatforms = ref([]);
const completeRescan = ref(false);
// Event listeners bus
const emitter = inject('emitter')
const emitter = inject("emitter");
// Functions
async function scan() {
scanning.set(true);
scannedPlatforms.value = []
const socket = io({ path: '/ws/socket.io/', transports: ['websocket', 'polling'] })
socket.on("scanning_platform", (platform) => { scannedPlatforms.value.push({'p_name': platform[0], 'p_slug': platform[1], 'r': []}); scanningPlatform.value = platform[1] })
socket.on("scanning_rom", (r) => { scannedPlatforms.value.forEach(e => { if(e['p_slug'] == scanningPlatform.value){ e['r'].push(r) } }) })
socket.on("done", () => {
scanning.set(false)
emitter.emit('refresPlatforms')
emitter.emit('snackbarScan', {'msg': "Scan completed successfully!", 'icon': 'mdi-check-bold', 'color': 'green'})
socket.close()
})
socket.on("done_ko", (msg) => {
scanning.set(false)
emitter.emit('snackbarScan', {'msg': `Scan couldn't be completed. Something went wrong: ${msg}`, 'icon': 'mdi-close-circle', 'color': 'red'})
socket.close()
})
socket.emit("scan", JSON.stringify(platformsToScan.value.map(p => p.fs_slug)), completeRescan.value)
scanning.set(true);
scannedPlatforms.value = [];
if (!socket.connected) socket.connect();
socket.on("scan:scanning_platform", (platform) => {
scannedPlatforms.value.push({
p_name: platform[0],
p_slug: platform[1],
r: [],
});
scanningPlatform.value = platform[1];
});
socket.on("scan:scanning_rom", (r) => {
scannedPlatforms.value.forEach((e) => {
if (e["p_slug"] == scanningPlatform.value) {
e["r"].push(r);
}
});
});
socket.on("scan:done", () => {
scanning.set(false);
emitter.emit("refresPlatforms");
emitter.emit("snackbarShow", {
msg: "Scan completed successfully!",
icon: "mdi-check-bold",
color: "green",
});
socket.disconnect();
});
socket.on("scan:done_ko", (msg) => {
scanning.set(false);
emitter.emit("snackbarShow", {
msg: `Scan couldn't be completed. Something went wrong: ${msg}`,
icon: "mdi-close-circle",
color: "red",
});
socket.disconnect();
});
socket.emit(
"scan",
JSON.stringify(platformsToScan.value.map((p) => p.fs_slug)),
completeRescan.value
);
}
</script>
<template>
<v-row class="pa-4" no-gutters>
<v-select label="Platforms" item-title="name" v-model="platformsToScan" :items="platforms.value" variant="outlined"
density="comfortable" multiple return-object clearable hide-details rounded="0" chips />
</v-row>
<v-row class="pa-4" no-gutters>
<v-select
label="Platforms"
item-title="name"
v-model="platformsToScan"
:items="platforms.value"
variant="outlined"
density="comfortable"
multiple
return-object
clearable
hide-details
rounded="0"
chips/>
</v-row>
<v-row class="pa-4" no-gutters>
<v-checkbox v-model="completeRescan" label="Complete Rescan" prepend-icon="mdi-cached"
hint="Rescan every rom, including already scanned roms" persistent-hint />
</v-row>
<v-row class="pa-4" no-gutters>
<v-checkbox
v-model="completeRescan"
label="Complete Rescan"
prepend-icon="mdi-cached"
hint="Rescan every rom, including already scanned roms"
persistent-hint/>
</v-row>
<v-row class="pa-4" no-gutters>
<v-btn @click="scan()" :disabled="scanning.value" prepend-icon="mdi-magnify-scan" rounded="0">
<span v-if="!scanning.value">Scan</span>
<v-progress-circular v-show="scanning.value" color="rommAccent1" class="ml-3 mr-2" :width="2" :size="20"
indeterminate />
</v-btn>
</v-row>
<v-row class="pa-4" no-gutters>
<v-btn
@click="scan()"
:disabled="scanning.value"
prepend-icon="mdi-magnify-scan"
rounded="0">
<span v-if="!scanning.value">Scan</span>
<v-progress-circular
v-show="scanning.value"
color="rommAccent1"
class="ml-3 mr-2"
:width="2"
:size="20"
indeterminate/>
</v-btn>
</v-row>
<v-divider class="border-opacity-100 ma-4" color="rommAccent1" :thickness="1" />
<v-divider class="border-opacity-100 ma-4" color="rommAccent1" :thickness="1"/>
<v-row no-gutters class="align-center pa-4" v-for="d in scannedPlatforms">
<v-col>
<v-avatar :rounded="0" size="40"><v-img :src="`/assets/platforms/${d['p_slug']}.ico`"></v-img></v-avatar>
<span class="text-body-2 ml-5"> {{ d['p_name'] }}</span>
<v-list-item v-for="r in d['r']" class="text-body-2" disabled> - {{ r }}</v-list-item>
</v-col>
</v-row>
</template>
<v-row no-gutters class="align-center pa-4" v-for="d in scannedPlatforms">
<v-col>
<v-avatar :rounded="0" size="40"><v-img :src="`/assets/platforms/${d['p_slug']}.ico`"></v-img></v-avatar>
<span class="text-body-2 ml-5"> {{ d["p_name"] }}</span>
<v-list-item v-for="r in d['r']" class="text-body-2" disabled>
- {{ r }}</v-list-item>
</v-col>
</v-row>
</template>