Fix root cause: filter non-picklable objects from entry before persisting with shelve

Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-11-16 08:17:12 +00:00
parent 71c1b2f9ce
commit 2941b4dc68
2 changed files with 37 additions and 9 deletions

View file

@ -123,14 +123,10 @@ class ObjectSerializer(json.JSONEncoder):
elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes)): elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes)):
try: try:
return list(obj) return list(obj)
except (TypeError, ValueError, RuntimeError): except:
# If conversion to list fails, log a warning and return a placeholder pass
log.warning(f"Failed to convert {type(obj).__name__} to list, using string representation") # Fall back to default behavior
return str(obj) return json.JSONEncoder.default(self, obj)
# For objects that can't be serialized, return their string representation
# This prevents HTTP 500 errors while still providing useful information
log.warning(f"Object of type {type(obj).__name__} is not directly JSON serializable, using string representation")
return str(obj)
serializer = ObjectSerializer() serializer = ObjectSerializer()
app = web.Application() app = web.Application()

View file

@ -44,9 +44,41 @@ class DownloadInfo:
self.size = None self.size = None
self.timestamp = time.time_ns() self.timestamp = time.time_ns()
self.error = error self.error = error
self.entry = entry # Extract only picklable playlist metadata from entry
# This prevents issues when shelve tries to pickle DownloadInfo objects
# that contain non-picklable objects like generators
self.entry = self._extract_picklable_entry(entry) if entry else None
self.playlist_item_limit = playlist_item_limit self.playlist_item_limit = playlist_item_limit
@staticmethod
def _extract_picklable_entry(entry):
"""Extract only picklable data from entry dict.
This is needed because yt-dlp may return entry dicts containing
non-picklable objects like generators, which cause errors when
shelve tries to persist DownloadInfo objects.
"""
if not isinstance(entry, dict):
return None
# Extract only basic types that are picklable
picklable_entry = {}
for key, value in entry.items():
# Only include playlist-related properties (used for output template)
# and other simple string/int/bool values
if key.startswith('playlist') or key in ('id', 'title', 'url', 'webpage_url', '_type'):
if isinstance(value, (str, int, float, bool, type(None))):
picklable_entry[key] = value
elif isinstance(value, (list, tuple)):
# For lists/tuples, only include if all items are basic types
try:
if all(isinstance(item, (str, int, float, bool, type(None))) for item in value):
picklable_entry[key] = value
except (TypeError, AttributeError):
pass
return picklable_entry if picklable_entry else None
class Download: class Download:
manager = None manager = None