nice audio player
This commit is contained in:
parent
ab79098e9a
commit
b7c4360560
6
allowed_secrets.json.example.json
Normal file
6
allowed_secrets.json.example.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"dev_key_f83745ft0g5rg3": {
|
||||
"expiry" : "31.12.2000",
|
||||
"file_root": "\\\\path\\if\\using\\windows"
|
||||
}
|
||||
}
|
||||
17
app.py
17
app.py
@ -18,6 +18,7 @@ app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)
|
||||
|
||||
app.config['SECRET_KEY'] = '85c1117eb3a5f2c79f0ff395bada8ff8d9a257b99ef5e143'
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=90)
|
||||
if os.environ.get('FLASK_ENV') == 'production':
|
||||
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
|
||||
app.config['SESSION_COOKIE_SECURE'] = True
|
||||
|
||||
@ -29,20 +30,23 @@ def load_allowed_secrets(filename='allowed_secrets.json'):
|
||||
value['expiry'] = datetime.strptime(value['expiry'], '%d.%m.%Y').date()
|
||||
return secrets
|
||||
|
||||
app.config['ALLOWED_SECRETS'] = load_allowed_secrets()
|
||||
|
||||
def require_secret(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
allowed_secrets = app.config['ALLOWED_SECRETS']
|
||||
allowed_secrets = load_allowed_secrets()
|
||||
today = date.today()
|
||||
|
||||
def is_valid(secret_data):
|
||||
print("is_valid:", secret_data)
|
||||
expiry_date = secret_data.get('expiry')
|
||||
return expiry_date and today <= expiry_date
|
||||
is_valid = expiry_date and today <= expiry_date
|
||||
print("is_valid", is_valid)
|
||||
return is_valid
|
||||
|
||||
# Check if a secret was provided via GET parameter
|
||||
get_secret = request.args.get('secret')
|
||||
print("request.args:", request.args)
|
||||
print("get_secret:", get_secret)
|
||||
if get_secret is not None:
|
||||
secret_data = allowed_secrets.get(get_secret)
|
||||
if secret_data:
|
||||
@ -51,13 +55,17 @@ def require_secret(f):
|
||||
session['secret'] = get_secret
|
||||
session.permanent = True
|
||||
app.config['FILE_ROOT'] = secret_data.get('file_root')
|
||||
print("session:", session['secret'])
|
||||
return f(*args, **kwargs)
|
||||
else:
|
||||
# Secret provided via URL is expired or invalid
|
||||
return render_template('error.html', message="Invalid or expired secret."), 403
|
||||
|
||||
|
||||
|
||||
# If no secret provided via GET, check the session
|
||||
session_secret = session.get('secret')
|
||||
print("session_secret", session_secret)
|
||||
if session_secret is not None:
|
||||
secret_data = allowed_secrets.get(session_secret)
|
||||
if secret_data:
|
||||
@ -186,6 +194,7 @@ def generate_breadcrumbs(subpath):
|
||||
@app.route('/api/path/<path:subpath>')
|
||||
@require_secret
|
||||
def api_browse(subpath):
|
||||
print("subpath:", subpath)
|
||||
file_root = app.config['FILE_ROOT']
|
||||
directory = os.path.join(file_root, subpath.replace('/', os.sep))
|
||||
|
||||
|
||||
5
requirements_transcription.txt
Normal file
5
requirements_transcription.txt
Normal file
@ -0,0 +1,5 @@
|
||||
openai-whisper
|
||||
#https://pytorch.org/get-started/locally/
|
||||
torch==2.5.1
|
||||
torchvision==0.20.1
|
||||
torchaudio==2.5.1 --index-url https://download.pytorch.org/whl/cu124
|
||||
@ -91,7 +91,7 @@ function loadDirectory(subpath) {
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading directory:', error);
|
||||
document.getElementById('content').innerHTML = '<p>Error loading directory.</p>';
|
||||
document.getElementById('content').innerHTML = '<p>Error loading directory!</p><p>'+error+'</p>';
|
||||
});
|
||||
}
|
||||
|
||||
@ -143,6 +143,7 @@ document.querySelectorAll('.play-file').forEach(link => {
|
||||
const fileType = this.getAttribute('data-file-type');
|
||||
const relUrl = this.getAttribute('data-url');
|
||||
const nowPlayingInfo = document.getElementById('nowPlayingInfo');
|
||||
document.getElementById('audioPlayerContainer').style.display = "block"
|
||||
|
||||
// Remove the class from all file items.
|
||||
document.querySelectorAll('.file-item').forEach(item => {
|
||||
@ -154,6 +155,7 @@ document.querySelectorAll('.play-file').forEach(link => {
|
||||
const idx = this.getAttribute('data-index');
|
||||
const audioPlayer = document.getElementById('globalAudio');
|
||||
currentMusicIndex = idx !== null ? parseInt(idx) : -1;
|
||||
const playerButton = document.querySelector('.player-button');
|
||||
|
||||
// Add the class to the clicked file item's parent.
|
||||
this.closest('.file-item').classList.add('currently-playing');
|
||||
@ -167,7 +169,11 @@ document.querySelectorAll('.play-file').forEach(link => {
|
||||
|
||||
audioPlayer.pause();
|
||||
audioPlayer.src = ''; // Clear existing source.
|
||||
// only show this if it takes longer. avoid flicker
|
||||
let loaderTimeout = setTimeout(() => {
|
||||
playerButton.innerHTML = playIcon;
|
||||
nowPlayingInfo.textContent = "Wird geladen...";
|
||||
}, 500);
|
||||
document.querySelector('footer').style.display = 'flex';
|
||||
|
||||
const mediaUrl = '/media/' + relUrl;
|
||||
@ -175,7 +181,7 @@ document.querySelectorAll('.play-file').forEach(link => {
|
||||
try {
|
||||
// Pass the signal to the fetch so it can be aborted.
|
||||
const response = await fetch(mediaUrl, { method: 'HEAD', signal: currentFetchController.signal });
|
||||
|
||||
clearTimeout(loaderTimeout); // done loading. stop timeout
|
||||
if (response.status === 403) {
|
||||
nowPlayingInfo.textContent = "Fehler: Zugriff verweigert.";
|
||||
window.location.href = '/'; // Redirect if forbidden.
|
||||
@ -186,6 +192,7 @@ document.querySelectorAll('.play-file').forEach(link => {
|
||||
audioPlayer.src = mediaUrl;
|
||||
audioPlayer.load();
|
||||
audioPlayer.play();
|
||||
playerButton.innerHTML = pauseIcon;
|
||||
const pathParts = relUrl.split('/');
|
||||
const fileName = pathParts.pop();
|
||||
const pathStr = pathParts.join('/');
|
||||
|
||||
121
static/audioplayer.css
Normal file
121
static/audioplayer.css
Normal file
@ -0,0 +1,121 @@
|
||||
.audio-player-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
padding: 7px;
|
||||
width: 100%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Now Playing Info */
|
||||
.now-playing-info {
|
||||
margin-top: 5px;
|
||||
font-size: 16px;
|
||||
color: #23313f;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.audio-player {
|
||||
--player-button-width: 4em;
|
||||
--sound-button-width: 3em;
|
||||
--space: 0.5em;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: var(--space); /* ensures spacing between controls */
|
||||
}
|
||||
|
||||
/* Make the slider container fill the available space and stack its children vertically */
|
||||
.slider {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* The range input takes the full width of its container */
|
||||
.timeline {
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
height: 0.5em;
|
||||
background-color: #ccc; /* light gray background */
|
||||
border-radius: 5px;
|
||||
background-size: 0% 100%;
|
||||
background-image: linear-gradient(#34495e, #34495e); /* progress bar */
|
||||
background-repeat: no-repeat;
|
||||
appearance: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Slider Thumb Styling */
|
||||
/* WebKit browsers (Chrome, Safari) */
|
||||
.timeline::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
background-color: #34495e;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
.timeline::-moz-range-thumb {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
background-color: #34495e;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Internet Explorer and Edge */
|
||||
.timeline::-ms-thumb {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
background-color: #34495e;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
/* Remove default track styling */
|
||||
.timeline::-webkit-slider-runnable-track,
|
||||
.timeline::-moz-range-track,
|
||||
.timeline::-ms-track {
|
||||
-webkit-appearance: none;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Style the time info (positioned right below the slider) */
|
||||
.now-playing-info {
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
.player-button,
|
||||
.sound-button {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.player-button {
|
||||
width: var(--player-button-width);
|
||||
height: var(--player-button-width);
|
||||
}
|
||||
|
||||
.sound-button {
|
||||
width: var(--sound-button-width);
|
||||
height: var(--sound-button-width);
|
||||
}
|
||||
157
static/audioplayer.js
Normal file
157
static/audioplayer.js
Normal file
@ -0,0 +1,157 @@
|
||||
const playerButton = document.querySelector('.player-button'),
|
||||
audio = document.querySelector('audio'),
|
||||
timeline = document.querySelector('.timeline'),
|
||||
soundButton = document.querySelector('.sound-button'),
|
||||
timeInfo = document.getElementById('timeInfo'),
|
||||
playIcon = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#34495e">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
`,
|
||||
pauseIcon = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#34495e">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
`,
|
||||
soundIcon = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#34495e">
|
||||
<path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
`,
|
||||
muteIcon = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#34495e">
|
||||
<path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM12.293 7.293a1 1 0 011.414 0L15 8.586l1.293-1.293a1 1 0 111.414 1.414L16.414 10l1.293 1.293a1 1 0 01-1.414 1.414L15 11.414l-1.293 1.293a1 1 0 01-1.414-1.414L13.586 10l-1.293-1.293a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
`;
|
||||
|
||||
function toggleAudio () {
|
||||
if (audio.paused) {
|
||||
audio.play();
|
||||
playerButton.innerHTML = pauseIcon;
|
||||
} else {
|
||||
audio.pause();
|
||||
playerButton.innerHTML = playIcon;
|
||||
}
|
||||
}
|
||||
playerButton.addEventListener('click', toggleAudio);
|
||||
|
||||
let isSeeking = false;
|
||||
|
||||
// --- Slider (Timeline) events ---
|
||||
// Mouse events
|
||||
timeline.addEventListener('mousedown', () => { isSeeking = true; });
|
||||
timeline.addEventListener('mouseup', () => {
|
||||
isSeeking = false;
|
||||
changeSeek(); // Update the audio currentTime based on the slider position
|
||||
});
|
||||
timeline.addEventListener('change', changeSeek);
|
||||
|
||||
// Touch events
|
||||
timeline.addEventListener('touchstart', () => { isSeeking = true; });
|
||||
timeline.addEventListener('touchend', () => {
|
||||
isSeeking = false;
|
||||
changeSeek();
|
||||
});
|
||||
timeline.addEventListener('touchcancel', () => { isSeeking = false; });
|
||||
|
||||
// --- When metadata is loaded, set slider range in seconds ---
|
||||
audio.addEventListener('loadedmetadata', () => {
|
||||
updateTimeInfo();
|
||||
timeline.min = 0;
|
||||
timeline.max = audio.duration;
|
||||
timeline.value = 0; // Ensures the thumb starts at the left.
|
||||
});
|
||||
|
||||
// --- Update the slider thumb position (in seconds) while playing ---
|
||||
function changeTimelinePosition() {
|
||||
if (!isSeeking) {
|
||||
timeline.value = audio.currentTime;
|
||||
const percentagePosition = (audio.currentTime / audio.duration) * 100;
|
||||
timeline.style.backgroundSize = `${percentagePosition}% 100%`;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Seek function: directly set audio.currentTime using slider's value (in seconds) ---
|
||||
function changeSeek() {
|
||||
audio.currentTime = timeline.value;
|
||||
}
|
||||
|
||||
// --- Sound toggle ---
|
||||
function toggleSound () {
|
||||
audio.muted = !audio.muted;
|
||||
soundButton.innerHTML = audio.muted ? muteIcon : soundIcon;
|
||||
}
|
||||
soundButton.addEventListener('click', toggleSound);
|
||||
|
||||
// --- Utility: Format seconds as mm:ss ---
|
||||
function formatTime(seconds) {
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const secs = Math.floor(seconds % 60);
|
||||
return `${minutes < 10 ? '0' : ''}${minutes}:${secs < 10 ? '0' : ''}${secs}`;
|
||||
}
|
||||
|
||||
// --- Update the time display ---
|
||||
function updateTimeInfo() {
|
||||
const currentTimeFormatted = formatTime(audio.currentTime);
|
||||
const durationFormatted = isNaN(audio.duration) ? "00:00" : formatTime(audio.duration);
|
||||
timeInfo.innerHTML = `${currentTimeFormatted} / ${durationFormatted}`;
|
||||
}
|
||||
|
||||
// --- Update timeline, time info, and media session on each time update ---
|
||||
audio.ontimeupdate = function() {
|
||||
changeTimelinePosition();
|
||||
updateTimeInfo();
|
||||
if ('mediaSession' in navigator && typeof navigator.mediaSession.setPositionState === 'function') {
|
||||
navigator.mediaSession.setPositionState({
|
||||
duration: audio.duration,
|
||||
playbackRate: audio.playbackRate,
|
||||
position: audio.currentTime
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// --- Also update media session on play (in case timeupdate lags) ---
|
||||
audio.onplay = function() {
|
||||
if ('mediaSession' in navigator && typeof navigator.mediaSession.setPositionState === 'function') {
|
||||
navigator.mediaSession.setPositionState({
|
||||
duration: audio.duration,
|
||||
playbackRate: audio.playbackRate,
|
||||
position: audio.currentTime
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// --- Handle external seek requests (e.g. from Android or Windows system controls) ---
|
||||
if ('mediaSession' in navigator) {
|
||||
navigator.mediaSession.setActionHandler('seekto', function(details) {
|
||||
if (details.fastSeek && 'fastSeek' in audio) {
|
||||
audio.fastSeek(details.seekTime);
|
||||
} else {
|
||||
audio.currentTime = details.seekTime;
|
||||
}
|
||||
changeTimelinePosition();
|
||||
});
|
||||
}
|
||||
|
||||
// --- When audio ends ---
|
||||
audio.onended = function() {
|
||||
playerButton.innerHTML = playIcon;
|
||||
};
|
||||
|
||||
function downloadAudio() {
|
||||
// Get the current audio source URL
|
||||
const audioSrc = audio.currentSrc || audio.src;
|
||||
if (audioSrc) {
|
||||
// Create a temporary link element
|
||||
const a = document.createElement('a');
|
||||
a.href = audioSrc;
|
||||
// Extract the file name from the URL, decode it, and set as download attribute
|
||||
let fileName = audioSrc.split('/').pop() || 'audio';
|
||||
fileName = decodeURIComponent(fileName);
|
||||
a.download = fileName;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,34 +127,6 @@ footer {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
footer audio {
|
||||
width: 75%;
|
||||
transform: scale(1.3); /* Default for other devices */
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
/* Target Safari specifically */
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
footer audio {
|
||||
width: 60%;
|
||||
transform: scale(1.6); /* Different scale for Apple devices */
|
||||
}
|
||||
}
|
||||
.audio-player-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
padding: 7px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Now Playing Info */
|
||||
.now-playing-info {
|
||||
margin-top: 5px;
|
||||
font-size: 16px;
|
||||
color: #2c3e50;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Modal Styles */
|
||||
#transcriptModal {
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
<!-- Your CSS -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='gallery.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='audioplayer.css') }}">
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<style>
|
||||
/* Header styles */
|
||||
@ -62,12 +63,35 @@
|
||||
<div id="content"></div>
|
||||
</div>
|
||||
|
||||
<!-- Global Audio Player in Footer -->
|
||||
<footer style="display: none;">
|
||||
<div class="audio-player-container">
|
||||
<audio id="globalAudio" controls preload="auto">
|
||||
<!-- Global Audio Player in Footer style="display: none;"-->
|
||||
<footer >
|
||||
<div class="audio-player-container" id="audioPlayerContainer">
|
||||
<div class="audio-player">
|
||||
<audio id="globalAudio">
|
||||
Your browser does not support the audio element.
|
||||
</audio>
|
||||
<div class="controls">
|
||||
<button class="player-button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#34495e">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="slider">
|
||||
<input type="range" class="timeline" max="100" value="0" step="0.1">
|
||||
<div id="timeInfo" class="now-playing-info"></div>
|
||||
</div>
|
||||
<button class="sound-button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#34495e">
|
||||
<path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="sound-button" onclick="downloadAudio()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 120.89" fill="#34495e" width="30" height="30">
|
||||
<path fill-rule="evenodd" d="M84.58,47a7.71,7.71,0,1,1,10.8,11L66.09,86.88a7.72,7.72,0,0,1-10.82,0L26.4,58.37a7.71,7.71,0,1,1,10.81-11L53.1,63.12l.16-55.47a7.72,7.72,0,0,1,15.43.13l-.15,55L84.58,47ZM0,113.48.1,83.3a7.72,7.72,0,1,1,15.43.14l-.07,22q46,.09,91.91,0l.07-22.12a7.72,7.72,0,1,1,15.44.14l-.1,30h-.09a7.71,7.71,0,0,1-7.64,7.36q-53.73.1-107.38,0A7.7,7.7,0,0,1,0,113.48Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nowPlayingInfo" class="now-playing-info"></div>
|
||||
</div>
|
||||
</footer>
|
||||
@ -93,6 +117,7 @@
|
||||
<!-- Load main app JS first, then gallery JS -->
|
||||
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='gallery.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='audioplayer.js') }}"></script>
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
|
||||
@ -212,7 +212,7 @@ def process_folder(root_folder):
|
||||
print(f"Checked {checked_files} files. Start to transcribe {len(valid_files)} files.")
|
||||
|
||||
print("Loading Whisper model...")
|
||||
model = whisper.load_model(model_name)
|
||||
model = whisper.load_model(model_name, device="cuda")
|
||||
|
||||
# Use a thread pool to pre-load files concurrently.
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user