v3.6.8: Header search bar, GitHub releases auto-update
- Move streamer search to header (top right) - Switch from custom server to GitHub Releases for auto-updates - Add force-close running app in installer - Center empty state vertically - Add sidebar scroll fix - Add desktop shortcut with version name Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
1409d77d38
commit
f058c71946
4
typescript-version/build/installer.nsh
Normal file
4
typescript-version/build/installer.nsh
Normal file
@ -0,0 +1,4 @@
|
||||
!macro customInit
|
||||
; Kill running Twitch VOD Manager process before installation
|
||||
nsExec::ExecToLog 'taskkill /F /IM "Twitch VOD Manager.exe"'
|
||||
!macroend
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "twitch-vod-manager",
|
||||
"version": "3.6.3",
|
||||
"version": "3.6.8",
|
||||
"description": "Twitch VOD Manager - Download Twitch VODs easily",
|
||||
"main": "dist/main.js",
|
||||
"author": "xRangerDE",
|
||||
@ -35,11 +35,16 @@
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"deleteAppDataOnUninstall": false
|
||||
"deleteAppDataOnUninstall": false,
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true,
|
||||
"shortcutName": "Twitch VOD Manager v${version}",
|
||||
"include": "build/installer.nsh"
|
||||
},
|
||||
"publish": {
|
||||
"provider": "generic",
|
||||
"url": "http://24-music.de/"
|
||||
"provider": "github",
|
||||
"owner": "Sucukdeluxe",
|
||||
"repo": "Twitch-VOD-Manager"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +45,8 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid rgba(255,255,255,0.1);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.logo {
|
||||
@ -312,7 +314,43 @@
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.header-search {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.header-search input {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
color: var(--text);
|
||||
font-size: 13px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.header-search input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.header-search button {
|
||||
background: var(--accent);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.header-search button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
@ -544,8 +582,13 @@
|
||||
|
||||
/* Empty State */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
min-height: 60vh;
|
||||
padding: 20px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
@ -1049,8 +1092,13 @@
|
||||
</head>
|
||||
<body class="theme-twitch">
|
||||
<div class="update-banner" id="updateBanner">
|
||||
<span id="updateText">Neue Version verfugbar!</span>
|
||||
<button onclick="downloadUpdate()">Jetzt aktualisieren</button>
|
||||
<span id="updateText">Neue Version verfügbar!</span>
|
||||
<div id="updateProgress" style="display: none; flex: 1; margin: 0 15px;">
|
||||
<div style="background: rgba(0,0,0,0.3); border-radius: 4px; height: 8px; overflow: hidden;">
|
||||
<div id="updateProgressBar" style="background: white; height: 100%; width: 0%; transition: width 0.3s;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="updateButton" onclick="downloadUpdate()">Jetzt herunterladen</button>
|
||||
</div>
|
||||
|
||||
<!-- Clip Dialog Modal -->
|
||||
@ -1156,10 +1204,6 @@
|
||||
|
||||
<div class="section-title">Streamer</div>
|
||||
<div class="streamers" id="streamerList"></div>
|
||||
<div class="add-streamer">
|
||||
<input type="text" id="newStreamer" placeholder="Streamer hinzufugen..." onkeypress="if(event.key==='Enter')addStreamer()">
|
||||
<button onclick="addStreamer()">+</button>
|
||||
</div>
|
||||
|
||||
<div class="queue-section">
|
||||
<div class="queue-header">
|
||||
@ -1178,6 +1222,10 @@
|
||||
<header class="header">
|
||||
<h1 id="pageTitle">VODs</h1>
|
||||
<div class="header-actions">
|
||||
<div class="header-search">
|
||||
<input type="text" id="newStreamer" placeholder="Streamer hinzufugen..." onkeypress="if(event.key==='Enter')addStreamer()">
|
||||
<button onclick="addStreamer()">+</button>
|
||||
</div>
|
||||
<button class="btn-icon" onclick="refreshVODs()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
||||
Aktualisieren
|
||||
@ -2080,31 +2128,58 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Updates
|
||||
// Updates - wird jetzt automatisch vom main process via Events gesteuert
|
||||
async function checkUpdateSilent() {
|
||||
const result = await window.api.checkUpdate();
|
||||
if (result.hasUpdate) {
|
||||
document.getElementById('updateBanner').classList.add('show');
|
||||
document.getElementById('updateText').textContent = `Version ${result.version} verfugbar: ${result.changelog}`;
|
||||
}
|
||||
// Auto-Updater läuft automatisch beim App-Start
|
||||
await window.api.checkUpdate();
|
||||
}
|
||||
|
||||
async function checkUpdate() {
|
||||
const result = await window.api.checkUpdate();
|
||||
if (result.hasUpdate) {
|
||||
alert(`Neue Version ${result.version} verfugbar!\n\n${result.changelog}`);
|
||||
// Manueller Check - zeigt Info wenn kein Update
|
||||
await window.api.checkUpdate();
|
||||
// Wenn kein Update, kommt kein Event - kurz warten dann Info zeigen
|
||||
setTimeout(() => {
|
||||
if (document.getElementById('updateBanner').style.display !== 'flex') {
|
||||
alert('Du hast die neueste Version!');
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
let updateReady = false;
|
||||
|
||||
function downloadUpdate() {
|
||||
if (updateReady) {
|
||||
// Update ist heruntergeladen - installieren
|
||||
window.api.installUpdate();
|
||||
} else {
|
||||
alert('Du hast die neueste Version!');
|
||||
// Update herunterladen
|
||||
document.getElementById('updateButton').textContent = 'Wird heruntergeladen...';
|
||||
document.getElementById('updateButton').disabled = true;
|
||||
document.getElementById('updateProgress').style.display = 'block';
|
||||
window.api.downloadUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
function downloadUpdate() {
|
||||
window.api.checkUpdate().then(result => {
|
||||
if (result.downloadUrl) {
|
||||
require('electron').shell.openExternal(result.downloadUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Auto-Update Event Listeners
|
||||
window.api.onUpdateAvailable((info) => {
|
||||
document.getElementById('updateBanner').style.display = 'flex';
|
||||
document.getElementById('updateText').textContent = `Version ${info.version} verfügbar!`;
|
||||
});
|
||||
|
||||
window.api.onUpdateDownloadProgress((progress) => {
|
||||
document.getElementById('updateProgressBar').style.width = progress.percent + '%';
|
||||
const mb = (progress.transferred / 1024 / 1024).toFixed(1);
|
||||
const totalMb = (progress.total / 1024 / 1024).toFixed(1);
|
||||
document.getElementById('updateText').textContent = `Downloading: ${mb} / ${totalMb} MB`;
|
||||
});
|
||||
|
||||
window.api.onUpdateDownloaded((info) => {
|
||||
updateReady = true;
|
||||
document.getElementById('updateProgress').style.display = 'none';
|
||||
document.getElementById('updateText').textContent = `Version ${info.version} bereit!`;
|
||||
document.getElementById('updateButton').textContent = 'Jetzt installieren';
|
||||
document.getElementById('updateButton').disabled = false;
|
||||
});
|
||||
|
||||
// Start
|
||||
init();
|
||||
|
||||
@ -8,7 +8,7 @@ import { autoUpdater } from 'electron-updater';
|
||||
// ==========================================
|
||||
// CONFIG & CONSTANTS
|
||||
// ==========================================
|
||||
const APP_VERSION = '3.6.3';
|
||||
const APP_VERSION = '3.6.8';
|
||||
const UPDATE_CHECK_URL = 'http://24-music.de/version.json';
|
||||
|
||||
// Paths
|
||||
@ -836,28 +836,66 @@ function createWindow(): void {
|
||||
mainWindow = null;
|
||||
});
|
||||
|
||||
// Setup auto-updater after window is ready
|
||||
setTimeout(() => {
|
||||
checkForUpdates();
|
||||
setupAutoUpdater();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
async function checkForUpdates(): Promise<{ hasUpdate: boolean; version?: string; changelog?: string; downloadUrl?: string }> {
|
||||
try {
|
||||
const response = await axios.get(UPDATE_CHECK_URL, { timeout: 5000 });
|
||||
const latest = response.data.version;
|
||||
// ==========================================
|
||||
// AUTO-UPDATER (electron-updater)
|
||||
// ==========================================
|
||||
function setupAutoUpdater() {
|
||||
autoUpdater.autoDownload = false;
|
||||
autoUpdater.autoInstallOnAppQuit = true;
|
||||
|
||||
if (latest !== APP_VERSION) {
|
||||
return {
|
||||
hasUpdate: true,
|
||||
version: latest,
|
||||
changelog: response.data.changelog,
|
||||
downloadUrl: response.data.download_url
|
||||
};
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
console.log('Checking for updates...');
|
||||
});
|
||||
|
||||
autoUpdater.on('update-available', (info) => {
|
||||
console.log('Update available:', info.version);
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('update-available', {
|
||||
version: info.version,
|
||||
releaseDate: info.releaseDate
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Update check failed:', e);
|
||||
}
|
||||
return { hasUpdate: false };
|
||||
});
|
||||
|
||||
autoUpdater.on('update-not-available', () => {
|
||||
console.log('No updates available');
|
||||
});
|
||||
|
||||
autoUpdater.on('download-progress', (progress) => {
|
||||
console.log(`Download progress: ${progress.percent.toFixed(1)}%`);
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('update-download-progress', {
|
||||
percent: progress.percent,
|
||||
bytesPerSecond: progress.bytesPerSecond,
|
||||
transferred: progress.transferred,
|
||||
total: progress.total
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
autoUpdater.on('update-downloaded', (info) => {
|
||||
console.log('Update downloaded:', info.version);
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('update-downloaded', {
|
||||
version: info.version
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
autoUpdater.on('error', (err) => {
|
||||
console.error('Auto-updater error:', err);
|
||||
});
|
||||
|
||||
// Check for updates
|
||||
autoUpdater.checkForUpdates().catch(err => {
|
||||
console.error('Update check failed:', err);
|
||||
});
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
@ -949,7 +987,31 @@ ipcMain.handle('open-folder', (_, folderPath: string) => {
|
||||
ipcMain.handle('get-version', () => APP_VERSION);
|
||||
|
||||
ipcMain.handle('check-update', async () => {
|
||||
return await checkForUpdates();
|
||||
try {
|
||||
const result = await autoUpdater.checkForUpdates();
|
||||
return { checking: true };
|
||||
} catch (err) {
|
||||
console.error('Update check failed:', err);
|
||||
return { error: true };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('download-update', async () => {
|
||||
try {
|
||||
await autoUpdater.downloadUpdate();
|
||||
return { downloading: true };
|
||||
} catch (err) {
|
||||
console.error('Download failed:', err);
|
||||
return { error: true };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('install-update', () => {
|
||||
autoUpdater.quitAndInstall(false, true);
|
||||
});
|
||||
|
||||
ipcMain.handle('open-external', async (_, url: string) => {
|
||||
await shell.openExternal(url);
|
||||
});
|
||||
|
||||
ipcMain.handle('download-clip', async (_, clipUrl: string) => {
|
||||
|
||||
@ -88,6 +88,9 @@ contextBridge.exposeInMainWorld('api', {
|
||||
// App
|
||||
getVersion: () => ipcRenderer.invoke('get-version'),
|
||||
checkUpdate: () => ipcRenderer.invoke('check-update'),
|
||||
downloadUpdate: () => ipcRenderer.invoke('download-update'),
|
||||
installUpdate: () => ipcRenderer.invoke('install-update'),
|
||||
openExternal: (url: string) => ipcRenderer.invoke('open-external', url),
|
||||
|
||||
// Events
|
||||
onDownloadProgress: (callback: (progress: DownloadProgress) => void) => {
|
||||
@ -107,5 +110,16 @@ contextBridge.exposeInMainWorld('api', {
|
||||
},
|
||||
onMergeProgress: (callback: (percent: number) => void) => {
|
||||
ipcRenderer.on('merge-progress', (_, percent) => callback(percent));
|
||||
},
|
||||
|
||||
// Auto-Update Events
|
||||
onUpdateAvailable: (callback: (info: { version: string; releaseDate?: string }) => void) => {
|
||||
ipcRenderer.on('update-available', (_, info) => callback(info));
|
||||
},
|
||||
onUpdateDownloadProgress: (callback: (progress: { percent: number; bytesPerSecond: number; transferred: number; total: number }) => void) => {
|
||||
ipcRenderer.on('update-download-progress', (_, progress) => callback(progress));
|
||||
},
|
||||
onUpdateDownloaded: (callback: (info: { version: string }) => void) => {
|
||||
ipcRenderer.on('update-downloaded', (_, info) => callback(info));
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user