feat: Improve UI spacing and layout - Increase window size from 1200x800 to 1400x900 - Add more padding and gaps between UI elements - Improve button sizing and spacing - Better status panel layout with flex distribution - Enhanced activity log readability - Remove unnecessary start/stop buttons (auto-sync only) - Remove progress bar (not needed for continuous sync) - Clean up unused sync methods
This commit is contained in:
parent
73d9a41ca8
commit
39b7fb59aa
22
packages/desktop-sync/.env
Normal file
22
packages/desktop-sync/.env
Normal file
@ -0,0 +1,22 @@
|
||||
# Rekordbox Sync Desktop Application Configuration
|
||||
# Generated on Sat Aug 16 15:03:29 CEST 2025
|
||||
|
||||
# S3 Configuration
|
||||
S3_ENDPOINT=https://garage.geertrademakers.nl
|
||||
S3_REGION=garage
|
||||
S3_ACCESS_KEY_ID=GK1c1a4a30946eb1e7f8d60847
|
||||
S3_SECRET_ACCESS_KEY=2ed6673f0e3c42d347adeb54ba6b95a1ebc6414750f2a95e1d3d89758f1add63
|
||||
S3_BUCKET_NAME=music
|
||||
S3_USE_SSL=true
|
||||
|
||||
# Sync Configuration
|
||||
SYNC_LOCAL_PATH=/Users/geertrademakers/Music/s3-sync-test
|
||||
SYNC_INTERVAL=30000
|
||||
SYNC_AUTO_START=true
|
||||
SYNC_CONFLICT_RESOLUTION=newer-wins
|
||||
|
||||
# UI Configuration
|
||||
UI_THEME=system
|
||||
UI_LANGUAGE=en
|
||||
UI_NOTIFICATIONS=true
|
||||
UI_MINIMIZE_TO_TRAY=true
|
||||
22
packages/desktop-sync/.env.backup
Normal file
22
packages/desktop-sync/.env.backup
Normal file
@ -0,0 +1,22 @@
|
||||
# Rekordbox Sync Desktop Application Configuration
|
||||
# Generated on Sat Aug 16 15:03:29 CEST 2025
|
||||
|
||||
# S3 Configuration
|
||||
S3_ENDPOINT=https://garage.geertrademakers.nl
|
||||
S3_REGION=garage
|
||||
S3_ACCESS_KEY_ID=GK1c1a4a30946eb1e7f8d60847
|
||||
S3_SECRET_ACCESS_KEY=2ed6673f0e3c42d347adeb54ba6b95a1ebc6414750f2a95e1d3d89758f1add63
|
||||
S3_BUCKET_NAME=music
|
||||
S3_USE_SSL=true
|
||||
|
||||
# Sync Configuration
|
||||
SYNC_LOCAL_PATH=/Users/geertrademakers/Desktop/s3-music-sync-dir
|
||||
SYNC_INTERVAL=30000
|
||||
SYNC_AUTO_START=true
|
||||
SYNC_CONFLICT_RESOLUTION=newer-wins
|
||||
|
||||
# UI Configuration
|
||||
UI_THEME=system
|
||||
UI_LANGUAGE=en
|
||||
UI_NOTIFICATIONS=true
|
||||
UI_MINIMIZE_TO_TRAY=true
|
||||
255
packages/desktop-sync/README.md
Normal file
255
packages/desktop-sync/README.md
Normal file
@ -0,0 +1,255 @@
|
||||
# Rekordbox Sync - Desktop Companion
|
||||
|
||||
A desktop application for bidirectional synchronization between a Garage-hosted S3 instance and your local computer, specifically designed for Rekordbox music libraries.
|
||||
|
||||
## 🚀 Features
|
||||
|
||||
- **Bidirectional S3 Sync**: Seamlessly sync files between your local machine and S3 storage
|
||||
- **Incremental Sync**: Only sync files that have changed since the last sync
|
||||
- **Automatic Cleanup**: Removes temporary files before syncing
|
||||
- **Real-time Monitoring**: Continuous sync with configurable intervals
|
||||
- **Error Handling**: Robust error handling with automatic retries
|
||||
- **Progress Tracking**: Real-time progress updates and file counting
|
||||
- **Cross-platform**: Built with Electron for macOS, Windows, and Linux
|
||||
|
||||
## 🔧 Prerequisites
|
||||
|
||||
### AWS CLI v2
|
||||
This tool requires AWS CLI v2 to be installed on your system. The AWS CLI provides the `aws s3 sync` command which offers superior performance and reliability compared to other S3 sync tools.
|
||||
|
||||
#### Installation Options:
|
||||
|
||||
**Option 1: Automatic Installation (macOS)**
|
||||
```bash
|
||||
npm run install-aws-cli
|
||||
```
|
||||
|
||||
**Option 2: Manual Installation**
|
||||
- Download from: https://awscli.amazonaws.com/
|
||||
- Follow the installation guide: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
|
||||
|
||||
**Option 3: Homebrew (macOS)**
|
||||
```bash
|
||||
brew install awscli
|
||||
```
|
||||
|
||||
#### Verify Installation
|
||||
```bash
|
||||
aws --version
|
||||
```
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
1. **Clone the repository**
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd rekordbox-reader/packages/desktop-sync
|
||||
```
|
||||
|
||||
2. **Install dependencies**
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
3. **Configure environment**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# Edit .env with your S3 configuration
|
||||
```
|
||||
|
||||
4. **Build the application**
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
5. **Start the application**
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
Create a `.env` file in the project root with the following variables:
|
||||
|
||||
```env
|
||||
# S3 Configuration (Garage)
|
||||
S3_ENDPOINT=http://your-garage-instance:3900
|
||||
S3_REGION=garage
|
||||
S3_ACCESS_KEY_ID=your-access-key
|
||||
S3_SECRET_ACCESS_KEY=your-secret-key
|
||||
S3_BUCKET_NAME=your-bucket-name
|
||||
S3_USE_SSL=false
|
||||
|
||||
# Sync Configuration
|
||||
SYNC_LOCAL_PATH=/path/to/your/local/music/folder
|
||||
SYNC_INTERVAL=30000
|
||||
SYNC_AUTO_START=false
|
||||
SYNC_CONFLICT_RESOLUTION=newer-wins
|
||||
|
||||
# UI Configuration
|
||||
UI_THEME=dark
|
||||
UI_LANGUAGE=en
|
||||
```
|
||||
|
||||
### Garage S3 Configuration
|
||||
|
||||
For Garage S3 compatibility, ensure your configuration includes:
|
||||
- **Endpoint**: Your Garage instance URL (e.g., `http://localhost:3900`)
|
||||
- **Region**: Usually `garage` or `us-east-1`
|
||||
- **SSL**: Set to `false` for local Garage instances
|
||||
|
||||
## 🎯 Usage
|
||||
|
||||
### Starting Sync
|
||||
1. Launch the application
|
||||
2. Click "Start Sync" to begin bidirectional synchronization
|
||||
3. The app will:
|
||||
- Download all files from S3 to local (first time)
|
||||
- Upload new/changed local files to S3
|
||||
- Start continuous bidirectional sync
|
||||
|
||||
### Sync Modes
|
||||
|
||||
#### **Initial Sync**
|
||||
- Downloads all files from S3 to local
|
||||
- Ensures local folder matches S3 bucket contents
|
||||
- Excludes temporary files (`.tmp`, `.temp`, `.part`, `.DS_Store`)
|
||||
|
||||
#### **Continuous Sync**
|
||||
- Monitors both local and S3 for changes
|
||||
- Automatically syncs new, modified, or deleted files
|
||||
- Runs every 30 seconds by default
|
||||
- Maintains bidirectional consistency
|
||||
|
||||
#### **Force Full Sync**
|
||||
- Completely resynchronizes all files
|
||||
- Useful for resolving sync conflicts
|
||||
- Deletes and re-downloads all files
|
||||
|
||||
### File Handling
|
||||
|
||||
- **Temporary Files**: Automatically excluded and cleaned up
|
||||
- **Conflict Resolution**: Newer timestamp wins by default
|
||||
- **Delete Propagation**: Files deleted locally are removed from S3 and vice versa
|
||||
- **Incremental Updates**: Only changed files are transferred
|
||||
|
||||
## 🔍 Monitoring
|
||||
|
||||
### Real-time Status
|
||||
- Current sync phase (downloading, uploading, watching)
|
||||
- Progress percentage and file counts
|
||||
- Transfer speed and ETA
|
||||
- Error messages and retry attempts
|
||||
|
||||
### Activity Log
|
||||
- Detailed AWS CLI output
|
||||
- File operations and sync events
|
||||
- Error tracking and resolution
|
||||
|
||||
### File Counts
|
||||
- Accurate local file counting
|
||||
- S3 bucket file statistics
|
||||
- Sync progress tracking
|
||||
|
||||
## 🛠️ Development
|
||||
|
||||
### Project Structure
|
||||
```
|
||||
src/
|
||||
├── main.ts # Main Electron process
|
||||
├── preload.ts # Preload script for IPC
|
||||
├── services/
|
||||
│ ├── awsS3Service.ts # AWS S3 sync service
|
||||
│ ├── configManager.ts # Configuration management
|
||||
│ ├── fileWatcher.ts # Local file system monitoring
|
||||
│ └── syncManager.ts # Sync orchestration
|
||||
└── renderer/ # UI components
|
||||
├── index.html
|
||||
├── renderer.js
|
||||
└── styles.css
|
||||
```
|
||||
|
||||
### Available Scripts
|
||||
- `npm run dev` - Development mode with hot reload
|
||||
- `npm run build` - Build TypeScript to JavaScript
|
||||
- `npm run start` - Start the built application
|
||||
- `npm run package` - Package for distribution
|
||||
- `npm run install-aws-cli` - Install AWS CLI (macOS)
|
||||
|
||||
### Building
|
||||
```bash
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
|
||||
## 🚨 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**AWS CLI Not Found**
|
||||
```bash
|
||||
# Check if AWS CLI is installed
|
||||
aws --version
|
||||
|
||||
# Install if missing
|
||||
npm run install-aws-cli
|
||||
```
|
||||
|
||||
**Sync Fails to Start**
|
||||
- Verify S3 credentials in `.env`
|
||||
- Check network connectivity to Garage instance
|
||||
- Ensure local sync path exists and is writable
|
||||
|
||||
**Files Not Syncing**
|
||||
- Check file permissions
|
||||
- Verify S3 bucket access
|
||||
- Review activity log for error messages
|
||||
|
||||
**Performance Issues**
|
||||
- AWS CLI v2 provides optimal performance
|
||||
- Consider adjusting sync interval
|
||||
- Monitor network bandwidth usage
|
||||
|
||||
### Debug Mode
|
||||
Enable detailed logging by setting environment variables:
|
||||
```bash
|
||||
DEBUG=* npm run dev
|
||||
```
|
||||
|
||||
## 📊 Performance
|
||||
|
||||
- **AWS CLI v2**: Optimized for S3 operations
|
||||
- **Incremental Sync**: Only transfers changed files
|
||||
- **Parallel Operations**: Efficient file transfer
|
||||
- **Memory Management**: Minimal memory footprint
|
||||
- **Network Optimization**: Intelligent retry and backoff
|
||||
|
||||
## 🔒 Security
|
||||
|
||||
- **Credential Management**: Secure storage of S3 credentials
|
||||
- **Local Storage**: Credentials stored locally, never transmitted
|
||||
- **SSL Support**: Configurable SSL/TLS for S3 endpoints
|
||||
- **Access Control**: Follows S3 bucket policies and IAM permissions
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Add tests if applicable
|
||||
5. Submit a pull request
|
||||
|
||||
## 📄 License
|
||||
|
||||
MIT License - see LICENSE file for details
|
||||
|
||||
## 🙏 Acknowledgments
|
||||
|
||||
- **AWS CLI**: Powerful S3 sync capabilities
|
||||
- **Electron**: Cross-platform desktop framework
|
||||
- **Garage**: Self-hosted S3-compatible storage
|
||||
- **Rekordbox**: Professional DJ software
|
||||
|
||||
---
|
||||
|
||||
**Note**: This tool is designed for personal and professional use with Garage S3 storage. Ensure compliance with your organization's data policies and S3 usage guidelines.
|
||||
5
packages/desktop-sync/assets/icon.png
Normal file
5
packages/desktop-sync/assets/icon.png
Normal file
@ -0,0 +1,5 @@
|
||||
# This is a placeholder for the PNG icon
|
||||
# You can convert the SVG to PNG using:
|
||||
# - Online tools like convertio.co
|
||||
# - Command line: convert icon.svg icon.png (if ImageMagick is installed)
|
||||
# - Or use any image editor that supports SVG import
|
||||
36
packages/desktop-sync/assets/icon.svg
Normal file
36
packages/desktop-sync/assets/icon.svg
Normal file
@ -0,0 +1,36 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#3498db;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#2980b9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="sync" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#27ae60;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#2ecc71;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background circle -->
|
||||
<circle cx="256" cy="256" r="240" fill="url(#bg)" stroke="#2c3e50" stroke-width="16"/>
|
||||
|
||||
<!-- Sync arrows -->
|
||||
<g fill="url(#sync)">
|
||||
<!-- Left arrow -->
|
||||
<path d="M 120 200 L 160 200 L 160 160 L 200 200 L 160 240 L 160 200 Z"/>
|
||||
|
||||
<!-- Right arrow -->
|
||||
<path d="M 392 312 L 352 312 L 352 352 L 312 312 L 352 272 L 352 312 Z"/>
|
||||
</g>
|
||||
|
||||
<!-- Music note -->
|
||||
<g fill="white">
|
||||
<ellipse cx="200" cy="280" rx="12" ry="16"/>
|
||||
<rect x="188" y="240" width="8" height="40" rx="4"/>
|
||||
<ellipse cx="312" cy="232" rx="12" ry="16"/>
|
||||
<rect x="300" y="192" width="8" height="40" rx="4"/>
|
||||
</g>
|
||||
|
||||
<!-- Center sync symbol -->
|
||||
<circle cx="256" cy="256" r="40" fill="none" stroke="white" stroke-width="8" stroke-dasharray="20,10"/>
|
||||
<circle cx="256" cy="256" r="20" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
29
packages/desktop-sync/env.example
Normal file
29
packages/desktop-sync/env.example
Normal file
@ -0,0 +1,29 @@
|
||||
# Rekordbox Sync Desktop Application Configuration
|
||||
# Copy this file to .env and fill in your values
|
||||
|
||||
# S3 Configuration
|
||||
S3_ENDPOINT=https://garage.geertrademakers.nl
|
||||
S3_REGION=garage
|
||||
S3_ACCESS_KEY_ID=your_access_key_here
|
||||
S3_SECRET_ACCESS_KEY=your_secret_key_here
|
||||
S3_BUCKET_NAME=music
|
||||
S3_USE_SSL=true
|
||||
|
||||
# Sync Configuration
|
||||
SYNC_LOCAL_PATH=/path/to/your/music/folder
|
||||
SYNC_INTERVAL=30000
|
||||
SYNC_AUTO_START=false
|
||||
SYNC_CONFLICT_RESOLUTION=newer-wins
|
||||
|
||||
# UI Configuration
|
||||
UI_THEME=system
|
||||
UI_LANGUAGE=en
|
||||
UI_NOTIFICATIONS=true
|
||||
UI_MINIMIZE_TO_TRAY=true
|
||||
|
||||
# Notes:
|
||||
# - SYNC_INTERVAL is in milliseconds (30000 = 30 seconds)
|
||||
# - SYNC_CONFLICT_RESOLUTION options: newer-wins, local-wins, remote-wins
|
||||
# - UI_THEME options: system, light, dark
|
||||
# - Boolean values: true/false (as strings)
|
||||
# - Paths should use forward slashes (/) even on Windows
|
||||
53
packages/desktop-sync/install-aws-cli.sh
Executable file
53
packages/desktop-sync/install-aws-cli.sh
Executable file
@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
# AWS CLI v2 Installer for macOS
|
||||
# This script downloads and installs AWS CLI v2 on macOS
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Installing AWS CLI v2 for macOS..."
|
||||
|
||||
# Check if AWS CLI is already installed
|
||||
if command -v aws &> /dev/null; then
|
||||
echo "✅ AWS CLI is already installed:"
|
||||
aws --version
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if we're on macOS
|
||||
if [[ "$OSTYPE" != "darwin"* ]]; then
|
||||
echo "❌ This script is for macOS only. Please install AWS CLI manually for your platform."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create temporary directory
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
cd "$TEMP_DIR"
|
||||
|
||||
echo "📥 Downloading AWS CLI v2..."
|
||||
|
||||
# Download AWS CLI v2 for macOS
|
||||
curl -O https://awscli.amazonaws.com/AWSCLIV2.pkg
|
||||
|
||||
echo "🔧 Installing AWS CLI v2..."
|
||||
|
||||
# Install the package
|
||||
sudo installer -pkg AWSCLIV2.pkg -target /
|
||||
|
||||
# Clean up
|
||||
cd - > /dev/null
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
echo "✅ AWS CLI v2 installed successfully!"
|
||||
|
||||
# Verify installation
|
||||
if command -v aws &> /dev/null; then
|
||||
echo "🔍 AWS CLI version:"
|
||||
aws --version
|
||||
echo ""
|
||||
echo "🎉 Installation completed! You can now use the desktop sync tool."
|
||||
else
|
||||
echo "❌ Installation failed. Please try installing manually:"
|
||||
echo " https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"
|
||||
exit 1
|
||||
fi
|
||||
@ -33,7 +33,7 @@
|
||||
<div class="status-panel">
|
||||
<div class="status-item">
|
||||
<span class="status-label">Sync Status:</span>
|
||||
<span id="syncStatus" class="status-value">Stopped</span>
|
||||
<span id="syncStatus" class="status-value">Initializing...</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-label">Last Sync:</span>
|
||||
@ -44,22 +44,14 @@
|
||||
<span id="filesSynced" class="status-value">0</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-label">Progress:</span>
|
||||
<div class="progress-container">
|
||||
<div id="progressBar" class="progress-bar" style="width: 0%"></div>
|
||||
</div>
|
||||
<span class="status-label">Sync Mode:</span>
|
||||
<span id="syncMode" class="status-value">Auto-sync</span>
|
||||
</div>
|
||||
<div id="syncDetails" class="sync-details"></div>
|
||||
</div>
|
||||
|
||||
<!-- Control Panel -->
|
||||
<div class="control-panel">
|
||||
<button id="startSyncBtn" class="btn btn-primary">
|
||||
<i class="fas fa-play"></i> Start Sync
|
||||
</button>
|
||||
<button id="stopSyncBtn" class="btn btn-danger" disabled>
|
||||
<i class="fas fa-stop"></i> Stop Sync
|
||||
</button>
|
||||
<button id="forceSyncBtn" class="btn btn-secondary">
|
||||
<i class="fas fa-sync"></i> Force Full Sync
|
||||
</button>
|
||||
|
||||
@ -11,8 +11,6 @@ class RekordboxSyncRenderer {
|
||||
*/
|
||||
initializeElements() {
|
||||
// Buttons
|
||||
this.startBtn = document.getElementById('startSyncBtn');
|
||||
this.stopBtn = document.getElementById('stopSyncBtn');
|
||||
this.exportEnvBtn = document.getElementById('exportEnvBtn');
|
||||
|
||||
// Status elements
|
||||
@ -20,6 +18,7 @@ class RekordboxSyncRenderer {
|
||||
this.filesSyncedElement = document.getElementById('filesSynced');
|
||||
this.lastSyncElement = document.getElementById('lastSync');
|
||||
this.syncDetailsElement = document.getElementById('syncDetails');
|
||||
this.syncModeElement = document.getElementById('syncMode');
|
||||
|
||||
// Activity log
|
||||
this.activityLogElement = document.getElementById('activityLog');
|
||||
@ -76,30 +75,14 @@ class RekordboxSyncRenderer {
|
||||
* Setup Electron IPC listeners
|
||||
*/
|
||||
setupElectronListeners() {
|
||||
console.log('🔌 Setting up Electron IPC listeners...');
|
||||
console.log('🔍 Window object:', window);
|
||||
console.log('🔍 Electron API object:', window.electronAPI);
|
||||
|
||||
if (!window.electronAPI) {
|
||||
console.error('❌ Electron API not available');
|
||||
console.error('❌ This means the preload script failed to load');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Electron API is available, setting up listeners...');
|
||||
|
||||
// Sync status updates
|
||||
window.electronAPI.on('sync-status-changed', (status) => {
|
||||
console.log('📊 Received sync status update:', status);
|
||||
console.log('🔍 Status details:', {
|
||||
isRunning: status.isRunning,
|
||||
currentPhase: status.currentPhase,
|
||||
actualFileCount: status.actualFileCount,
|
||||
stats: status.stats
|
||||
});
|
||||
console.log('🔄 Calling updateSyncStatus...');
|
||||
this.updateSyncStatus(status);
|
||||
console.log('✅ updateSyncStatus completed');
|
||||
});
|
||||
|
||||
// File change events
|
||||
@ -159,11 +142,7 @@ class RekordboxSyncRenderer {
|
||||
if (this.startBtn) this.startBtn.disabled = false;
|
||||
if (this.stopBtn) this.stopBtn.disabled = true;
|
||||
|
||||
// Reset progress bar on error
|
||||
const progressBar = document.getElementById('progressBar');
|
||||
if (progressBar) {
|
||||
progressBar.style.width = '0%';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Engine events
|
||||
@ -213,33 +192,7 @@ class RekordboxSyncRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start sync
|
||||
*/
|
||||
async startSync() {
|
||||
try {
|
||||
this.addActivityLog('info', 'Starting sync...');
|
||||
await window.electronAPI.invoke('sync:start');
|
||||
this.addActivityLog('success', 'Sync started successfully');
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to start sync:', error);
|
||||
this.addActivityLog('error', `Failed to start sync: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop sync
|
||||
*/
|
||||
async stopSync() {
|
||||
try {
|
||||
this.addActivityLog('info', 'Stopping sync...');
|
||||
await window.electronAPI.invoke('sync:stop');
|
||||
this.addActivityLog('info', 'Sync stopped');
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to stop sync:', error);
|
||||
this.addActivityLog('error', `Failed to stop sync: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force full sync
|
||||
@ -278,8 +231,6 @@ class RekordboxSyncRenderer {
|
||||
|
||||
this.syncStatusElement.textContent = statusText;
|
||||
this.syncStatusElement.className = 'status-value running';
|
||||
if (this.startBtn) this.startBtn.disabled = true;
|
||||
if (this.stopBtn) this.stopBtn.disabled = false;
|
||||
} else {
|
||||
if (status.completedCount > 0 && status.pendingCount === 0 && status.inProgressCount === 0) {
|
||||
this.syncStatusElement.textContent = 'Completed';
|
||||
@ -288,11 +239,7 @@ class RekordboxSyncRenderer {
|
||||
this.syncStatusElement.textContent = 'Stopped';
|
||||
this.syncStatusElement.className = 'status-value stopped';
|
||||
|
||||
// Reset progress bar when stopped
|
||||
const progressBar = document.getElementById('progressBar');
|
||||
if (progressBar) {
|
||||
progressBar.style.width = '0%';
|
||||
}
|
||||
|
||||
}
|
||||
if (this.startBtn) this.startBtn.disabled = false;
|
||||
if (this.stopBtn) this.stopBtn.disabled = true;
|
||||
@ -347,14 +294,8 @@ class RekordboxSyncRenderer {
|
||||
this.addActivityLog('info', `${status.currentPhase}: ${status.progressMessage}`);
|
||||
}
|
||||
|
||||
// Update progress bar
|
||||
// Log progress changes to reduce spam
|
||||
if (status.progress && status.progress.percent > 0) {
|
||||
const progressBar = document.getElementById('progressBar');
|
||||
if (progressBar) {
|
||||
progressBar.style.width = `${status.progress.percent}%`;
|
||||
}
|
||||
|
||||
// Only log progress changes to reduce spam
|
||||
const currentProgress = status.progress.percent;
|
||||
if (!this.lastLoggedProgress || Math.abs(currentProgress - this.lastLoggedProgress) >= 10) {
|
||||
const progressText = `Progress: ${currentProgress}% - ${status.progress.message || 'Syncing files...'}`;
|
||||
|
||||
@ -52,17 +52,19 @@ body {
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
padding: 0.5rem 1rem;
|
||||
padding: 1rem 2rem;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
gap: 0.75rem;
|
||||
text-decoration: none;
|
||||
min-width: 140px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
@ -124,12 +126,12 @@ body {
|
||||
/* Main content */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 1.5rem;
|
||||
padding: 2rem;
|
||||
overflow-y: auto;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: auto auto 1fr;
|
||||
gap: 1.5rem;
|
||||
gap: 2rem;
|
||||
grid-template-areas:
|
||||
"status control"
|
||||
"activity activity"
|
||||
@ -140,32 +142,37 @@ body {
|
||||
.status-panel {
|
||||
grid-area: status;
|
||||
background: white;
|
||||
padding: 1.5rem;
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
display: block;
|
||||
font-size: 0.8rem;
|
||||
font-size: 0.85rem;
|
||||
color: #7f8c8d;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-value {
|
||||
display: block;
|
||||
font-size: 1.2rem;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
#syncStatus {
|
||||
@ -195,49 +202,17 @@ body {
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
/* Progress Bar */
|
||||
.progress-container {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: #e0e0e0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #3498db, #2ecc71);
|
||||
border-radius: 4px;
|
||||
transition: width 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.progress-bar::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||
animation: shimmer 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
/* Control Panel */
|
||||
.control-panel {
|
||||
grid-area: control;
|
||||
background: white;
|
||||
padding: 1.5rem;
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
gap: 1.5rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
@ -248,7 +223,7 @@ body {
|
||||
.activity-panel {
|
||||
grid-area: activity;
|
||||
background: white;
|
||||
padding: 1.5rem;
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
@ -256,11 +231,12 @@ body {
|
||||
}
|
||||
|
||||
.activity-panel h3 {
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: #2c3e50;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
gap: 0.75rem;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.activity-log {
|
||||
@ -272,16 +248,18 @@ body {
|
||||
text-align: center;
|
||||
color: #95a5a6;
|
||||
font-style: italic;
|
||||
padding: 2rem;
|
||||
padding: 3rem 2rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.activity-item {
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
border-radius: 6px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
border-radius: 8px;
|
||||
background: #f8f9fa;
|
||||
border-left: 4px solid #95a5a6;
|
||||
font-size: 0.9rem;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.activity-item.info {
|
||||
|
||||
61
packages/desktop-sync/scripts/check-aws-cli.js
Normal file
61
packages/desktop-sync/scripts/check-aws-cli.js
Normal file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Check if AWS CLI is available on the system
|
||||
* This script is run during postinstall to ensure AWS CLI is available
|
||||
*/
|
||||
|
||||
const { spawn } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
console.log('🔍 Checking AWS CLI availability...');
|
||||
|
||||
// Check if AWS CLI is available
|
||||
function checkAwsCli() {
|
||||
return new Promise((resolve) => {
|
||||
const process = spawn('aws', ['--version'], { stdio: 'pipe' });
|
||||
|
||||
process.on('close', (code) => {
|
||||
resolve(code === 0);
|
||||
});
|
||||
|
||||
process.on('error', () => {
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const isAvailable = await checkAwsCli();
|
||||
|
||||
if (isAvailable) {
|
||||
console.log('✅ AWS CLI is available');
|
||||
|
||||
// Get version
|
||||
const versionProcess = spawn('aws', ['--version'], { stdio: 'pipe' });
|
||||
versionProcess.stdout.on('data', (data) => {
|
||||
console.log(`📋 Version: ${data.toString().trim()}`);
|
||||
});
|
||||
|
||||
console.log('🎉 You can now use the desktop sync tool with AWS S3!');
|
||||
} else {
|
||||
console.log('❌ AWS CLI is not available');
|
||||
console.log('');
|
||||
console.log('📋 To install AWS CLI v2:');
|
||||
console.log('');
|
||||
console.log(' Option 1: Run the installer script:');
|
||||
console.log(' npm run install-aws-cli');
|
||||
console.log('');
|
||||
console.log(' Option 2: Install manually:');
|
||||
console.log(' https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html');
|
||||
console.log('');
|
||||
console.log(' Option 3: Use Homebrew (macOS):');
|
||||
console.log(' brew install awscli');
|
||||
console.log('');
|
||||
console.log('⚠️ The desktop sync tool requires AWS CLI to function properly.');
|
||||
console.log(' Please install AWS CLI before using the sync functionality.');
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
87
packages/desktop-sync/scripts/check-minio.js
Normal file
87
packages/desktop-sync/scripts/check-minio.js
Normal file
@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { spawn } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
console.log('🔍 Checking MinIO Client installation...');
|
||||
|
||||
function checkMinio() {
|
||||
return new Promise((resolve) => {
|
||||
const minio = spawn('mc', ['--version'], { stdio: 'pipe' });
|
||||
|
||||
let output = '';
|
||||
let error = '';
|
||||
|
||||
minio.stdout.on('data', (data) => {
|
||||
output += data.toString();
|
||||
});
|
||||
|
||||
minio.stderr.on('data', (data) => {
|
||||
error += data.toString();
|
||||
});
|
||||
|
||||
minio.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
const versionMatch = output.match(/mc version (RELEASE\.\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z)/);
|
||||
if (versionMatch) {
|
||||
console.log(`✅ MinIO Client is installed: ${versionMatch[1]}`);
|
||||
resolve(true);
|
||||
} else {
|
||||
console.log('✅ MinIO Client is installed (version unknown)');
|
||||
resolve(true);
|
||||
}
|
||||
} else {
|
||||
console.log('❌ MinIO Client is not installed or not in PATH');
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
|
||||
minio.on('error', () => {
|
||||
console.log('❌ MinIO Client is not installed or not in PATH');
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showInstallInstructions() {
|
||||
console.log('\n📥 MinIO Client Installation Instructions:');
|
||||
console.log('==========================================');
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
console.log('\n🍎 macOS:');
|
||||
console.log(' brew install minio/stable/mc');
|
||||
console.log(' # Or download from: https://min.io/download');
|
||||
} else if (process.platform === 'win32') {
|
||||
console.log('\n🪟 Windows:');
|
||||
console.log(' # Download from: https://min.io/download');
|
||||
console.log(' # Extract and add to PATH');
|
||||
} else if (process.platform === 'linux') {
|
||||
console.log('\n🐧 Linux:');
|
||||
console.log(' # Ubuntu/Debian:');
|
||||
console.log(' wget https://dl.min.io/client/mc/release/linux-amd64/mc');
|
||||
console.log(' chmod +x mc');
|
||||
console.log(' sudo mv mc /usr/local/bin/');
|
||||
console.log(' # Or: curl https://dl.min.io/client/mc/release/linux-amd64/mc -o mc && chmod +x mc && sudo mv mc /usr/local/bin/');
|
||||
}
|
||||
|
||||
console.log('\n📚 After installation:');
|
||||
console.log(' 1. Run: mc alias set garage https://your-garage-endpoint access-key secret-key');
|
||||
console.log(' 2. Test with: mc ls garage/bucket-name');
|
||||
console.log('\n🔗 Documentation: https://min.io/docs/minio/linux/reference/minio-mc.html');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const isInstalled = await checkMinio();
|
||||
|
||||
if (!isInstalled) {
|
||||
showInstallInstructions();
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('\n🎉 MinIO Client is ready to use!');
|
||||
console.log('💡 You can now run: npm start');
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
|
||||
89
packages/desktop-sync/scripts/check-rclone.js
Normal file
89
packages/desktop-sync/scripts/check-rclone.js
Normal file
@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { spawn } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
console.log('🔍 Checking rclone installation...');
|
||||
|
||||
function checkRclone() {
|
||||
return new Promise((resolve) => {
|
||||
const rclone = spawn('rclone', ['version'], { stdio: 'pipe' });
|
||||
|
||||
let output = '';
|
||||
let error = '';
|
||||
|
||||
rclone.stdout.on('data', (data) => {
|
||||
output += data.toString();
|
||||
});
|
||||
|
||||
rclone.stderr.on('data', (data) => {
|
||||
error += data.toString();
|
||||
});
|
||||
|
||||
rclone.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
const versionMatch = output.match(/rclone v(\d+\.\d+\.\d+)/);
|
||||
if (versionMatch) {
|
||||
console.log(`✅ Rclone is installed: ${versionMatch[1]}`);
|
||||
resolve(true);
|
||||
} else {
|
||||
console.log('✅ Rclone is installed (version unknown)');
|
||||
resolve(true);
|
||||
}
|
||||
} else {
|
||||
console.log('❌ Rclone is not installed or not in PATH');
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
|
||||
rclone.on('error', () => {
|
||||
console.log('❌ Rclone is not installed or not in PATH');
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showInstallInstructions() {
|
||||
console.log('\n📥 Rclone Installation Instructions:');
|
||||
console.log('=====================================');
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
console.log('\n🍎 macOS:');
|
||||
console.log(' brew install rclone');
|
||||
console.log(' # Or download from: https://rclone.org/downloads/');
|
||||
} else if (process.platform === 'win32') {
|
||||
console.log('\n🪟 Windows:');
|
||||
console.log(' # Download from: https://rclone.org/downloads/');
|
||||
console.log(' # Extract and add to PATH');
|
||||
} else if (process.platform === 'linux') {
|
||||
console.log('\n🐧 Linux:');
|
||||
console.log(' # Ubuntu/Debian:');
|
||||
console.log(' curl https://rclone.org/install.sh | sudo bash');
|
||||
console.log(' # Or: sudo apt install rclone');
|
||||
console.log(' # CentOS/RHEL:');
|
||||
console.log(' sudo yum install rclone');
|
||||
}
|
||||
|
||||
console.log('\n📚 After installation:');
|
||||
console.log(' 1. Run: rclone config');
|
||||
console.log(' 2. Create a new remote named "music"');
|
||||
console.log(' 3. Choose S3 provider');
|
||||
console.log(' 4. Enter your Garage S3 credentials');
|
||||
console.log('\n🔗 Documentation: https://rclone.org/s3/');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const isInstalled = await checkRclone();
|
||||
|
||||
if (!isInstalled) {
|
||||
showInstallInstructions();
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('\n🎉 Rclone is ready to use!');
|
||||
console.log('💡 You can now run: npm start');
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
|
||||
152
packages/desktop-sync/setup-env.sh
Executable file
152
packages/desktop-sync/setup-env.sh
Executable file
@ -0,0 +1,152 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Rekordbox Sync .env Setup Script
|
||||
|
||||
echo "🔧 Setting up Rekordbox Sync .env configuration file..."
|
||||
|
||||
# Check if .env already exists
|
||||
if [ -f ".env" ]; then
|
||||
echo "⚠️ .env file already exists!"
|
||||
read -p "Do you want to overwrite it? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "❌ Setup cancelled."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get S3 configuration
|
||||
echo ""
|
||||
echo "🌐 S3 Configuration:"
|
||||
read -p "S3 Endpoint (default: https://garage.geertrademakers.nl): " s3_endpoint
|
||||
s3_endpoint=${s3_endpoint:-https://garage.geertrademakers.nl}
|
||||
|
||||
read -p "S3 Region (default: garage): " s3_region
|
||||
s3_region=${s3_region:-garage}
|
||||
|
||||
read -p "S3 Access Key ID: " s3_access_key
|
||||
if [ -z "$s3_access_key" ]; then
|
||||
echo "❌ S3 Access Key ID is required!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -s -p "S3 Secret Access Key: " s3_secret_key
|
||||
echo
|
||||
if [ -z "$s3_secret_key" ]; then
|
||||
echo "❌ S3 Secret Access Key is required!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -p "S3 Bucket Name (default: music): " s3_bucket
|
||||
s3_bucket=${s3_bucket:-music}
|
||||
|
||||
read -p "Use SSL? (Y/n): " -n 1 -r
|
||||
echo
|
||||
s3_use_ssl="true"
|
||||
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
||||
s3_use_ssl="false"
|
||||
fi
|
||||
|
||||
# Get sync configuration
|
||||
echo ""
|
||||
echo "🔄 Sync Configuration:"
|
||||
read -p "Local Music Folder Path: " sync_local_path
|
||||
if [ -z "$sync_local_path" ]; then
|
||||
echo "❌ Local music folder path is required!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -p "Sync Interval in seconds (default: 30): " sync_interval
|
||||
sync_interval=${sync_interval:-30}
|
||||
sync_interval=$((sync_interval * 1000)) # Convert to milliseconds
|
||||
|
||||
read -p "Auto-start sync on app launch? (y/N): " -n 1 -r
|
||||
echo
|
||||
sync_auto_start="false"
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
sync_auto_start="true"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Conflict Resolution Strategy:"
|
||||
echo "1) newer-wins (recommended)"
|
||||
echo "2) local-wins"
|
||||
echo "3) remote-wins"
|
||||
read -p "Choose strategy (1-3, default: 1): " conflict_resolution
|
||||
case $conflict_resolution in
|
||||
2) conflict_resolution="local-wins" ;;
|
||||
3) conflict_resolution="remote-wins" ;;
|
||||
*) conflict_resolution="newer-wins" ;;
|
||||
esac
|
||||
|
||||
# Get UI configuration
|
||||
echo ""
|
||||
echo "🎨 UI Configuration:"
|
||||
echo "Theme options:"
|
||||
echo "1) system (follows OS theme)"
|
||||
echo "2) light"
|
||||
echo "3) dark"
|
||||
read -p "Choose theme (1-3, default: 1): " ui_theme
|
||||
case $ui_theme in
|
||||
2) ui_theme="light" ;;
|
||||
3) ui_theme="dark" ;;
|
||||
*) ui_theme="system" ;;
|
||||
esac
|
||||
|
||||
read -p "Show notifications? (Y/n): " -n 1 -r
|
||||
echo
|
||||
ui_notifications="true"
|
||||
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
||||
ui_notifications="false"
|
||||
fi
|
||||
|
||||
read -p "Minimize to system tray? (Y/n): " -n 1 -r
|
||||
echo
|
||||
ui_minimize_to_tray="true"
|
||||
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
||||
ui_minimize_to_tray="false"
|
||||
fi
|
||||
|
||||
# Create .env file
|
||||
echo ""
|
||||
echo "📝 Creating .env file..."
|
||||
|
||||
cat > .env << EOF
|
||||
# Rekordbox Sync Desktop Application Configuration
|
||||
# Generated on $(date)
|
||||
|
||||
# S3 Configuration
|
||||
S3_ENDPOINT=$s3_endpoint
|
||||
S3_REGION=$s3_region
|
||||
S3_ACCESS_KEY_ID=$s3_access_key
|
||||
S3_SECRET_ACCESS_KEY=$s3_secret_key
|
||||
S3_BUCKET_NAME=$s3_bucket
|
||||
S3_USE_SSL=$s3_use_ssl
|
||||
|
||||
# Sync Configuration
|
||||
SYNC_LOCAL_PATH=$sync_local_path
|
||||
SYNC_INTERVAL=$sync_interval
|
||||
SYNC_AUTO_START=$sync_auto_start
|
||||
SYNC_CONFLICT_RESOLUTION=$conflict_resolution
|
||||
|
||||
# UI Configuration
|
||||
UI_THEME=$ui_theme
|
||||
UI_LANGUAGE=en
|
||||
UI_NOTIFICATIONS=$ui_notifications
|
||||
UI_MINIMIZE_TO_TRAY=$ui_minimize_to_tray
|
||||
EOF
|
||||
|
||||
echo "✅ .env file created successfully!"
|
||||
echo ""
|
||||
echo "🔍 Configuration summary:"
|
||||
echo " S3 Endpoint: $s3_endpoint"
|
||||
echo " S3 Region: $s3_region"
|
||||
echo " S3 Bucket: $s3_bucket"
|
||||
echo " Local Path: $sync_local_path"
|
||||
echo " Sync Interval: $((sync_interval / 1000)) seconds"
|
||||
echo " Auto-start: $sync_auto_start"
|
||||
echo " Conflict Resolution: $conflict_resolution"
|
||||
echo " Theme: $ui_theme"
|
||||
echo ""
|
||||
echo "🚀 You can now start the application with: npm run dev"
|
||||
echo "📖 The .env file will be automatically loaded on startup."
|
||||
@ -61,10 +61,10 @@ class RekordboxSyncApp {
|
||||
*/
|
||||
private async createMainWindow(): Promise<void> {
|
||||
this.mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
width: 1400,
|
||||
height: 900,
|
||||
minWidth: 1000,
|
||||
minHeight: 700,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
|
||||
@ -469,6 +469,7 @@ export class AwsS3Service extends EventEmitter {
|
||||
'--exclude', '*.temp',
|
||||
'--exclude', '*.part',
|
||||
'--exclude', '.DS_Store',
|
||||
'--exclude', '**/.DS_Store',
|
||||
'--exclude', '*.crdownload'
|
||||
]);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user