-
-
Notifications
You must be signed in to change notification settings - Fork 47
Add preview time picker with audio player #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -964,6 +964,7 @@ $(document).ready(function() { | |
| DescriptorManager.init(); | ||
| ConfigManager.init(); | ||
| InferenceManager.init(); | ||
| CustomizationManager.init(); | ||
|
|
||
| // Attach event handlers | ||
| $("#model").on('change', () => UIManager.updateModelSettings()); | ||
|
|
@@ -973,6 +974,188 @@ $(document).ready(function() { | |
| UIManager.updateModelSettings(); | ||
| } | ||
|
|
||
| // Customization Manager for preview time and background | ||
| const CustomizationManager = { | ||
| audio: null, | ||
| lastPickerPosition: 0, | ||
|
|
||
| init() { | ||
| this.attachEventHandlers(); | ||
| this.updatePreviewDisplay(); | ||
| }, | ||
|
|
||
| attachEventHandlers() { | ||
| $('#pick-preview-btn').on('click', () => this.openPreviewPicker()); | ||
| $('#preview_time').on('input', () => this.updatePreviewDisplay()); | ||
| $('#background_path').on('input blur', () => this.updateBackgroundPreview()); | ||
| }, | ||
|
|
||
| updatePreviewDisplay() { | ||
| const ms = parseInt($('#preview_time').val()); | ||
| const $display = $('#preview-time-display'); | ||
|
|
||
| if (!isNaN(ms) && ms >= 0) { | ||
| const seconds = ms / 1000; | ||
| const minutes = Math.floor(seconds / 60); | ||
| const secs = Math.floor(seconds % 60); | ||
| $display.text(`(${minutes}:${secs.toString().padStart(2, '0')})`).addClass('has-value'); | ||
| } else { | ||
| $display.text('').removeClass('has-value'); | ||
| } | ||
| }, | ||
|
|
||
| updateBackgroundPreview() { | ||
| const bgPath = $('#background_path').val().trim(); | ||
| const $preview = $('#background-preview'); | ||
| const $img = $('#background-preview-img'); | ||
|
|
||
| if (!bgPath) { | ||
| $preview.hide(); | ||
| return; | ||
| } | ||
|
|
||
| $.ajax({ | ||
| url: '/get_image_preview', | ||
| method: 'POST', | ||
| contentType: 'application/json', | ||
| data: JSON.stringify({ path: bgPath }), | ||
| success: (response) => { | ||
| if (response.success && response.data) { | ||
| $img.attr('src', 'data:image/' + response.type + ';base64,' + response.data); | ||
| $preview.show(); | ||
| } else { | ||
| $preview.hide(); | ||
| } | ||
| }, | ||
| error: () => $preview.hide() | ||
| }); | ||
| }, | ||
|
|
||
| openPreviewPicker() { | ||
| const audioPath = $('#audio_path').val().trim(); | ||
|
|
||
| if (!audioPath) { | ||
| Utils.showFlashMessage('Please select an audio file first.', 'error'); | ||
| return; | ||
| } | ||
|
|
||
| this.createPreviewModal(audioPath); | ||
| }, | ||
|
|
||
| createPreviewModal(audioPath) { | ||
| $('#preview-picker-modal').remove(); | ||
|
|
||
| const modalHtml = ` | ||
| <div id="preview-picker-modal" class="preview-modal-overlay"> | ||
| <div class="preview-modal"> | ||
| <div class="preview-modal-header"> | ||
| <h3>Pick Preview Point</h3> | ||
| <button type="button" class="preview-modal-close">×</button> | ||
| </div> | ||
| <div class="preview-modal-body"> | ||
| <audio id="preview-audio" controls style="width: 100%; margin-bottom: 15px;"></audio> | ||
| <div class="preview-time-labels"> | ||
| <span id="preview-current-time">0:00</span> | ||
| </div> | ||
| <div class="preview-input-row"> | ||
| <label>Milliseconds:</label> | ||
| <input type="number" id="preview-ms-input" min="0" value="${this.lastPickerPosition}" /> | ||
| </div> | ||
| <div class="preview-buttons-row"> | ||
| <button type="button" id="preview-goto-btn" class="browse-button">Go to Time</button> | ||
| <button type="button" id="preview-setcurrent-btn" class="browse-button">Use Current</button> | ||
| <button type="button" id="preview-set-btn" class="browse-button accent">Use This Point</button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| `; | ||
|
|
||
| $('body').append(modalHtml); | ||
| this.setupPreviewAudio(audioPath); | ||
| }, | ||
|
|
||
| setupPreviewAudio(audioPath) { | ||
| $.ajax({ | ||
| url: '/get_audio_info', | ||
| method: 'POST', | ||
| contentType: 'application/json', | ||
| data: JSON.stringify({ path: audioPath }), | ||
| success: (response) => { | ||
| if (response.success) { | ||
| this.initializeAudioPlayer(response.url); | ||
| } else { | ||
| Utils.showFlashMessage('Failed to load audio: ' + (response.message || 'Unknown error'), 'error'); | ||
| $('#preview-picker-modal').remove(); | ||
| } | ||
| }, | ||
| error: () => { | ||
| Utils.showFlashMessage('Failed to load audio file', 'error'); | ||
| $('#preview-picker-modal').remove(); | ||
| } | ||
| }); | ||
|
|
||
| $('.preview-modal-close, .preview-modal-overlay').on('click', (e) => { | ||
| if (e.target === e.currentTarget) { | ||
| this.closePreviewModal(); | ||
| } | ||
| }); | ||
| }, | ||
|
|
||
| initializeAudioPlayer(audioUrl) { | ||
| const $audio = $('#preview-audio'); | ||
| $audio.attr('src', audioUrl); | ||
|
|
||
| const audio = $audio[0]; | ||
| this.audio = audio; | ||
|
|
||
| // Seek to last position | ||
| audio.addEventListener('loadedmetadata', () => { | ||
| if (this.lastPickerPosition > 0) { | ||
| audio.currentTime = this.lastPickerPosition / 1000; | ||
| } | ||
| }); | ||
|
|
||
| // Update time display | ||
| audio.addEventListener('timeupdate', () => { | ||
| const ms = Math.floor(audio.currentTime * 1000); | ||
| const minutes = Math.floor(audio.currentTime / 60); | ||
| const secs = Math.floor(audio.currentTime % 60); | ||
| $('#preview-current-time').text(`${minutes}:${secs.toString().padStart(2, '0')} (${ms}ms)`); | ||
| }); | ||
|
Comment on lines
+1113
to
+1125
|
||
|
|
||
| // Go to time button | ||
| $('#preview-goto-btn').on('click', () => { | ||
| const ms = parseInt($('#preview-ms-input').val()) || 0; | ||
| audio.currentTime = ms / 1000; | ||
| }); | ||
|
|
||
| // Use current button | ||
| $('#preview-setcurrent-btn').on('click', () => { | ||
| const ms = Math.floor(audio.currentTime * 1000); | ||
| $('#preview-ms-input').val(ms); | ||
| }); | ||
|
|
||
| // Set button | ||
| $('#preview-set-btn').on('click', () => { | ||
| const ms = parseInt($('#preview-ms-input').val()) || 0; | ||
| $('#preview_time').val(ms); | ||
| this.lastPickerPosition = ms; | ||
| this.updatePreviewDisplay(); | ||
| this.closePreviewModal(); | ||
| Utils.showFlashMessage(`Preview time set to ${ms}ms`, 'success'); | ||
| }); | ||
| }, | ||
|
|
||
| closePreviewModal() { | ||
| if (this.audio) { | ||
| this.audio.pause(); | ||
| this.audio = null; | ||
| } | ||
| $('#preview-picker-modal').remove(); | ||
| } | ||
| }; | ||
|
|
||
| // Start the application | ||
| initializeApp(); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -150,6 +150,33 @@ <h4 title="List of additional context to provide to the model">In-Context Option | |||||||||||||||||||
| <input type="text" id="seed" name="seed" /> | ||||||||||||||||||||
| </fieldset> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| <!-- Beatmap Customization --> | ||||||||||||||||||||
| <fieldset> | ||||||||||||||||||||
| <legend>Beatmap Customization</legend> | ||||||||||||||||||||
| <div class="form-group"> | ||||||||||||||||||||
| <label for="preview_time" title="Preview time in milliseconds (the point in the song that plays during beatmap selection)">Preview Time (ms):</label> | ||||||||||||||||||||
| <div class="preview-input-group"> | ||||||||||||||||||||
| <input type="number" id="preview_time" name="preview_time" min="0" placeholder="Auto" /> | ||||||||||||||||||||
| <button type="button" id="pick-preview-btn" class="browse-button" title="Pick preview point from audio">Pick...</button> | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
| <span id="preview-time-display" class="preview-time-display"></span> | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| <div class="form-group"> | ||||||||||||||||||||
| <label for="background_path" title="Background image for the beatmap">Background Image:</label> | ||||||||||||||||||||
| <div class="path-input-group" style="margin-bottom: 0;"> | ||||||||||||||||||||
| <div class="input-with-clear"> | ||||||||||||||||||||
| <input type="text" id="background_path" name="background_path" placeholder="None" /> | ||||||||||||||||||||
| <button type="button" class="clear-input-btn" data-target="background_path" style="display: none;">×</button> | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
| <button type="button" class="browse-button" data-browse-type="file" data-target="background_path">Browse...</button> | ||||||||||||||||||||
|
Comment on lines
+169
to
+172
|
||||||||||||||||||||
| <input type="text" id="background_path" name="background_path" placeholder="None" /> | |
| <button type="button" class="clear-input-btn" data-target="background_path" style="display: none;">×</button> | |
| </div> | |
| <button type="button" class="browse-button" data-browse-type="file" data-target="background_path">Browse...</button> | |
| <input type="text" id="background_path" name="background_path" placeholder="None" aria-describedby="background_path_desc" /> | |
| <button type="button" class="clear-input-btn" data-target="background_path" style="display: none;">×</button> | |
| <span id="background_path_desc" class="visually-hidden">Enter the path to the background image for the beatmap, or use the browse button to select a file.</span> | |
| </div> | |
| <button type="button" class="browse-button" data-browse-type="file" data-target="background_path" aria-label="Browse for background image">Browse...</button> |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The background preview image lacks appropriate alt text. While 'Background preview' is provided, it would be more helpful to provide more descriptive alt text or use an empty alt attribute (alt="") if the image is purely decorative, with the context provided by the surrounding elements.
| <img id="background-preview-img" src="" alt="Background preview" /> | |
| <img id="background-preview-img" src="" alt="" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Event handlers for modal close are attached multiple times if the modal is opened repeatedly. The click handler on '.preview-modal-overlay' is attached inside setupPreviewAudio which is called every time the modal opens, leading to multiple handlers being bound. Move this event handler attachment to createPreviewModal or use event delegation to avoid duplicate handlers.