Fixed up old table search

Added a toggle for table search
added more info page
god knows what else
This commit is contained in:
Alexandra
2025-05-26 14:31:12 -06:00
parent 91e5feefc6
commit e94a21efcc
18 changed files with 420 additions and 122 deletions

View File

@@ -16,6 +16,11 @@
<a href="https://myrient.erista.me/donate/" class="btn btn-secondary"><%= __('about.donate') %></a>
</div>
<div class="mb-4 border-top pt-3">
<h5><%= __('about.metadata.title') %></h5>
<p><%= __('about.metadata.description', {metadata_source: 'IGDB'}) %></p>
</div>
<div class="mb-4 border-top pt-3">
<h5><%= __('about.emulator.title') %></h5>
<% if (process.env.EMULATOR_ENABLED === 'true') { %>

99
views/pages/info.ejs Normal file
View File

@@ -0,0 +1,99 @@
<%
const metadata = romFile.metadata || new Object()
const file = romFile.file || new Object()
const coverUrl = metadata.coverartid ? `/proxy-image?url=https://images.igdb.com/igdb/image/upload/t_cover_big/${metadata.coverartid}.webp` : "/public/images/coverart/nocoverart.png"
let images = []
if(metadata.screenshots){
images = JSON.parse(metadata.screenshots).map((im) => `/proxy-image?url=https://images.igdb.com/igdb/image/upload/t_720p/${im}.webp`)
}
let videos = []
if(metadata.videos){
videos = JSON.parse(metadata.videos)
}
%>
<link rel="stylesheet" href="/public/css/info.css">
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-12 col-lg-10 col-xl-8">
<div class="col-12 text-center">
<h2 class="text-white"><%= metadata.title %></h2>
<p class="text-secondary"><%= file.category %></p>
</div>
<div class="row ml-1">
<img class="coverart col-md d-block mx-auto" src="<%= coverUrl %>" href="<%= file.path %>">
<div class="col-md">
<% if(metadata.rating) {%>
<div>
<%
const fullstars = Math.floor(metadata.rating / 20)
const halfstars = Math.floor(metadata.rating % 20 / 10)
const nostars = 5 - fullstars - halfstars
const nostarstring = '<i class="bi bi-star"></i>'
const fullstarstring = '<i class="bi bi-star-fill"></i>'
const halfstarstring = '<i class="bi bi-star-half"></i>'
let stars = ''
for(let x = 0; x < fullstars ; x++){
stars += fullstarstring
}
if(halfstars){
stars += halfstarstring
}
for(let x = 0; x < nostars; x++){
stars += nostarstring
}
%>
<p> <span class="stars"><%- stars%></span> (<%= Math.floor(metadata.rating) %>%)</p>
</div>
<% } %>
<% if(metadata.developers) {%>
<div>
<p><span class="info">Developed by:</span> <%= JSON.parse(metadata.developers).join(", ") %></p>
</div>
<% } %>
<% if(metadata.publishers) {%>
<div>
<p><span class="info">Published by:</span> <%= JSON.parse(metadata.publishers).join(", ") %></p>
</div>
<% } %>
<% if(metadata.releasedate) {%>
<div>
<p><span class="info">Release date:</span> <%= metadata.releasedate %></p>
</div>
<% } %>
<% if(file.region) {%>
<div>
<p><span class="info">Region:</span> <%= file.region %></p>
</div>
<% } %>
<% if(metadata.genre) {%>
<div>
<p><span class="info">Genre:</span> <%= JSON.parse(metadata.genre).join(", ") %></p>
</div>
<% } %>
<% if(metadata.gamemodes) {%>
<div>
<p><span class="info">Gameplay modes:</span> <%= JSON.parse(metadata.gamemodes).join(", ") %></p>
</div>
<% } %>
<div class="">
<p class="description"><%= metadata.description %></p>
</div>
<div>
<p class="actions">
<a class="btn btn-sm btn-secondary" href="<%= file.path %>">Download</a>
<% if (process.env.EMULATOR_ENABLED === 'true') { %>
<% if (isEmulatorCompatible(file.category)) { %>
<a class="btn btn-sm btn-secondary" href="/play/<%= file.id %>"><%= __('emulator.play')%></a></a> <% } else { %>
<button class="btn btn-sm btn-secondary" disabled><%= __('emulator.not_available') %></button>
<% }
}%>
</div>
</div>
</div>
<div class="row col-md container mx-auto">
<%- include("../partials/carousel", {images: images, videos: videos})%>
</div>
</div>
</div>
</div>

View File

@@ -4,12 +4,6 @@
pageNum = 1
}
%>
<script src='https://code.jquery.com/jquery-3.7.1.js' crossorigin="anonymous"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js' crossorigin="anonymous"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.2/js/bootstrap.min.js' crossorigin="anonymous"></script>
<script src='https://cdn.datatables.net/2.1.8/js/dataTables.js' crossorigin="anonymous"></script>
<script src='https://cdn.datatables.net/2.1.8/js/dataTables.bootstrap4.js' crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.datatables.net/2.1.8/css/dataTables.bootstrap4.css" crossorigin="anonymous">
<link rel="stylesheet" href="/public/css/result.css">
<div class="row w-100 m-0">
<form class="ml-2 form-inline w-100" action="/search">

