diff --git a/core/jellyfin_client.py b/core/jellyfin_client.py index 0e674ab..ac023f8 100644 --- a/core/jellyfin_client.py +++ b/core/jellyfin_client.py @@ -205,38 +205,76 @@ class JellyfinClient: logger.error("No users found on Jellyfin server") return - # LOGIC CHANGE: Iterate through users instead of blindly picking the first one + # Check for a saved user preference + from database.music_database import MusicDatabase + db = MusicDatabase() + preferred_user = db.get_preference('jellyfin_user') + valid_user_found = False - for user in users_response: - candidate_id = user['Id'] - candidate_name = user.get('Name', 'Unknown') + # If a preferred user is saved, try that user first + if preferred_user: + for user in users_response: + if user.get('Name') == preferred_user: + candidate_id = user['Id'] + try: + views_response = self._make_request(f'/Users/{candidate_id}/Views') + if views_response: + for view in views_response.get('Items', []): + collection_type = (view.get('CollectionType') or '').lower() + if collection_type == 'music': + self.user_id = candidate_id + self.music_library_id = view['Id'] + logger.info(f"Using preferred user: {preferred_user} (Music Library: {view.get('Name')})") + valid_user_found = True + break + except Exception as e: + logger.debug(f"Preferred user {preferred_user} failed: {e}") + break + + # Check for a saved library preference for the selected user + preferred_library = db.get_preference('jellyfin_music_library') + + # Fall back to auto-detect if preference is missing or invalid + if not valid_user_found: + for user in users_response: + candidate_id = user['Id'] + candidate_name = user.get('Name', 'Unknown') + + try: + views_response = self._make_request(f'/Users/{candidate_id}/Views') + if views_response: + for view in views_response.get('Items', []): + collection_type = (view.get('CollectionType') or '').lower() + + if collection_type == 'music': + self.user_id = candidate_id + self.music_library_id = view['Id'] + logger.info(f"Using user: {candidate_name} (Music Library: {view.get('Name')})") + valid_user_found = True + break + except Exception as e: + logger.debug(f"Skipping user {candidate_name} due to error: {e}") + continue + + if valid_user_found: + break + + # If we found a user, check if there's a saved library preference to apply + if valid_user_found and preferred_library: try: - # Check this specific user's views (libraries) - views_response = self._make_request(f'/Users/{candidate_id}/Views') - + views_response = self._make_request(f'/Users/{self.user_id}/Views') if views_response: for view in views_response.get('Items', []): - # Check if they have a 'music' collection (case-insensitive safe check) collection_type = (view.get('CollectionType') or '').lower() - - if collection_type == 'music': - # Found a winner! Set the class variables. - self.user_id = candidate_id + if collection_type == 'music' and view.get('Name') == preferred_library: self.music_library_id = view['Id'] - logger.info(f"Using user: {candidate_name} (Music Library: {view.get('Name')})") - valid_user_found = True + logger.info(f"Applied saved library preference: {preferred_library}") break except Exception as e: - # If this user fails (e.g. permission error), just log it and try the next user - logger.debug(f"Skipping user {candidate_name} due to error: {e}") - continue - - # If we found a valid user, stop looping - if valid_user_found: - break - + logger.debug(f"Could not apply library preference: {e}") + if not valid_user_found: logger.error("Connected to Jellyfin, but could not find any user with access to a Music library") @@ -321,6 +359,87 @@ class JellyfinClient: logger.error(f"Error setting music library: {e}") return False + def get_available_users(self) -> List[Dict[str, str]]: + """Get list of users that have music libraries""" + if not self.ensure_connection(): + return [] + + try: + users_response = self._make_request('/Users') + if not users_response: + return [] + + users_with_music = [] + for user in users_response: + candidate_id = user['Id'] + candidate_name = user.get('Name', 'Unknown') + + try: + views_response = self._make_request(f'/Users/{candidate_id}/Views') + if views_response: + for view in views_response.get('Items', []): + collection_type = (view.get('CollectionType') or '').lower() + if collection_type == 'music': + users_with_music.append({ + 'id': candidate_id, + 'name': candidate_name + }) + break + except Exception as e: + logger.debug(f"Skipping user {candidate_name} during enumeration: {e}") + continue + + logger.debug(f"Found {len(users_with_music)} users with music libraries") + return users_with_music + except Exception as e: + logger.error(f"Error getting available users: {e}") + return [] + + def set_user_by_name(self, username: str) -> bool: + """Set the active user by name, re-discover their music library, and save preference""" + if not self.ensure_connection(): + return False + + try: + users_response = self._make_request('/Users') + if not users_response: + return False + + for user in users_response: + if user.get('Name') == username: + candidate_id = user['Id'] + + # Verify this user has a music library + views_response = self._make_request(f'/Users/{candidate_id}/Views') + if not views_response: + return False + + for view in views_response.get('Items', []): + collection_type = (view.get('CollectionType') or '').lower() + if collection_type == 'music': + self.user_id = candidate_id + self.music_library_id = view['Id'] + logger.info(f"Switched to user: {username} (Music Library: {view.get('Name')})") + + # Save preference to database + from database.music_database import MusicDatabase + db = MusicDatabase() + db.set_preference('jellyfin_user', username) + + # Clear caches since we switched users + self.clear_cache() + + return True + + logger.warning(f"User '{username}' has no music library") + return False + + logger.warning(f"User '{username}' not found") + return False + except Exception as e: + logger.error(f"Error setting user: {e}") + return False + def _make_request(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]: """Make authenticated request to Jellyfin API""" if not self.base_url or not self.api_key: diff --git a/web_server.py b/web_server.py index 9b78e94..e6c5dcc 100644 --- a/web_server.py +++ b/web_server.py @@ -2617,6 +2617,57 @@ def select_plex_music_library(): logger.error(f"Error setting Plex music library: {e}") return jsonify({"success": False, "error": str(e)}), 500 +@app.route('/api/jellyfin/users', methods=['GET']) +def get_jellyfin_users(): + """Get list of Jellyfin users that have music libraries""" + try: + users = jellyfin_client.get_available_users() + + # Get currently selected user + from database.music_database import MusicDatabase + db = MusicDatabase() + selected_user = db.get_preference('jellyfin_user') + + # Determine the current user name from user_id + current_user = None + if jellyfin_client.user_id: + for u in users: + if u['id'] == jellyfin_client.user_id: + current_user = u['name'] + break + + return jsonify({ + "success": True, + "users": users, + "selected": selected_user, + "current": current_user + }) + except Exception as e: + logger.error(f"Error getting Jellyfin users: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + +@app.route('/api/jellyfin/select-user', methods=['POST']) +def select_jellyfin_user(): + """Set the active Jellyfin user""" + try: + data = request.get_json() + username = data.get('username') + + if not username: + return jsonify({"success": False, "error": "No username provided"}), 400 + + success = jellyfin_client.set_user_by_name(username) + + if success: + add_activity_item("👤", "User Selected", f"Jellyfin user set to: {username}", "Now") + return jsonify({"success": True, "message": f"User set to: {username}"}) + else: + return jsonify({"success": False, "error": f"User '{username}' not found or has no music library"}), 404 + + except Exception as e: + logger.error(f"Error setting Jellyfin user: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + @app.route('/api/jellyfin/music-libraries', methods=['GET']) def get_jellyfin_music_libraries(): """Get list of all available music libraries from Jellyfin""" diff --git a/webui/index.html b/webui/index.html index 3457938..34efa27 100644 --- a/webui/index.html +++ b/webui/index.html @@ -2754,6 +2754,15 @@ + +