UNPKG

discord-media-server

Version:

Self-hosted Discord bot for scanning and serving information on local media files.

342 lines (293 loc) 12.2 kB
<!DOCTYPE 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}">&#x2913; 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> -->