View File

@@ -1,12 +1,8 @@
<%
let pageCount = Math.ceil(results.items.length / 100)
pageCount = pageCount ? pageCount : 1 //always ensure 1 page
if(pageNum > pageCount){
pageNum = 1
}
let entryStart = Math.floor((pageNum - 1) * 100)
let entryEnd = entryStart + 100
entryEnd = entryEnd > results.items.length ? results.items.length : entryEnd
%>
<script src='https://code.jquery.com/jquery-3.7.1.js' crossorigin="anonymous"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js' crossorigin="anonymous"></script>
@@ -14,6 +10,7 @@
<script src='https://cdn.datatables.net/2.1.8/js/dataTables.js' crossorigin="anonymous"></script>
<script src='https://cdn.datatables.net/2.1.8/js/dataTables.bootstrap4.js' crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.datatables.net/2.1.8/css/dataTables.bootstrap4.css" crossorigin="anonymous">
<link rel="stylesheet" href="/public/css/resultsold.css">
<div class="row w-100 m-0">
<form class="ml-2 form-inline w-100" action="/search">
<div class="w-100 align-items-center">
@@ -31,7 +28,7 @@
<ul class="SuggestionList col-sm-12" id="suggestionList" style="width: 50%;left: 195px;"></ul>
</div>
<p class="m-2">
<%= __('search.found_plural', { count: results.items.length }) %> <%= __('search.in_seconds', { seconds: results.elapsed }) %>.
<%= __('search.found_plural', { count: count }) %> <%= __('search.in_seconds', { seconds: elapsed }) %>.
<%= indexing ? __('search.indexing') : "" %>
<% if (settings.hideNonGame) { %>
<span class="badge badge-info" data-toggle="tooltip" data-placement="top" title="<%= __('settings.extras.hide_non_game.tooltip') %>">
@@ -43,55 +40,55 @@
</form>
<div class="col-sm-12 w-100 mt-3">
<p><%= __('search.displaying_results', { start: entryStart, end: entryEnd }) %></p>
<p><%= __('search.displaying_results', { start: ((pageNum - 1) * 100), end: pageNum * 100 < count ? pageNum * 100 : count }) %></p>
<table class="table text-white table-bordered" id="results">
<thead>
<tr>
<th class="dt-orderable"><span><%= __('results.table.name') %></span><span class="dt-column-order"></span></th>
<th><%= __('results.table.group') %></th>
<th><%= __('results.table.category') %></th>
<th><%= __('results.table.region') %></th>
<th><%= __('results.table.type') %></th>
<th><%= __('results.table.size') %></th>
<th><%= __('results.table.date') %></th>
<th><%= __('results.table.score') %></th>
<th class="" id="filename"><span><%= __('results.table.name') %></span><span class="dt-column-order"></span></th>
<th class="" id="group"><span><%= __('results.table.group') %></span><span class="dt-column-order"></span></th>
<th class="" id="category"><span><%= __('results.table.category') %></span><span class="dt-column-order"></span></th>
<th class="" id="region"><span><%= __('results.table.region') %></span><span class="dt-column-order"></span></th>
<th class="" id="type"><span><%= __('results.table.type') %></span><span class="dt-column-order"></span></th>
<th class="" id="size"><span><%= __('results.table.size') %></span><span class="dt-column-order"></span></th>
<th class="" id="date"><span><%= __('results.table.date') %></span><span class="dt-column-order"></span></th>
<th class="" id="score"><span><%= __('results.table.score') %></span><span class="dt-column-order"></span></th>
<% if (process.env.EMULATOR_ENABLED === 'true') { %>
<th><%= __('results.table.play') %></th>
<% } %>
</tr>
</thead>
<% for (let x = entryStart; x < entryEnd; x++) { %>
<% for (let x = 0; x < results.length; x++) { %>
<tr>
<td>
<a href="<%= results.items[x].path %>">
<%= results.items[x].filename %>
<a href="<%= results[x].file.path %>">
<%= results[x].file.filename %>
</a>
</td>
<td>
<%= results.items[x].group %>
<%= results[x].file.group %>
</td>
<td>
<%= results.items[x].category %>
<%= results[x].file.category %>
</td>
<td>
<%= results.items[x].region %>
<%= results[x].file.region %>
</td>
<td>
<%= results.items[x].type %>
<%= results[x].file.type %>
</td>
<td>
<%= results.items[x].size %>
<%= results[x].file.size %>
</td>
<td>
<%= results.items[x].date %>
<%= results[x].file.date %>
</td>
<td>
<%= results.items[x].score.toFixed(2) %>
<%= results[x].score.toFixed(2) %>
</td>
<% if (process.env.EMULATOR_ENABLED === 'true') { %>
<td>
<% if (isEmulatorCompatible(results.items[x].category)) { %>
<a href="/play/<%= results.items[x].id %>" class="btn btn-sm btn-secondary"><%= __('emulator.play') %></a>
<% if (isEmulatorCompatible(results[x].category)) { %>
<a href="/play/<%= results[x].id %>" class="btn btn-sm btn-secondary"><%= __('emulator.play') %></a>
<% } else { %>
<button class="btn btn-sm btn-secondary" disabled><%= __('emulator.not_available') %></button>
<% } %>
@@ -137,7 +134,9 @@
<li class="dt-paging-button page-item <%= pageNum == 1 ? 'disabled' : '' %>"><a <%= pageNum != 1 ? `href=${urlPrefix + (pageNum - 1)}` : '' %> class="page-link previous" aria-controls="results" aria-disabled="true" aria-label="Previous" data-dt-idx="previous" tabindex="-1"></a></li>
<li class="dt-paging-button page-item <%= pageNum == 1 ? 'active' : '' %>"><a href="<%= urlPrefix + 1 %>" class="page-link" aria-controls="results" aria-current="page" data-dt-idx="0">1</a></li>
<%- pageNum >= 5 ? ellipsesElem : '' %>
<% for(let x = pageRange.lower; x <= pageRange.upper; x++){ %>
<% for(let x = pageRange.lower; x <= pageRange.upper; x++){
if(x == pageCount) break;
%>
<li class="dt-paging-button page-item <%= pageNum == x ? 'active' : '' %>"><a href="<%= urlPrefix + x %>" class="page-link" aria-controls="results" aria-current="page" data-dt-idx="<%= x - 1 %>"><%= x %></a></li>
<% } %>
<%- pageNum <= pageCount - 5 ? ellipsesElem : '' %>
@@ -152,31 +151,28 @@
</div>
</div>
<script defer>
/*resultTable = new DataTable('#results', {
"order": [[7, 'desc']],
"columns": [
{ "data": "name" }, // Name
{ "data": "group" }, // Group
{ "data": "category" }, // Category
{ "data": "region" }, // Region
{ "data": "type" }, // Type
{ "data": "size" }, // Size
{ "data": "date" }, // Date
{ "data": "score" }, // Search Score
<% if (process.env.EMULATOR_ENABLED === 'true') { %>
{ "data": "play", "orderable": false } // Play button column
<% } %>
],
"lengthMenu": [100, { label: 'All', value: -1 }, 50, 25, 10],
"paging": false,
"filter": false,
"layout": {
"bottomStart": ''
}
});*/
// Initialize tooltips
$(function () {
$('[data-toggle="tooltip"]').tooltip()
});
// URLParams = new URLSearchParams(window.location.search);
// if(!URLParams.get('o')){
// // URLParams.set('o', 'score:asc');
// // $('#score .dt-column-order').addClass('order-asc')
// }
// else{
// let sorting = URLParams.get('o').split(':');
// $(`#${sorting[0]} .dt-column-order`).addClass(`order-${sorting[1]}`)
// }
// $(".dt-orderable").each(function( i ){
// $( this ).on( "click", function (){
// if($(this).find('.dt-column-order').hasClass('order-asc')){
// URLParams.set('o', `${$(this).attr('id')}:desc`)
// }
// else if($(this).find('.dt-column-order').hasClass('order-desc')){
// URLParams.delete('o')
// }
// else {
// URLParams.set('o', `${$(this).attr('id')}:asc`)
// }
// window.location = location.protocol + '//' + location.host + location.pathname + '?' + URLParams.toString()
// })
// })
</script>

View File

@@ -48,11 +48,15 @@
<label class="checkbox-inline p-1">
<input type="checkbox" id="combineWith" value="AND">
<%= __('settings.extras.match_all.label') %> <i class="bi bi-question-circle" data-toggle="tooltip" data-placement="top" title="<%= __('settings.extras.match_all.tooltip') %>"></i>
</label>
</label><br>
<label class="checkbox-inline p-1">
<input type="checkbox" id="hideNonGame" value="true">
<%= __('settings.extras.hide_non_game.label') %> <i class="bi bi-question-circle" data-toggle="tooltip" data-placement="top" title="<%= __('settings.extras.hide_non_game.tooltip') %>"></i>
</label>
<label class="checkbox-inline p-1">
<input type="checkbox" id="useOldResults" value="true">
<%= __('settings.extras.use_old_results.label') %> <i class="bi bi-question-circle" data-toggle="tooltip" data-placement="top" title="<%= __('settings.extras.use_old_results.tooltip') %>"></i>
</label>
</div>
</div>
</div>

View File

@@ -0,0 +1,45 @@
<%
if(typeof videos === 'undefined'){
let videos = []
}
if(typeof images === 'undefined'){
let images = []
}
%>
<script src="/public/js/video.js" defer></script>
<div id="carouselIndicators" class="carousel slide slideshow-container" data-ride="carousel" data-interval="10000">
<ol class="carousel-indicators">
<li data-target="#carouselIndicators" data-slide-to="0" class="active"></li>
<% for(let x = 1; x < images.length; x++){%>
<li data-target="#carouselIndicators" data-slide-to="<%= x %>"></li>
<% } %>
<% for(let x = 0; x < videos.length; x++){%>
<li data-target="#carouselIndicators" data-slide-to="<%= x + images.length %>"></li>
<% } %>
</ol>
<div class="carousel-inner">
<div class="carousel-item active">
<img class="d-block w-100" src="<%= images[0] %>">
</div>
<% for(let x = 1; x < images.length; x++){%>
<div class="carousel-item">
<img class="d-block w-100" src="<%= images[x] %>">
</div>
<% } %>
<% for(let x = 0; x < videos.length; x++){ %>
<div class="carousel-item carousel-video">
<div class="d-block col-md carousel-video-inner embed-responsive embed-responsive-16by9">
<div class="video-player" id="player<%= x %>" data-video-id="<%= videos[x] %>"></div>
</div>
</div>
<% } %>
</div>
<a class="carousel-control-prev" href="#carouselIndicators" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#carouselIndicators" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>

View File

@@ -9,21 +9,31 @@
</div>
<div class="col-md">
<p class="title"><a href="<%= file.path %>"><%= metadata.title || file.filename %></a></p>
<p class="info"><span class="infoitem badge badge-secondary">Released: <%= metadata.releasedate || file.date %></span>
<span class="infoitem badge badge-secondary">Region: <%= file.region %></span>
<span class="infoitem badge badge-secondary">Platform: <%= file.category %></span>
<p class="info"><span class="infoitem badge badge-secondary"><%= __('search.released') %>: <%= metadata.releasedate || file.date %></span>
<span class="infoitem badge badge-secondary"><%= __('search.region') %>: <%= file.region %></span>
<span class="infoitem badge badge-secondary"><%= __('search.platform') %>: <%= file.category %></span>
<% if(metadata.genre){ %>
<span class="infoitem badge badge-secondary">Genres: <%= JSON.parse(metadata.genre).join(' / ') %></span>
<% } %>
</p>
<p class="description"><%= metadata.description || "No description was found."//todo: localize %></p>
<% if(metadata.title) {%>
<p class="description"><%= metadata.description || __('search.no_description') %></p>
<% } else { %>
<p class="description"><%= __('search.no_metadata') %></p>
<% } %>
<% if(metadata.title) {%>
<p class="file">Filename: <%= file.filename %></p>
<% } %>
<p class="group">Release Group: <%= file.group %></p>
<p class="actions">
<a class="btn btn-sm btn-secondary" href="">More Info</a>
<a class="btn btn-sm btn-secondary" href="<%= file.path %>">Download</a>
<a class="btn btn-sm btn-secondary" href="/play/<%= file.id %>">Play In Browser</a></a>
<a class="btn btn-sm btn-secondary" href="/info/<%=file.id %>">More Info</a>
<a class="btn btn-sm btn-secondary" href="<%= file.path %>">Download</a>
<% if (process.env.EMULATOR_ENABLED === 'true') { %>
<% if (isEmulatorCompatible(file.category)) { %>
<a class="btn btn-sm btn-secondary" href="/play/<%= file.id %>"><%= __('emulator.play')%></a></a> <% } else { %>
<button class="btn btn-sm btn-secondary" disabled><%= __('emulator.not_available') %></button>
<% }
}%>
</div>
</div>

46
views/public/css/info.css Normal file
View File

@@ -0,0 +1,46 @@
.info {
font-weight: bold;
color: #f0a400;
}
.stars {
color: #f0a400;
}
.embed-responsive iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.embed-responsive {
position: relative;
overflow: hidden;
width: 100%;
}
.slideshow-container {
position: relative;
width: 100%;
margin: auto;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
.container {
background-color: none;
margin-bottom: 50px;
margin-top: 10px;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.coverart{
object-fit: contain;
height: auto;
width: 240px;
}

View File

@@ -0,0 +1,42 @@
.dt-orderable {
position: absolute;
}
.dt-column-order {
position: absolute;
width: 12px;
color: #ffffff20;
line-height: 9px;
font-size: 0.8em;
right: 12px;
top: 0;
bottom: 0;
}
.table thead > tr > th {
position: relative;
padding-right: 30px;
}
thead > tr > th.dt-orderable span.dt-column-order::before {
position: absolute;
display: block;
bottom: 50%;
content: "▲";
content: "▲"/"";
}
thead > tr > th.dt-orderable span.dt-column-order::after {
position: absolute;
display: block;
top: 50%;
content: "▼";
content: "▼"/"";
}
thead > tr > th.dt-orderable span.dt-column-order.order-asc::before{
opacity: 1;
color: #ffffffff;
}
thead > tr > th.dt-orderable span.dt-column-order.order-desc::after{
opacity: 1;
color: #ffffffff;
}

View File

@@ -144,43 +144,3 @@ td a {
position: relative;
z-index: 9999 !important;
}
.dt-orderable {
position: absolute;
}
.dt-column-order {
position: absolute;
width: 12px;
opacity: 0.125;
line-height: 9px;
font-size: 0.8em;
right: 12px;
top: 0;
bottom: 0;
}
.table thead > tr > th {
position: relative;
padding-right: 30px;
}
thead > tr > th.dt-orderable span.dt-column-order::before {
position: absolute;
display: block;
bottom: 50%;
content: "▲";
content: "▲"/"";
}
thead > tr > th.dt-orderable span.dt-column-order::after {
position: absolute;
display: block;
top: 50%;
content: "▼";
content: "▼"/"";
}
thead > tr > th.dt-orderable span.dt-column-order.order-desc::before{
opacity: 1;
}
thead > tr > th.dt-orderable span.dt-column-order.order-asc::after{
opacity: 1;
}

View File

@@ -32,10 +32,12 @@
if(typeof settings.fuzzy == 'undefined') {settings.fuzzy = defaults.fuzzy}
if(typeof settings.prefix == 'undefined') {settings.prefix = defaults.prefix}
if(typeof settings.hideNonGame == 'undefined') {settings.hideNonGame = defaults.hideNonGame}
if(typeof settings.hideNonGame == 'undefined') {settings.useOldResults = defaults.useOldResults}
document.getElementById('combineWith').checked = settings.combineWith ? true : false
document.getElementById('fuzzy').value = settings.fuzzy
document.getElementById('prefix').checked = settings.prefix
document.getElementById('hideNonGame').checked = settings.hideNonGame
document.getElementById('useOldResults').checked = settings.useOldResults
}
function saveSettings(){
@@ -50,6 +52,7 @@
settings.fuzzy = parseFloat (document.getElementById('fuzzy').value)
settings.prefix = document.getElementById('prefix').checked
settings.hideNonGame = document.getElementById('hideNonGame').checked
settings.useOldResults = document.getElementById('useOldResults').checked
localStorage.setItem('settings', JSON.stringify(settings))
window.location.href = '/'
}

32
views/public/js/video.js Normal file
View File

@@ -0,0 +1,32 @@
// index.js
const videos = [];
const tag = document.createElement("script");
const firstScriptTag = document.getElementsByTagName("script")[0];
tag.src = "https://www.youtube.com/iframe_api";
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// YouTube wants this function, don't rename it
function onYouTubeIframeAPIReady() {
const slides = Array.from(document.querySelectorAll(".carousel-item"));
slides.forEach((slide, index) => {
// does this slide have a video?
const video = slide.querySelector(".video-player");
if (video && video.dataset) {
const player = createPlayer({
id: video.id,
videoId: video.dataset.videoId,
});
videos.push({ player, index });
}
});
}
function createPlayer(playerInfo) {
return new YT.Player(playerInfo.id, {
videoId: playerInfo.videoId,
playerVars: {
showinfo: 0,
},
});
}