manual library selection for plex servers
This commit is contained in:
parent
53193d7b38
commit
8c7cb6d448
5 changed files with 216 additions and 10 deletions
|
|
@ -121,25 +121,85 @@ class PlexClient:
|
|||
logger.error(f"Failed to connect to Plex server: {e}")
|
||||
self.server = None
|
||||
|
||||
def get_available_music_libraries(self) -> List[Dict[str, str]]:
|
||||
"""Get list of all available music libraries on the Plex server"""
|
||||
if not self.ensure_connection() or not self.server:
|
||||
return []
|
||||
|
||||
try:
|
||||
music_libraries = []
|
||||
for section in self.server.library.sections():
|
||||
if section.type == 'artist':
|
||||
music_libraries.append({
|
||||
'title': section.title,
|
||||
'key': str(section.key)
|
||||
})
|
||||
|
||||
logger.debug(f"Found {len(music_libraries)} music libraries")
|
||||
return music_libraries
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting music libraries: {e}")
|
||||
return []
|
||||
|
||||
def set_music_library_by_name(self, library_name: str) -> bool:
|
||||
"""Set the active music library by name"""
|
||||
if not self.server:
|
||||
return False
|
||||
|
||||
try:
|
||||
for section in self.server.library.sections():
|
||||
if section.type == 'artist' and section.title == library_name:
|
||||
self.music_library = section
|
||||
logger.info(f"Set music library to: {library_name}")
|
||||
|
||||
# Store preference in database
|
||||
from database.music_database import MusicDatabase
|
||||
db = MusicDatabase()
|
||||
db.set_preference('plex_music_library', library_name)
|
||||
|
||||
return True
|
||||
|
||||
logger.warning(f"Music library '{library_name}' not found")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting music library: {e}")
|
||||
return False
|
||||
|
||||
def _find_music_library(self):
|
||||
if not self.server:
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
music_sections = []
|
||||
|
||||
|
||||
# Collect all music libraries
|
||||
for section in self.server.library.sections():
|
||||
if section.type == 'artist':
|
||||
music_sections.append(section)
|
||||
|
||||
|
||||
if not music_sections:
|
||||
logger.warning("No music library found on Plex server")
|
||||
return
|
||||
|
||||
|
||||
# Check if user has a saved preference
|
||||
try:
|
||||
from database.music_database import MusicDatabase
|
||||
db = MusicDatabase()
|
||||
preferred_library = db.get_preference('plex_music_library')
|
||||
|
||||
if preferred_library:
|
||||
# Try to find the preferred library
|
||||
for section in music_sections:
|
||||
if section.title == preferred_library:
|
||||
self.music_library = section
|
||||
logger.debug(f"Using user-selected music library: {section.title}")
|
||||
return
|
||||
except Exception as e:
|
||||
logger.debug(f"Could not check library preference: {e}")
|
||||
|
||||
# Priority order for common library names
|
||||
priority_names = ['Music', 'music', 'Audio', 'audio', 'Songs', 'songs']
|
||||
|
||||
|
||||
# First, try to find a library with a priority name
|
||||
for priority_name in priority_names:
|
||||
for section in music_sections:
|
||||
|
|
@ -147,16 +207,16 @@ class PlexClient:
|
|||
self.music_library = section
|
||||
logger.debug(f"Found preferred music library: {section.title}")
|
||||
return
|
||||
|
||||
|
||||
# If no priority match found, use the first one
|
||||
self.music_library = music_sections[0]
|
||||
logger.debug(f"Found music library (first available): {self.music_library.title}")
|
||||
|
||||
|
||||
# Log other available libraries if multiple exist
|
||||
if len(music_sections) > 1:
|
||||
other_libraries = [s.title for s in music_sections[1:]]
|
||||
logger.info(f"Other music libraries available: {', '.join(other_libraries)}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error finding music library: {e}")
|
||||
|
||||
|
|
|
|||
|
|
@ -1875,6 +1875,14 @@ class MusicDatabase:
|
|||
def get_last_full_refresh(self) -> Optional[str]:
|
||||
"""Get the date of the last full refresh"""
|
||||
return self.get_metadata('last_full_refresh')
|
||||
|
||||
def set_preference(self, key: str, value: str):
|
||||
"""Set a user preference (alias for set_metadata for clarity)"""
|
||||
self.set_metadata(key, value)
|
||||
|
||||
def get_preference(self, key: str) -> Optional[str]:
|
||||
"""Get a user preference (alias for get_metadata for clarity)"""
|
||||
return self.get_metadata(key)
|
||||
|
||||
# Wishlist management methods
|
||||
|
||||
|
|
|
|||
|
|
@ -1991,10 +1991,58 @@ def detect_media_server_endpoint():
|
|||
add_activity_item("❌", "Auto-Detect Failed", f"No {server_type} server found", "Now")
|
||||
return jsonify({"success": False, "error": f"No {server_type} server found on common local addresses."})
|
||||
|
||||
@app.route('/api/plex/music-libraries', methods=['GET'])
|
||||
def get_plex_music_libraries():
|
||||
"""Get list of all available music libraries from Plex"""
|
||||
try:
|
||||
libraries = plex_client.get_available_music_libraries()
|
||||
|
||||
# Get currently selected library
|
||||
from database.music_database import MusicDatabase
|
||||
db = MusicDatabase()
|
||||
selected_library = db.get_preference('plex_music_library')
|
||||
|
||||
# Get the currently active library name
|
||||
current_library = None
|
||||
if plex_client.music_library:
|
||||
current_library = plex_client.music_library.title
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"libraries": libraries,
|
||||
"selected": selected_library,
|
||||
"current": current_library
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting Plex music libraries: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@app.route('/api/plex/select-music-library', methods=['POST'])
|
||||
def select_plex_music_library():
|
||||
"""Set the active Plex music library"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
library_name = data.get('library_name')
|
||||
|
||||
if not library_name:
|
||||
return jsonify({"success": False, "error": "No library name provided"}), 400
|
||||
|
||||
success = plex_client.set_music_library_by_name(library_name)
|
||||
|
||||
if success:
|
||||
add_activity_item("📚", "Library Selected", f"Plex music library set to: {library_name}", "Now")
|
||||
return jsonify({"success": True, "message": f"Music library set to: {library_name}"})
|
||||
else:
|
||||
return jsonify({"success": False, "error": f"Library '{library_name}' not found"}), 404
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting Plex music library: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@app.route('/api/detect-soulseek', methods=['POST'])
|
||||
def detect_soulseek_endpoint():
|
||||
print("Received auto-detect request for slskd")
|
||||
|
||||
|
||||
# Add activity for soulseek auto-detect start
|
||||
add_activity_item("🔍", "Auto-Detect Started", "Searching for slskd server", "Now")
|
||||
found_url = run_detection('slskd')
|
||||
|
|
|
|||
|
|
@ -1653,6 +1653,15 @@
|
|||
<label>Plex Token:</label>
|
||||
<input type="password" id="plex-token" placeholder="X-Plex-Token">
|
||||
</div>
|
||||
<div class="form-group" id="plex-library-selector-container" style="display: none;">
|
||||
<label>Music Library:</label>
|
||||
<select id="plex-music-library" onchange="selectPlexLibrary()">
|
||||
<option value="">Loading...</option>
|
||||
</select>
|
||||
<small style="color: #999; font-size: 0.9em; display: block; margin-top: 5px;">
|
||||
Select which music library to use (doesn't affect config file)
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button class="detect-button" onclick="autoDetectPlex()">Auto-detect</button>
|
||||
<button class="test-button" onclick="testConnection('plex')">Test</button>
|
||||
|
|
|
|||
|
|
@ -1451,7 +1451,12 @@ async function loadSettingsData() {
|
|||
// Set active server and toggle visibility
|
||||
const activeServer = settings.active_media_server || 'plex';
|
||||
toggleServer(activeServer);
|
||||
|
||||
|
||||
// Load Plex music libraries if Plex is the active server
|
||||
if (activeServer === 'plex') {
|
||||
loadPlexMusicLibraries();
|
||||
}
|
||||
|
||||
// Populate Soulseek settings
|
||||
document.getElementById('soulseek-url').value = settings.soulseek?.slskd_url || '';
|
||||
document.getElementById('soulseek-api-key').value = settings.soulseek?.api_key || '';
|
||||
|
|
@ -1506,6 +1511,11 @@ function toggleServer(serverType) {
|
|||
document.getElementById('plex-container').classList.toggle('hidden', serverType !== 'plex');
|
||||
document.getElementById('jellyfin-container').classList.toggle('hidden', serverType !== 'jellyfin');
|
||||
document.getElementById('navidrome-container').classList.toggle('hidden', serverType !== 'navidrome');
|
||||
|
||||
// Load Plex music libraries when switching to Plex
|
||||
if (serverType === 'plex') {
|
||||
loadPlexMusicLibraries();
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSettings() {
|
||||
|
|
@ -23278,3 +23288,74 @@ function showGenreTop10ReleasesError(errorMessage) {
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initializeGenreBrowserModal();
|
||||
});
|
||||
|
||||
// ============ Plex Music Library Selection ============
|
||||
|
||||
async function loadPlexMusicLibraries() {
|
||||
try {
|
||||
const response = await fetch('/api/plex/music-libraries');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success && data.libraries && data.libraries.length > 0) {
|
||||
const selector = document.getElementById('plex-music-library');
|
||||
const container = document.getElementById('plex-library-selector-container');
|
||||
|
||||
// Clear existing options
|
||||
selector.innerHTML = '';
|
||||
|
||||
// Add options for each library
|
||||
data.libraries.forEach(library => {
|
||||
const option = document.createElement('option');
|
||||
option.value = library.title;
|
||||
option.textContent = library.title;
|
||||
|
||||
// Mark the currently selected library
|
||||
if (library.title === data.current || library.title === data.selected) {
|
||||
option.selected = true;
|
||||
}
|
||||
|
||||
selector.appendChild(option);
|
||||
});
|
||||
|
||||
// Show the container
|
||||
container.style.display = 'block';
|
||||
} else {
|
||||
// Hide if no libraries found or not connected
|
||||
document.getElementById('plex-library-selector-container').style.display = 'none';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading Plex music libraries:', error);
|
||||
document.getElementById('plex-library-selector-container').style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
async function selectPlexLibrary() {
|
||||
const selector = document.getElementById('plex-music-library');
|
||||
const selectedLibrary = selector.value;
|
||||
|
||||
if (!selectedLibrary) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/plex/select-music-library', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
library_name: selectedLibrary
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
console.log(`Plex music library switched to: ${selectedLibrary}`);
|
||||
} else {
|
||||
console.error('Failed to switch library:', data.error);
|
||||
alert(`Failed to switch library: ${data.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error selecting Plex library:', error);
|
||||
alert('Error selecting library. Please try again.');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue