headless mode foundation
This commit is contained in:
parent
d9e981c93e
commit
bf445f9939
5 changed files with 193 additions and 10 deletions
100
main.py
100
main.py
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
@ -404,18 +405,73 @@ class MainWindow(QMainWindow):
|
||||||
# Force accept the event to prevent hanging
|
# Force accept the event to prevent hanging
|
||||||
event.accept()
|
event.accept()
|
||||||
|
|
||||||
def main():
|
def run_web_app():
|
||||||
logging_config = config_manager.get_logging_config()
|
"""Run the application in headless web mode using Flask"""
|
||||||
log_level = logging_config.get('level', 'INFO')
|
from flask import Flask, render_template, jsonify
|
||||||
log_file = logging_config.get('path', 'logs/newmusic.log')
|
|
||||||
setup_logging(level=log_level, log_file=log_file)
|
|
||||||
|
|
||||||
logger.info("Starting Soulsync application")
|
app = Flask(__name__, template_folder='webui', static_folder='webui/static')
|
||||||
|
|
||||||
if not config_manager.config_path.exists():
|
# Initialize service clients (same as GUI mode)
|
||||||
logger.error("Configuration file not found. Please check config/config.json")
|
spotify_client = SpotifyClient()
|
||||||
sys.exit(1)
|
plex_client = PlexClient()
|
||||||
|
jellyfin_client = JellyfinClient()
|
||||||
|
soulseek_client = SoulseekClient()
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def home():
|
||||||
|
return render_template('index.html')
|
||||||
|
|
||||||
|
@app.route('/status')
|
||||||
|
def status():
|
||||||
|
"""Return service connection status as JSON"""
|
||||||
|
try:
|
||||||
|
# Check Spotify
|
||||||
|
spotify_status = spotify_client.sp is not None
|
||||||
|
|
||||||
|
# Check active media server
|
||||||
|
active_server = config_manager.get_active_media_server()
|
||||||
|
if active_server == "plex":
|
||||||
|
media_status = plex_client.is_connected()
|
||||||
|
elif active_server == "jellyfin":
|
||||||
|
media_status = jellyfin_client.is_connected()
|
||||||
|
else:
|
||||||
|
media_status = False
|
||||||
|
|
||||||
|
# Check Soulseek
|
||||||
|
soulseek_status = soulseek_client.is_configured()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'spotify': spotify_status,
|
||||||
|
'media_server': media_status,
|
||||||
|
'soulseek': soulseek_status,
|
||||||
|
'active_media_server': active_server
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting service status: {e}")
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/config')
|
||||||
|
def config_info():
|
||||||
|
"""Return basic configuration info"""
|
||||||
|
try:
|
||||||
|
validation = config_manager.validate_config()
|
||||||
|
return jsonify({
|
||||||
|
'configured': config_manager.is_configured(),
|
||||||
|
'active_media_server': config_manager.get_active_media_server(),
|
||||||
|
'validation': validation
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting config info: {e}")
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
logger.info("Starting SoulSync in web mode...")
|
||||||
|
logger.info("Web interface available at http://localhost:5000")
|
||||||
|
|
||||||
|
# Run Flask app
|
||||||
|
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||||
|
|
||||||
|
def run_gui_app():
|
||||||
|
"""Run the application in GUI mode using PyQt6"""
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
app.setApplicationName("SoulSync")
|
app.setApplicationName("SoulSync")
|
||||||
app.setApplicationVersion("0.6")
|
app.setApplicationVersion("0.6")
|
||||||
|
|
@ -432,5 +488,31 @@ def main():
|
||||||
logger.error(f"Unexpected error: {e}")
|
logger.error(f"Unexpected error: {e}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Parse command line arguments
|
||||||
|
parser = argparse.ArgumentParser(description='SoulSync - Music Sync & Manager')
|
||||||
|
parser.add_argument('--headless', action='store_true',
|
||||||
|
help='Run in headless mode with web interface instead of GUI')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Setup logging
|
||||||
|
logging_config = config_manager.get_logging_config()
|
||||||
|
log_level = logging_config.get('level', 'INFO')
|
||||||
|
log_file = logging_config.get('path', 'logs/newmusic.log')
|
||||||
|
setup_logging(level=log_level, log_file=log_file)
|
||||||
|
|
||||||
|
# Check configuration
|
||||||
|
if not config_manager.config_path.exists():
|
||||||
|
logger.error("Configuration file not found. Please check config/config.json")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Choose mode based on arguments
|
||||||
|
if args.headless:
|
||||||
|
logger.info("Starting SoulSync in headless web mode")
|
||||||
|
run_web_app()
|
||||||
|
else:
|
||||||
|
logger.info("Starting SoulSync in GUI mode")
|
||||||
|
run_gui_app()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
@ -9,4 +9,5 @@ mutagen>=1.47.0
|
||||||
Pillow>=10.0.0
|
Pillow>=10.0.0
|
||||||
aiohttp>=3.9.0
|
aiohttp>=3.9.0
|
||||||
unidecode>=1.3.8
|
unidecode>=1.3.8
|
||||||
yt-dlp>=2024.12.13
|
yt-dlp>=2024.12.13
|
||||||
|
Flask>=3.0.0
|
||||||
41
webui/index.html
Normal file
41
webui/index.html
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>SoulSync - Web Mode</title>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>🎵 SoulSync - Web Mode</h1>
|
||||||
|
<p>Your music sync application is running in headless mode.</p>
|
||||||
|
|
||||||
|
<div class="status">
|
||||||
|
<h3>Service Status</h3>
|
||||||
|
<div id="status-container">
|
||||||
|
<div class="service">
|
||||||
|
<span>Spotify:</span>
|
||||||
|
<span id="spotify-status" class="disconnected">Checking...</span>
|
||||||
|
</div>
|
||||||
|
<div class="service">
|
||||||
|
<span>Media Server:</span>
|
||||||
|
<span id="media-status" class="disconnected">Checking...</span>
|
||||||
|
</div>
|
||||||
|
<div class="service">
|
||||||
|
<span>Soulseek:</span>
|
||||||
|
<span id="soulseek-status" class="disconnected">Checking...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="status">
|
||||||
|
<h3>Available Endpoints</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/status" style="color: #4caf50;">/status</a> - Service status JSON</li>
|
||||||
|
<li><a href="/config" style="color: #4caf50;">/config</a> - Configuration info</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
26
webui/static/script.js
Normal file
26
webui/static/script.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
function updateStatus() {
|
||||||
|
fetch('/status')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
document.getElementById('spotify-status').textContent = data.spotify ? 'Connected' : 'Disconnected';
|
||||||
|
document.getElementById('spotify-status').className = data.spotify ? 'connected' : 'disconnected';
|
||||||
|
|
||||||
|
document.getElementById('media-status').textContent = data.media_server ? 'Connected' : 'Disconnected';
|
||||||
|
document.getElementById('media-status').className = data.media_server ? 'connected' : 'disconnected';
|
||||||
|
|
||||||
|
document.getElementById('soulseek-status').textContent = data.soulseek ? 'Connected' : 'Disconnected';
|
||||||
|
document.getElementById('soulseek-status').className = data.soulseek ? 'connected' : 'disconnected';
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error fetching status:', error));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize when page loads
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Update status immediately
|
||||||
|
updateStatus();
|
||||||
|
|
||||||
|
// Update status every 5 seconds
|
||||||
|
setInterval(updateStatus, 5000);
|
||||||
|
|
||||||
|
console.log('SoulSync Web UI loaded successfully!');
|
||||||
|
});
|
||||||
33
webui/static/style.css
Normal file
33
webui/static/style.css
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 40px;
|
||||||
|
background: #121212;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
padding: 20px;
|
||||||
|
background: #1e1e1e;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-bottom: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connected {
|
||||||
|
color: #4caf50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disconnected {
|
||||||
|
color: #f44336;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue