d3v1l1989 0 Posted May 25, 2025 Posted May 25, 2025 (edited) Hey guys, I'm having settings collections order using API. I'd love to sort by year descending ( or even better year descending + sort by name) but no matter what i try emby ends up setting sort order by year ascending showing oldest movies first. Using descending parrameter gives me unknown param so I'm sure I'm just dumb and can't figure it out. Can anyone help me out figure out whats wrong? import logging from typing import List, Dict, Any # Assuming you have requests.Session available as self.session import requests # For type hinting Session # Setup logger if not already configured elsewhere logger = logging.getLogger(__name__) # Example basic config for testing: # logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') class EmbyClient: # Mocking your class structure for testing def __init__(self, server_url: str, api_key: str, user_id: str): self.server_url = server_url.rstrip('/') self.api_key = api_key self.user_id = user_id self.session = requests.Session() self._temp_collections: Dict[str, str] = {} # Example def _make_api_request(self, method: str, endpoint_path: str, params: Dict[str, Any] = None, json_data: Dict[str, Any] = None) -> Any: """Helper function to make API requests and return JSON data or None.""" url = f"{self.server_url}{endpoint_path}" try: if method.upper() == 'GET': response = self.session.get(url, params=params, timeout=30) elif method.upper() == 'POST': response = self.session.post(url, params=params, json=json_data, timeout=30) else: logger.error(f"Unsupported HTTP method: {method}") return None response.raise_for_status() if response.status_code == 204: return {} return response.json() except requests.exceptions.HTTPError as e: logger.error(f"HTTP error during {method} {url}: {e.response.status_code} - {e.response.text[:200]}") except requests.exceptions.RequestException as e: logger.error(f"Request error during {method} {url}: {e}") except ValueError: logger.error(f"Failed to decode JSON response from {method} {url}") return None def update_collection_items(self, collection_id: str, item_ids: List[str]) -> bool: """ Set the items for a given Emby collection, applying custom sort order. The item_ids list should be in the desired final sort order (though this function will override with collection-level sorting). Args: collection_id: The Emby collection ID. item_ids: List of Emby item IDs to include in the collection. Returns: True if successful, False otherwise. """ if not collection_id: logger.error("Error: Invalid collection_id") return False if hasattr(self, '_temp_collections') and collection_id in self._temp_collections: collection_name = self._temp_collections[collection_id] logger.info(f"Cannot update items for pseudo-collection '{collection_name}'") return True # Pretend success # 1. Add/Update items in the collection unique_item_ids = list(dict.fromkeys(item_ids)) if len(unique_item_ids) < len(item_ids): logger.info(f"Removed {len(item_ids) - len(unique_item_ids)} duplicate item IDs from collection update") items_to_set_str = ",".join(unique_item_ids) if unique_item_ids else "" add_items_url = f"{self.server_url}/Collections/{collection_id}/Items?api_key={self.api_key}&Ids={items_to_set_str}" try: logger.info(f"Setting {len(unique_item_ids)} items for collection {collection_id}...") response = self.session.post(add_items_url, timeout=30) if response.status_code == 204: # 204 No Content is success logger.info(f"Successfully set items in collection {collection_id}.") # 2. Set and VERIFY the Collection's sort order logger.info(f"Attempting to set collection {collection_id} sort to PremiereDate (Descending)...") update_response = None display_order_update_attempted = False # Fetch current collection data. This is important to avoid overwriting other metadata. # Using /Items/{Id} endpoint, as /Users/{UserId}/Items/{Id} can sometimes return user-specific views # that might not be ideal as a base for updating the item itself. # However, for reading initial LockedFields, etc., it should be okay. # Sticking with user-specific item endpoint for now as in original code. collection_data_url = f"/Users/{self.user_id}/Items/{collection_id}" collection_data = self._make_api_request('GET', collection_data_url, params={'api_key': self.api_key}) if not collection_data: logger.error(f"Failed to fetch existing collection data for ID: {collection_id}. Cannot reliably update sort order.") else: collection_metadata_payload = collection_data.copy() # Start with existing data # Define desired sort settings desired_display_order = "PremiereDate" desired_sort_by = "PremiereDate" # Usually aligns with DisplayOrder for clarity desired_sort_order = "Descending" collection_metadata_payload["DisplayOrder"] = desired_display_order collection_metadata_payload["SortBy"] = desired_sort_by collection_metadata_payload["SortOrder"] = desired_sort_order # These are not standard Emby fields for sorting, good to remove if they were experiments collection_metadata_payload.pop("SortByPremiereDate", None) collection_metadata_payload.pop("SortByPremiereFirst", None) # Ensure LockedFields allows DisplayOrder, SortBy, AND SortOrder to be changed locked_fields = collection_metadata_payload.get('LockedFields', []) if not isinstance(locked_fields, list): # Ensure it's a list logger.warning(f"LockedFields for {collection_id} was not a list: {locked_fields}. Resetting to empty list for update.") locked_fields = [] fields_to_unlock = ['DisplayOrder', 'SortBy', 'SortOrder'] current_locked_fields_set = set(locked_fields) for field in fields_to_unlock: if field in current_locked_fields_set: current_locked_fields_set.remove(field) collection_metadata_payload['LockedFields'] = sorted(list(current_locked_fields_set)) # Keep it sorted for consistency # Send the update to the collection # Use the /Items/{Id} endpoint for updating item metadata collection_item_update_url = f"{self.server_url}/Items/{collection_id}?api_key={self.api_key}" logger.info(f"Collection metadata update payload for {collection_id}: { {k:v for k,v in collection_metadata_payload.items() if k in ['Name', 'DisplayOrder', 'SortBy', 'SortOrder', 'LockedFields']} }") # Log key parts update_item_response = self.session.post(collection_item_update_url, json=collection_metadata_payload, timeout=30) display_order_update_attempted = True # Renaming variable for clarity # Verification logic if display_order_update_attempted: if update_item_response and update_item_response.status_code in [200, 204]: logger.info(f"Attempt to set collection sort to {desired_display_order} ({desired_sort_order}) successful (HTTP {update_item_response.status_code}). Verifying...") # IMMEDIATE VERIFICATION # Use the same user-specific endpoint for verification if that's how user will see it verify_url = f"/Users/{self.user_id}/Items/{collection_id}" verify_params = { 'api_key': self.api_key, 'Fields': 'DisplayOrder,SortOrder,SortBy,Name,LockedFields' # Request all relevant fields } verify_data = self._make_api_request('GET', verify_url, params=verify_params) if verify_data: actual_display_order = verify_data.get('DisplayOrder', 'NotSet') actual_sort_by = verify_data.get('SortBy', 'NotSet') actual_sort_order = verify_data.get('SortOrder', 'NotSet') # Default to 'NotSet' if key missing collection_name = verify_data.get('Name', 'UnknownName') logger.info(f"VERIFIED: Collection '{collection_name}' (ID: {collection_id}) has DisplayOrder: {actual_display_order}, SortBy: {actual_sort_by}, SortOrder: {actual_sort_order}.") if actual_display_order != desired_display_order or \ actual_sort_by != desired_sort_by or \ actual_sort_order != desired_sort_order: logger.critical( f"CRITICAL: Collection sort settings did NOT fully apply for '{collection_name}' (ID: {collection_id})! " f"Expected DisplayOrder: {desired_display_order}, SortBy: {desired_sort_by}, SortOrder: {desired_sort_order}. " f"Got DisplayOrder: {actual_display_order}, SortBy: {actual_sort_by}, SortOrder: {actual_sort_order}. " f"LockedFields after update: {verify_data.get('LockedFields')}" ) else: logger.info(f"Successfully verified sort settings for collection '{collection_name}'.") else: logger.warning(f"Could not verify collection sort settings for {collection_id}. Verification GET request failed or returned no data.") elif update_item_response: logger.error(f"FAILED to set collection sort for {collection_id}. Status: {update_item_response.status_code} - {update_item_response.text[:200]}") else: # Should not happen if display_order_update_attempted is true and collection_data was present logger.error(f"FAILED to set collection sort for {collection_id}. No response received from update POST (should not happen if update was attempted).") else: # This case means collection_data was not fetched logger.warning(f"Skipped attempting to set collection sort for {collection_id} due to missing initial collection data.") # Optional: Trigger a refresh on the collection try: refresh_url = f"{self.server_url}/Items/{collection_id}/Refresh?api_key={self.api_key}" refresh_response = self.session.post(refresh_url, timeout=30) if refresh_response.status_code in [200, 204]: logger.info(f"Successfully sent refresh command for collection {collection_id}.") else: logger.warning(f"Failed to send refresh command for collection {collection_id}: {refresh_response.status_code}") except Exception as e_refresh: logger.warning(f"Error sending refresh command: {e_refresh}") return True # Overall success if items were added, metadata is best-effort else: logger.error(f"Failed to set items in collection {collection_id}: {response.status_code} - {response.text[:200]}") return False except Exception as e: logger.error(f"Unhandled error updating collection items for {collection_id}: {e}") return False Edited May 25, 2025 by d3v1l1989
Amything 122 Posted May 25, 2025 Posted May 25, 2025 The relevant method is ItemUpdateService: https://swagger.emby.media/?staticview=true#/ItemUpdateService/postItemsByItemid And DisplayOrder there within. It can be one of 2 values: PremiereDate or SortName Unfortunately it's not possible to do anything more with it than that I believe.
d3v1l1989 0 Posted May 25, 2025 Author Posted May 25, 2025 So PremiereDate can't use descending parameter at all? Any idea on how could i work around that so I can sort by year descending because I'm totally lost?
Luke 42077 Posted May 25, 2025 Posted May 25, 2025 It should be able to. Doesn’t the detail screen do that?
d3v1l1989 0 Posted May 25, 2025 Author Posted May 25, 2025 59 minutes ago, Luke said: It should be able to. Doesn’t the detail screen do that? I'm not sure I'm following. What detail screen? Are you referring to sorting withing the UI manually?
d3v1l19892 1 Posted May 25, 2025 Posted May 25, 2025 3 minutes ago, Luke said: Yes. I mean yes that works but then I'd have to tell users "hey you can sort movies inside collections too" and explain stuff. I'd rather just use api to set the collections sorting to at least year descending or ideally have the api support all of the default sorts that are inside the UI. I would love to see it in the near future because I really want emby to have proper collections and thats why Im working on my own kometa alternative for emby.
Amything 122 Posted May 26, 2025 Posted May 26, 2025 I wanted to be able to see what is new at a glance in my collections. I added custom sort names for items within collections so that they are sorted by Date Added, newest first. Of course that affects the alphabetical sorting everywhere, not the best solution.
d3v1l19892 1 Posted May 26, 2025 Posted May 26, 2025 Yeah, I also tried that but as you already said it affects entire library so its not a solution. I feel like emby could be so much better and community could help more if they offered better api options. Imagine properly sorted collections, custom collection rows on the homepage so you can create for example "Streaming right now" collection row on the homepage where you could at a glance see all the popular platforms and each collection would be sorted based on popularity etc etc. Ahh..maybe one day
Amything 122 Posted May 26, 2025 Posted May 26, 2025 Collection rows on home screen with full sorting control is the dream 1
hthgihwaymonk 34 Posted May 27, 2025 Posted May 27, 2025 I agree, being able to sort by desc using the API would be a welcome addition. at least the android app seems to remember your selection after you've set it in the UI 1
Luke 42077 Posted May 28, 2025 Posted May 28, 2025 The saved sort order in the metadata editor for collections doesn't support all of the same values that can be used manually on the collection screen. So we need to expand on that and then you should be able to do this. 1
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now