discord-media-server
Version:
Self-hosted Discord bot for scanning and serving information on local media files.
342 lines (293 loc) • 12.2 kB
HTML
<html>
<head>
<title>Media Dashboard</title>
<style>
.hidden { display: none; }
.flex { display:flex; }
.textInput { width: 500px; }
body { font-family: sans-serif; padding: 20px; background: #f4f4f4; }
header h1 { display: inline; margin-left: 10px; }
h1 { margin-bottom: 10px; }
nav h5 { margin-right: 15px; text-decoration: none; color: #0077cc; display: inline; cursor: pointer; }
nav h5:hover { color: #0007cc; }
input, select, button { margin: 5px; padding: 5px; width: auto;}
ul { list-style: none; padding-left: 0; }
li { background: white; padding: 10px; margin-bottom: 5px; border-radius: 4px; }
fieldset { margin-top: 15px; }
#videoModal {position:fixed;top:0;left:0;width:100%;height:100%;
background:rgba(0,0,0,0.8);align-items:center;
justify-content:center;z-index:1000;
}
.playBtn { cursor:pointer; }
</style>
<script src="/js/jquery-3.6.0.min.js"></script>
</head>
<body>
<header>
<img src="/logo.png" alt="Logo"><h1>Media Dashboard</h1>
</header>
<nav>
<h5 class="nav-buttons" id="nav-home">Home</h5>
<h5 class="nav-buttons" id="nav-movies">Recent Movies</h5>
<h5 class="nav-buttons" id="nav-settings">Settings</h5>
<h5 class="buttons" id="search" >Search</h5>
</nav>
<div id="media-search" class="media">
<form id="searchForm" onsubmit="doSearch(event)">
<input name="title" placeholder="Title">
<input name="year" placeholder="Year">
<input name="format" placeholder="Format (e.g. mkv)">
<button type="submit" >Search</button>
</form>
<ul id="mediaList"></ul>
</div>
<hr>
<p id="total"></p>
<div id="data"></div>
<!-- Video Modal -->
<div id="videoModal" class="hidden">
<div style="background:#000;padding:10px;max-width:80%;max-height:80%;position:relative;">
<video id="videoPlayer" controls style="width:100%;height:auto;max-height:80vh;">
<source id="videoSource" src="" type="video/mp4">
Your browser does not support the video tag.
</video>
<button id="closeModal"
style="position:absolute;top:5px;right:5px;background:red;color:white;border:none;
padding:5px 10px;cursor:pointer;">
✖
</button>
</div>
</div>
<script>
$(document).ready(function () {
$('#media-search').hide();
$('body').on('click', '.nav-buttons', function(){
//event.stopPropagation();
//event.stopImmediatePropagation();
let id = $(this).attr('id');
$('.media').hide();
$('#total').html('');
if (id == 'nav-edit' || id == 'edit') {
//$('#media-edit').show();
fetch('/api/config')
.then(res => res.json())
.then(env => {
$('#data').html(`
<h1>Discord Media Server Setup</h1>
<form method="POST" action="/submit">
<label><b>Media Directory:</b> <input class="textInput" name="mediaDirectory" ${env.MEDIA_DIR == '/media' ? 'disabled':''} value="${env.MEDIA_DIR || ''}" required></label><br>
<fieldset>
<legend><b>Database Information</b></legend>
<label><b>Type:</b>
<select id="dbType" name="dbType">
<option value="sqlite" ${env.DB_TYPE === 'sqlite' ? 'selected' : ''}>SQLite</option>
<option value="mysql" ${env.DB_TYPE === 'mysql' ? 'selected' : ''}>MySQL</option>
</select>
</label><br>
<label><b>Name:</b> <input name="dbName" value="${env.DB_NAME || ''}"></label><br>
<div id="mysqlFields" style="display:${env.DB_TYPE === 'mysql' ? 'block' : 'none'};">
<label><b>Host:</b> <input name="dbHost" value="${env.DB_HOST || ''}"></label><br>
<label><b>User:</b> <input name="dbUser" value="${env.DB_USER || ''}"></label><br>
<label><b>Pass:</b> <input name="dbPass" value="${env.DB_PASS || ''}" type="password"></label><br>
</div>
</fieldset>
<fieldset>
<legend><b>API Information</b></legend>
<label><b>Discord Bot Token:</b> <input class="textInput" name="discordToken" value="${env.DISCORD_TOKEN || ''}" type="password"></label><br>
<label><b>Discord Client ID:</b> <input class="textInput" name="discordClientId" value="${env.DISCORD_CLIENT_ID || ''}" type="password"></label><br>
<label><b>TMDb API Key:</b> <input class="textInput" name="tmdbKey" value="${env.TMDB_API_KEY || ''}" type="password"></label><br>
</fieldset>
<button type="submit">Save</button>
<button class="nav-buttons" id="cancel">Cancel</button>
</form>
`);
});
} else if (id == 'nav-home' || id == 'cancel') {
//loadStats();
//$('#media-home').show();
$('#data').html(`
<h1>Discord Media Server Setup</h1>
`);
} else if (id == 'nav-settings') {
fetch('/api/config')
.then(res => res.json())
.then(env => {
$('#data').html(`
<h1>Discord Media Server Setup</h1>
<fieldset>
<label><b>Media Directory:</b> ${env.MEDIA_DIR || ''}</label><br>
<label><b>DB Type:</b> ${env.DB_TYPE || ''}</label><br>
<label><b>DB Name:</b> ${env.DB_NAME || ''}</label><br>
<button class="nav-buttons" id="edit">Edit</button>
</fieldset>
`);
/*
<form action="/launch" method="get">
<button type="submit" id='getBot' name='startBot' value=${botRunning ? 'reset' : 'start'}>${botRunning ? 'Restart Bot' : 'Turn on Bot'}</button>
</form>
<button type="submit" id='startBot' value=${botRunning ? 'reset' : 'start'}>${botRunning ? 'Restart' : 'Off'}</button>
<div id='status'></div>
*/
});
} else if (id == 'nav-refresh') {
refreshIndex();
$('#media-refresh').show();
} else if (id == 'nav-movies') {
fetch('/dashboard/movies')
.then(res => res.json())
.then(data => {
document.getElementById('total').textContent = `Recently added Movies: ${data.total}`;
/*const list = document.getElementById('data');
list.innerHTML = '';
data.media.forEach(m => {
const li = document.createElement('li');
li.textContent = `${m.title} (${m.year}) – ${m.format} - added ${new Date(m.added_at).toLocaleDateString()}`;
list.appendChild(li);
});
*/
renderList(data.media);
});
}
});
// Attach change event listener to the select element
$('#data').on('change', '#dbType', function(){
if ($(this).val() === 'mysql') {
$('#mysqlFields').show(); // or $('#mysqlFields').css('display', 'block');
} else {
$('#mysqlFields').hide(); // or $('#mysqlFields').css('display', 'none');
}
});
$('#search').on('click', function(){
$('#media-search').toggle(1000);
});
// Open modal and play video
$('#data').on('click', '.playBtn', function() {
const path = $(this).data('path');
const ext = $(this).data('ext');
//if(ext) {
//} else{
//alert(path);
$('#videoSource').attr('src', `/video/${path}`);
$('#videoPlayer')[0].load();
$('#videoModal').removeClass('hidden');
$('#videoModal').addClass('flex');
$('#videoPlayer')[0].play();
//}
});
// Close modal
$('#closeModal').on('click', function() {
$('#videoPlayer')[0].pause();
$('#videoModal').removeClass('flex');
$('#videoModal').addClass('hidden');
});
});
function formatBytes(a, b = 2, k = 1024) {
if (a === 0) return '0 Bytes';
const i = Math.floor(Math.log(a) / Math.log(k));
return `${(a / Math.pow(k, i)).toFixed(b)} ${['Bytes', 'KB', 'MB', 'GB', 'TB'][i]}`;
}
function doSearch(event) {
event.preventDefault();
const form = document.getElementById('searchForm');
const params = new URLSearchParams(new FormData(form)).toString();
fetch(`/dashboard/search?${params}`)
.then(res => res.json())
.then(data => {
document.getElementById('total').textContent = `Search Results: ${data.count}`;
renderList(data.media);
});
}
function formatMovie(m) {
return `${m.title} ${m.year==='0000' ? '' : '('+m.year+')'} – ${m.format} – size: ${formatBytes(m.filesize)} - added ${new Date(m.added_at).toLocaleDateString()} ${m.format === 'AVI' ? `<button class="playBtn" data-path="${m.filepath}" data-ext="${m.format}">⤓ Download</button>` : `<button class="playBtn" data-path="${m.filepath}">▶ Play</button>`}`;
}
function renderList(data) {
document.getElementById('data').innerHTML = '';
data.forEach(m => {
const li = document.createElement('li');
li.innerHTML = formatMovie(m);
document.getElementById('data').appendChild(li);
});
/*
const container = document.getElementById('data');
container.innerHTML = '';
data.forEach(m => {
const li = document.createElement('li');
li.innerHTML = `
${formatMovie(m)}
<button class="playBtn" data-filename="${m.filepath}">▶ Play</button>
`;
container.appendChild(li);
});
*/
}
function loadStats() {
fetch('/dashboard/movies')
.then(res => res.json())
.then(data => {
document.getElementById('total').textContent = `Total Movies: ${data.total}`;
renderList(data.media);
});
}
function refreshIndex() {
fetch('/api/media/refresh', { method: 'POST' })
.then(res => res.json())
.then(data => {
alert(`Index refreshed. Total: ${data.count}`);
loadStats();
});
}
//loadStats(); // initial load
</script>
<script type="text/javascript">
</script>
</body>
</html>
<!--
<!DOCTYPE html>
<html>
<head>
<title>Media Dashboard</title>
<style>
body { font-family: sans-serif; padding: 20px; background: #f4f4f4; }
h1 { margin-bottom: 5px; }
ul { list-style: none; padding-left: 0; }
li { background: white; padding: 10px; margin-bottom: 5px; border-radius: 4px; }
</style>
</head>
<body>
<h1>Media Dashboard</h1>
<p id="total"></p>
<h2>Recently Added</h2>
<ul id="mediaList"></ul>
<script>
/*
fetch('/dashboard/media')
.then(res => res.json())
.then(data => {
document.getElementById('total').textContent = `Total Movies: ${data.total}`;
const list = document.getElementById('mediaList');
data.media.forEach(m => {
const li = document.createElement('li');
li.textContent = `${m.title} (${m.year}) – ${m.format} – added ${new Date(m.dateAdded).toLocaleDateString()}`;
list.appendChild(li);
});
});
*/
</script>
<script>
fetch('/dashboard/movies')
.then(res => res.json())
.then(data => {
document.getElementById('total').textContent = `Total Movies: ${data.count}`;
const list = document.getElementById('mediaList');
data.results.forEach(m => {
const li = document.createElement('li');
li.textContent = `${m.title} (${m.year}) – ${m.format} - ${m.imdb}`;
list.appendChild(li);
});
});
</script>
</body>
</html>
-->