Upload server now has dedicated audio book uploading.
This commit is contained in:
@@ -243,7 +243,7 @@
|
|||||||
<section class="upload-section" aria-labelledby="music-section-heading">
|
<section class="upload-section" aria-labelledby="music-section-heading">
|
||||||
<h2 id="music-section-heading">Upload Music Folder</h2>
|
<h2 id="music-section-heading">Upload Music Folder</h2>
|
||||||
<p>Upload an entire music folder with its structure directly to your Music directory.</p>
|
<p>Upload an entire music folder with its structure directly to your Music directory.</p>
|
||||||
|
|
||||||
<form action="/upload_music" method="POST" enctype="multipart/form-data">
|
<form action="/upload_music" method="POST" enctype="multipart/form-data">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="file-input" id="folder-drop-area">
|
<div class="file-input" id="folder-drop-area">
|
||||||
@@ -255,14 +255,38 @@
|
|||||||
<p id="folder-help" class="help-text">Selected folder: <span id="selected-folder">None</span></p>
|
<p id="folder-help" class="help-text">Selected folder: <span id="selected-folder">None</span></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button type="submit" class="btn" id="upload-folder-button">Upload Music Folder</button>
|
<button type="submit" class="btn" id="upload-folder-button">Upload Music Folder</button>
|
||||||
<div id="upload-folder-status" class="upload-status" style="display: none;" aria-live="polite"></div>
|
<div id="upload-folder-status" class="upload-status" style="display: none;" aria-live="polite"></div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Audiobook Upload Section -->
|
||||||
|
<section class="upload-section" aria-labelledby="audiobook-section-heading">
|
||||||
|
<h2 id="audiobook-section-heading">Upload Audiobooks</h2>
|
||||||
|
<p>Upload audiobook files (.m4b, .epub, or .zip) to your Library directory.</p>
|
||||||
|
|
||||||
|
<form action="/upload" method="POST" enctype="multipart/form-data">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="file-input" id="audiobook-drop-area">
|
||||||
|
<label for="audiobook-upload" class="file-label">
|
||||||
|
Choose audiobook files or drag and drop here
|
||||||
|
<span class="sr-only">Select one or more audiobook files to upload</span>
|
||||||
|
</label>
|
||||||
|
<input id="audiobook-upload" type="file" name="file" multiple accept=".m4b,.epub,.zip" aria-describedby="audiobook-help">
|
||||||
|
<p id="audiobook-help" class="help-text">Selected files: <span id="selected-audiobooks">None</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="btn" id="upload-audiobook-button">Upload Audiobooks</button>
|
||||||
|
<div id="upload-audiobook-status" class="upload-status" style="display: none;" aria-live="polite"></div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
<div class="form-group" style="margin-top: 30px; text-align: right;">
|
<div class="form-group" style="margin-top: 30px; text-align: right;">
|
||||||
<a href="/shutdown" class="btn btn-secondary" style="background-color: #e74c3c;">Shutdown Server</a>
|
<a href="/shutdown" class="btn btn-secondary" style="background-color: #e74c3c;">Shutdown Server</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -274,12 +298,18 @@
|
|||||||
if (e.target !== this) return;
|
if (e.target !== this) return;
|
||||||
document.getElementById('file-upload').click();
|
document.getElementById('file-upload').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make the entire area clickable for folder selection
|
// Make the entire area clickable for folder selection
|
||||||
document.getElementById('folder-drop-area').addEventListener('click', function(e) {
|
document.getElementById('folder-drop-area').addEventListener('click', function(e) {
|
||||||
if (e.target !== this) return;
|
if (e.target !== this) return;
|
||||||
document.getElementById('folder-upload').click();
|
document.getElementById('folder-upload').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Make the entire area clickable for audiobook selection
|
||||||
|
document.getElementById('audiobook-drop-area').addEventListener('click', function(e) {
|
||||||
|
if (e.target !== this) return;
|
||||||
|
document.getElementById('audiobook-upload').click();
|
||||||
|
});
|
||||||
|
|
||||||
// Show selected files
|
// Show selected files
|
||||||
document.getElementById('file-upload').addEventListener('change', function(e) {
|
document.getElementById('file-upload').addEventListener('change', function(e) {
|
||||||
@@ -303,29 +333,50 @@
|
|||||||
const fileList = e.target.files;
|
const fileList = e.target.files;
|
||||||
const fileCount = fileList.length;
|
const fileCount = fileList.length;
|
||||||
const selectedFolder = document.getElementById('selected-folder');
|
const selectedFolder = document.getElementById('selected-folder');
|
||||||
|
|
||||||
if (fileCount > 0) {
|
if (fileCount > 0) {
|
||||||
// Get the common folder path from the first file
|
// Get the common folder path from the first file
|
||||||
const firstFile = fileList[0];
|
const firstFile = fileList[0];
|
||||||
const folderPath = firstFile.webkitRelativePath.split('/')[0];
|
const folderPath = firstFile.webkitRelativePath.split('/')[0];
|
||||||
|
|
||||||
selectedFolder.textContent = `${folderPath} (${fileCount} files)`;
|
selectedFolder.textContent = `${folderPath} (${fileCount} files)`;
|
||||||
// Announce to screen readers
|
// Announce to screen readers
|
||||||
document.getElementById('status-announcer').textContent =
|
document.getElementById('status-announcer').textContent =
|
||||||
`Selected folder: ${folderPath} containing ${fileCount} files`;
|
`Selected folder: ${folderPath} containing ${fileCount} files`;
|
||||||
} else {
|
} else {
|
||||||
selectedFolder.textContent = 'None';
|
selectedFolder.textContent = 'None';
|
||||||
document.getElementById('status-announcer').textContent = 'No folder selected';
|
document.getElementById('status-announcer').textContent = 'No folder selected';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show selected audiobooks
|
||||||
|
document.getElementById('audiobook-upload').addEventListener('change', function(e) {
|
||||||
|
const fileList = e.target.files;
|
||||||
|
const fileNames = Array.from(fileList).map(file => file.name);
|
||||||
|
const selectedAudiobooks = document.getElementById('selected-audiobooks');
|
||||||
|
|
||||||
|
if (fileNames.length > 0) {
|
||||||
|
selectedAudiobooks.textContent = fileNames.join(', ');
|
||||||
|
// Announce to screen readers
|
||||||
|
document.getElementById('status-announcer').textContent =
|
||||||
|
`Selected ${fileNames.length} audiobook files: ${fileNames.join(', ')}`;
|
||||||
|
} else {
|
||||||
|
selectedAudiobooks.textContent = 'None';
|
||||||
|
document.getElementById('status-announcer').textContent = 'No audiobook files selected';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Handle drag and drop for files
|
// Handle drag and drop for files
|
||||||
const fileDropArea = document.getElementById('file-drop-area');
|
const fileDropArea = document.getElementById('file-drop-area');
|
||||||
setupDragDrop(fileDropArea, 'file-upload');
|
setupDragDrop(fileDropArea, 'file-upload');
|
||||||
|
|
||||||
// Handle drag and drop for folders
|
// Handle drag and drop for folders
|
||||||
const folderDropArea = document.getElementById('folder-drop-area');
|
const folderDropArea = document.getElementById('folder-drop-area');
|
||||||
setupDragDrop(folderDropArea, 'folder-upload');
|
setupDragDrop(folderDropArea, 'folder-upload');
|
||||||
|
|
||||||
|
// Handle drag and drop for audiobooks
|
||||||
|
const audiobookDropArea = document.getElementById('audiobook-drop-area');
|
||||||
|
setupDragDrop(audiobookDropArea, 'audiobook-upload');
|
||||||
|
|
||||||
function setupDragDrop(dropArea, inputId) {
|
function setupDragDrop(dropArea, inputId) {
|
||||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||||
@@ -354,17 +405,17 @@
|
|||||||
dropArea.addEventListener('drop', function(e) {
|
dropArea.addEventListener('drop', function(e) {
|
||||||
const dt = e.dataTransfer;
|
const dt = e.dataTransfer;
|
||||||
const files = dt.files;
|
const files = dt.files;
|
||||||
|
|
||||||
if (inputId === 'file-upload') {
|
if (inputId === 'file-upload' || inputId === 'audiobook-upload') {
|
||||||
document.getElementById(inputId).files = files;
|
document.getElementById(inputId).files = files;
|
||||||
// Trigger change event
|
// Trigger change event
|
||||||
const event = new Event('change');
|
const event = new Event('change');
|
||||||
document.getElementById(inputId).dispatchEvent(event);
|
document.getElementById(inputId).dispatchEvent(event);
|
||||||
} else {
|
} else {
|
||||||
// Show message that folder drop might not be supported
|
// Show message that folder drop might not be supported
|
||||||
document.getElementById('status-announcer').textContent =
|
document.getElementById('status-announcer').textContent =
|
||||||
'For folder upload, please use the file picker';
|
'For folder upload, please use the file picker';
|
||||||
document.getElementById('selected-folder').textContent =
|
document.getElementById('selected-folder').textContent =
|
||||||
'Please use the folder selection button';
|
'Please use the folder selection button';
|
||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
@@ -394,9 +445,23 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up progress handling for both forms
|
// Set up progress handling for all forms
|
||||||
setupUploadProgress('form[action="/upload"]', 'upload-button', 'upload-status');
|
setupUploadProgress('form[action="/upload"]', 'upload-button', 'upload-status');
|
||||||
setupUploadProgress('form[action="/upload_music"]', 'upload-folder-button', 'upload-folder-status');
|
setupUploadProgress('form[action="/upload_music"]', 'upload-folder-button', 'upload-folder-status');
|
||||||
|
|
||||||
|
// Handle audiobook form separately (also uses /upload action but different button)
|
||||||
|
const audiobookForm = document.getElementById('upload-audiobook-button').closest('form');
|
||||||
|
const audiobookButton = document.getElementById('upload-audiobook-button');
|
||||||
|
const audiobookStatus = document.getElementById('upload-audiobook-status');
|
||||||
|
const announcer = document.getElementById('status-announcer');
|
||||||
|
|
||||||
|
audiobookForm.addEventListener('submit', function(e) {
|
||||||
|
audiobookButton.disabled = true;
|
||||||
|
audiobookButton.textContent = 'Uploading...';
|
||||||
|
audiobookStatus.style.display = 'block';
|
||||||
|
audiobookStatus.textContent = 'Upload in progress, please wait...';
|
||||||
|
announcer.textContent = 'Audiobook upload started, please wait while files are being processed';
|
||||||
|
});
|
||||||
|
|
||||||
// Check if the client is on local network
|
// Check if the client is on local network
|
||||||
fetch('/check_local')
|
fetch('/check_local')
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ FINAL_DESTINATIONS = {
|
|||||||
'SNES': '/home/stormux/Roms/Super Nintendo Entertainment System',
|
'SNES': '/home/stormux/Roms/Super Nintendo Entertainment System',
|
||||||
'SMS': '/home/stormux/Roms/Sega Master System',
|
'SMS': '/home/stormux/Roms/Sega Master System',
|
||||||
'music': '/home/stormux/Music',
|
'music': '/home/stormux/Music',
|
||||||
|
'audiobook': '/home/stormux/Library',
|
||||||
'voxin': '/home/stormux/Downloads',
|
'voxin': '/home/stormux/Downloads',
|
||||||
'other': '/home/stormux/Downloads'
|
'other': '/home/stormux/Downloads'
|
||||||
}
|
}
|
||||||
@@ -57,6 +58,7 @@ FILE_TYPES = {
|
|||||||
'SNES': ['.fig', '.smc'],
|
'SNES': ['.fig', '.smc'],
|
||||||
'SMS': ['.sms'],
|
'SMS': ['.sms'],
|
||||||
'music': ['.mp3', '.ogg', '.flac', '.wav', '.m4a', '.opus'],
|
'music': ['.mp3', '.ogg', '.flac', '.wav', '.m4a', '.opus'],
|
||||||
|
'audiobook': ['.m4b', '.epub', '.zip'],
|
||||||
'voxin': ['.tgz'],
|
'voxin': ['.tgz'],
|
||||||
'other': [] # Empty list to represent any other file type
|
'other': [] # Empty list to represent any other file type
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,17 @@ Dates are given for the image. All items listed are available for the listed ima
|
|||||||
|
|
||||||
## October 1, 2025
|
## October 1, 2025
|
||||||
|
|
||||||
|
- Update system added
|
||||||
|
- Add upload audiobook capability to upload server
|
||||||
- Wine configuration update (x86_64)
|
- Wine configuration update (x86_64)
|
||||||
- System updates
|
- System updates
|
||||||
- Update Toby Doom
|
- Update Toby Doom
|
||||||
|
- Wreckingball added to installable games
|
||||||
- Extend install system to get games not included in the image
|
- Extend install system to get games not included in the image
|
||||||
- Work on system to require fewer reinstallations
|
- Work on system to require fewer reinstallations
|
||||||
|
- Add Wicked Christmas level pack
|
||||||
- Update Wicked Quest
|
- Update Wicked Quest
|
||||||
|
- Initial work on book reader
|
||||||
|
|
||||||
|
|
||||||
## September 1, 2025
|
## September 1, 2025
|
||||||
|
|||||||
Reference in New Issue
Block a user