|
|
|
|
@@ -119,817 +119,28 @@ For each task completed:
|
|
|
|
|
|
|
|
|
|
## TODO List:
|
|
|
|
|
|
|
|
|
|
✅ **COMPLETED (1):** Fixed greenlet_spawn async lazy-loading error - Added selectinload for episode relationship in DownloadQueueService.get_all()
|
|
|
|
|
### Completed ✅
|
|
|
|
|
|
|
|
|
|
✅ **COMPLETED (2):** Fixed anime add endpoint 500 error - Added explicit commit/rollback in database session dependencies
|
|
|
|
|
1. **Fast adding - Concurrent Anime Processing (Completed 2026-01-24)**
|
|
|
|
|
- **What was done:**
|
|
|
|
|
- Modified `BackgroundLoaderService` to support concurrent processing with multiple workers
|
|
|
|
|
- Added configurable `max_concurrent_loads` parameter (default: 5)
|
|
|
|
|
- Multiple anime can now be added and processed in parallel without blocking
|
|
|
|
|
- Created comprehensive unit tests verifying concurrent behavior
|
|
|
|
|
- Updated integration tests for compatibility
|
|
|
|
|
- Updated architecture documentation
|
|
|
|
|
- **Result:** Multiple anime additions now process simultaneously. Users can add second anime immediately while first is still loading.
|
|
|
|
|
- **Tests:** `tests/unit/test_parallel_anime_add.py`, integration tests updated
|
|
|
|
|
- **Files Changed:**
|
|
|
|
|
- `src/server/services/background_loader_service.py`
|
|
|
|
|
- `tests/unit/test_parallel_anime_add.py` (new)
|
|
|
|
|
- `tests/integration/test_async_series_loading.py` (updated)
|
|
|
|
|
- `docs/architecture/async_loading_architecture.md` (updated)
|
|
|
|
|
|
|
|
|
|
✅ **COMPLETED (3):** Added database transactions - All database operations properly use session context managers with automatic commit/rollback
|
|
|
|
|
### In Progress 🔄
|
|
|
|
|
|
|
|
|
|
✅ **COMPLETED (4):** Created series filter to filter all series with no episodes found in folder - Added `filter=no_episodes` parameter to list_anime endpoint and get_series_with_no_episodes() database method
|
|
|
|
|
(No active tasks)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
### Planned 📋
|
|
|
|
|
|
|
|
|
|
### ~~1. fix issue (COMPLETED):~~
|
|
|
|
|
|
|
|
|
|
2026-01-23 18:28:39 [info ] DownloadService initialized max*retries=3
|
|
|
|
|
INFO: QueueRepository initialized
|
|
|
|
|
ERROR: Failed to get all items: greenlet_spawn has not been called; can't call await_only() here. Was IO attempted in an unexpected place? (Background on this error at: https://sqlalche.me/e/20/xd2s)
|
|
|
|
|
2026-01-23 18:28:39 [error ] Failed to load queue from database: Failed to get all items: greenlet_spawn has not been called; can't call await_only() here. Was IO attempted in an unexpected place? (Background on this error at: https://sqlalche.me/e/20/xd2s)
|
|
|
|
|
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
|
|
|
|
|
│ /home/lukas/Volume/repo/Aniworld/src/server/services/queue_repository.py:292 in get_all_items │
|
|
|
|
|
│ │
|
|
|
|
|
│ 289 │ │ │ db_items = await DownloadQueueService.get_all( │
|
|
|
|
|
│ 290 │ │ │ │ session, with_series=True │
|
|
|
|
|
│ 291 │ │ │ ) │
|
|
|
|
|
│ ❱ 292 │ │ │ return [self._from_db_model(item) for item in db_items] │
|
|
|
|
|
│ 293 │ │ │
|
|
|
|
|
│ 294 │ │ except Exception as e: │
|
|
|
|
|
│ 295 │ │ │ logger.error("Failed to get all items: %s", e) │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ db = None │ │
|
|
|
|
|
│ │ db_items = [ │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=8, series_id=146, episode_id=26)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=9, series_id=146, episode_id=27)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=10, series_id=146, episode_id=28)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=11, series_id=146, episode_id=29)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=12, series_id=146, episode_id=30)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=13, series_id=146, episode_id=31)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=14, series_id=146, episode_id=32)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=15, series_id=146, episode_id=33)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=16, series_id=83, episode_id=1)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=17, series_id=83, episode_id=2)>, │ │
|
|
|
|
|
│ │ │ ... +21 │ │
|
|
|
|
|
│ │ ] │ │
|
|
|
|
|
│ │ manage_session = True │ │
|
|
|
|
|
│ │ self = <src.server.services.queue_repository.QueueRepository object at │ │
|
|
|
|
|
│ │ 0x7ed67ca6b770> │ │
|
|
|
|
|
│ │ session = <sqlalchemy.ext.asyncio.session.AsyncSession object at 0x7ed67caf4e90> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/Volume/repo/Aniworld/src/server/services/queue_repository.py:97 in \_from_db_model │
|
|
|
|
|
│ │
|
|
|
|
|
│ 94 │ │ │ Pydantic download item with default status/priority │
|
|
|
|
|
│ 95 │ │ """ │
|
|
|
|
|
│ 96 │ │ # Get episode info from the related Episode object │
|
|
|
|
|
│ ❱ 97 │ │ episode = db_item.episode │
|
|
|
|
|
│ 98 │ │ series = db_item.series │
|
|
|
|
|
│ 99 │ │ │
|
|
|
|
|
│ 100 │ │ episode_identifier = EpisodeIdentifier( │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭───────────────────────────────────────── locals ──────────────────────────────────────────╮ │
|
|
|
|
|
│ │ db_item = <DownloadQueueItem(id=8, series_id=146, episode_id=26)> │ │
|
|
|
|
|
│ │ item_id = None │ │
|
|
|
|
|
│ │ self = <src.server.services.queue_repository.QueueRepository object at 0x7ed67ca6b770> │ │
|
|
|
|
|
│ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/orm/attributes.py:5 │
|
|
|
|
|
│ 69 in **get** │
|
|
|
|
|
│ │
|
|
|
|
|
│ 566 │ │ │ │ state = instance_state(instance) │
|
|
|
|
|
│ 567 │ │ │ except AttributeError as err: │
|
|
|
|
|
│ 568 │ │ │ │ raise orm_exc.UnmappedInstanceError(instance) from err │
|
|
|
|
|
│ ❱ 569 │ │ │ return self.impl.get(state, dict*) # type: ignore[no-any-return] │
|
|
|
|
|
│ 570 │
|
|
|
|
|
│ 571 │
|
|
|
|
|
│ 572 @dataclasses.dataclass(frozen=True) │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ dict* = { │ │
|
|
|
|
|
│ │ │ '\_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>, │ │
|
|
|
|
|
│ │ │ 'id': 8, │ │
|
|
|
|
|
│ │ │ 'error_message': None, │ │
|
|
|
|
|
│ │ │ 'file_destination': None, │ │
|
|
|
|
|
│ │ │ 'completed_at': None, │ │
|
|
|
|
|
│ │ │ 'updated_at': datetime.datetime(2026, 1, 23, 17, 10, 47), │ │
|
|
|
|
|
│ │ │ 'series_id': 146, │ │
|
|
|
|
|
│ │ │ 'episode_id': 26, │ │
|
|
|
|
|
│ │ │ 'download_url': None, │ │
|
|
|
|
|
│ │ │ 'started_at': None, │ │
|
|
|
|
|
│ │ │ ... +2 │ │
|
|
|
|
|
│ │ } │ │
|
|
|
|
|
│ │ instance = <DownloadQueueItem(id=8, series_id=146, episode_id=26)> │ │
|
|
|
|
|
│ │ self = <sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x7ed681867b00> │ │
|
|
|
|
|
│ │ state = <sqlalchemy.orm.state.InstanceState object at 0x7ed67b61e330> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/orm/attributes.py:1 │
|
|
|
|
|
│ 096 in get │
|
|
|
|
|
│ │
|
|
|
|
|
│ 1093 │ │ │ │ if not passive & CALLABLES_OK: │
|
|
|
|
|
│ 1094 │ │ │ │ │ return PASSIVE_NO_RESULT │
|
|
|
|
|
│ 1095 │ │ │ │ │
|
|
|
|
|
│ ❱ 1096 │ │ │ │ value = self.\_fire_loader_callables(state, key, passive) │
|
|
|
|
|
│ 1097 │ │ │ │ │
|
|
|
|
|
│ 1098 │ │ │ │ if value is PASSIVE_NO_RESULT or value is NO_VALUE: │
|
|
|
|
|
│ 1099 │ │ │ │ │ return value │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ dict* = { │ │
|
|
|
|
|
│ │ │ '_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>, │ │
|
|
|
|
|
│ │ │ 'id': 8, │ │
|
|
|
|
|
│ │ │ 'error_message': None, │ │
|
|
|
|
|
│ │ │ 'file_destination': None, │ │
|
|
|
|
|
│ │ │ 'completed_at': None, │ │
|
|
|
|
|
│ │ │ 'updated_at': datetime.datetime(2026, 1, 23, 17, 10, 47), │ │
|
|
|
|
|
│ │ │ 'series_id': 146, │ │
|
|
|
|
|
│ │ │ 'episode_id': 26, │ │
|
|
|
|
|
│ │ │ 'download_url': None, │ │
|
|
|
|
|
│ │ │ 'started_at': None, │ │
|
|
|
|
|
│ │ │ ... +2 │ │
|
|
|
|
|
│ │ } │ │
|
|
|
|
|
│ │ key = 'episode' │ │
|
|
|
|
|
│ │ passive = symbol('PASSIVE_OFF') │ │
|
|
|
|
|
│ │ self = <sqlalchemy.orm.attributes.ScalarObjectAttributeImpl object at 0x7ed67cab6700> │ │
|
|
|
|
|
│ │ state = <sqlalchemy.orm.state.InstanceState object at 0x7ed67b61e330> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/orm/attributes.py:1 │
|
|
|
|
|
│ 131 in \_fire_loader_callables │
|
|
|
|
|
│ │
|
|
|
|
|
│ 1128 │ │ │ callable_ = state.callables[key] │
|
|
|
|
|
│ 1129 │ │ │ return callable*(state, passive) │
|
|
|
|
|
│ 1130 │ │ elif self.callable*: │
|
|
|
|
|
│ ❱ 1131 │ │ │ return self.callable*(state, passive) │
|
|
|
|
|
│ 1132 │ │ else: │
|
|
|
|
|
│ 1133 │ │ │ return ATTR_EMPTY │
|
|
|
|
|
│ 1134 │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭───────────────────────────────────────── locals ─────────────────────────────────────────╮ │
|
|
|
|
|
│ │ key = 'episode' │ │
|
|
|
|
|
│ │ passive = symbol('PASSIVE_OFF') │ │
|
|
|
|
|
│ │ self = <sqlalchemy.orm.attributes.ScalarObjectAttributeImpl object at 0x7ed67cab6700> │ │
|
|
|
|
|
│ │ state = <sqlalchemy.orm.state.InstanceState object at 0x7ed67b61e330> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/orm/strategies.py:9 │
|
|
|
|
|
│ 78 in \_load_for_state │
|
|
|
|
|
│ │
|
|
|
|
|
│ 975 │ │ │ ): │
|
|
|
|
|
│ 976 │ │ │ │ return LoaderCallableStatus.PASSIVE_NO_RESULT │
|
|
|
|
|
│ 977 │ │ │
|
|
|
|
|
│ ❱ 978 │ │ return self.\_emit_lazyload( │
|
|
|
|
|
│ 979 │ │ │ session, │
|
|
|
|
|
│ 980 │ │ │ state, │
|
|
|
|
|
│ 981 │ │ │ primary_key_identity, │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭────────────────────────────────────────── locals ──────────────────────────────────────────╮ │
|
|
|
|
|
│ │ alternate_effective_path = None │ │
|
|
|
|
|
│ │ execution_options = immutabledict({}) │ │
|
|
|
|
|
│ │ extra_criteria = () │ │
|
|
|
|
|
│ │ extra_options = () │ │
|
|
|
|
|
│ │ instance = None │ │
|
|
|
|
|
│ │ loadopt = None │ │
|
|
|
|
|
│ │ passive = symbol('PASSIVE_OFF') │ │
|
|
|
|
|
│ │ pending = False │ │
|
|
|
|
|
│ │ primary_key_identity = [26] │ │
|
|
|
|
|
│ │ self = <sqlalchemy.orm.strategies.LazyLoader object at 0x7ed67cabf4c0> │ │
|
|
|
|
|
│ │ session = <sqlalchemy.orm.session.Session object at 0x7ed67caf4fc0> │ │
|
|
|
|
|
│ │ state = <sqlalchemy.orm.state.InstanceState object at 0x7ed67b61e330> │ │
|
|
|
|
|
│ │ use_get = True │ │
|
|
|
|
|
│ ╰────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/orm/strategies.py:1 │
|
|
|
|
|
│ 079 in \_emit_lazyload │
|
|
|
|
|
│ │
|
|
|
|
|
│ 1076 │ │ │ if self.\_raise_on_sql and not passive & PassiveFlag.NO_RAISE: │
|
|
|
|
|
│ 1077 │ │ │ │ self.\_invoke_raise_load(state, passive, "raise_on_sql") │
|
|
|
|
|
│ 1078 │ │ │ │
|
|
|
|
|
│ ❱ 1079 │ │ │ return loading.load_on_pk_identity( │
|
|
|
|
|
│ 1080 │ │ │ │ session, │
|
|
|
|
|
│ 1081 │ │ │ │ stmt, │
|
|
|
|
|
│ 1082 │ │ │ │ primary_key_identity, │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ alternate_effective_path = None │ │
|
|
|
|
|
│ │ clauseelement = Table('episodes', MetaData(), Column('id', Integer(), │ │
|
|
|
|
|
│ │ table=<episodes>, primary_key=True, nullable=False), │ │
|
|
|
|
|
│ │ Column('series_id', Integer(), ForeignKey('anime_series.id'), │ │
|
|
|
|
|
│ │ table=<episodes>, nullable=False), Column('season', Integer(), │ │
|
|
|
|
|
│ │ table=<episodes>, nullable=False), Column('episode_number', │ │
|
|
|
|
|
│ │ Integer(), table=<episodes>, nullable=False), Column('title', │ │
|
|
|
|
|
│ │ String(length=500), table=<episodes>), Column('file_path', │ │
|
|
|
|
|
│ │ String(length=1000), table=<episodes>), Column('is_downloaded', │ │
|
|
|
|
|
│ │ Boolean(), table=<episodes>, nullable=False, │ │
|
|
|
|
|
│ │ default=ScalarElementColumnDefault(False)), Column('created_at', │ │
|
|
|
|
|
│ │ DateTime(timezone=True), table=<episodes>, nullable=False, │ │
|
|
|
|
|
│ │ server_default=DefaultClause(<sqlalchemy.sql.functions.now at │ │
|
|
|
|
|
│ │ 0x7ed681946120; now>, for_update=False)), Column('updated_at', │ │
|
|
|
|
|
│ │ DateTime(timezone=True), table=<episodes>, nullable=False, │ │
|
|
|
|
|
│ │ onupdate=ColumnElementColumnDefault(<sqlalchemy.sql.functions.now │ │
|
|
|
|
|
│ │ at 0x7ed681b1ae90; now>), │ │
|
|
|
|
|
│ │ server_default=DefaultClause(<sqlalchemy.sql.functions.now at │ │
|
|
|
|
|
│ │ 0x7ed681b1aad0; now>, for_update=False)), schema=None) │ │
|
|
|
|
|
│ │ effective_path = PropRegistry((<Mapper at 0x7ed681845590; DownloadQueueItem>, │ │
|
|
|
|
|
│ │ <\_RelationshipDeclared at 0x7ed681866df0; episode>)) │ │
|
|
|
|
|
│ │ execution_options = immutabledict({}) │ │
|
|
|
|
|
│ │ extra_criteria = () │ │
|
|
|
|
|
│ │ extra_options = () │ │
|
|
|
|
|
│ │ load_options = default_load_options(\_invoke_all_eagers=False, │ │
|
|
|
|
|
│ │ \_lazy_loaded_from=<sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>) │ │
|
|
|
|
|
│ │ loadopt = None │ │
|
|
|
|
|
│ │ opts = ( │ │
|
|
|
|
|
│ │ │ <sqlalchemy.orm.strategy_options.Load object at │ │
|
|
|
|
|
│ │ 0x7ed67b687e80>, │ │
|
|
|
|
|
│ │ ) │ │
|
|
|
|
|
│ │ passive = symbol('PASSIVE_OFF') │ │
|
|
|
|
|
│ │ pending = False │ │
|
|
|
|
|
│ │ primary_key_identity = [26] │ │
|
|
|
|
|
│ │ self = <sqlalchemy.orm.strategies.LazyLoader object at 0x7ed67cabf4c0> │ │
|
|
|
|
|
│ │ session = <sqlalchemy.orm.session.Session object at 0x7ed67caf4fc0> │ │
|
|
|
|
|
│ │ state = <sqlalchemy.orm.state.InstanceState object at 0x7ed67b61e330> │ │
|
|
|
|
|
│ │ stmt = <sqlalchemy.sql.selectable.Select object at 0x7ed67cad55e0> │ │
|
|
|
|
|
│ │ strategy_options = <module 'sqlalchemy.orm.strategy_options' from │ │
|
|
|
|
|
│ │ '/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packag… │ │
|
|
|
|
|
│ │ use_get = True │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/orm/loading.py:695 │
|
|
|
|
|
│ in load_on_pk_identity │
|
|
|
|
|
│ │
|
|
|
|
|
│ 692 │ │ execution_options, {"\_sa_orm_load_options": load_options} │
|
|
|
|
|
│ 693 │ ) │
|
|
|
|
|
│ 694 │ result = ( │
|
|
|
|
|
│ ❱ 695 │ │ session.execute( │
|
|
|
|
|
│ 696 │ │ │ q, │
|
|
|
|
|
│ 697 │ │ │ params=params, │
|
|
|
|
|
│ 698 │ │ │ execution_options=execution_options, │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ \_get_clause = <sqlalchemy.sql.elements.BinaryExpression object at 0x7ed67cab65d0> │ │
|
|
|
|
|
│ │ \_get_params = { │ │
|
|
|
|
|
│ │ │ Column('id', Integer(), table=<episodes>, primary_key=True, │ │
|
|
|
|
|
│ │ nullable=False): BindParameter('pk_1', None, type*=Integer()) │ │
|
|
|
|
|
│ │ } │ │
|
|
|
|
|
│ │ bind*arguments = immutabledict({}) │ │
|
|
|
|
|
│ │ compile_options = default_compile_options(\_current_path=PropRegistry((<Mapper at │ │
|
|
|
|
|
│ │ 0x7ed681845590; DownloadQueueItem>, <\_RelationshipDeclared at │ │
|
|
|
|
|
│ │ 0x7ed681866df0; episode>))) │ │
|
|
|
|
|
│ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │
|
|
|
|
|
│ │ default_load_options(\_invoke_all_eagers=False, │ │
|
|
|
|
|
│ │ \_lazy_loaded_from=<sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>)}) │ │
|
|
|
|
|
│ │ identity_token = None │ │
|
|
|
|
|
│ │ is_user_refresh = False │ │
|
|
|
|
|
│ │ load_options = default_load_options(\_invoke_all_eagers=False, │ │
|
|
|
|
|
│ │ \_lazy_loaded_from=<sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>) │ │
|
|
|
|
|
│ │ mapper = <Mapper at 0x7ed681844a50; Episode> │ │
|
|
|
|
|
│ │ new_compile_options = default_compile_options(\_current_path=PropRegistry((<Mapper at │ │
|
|
|
|
|
│ │ 0x7ed681845590; DownloadQueueItem>, <\_RelationshipDeclared at │ │
|
|
|
|
|
│ │ 0x7ed681866df0; episode>))) │ │
|
|
|
|
|
│ │ no_autoflush = False │ │
|
|
|
|
|
│ │ only_load_props = None │ │
|
|
|
|
|
│ │ params = {'pk_1': 26} │ │
|
|
|
|
|
│ │ primary_key_identity = [26] │ │
|
|
|
|
|
│ │ q = <sqlalchemy.sql.selectable.Select object at 0x7ed67cad73e0> │ │
|
|
|
|
|
│ │ query = <sqlalchemy.sql.selectable.Select object at 0x7ed67cad55e0> │ │
|
|
|
|
|
│ │ refresh_state = None │ │
|
|
|
|
|
│ │ require_pk_cols = False │ │
|
|
|
|
|
│ │ session = <sqlalchemy.orm.session.Session object at 0x7ed67caf4fc0> │ │
|
|
|
|
|
│ │ statement = <sqlalchemy.sql.selectable.Select object at 0x7ed67cad55e0> │ │
|
|
|
|
|
│ │ version_check = False │ │
|
|
|
|
|
│ │ with_for_update = None │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/orm/session.py:2351 │
|
|
|
|
|
│ in execute │
|
|
|
|
|
│ │
|
|
|
|
|
│ 2348 │ │ │
|
|
|
|
|
│ 2349 │ │ │
|
|
|
|
|
│ 2350 │ │ """ │
|
|
|
|
|
│ ❱ 2351 │ │ return self.\_execute_internal( │
|
|
|
|
|
│ 2352 │ │ │ statement, │
|
|
|
|
|
│ 2353 │ │ │ params, │
|
|
|
|
|
│ 2354 │ │ │ execution_options=execution_options, │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ \_add_event = None │ │
|
|
|
|
|
│ │ \_parent_execute_state = None │ │
|
|
|
|
|
│ │ bind_arguments = immutabledict({}) │ │
|
|
|
|
|
│ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │
|
|
|
|
|
│ │ default_load_options(\_invoke_all_eagers=False, │ │
|
|
|
|
|
│ │ \_lazy_loaded_from=<sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>)}) │ │
|
|
|
|
|
│ │ params = {'pk_1': 26} │ │
|
|
|
|
|
│ │ self = <sqlalchemy.orm.session.Session object at 0x7ed67caf4fc0> │ │
|
|
|
|
|
│ │ statement = <sqlalchemy.sql.selectable.Select object at 0x7ed67cad73e0> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/orm/session.py:2249 │
|
|
|
|
|
│ in \_execute_internal │
|
|
|
|
|
│ │
|
|
|
|
|
│ 2246 │ │ │ ) │
|
|
|
|
|
│ 2247 │ │ │
|
|
|
|
|
│ 2248 │ │ if compile_state_cls: │
|
|
|
|
|
│ ❱ 2249 │ │ │ result: Result[Any] = compile_state_cls.orm_execute_statement( │
|
|
|
|
|
│ 2250 │ │ │ │ self, │
|
|
|
|
|
│ 2251 │ │ │ │ statement, │
|
|
|
|
|
│ 2252 │ │ │ │ params or {}, │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ \_add_event = None │ │
|
|
|
|
|
│ │ \_parent_execute_state = None │ │
|
|
|
|
|
│ │ \_scalar_result = False │ │
|
|
|
|
|
│ │ bind = Engine(sqlite+aiosqlite:///./data/aniworld.db) │ │
|
|
|
|
|
│ │ bind_arguments = { │ │
|
|
|
|
|
│ │ │ 'clause': <sqlalchemy.sql.selectable.Select object at │ │
|
|
|
|
|
│ │ 0x7ed67cad73e0>, │ │
|
|
|
|
|
│ │ │ 'mapper': <Mapper at 0x7ed681844a50; Episode> │ │
|
|
|
|
|
│ │ } │ │
|
|
|
|
|
│ │ conn = <sqlalchemy.engine.base.Connection object at 0x7ed67ca85480> │ │
|
|
|
|
|
│ │ events_todo = <sqlalchemy.event.attr.\_EmptyListener object at 0x7ed67ce11380> │ │
|
|
|
|
|
│ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │
|
|
|
|
|
│ │ default_load_options(\_invoke_all_eagers=False, │ │
|
|
|
|
|
│ │ \_lazy_loaded_from=<sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>), '\_result_disable_adapt_to_context': True}) │ │
|
|
|
|
|
│ │ params = {'pk_1': 26} │ │
|
|
|
|
|
│ │ self = <sqlalchemy.orm.session.Session object at 0x7ed67caf4fc0> │ │
|
|
|
|
|
│ │ statement = <sqlalchemy.sql.selectable.Select object at 0x7ed67cad73e0> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/orm/context.py:306 │
|
|
|
|
|
│ in orm_execute_statement │
|
|
|
|
|
│ │
|
|
|
|
|
│ 303 │ │ bind_arguments, │
|
|
|
|
|
│ 304 │ │ conn, │
|
|
|
|
|
│ 305 │ ) -> Result: │
|
|
|
|
|
│ ❱ 306 │ │ result = conn.execute( │
|
|
|
|
|
│ 307 │ │ │ statement, params or {}, execution_options=execution_options │
|
|
|
|
|
│ 308 │ │ ) │
|
|
|
|
|
│ 309 │ │ return cls.orm_setup_cursor_result( │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ bind_arguments = { │ │
|
|
|
|
|
│ │ │ 'clause': <sqlalchemy.sql.selectable.Select object at │ │
|
|
|
|
|
│ │ 0x7ed67cad73e0>, │ │
|
|
|
|
|
│ │ │ 'mapper': <Mapper at 0x7ed681844a50; Episode> │ │
|
|
|
|
|
│ │ } │ │
|
|
|
|
|
│ │ conn = <sqlalchemy.engine.base.Connection object at 0x7ed67ca85480> │ │
|
|
|
|
|
│ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │
|
|
|
|
|
│ │ default_load_options(\_invoke_all_eagers=False, │ │
|
|
|
|
|
│ │ \_lazy_loaded_from=<sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>), '\_result_disable_adapt_to_context': True}) │ │
|
|
|
|
|
│ │ params = {'pk_1': 26} │ │
|
|
|
|
|
│ │ session = <sqlalchemy.orm.session.Session object at 0x7ed67caf4fc0> │ │
|
|
|
|
|
│ │ statement = <sqlalchemy.sql.selectable.Select object at 0x7ed67cad73e0> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1419 │
|
|
|
|
|
│ in execute │
|
|
|
|
|
│ │
|
|
|
|
|
│ 1416 │ │ except AttributeError as err: │
|
|
|
|
|
│ 1417 │ │ │ raise exc.ObjectNotExecutableError(statement) from err │
|
|
|
|
|
│ 1418 │ │ else: │
|
|
|
|
|
│ ❱ 1419 │ │ │ return meth( │
|
|
|
|
|
│ 1420 │ │ │ │ self, │
|
|
|
|
|
│ 1421 │ │ │ │ distilled_parameters, │
|
|
|
|
|
│ 1422 │ │ │ │ execution_options or NO_OPTIONS, │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ distilled_parameters = [{'pk_1': 26}] │ │
|
|
|
|
|
│ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │
|
|
|
|
|
│ │ default_load_options(\_invoke_all_eagers=False, │ │
|
|
|
|
|
│ │ \_lazy_loaded_from=<sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>), '\_result_disable_adapt_to_context': True}) │ │
|
|
|
|
|
│ │ meth = <bound method ClauseElement.\_execute_on_connection of │ │
|
|
|
|
|
│ │ <sqlalchemy.sql.selectable.Select object at 0x7ed67cad73e0>> │ │
|
|
|
|
|
│ │ parameters = {'pk_1': 26} │ │
|
|
|
|
|
│ │ self = <sqlalchemy.engine.base.Connection object at 0x7ed67ca85480> │ │
|
|
|
|
|
│ │ statement = <sqlalchemy.sql.selectable.Select object at 0x7ed67cad73e0> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/sql/elements.py:526 │
|
|
|
|
|
│ in \_execute_on_connection │
|
|
|
|
|
│ │
|
|
|
|
|
│ 523 │ │ if self.supports_execution: │
|
|
|
|
|
│ 524 │ │ │ if TYPE_CHECKING: │
|
|
|
|
|
│ 525 │ │ │ │ assert isinstance(self, Executable) │
|
|
|
|
|
│ ❱ 526 │ │ │ return connection.\_execute_clauseelement( │
|
|
|
|
|
│ 527 │ │ │ │ self, distilled_params, execution_options │
|
|
|
|
|
│ 528 │ │ │ ) │
|
|
|
|
|
│ 529 │ │ else: │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ connection = <sqlalchemy.engine.base.Connection object at 0x7ed67ca85480> │ │
|
|
|
|
|
│ │ distilled_params = [{'pk_1': 26}] │ │
|
|
|
|
|
│ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │
|
|
|
|
|
│ │ default_load_options(\_invoke_all_eagers=False, │ │
|
|
|
|
|
│ │ \_lazy_loaded_from=<sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>), '\_result_disable_adapt_to_context': True}) │ │
|
|
|
|
|
│ │ self = <sqlalchemy.sql.selectable.Select object at 0x7ed67cad73e0> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1641 │
|
|
|
|
|
│ in \_execute_clauseelement │
|
|
|
|
|
│ │
|
|
|
|
|
│ 1638 │ │ │ schema_translate_map=schema_translate_map, │
|
|
|
|
|
│ 1639 │ │ │ linting=self.dialect.compiler_linting | compiler.WARN_LINTING, │
|
|
|
|
|
│ 1640 │ │ ) │
|
|
|
|
|
│ ❱ 1641 │ │ ret = self.\_execute_context( │
|
|
|
|
|
│ 1642 │ │ │ dialect, │
|
|
|
|
|
│ 1643 │ │ │ dialect.execution_ctx_cls.\_init_compiled, │
|
|
|
|
|
│ 1644 │ │ │ compiled_sql, │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ cache_hit = <CacheStats.CACHE_MISS: 1> │ │
|
|
|
|
|
│ │ compiled_cache = <sqlalchemy.util.\_collections.LRUCache object at 0x7ed67ca2efc0> │ │
|
|
|
|
|
│ │ compiled_sql = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at │ │
|
|
|
|
|
│ │ 0x7ed67cae5950> │ │
|
|
|
|
|
│ │ dialect = <sqlalchemy.dialects.sqlite.aiosqlite.SQLiteDialect_aiosqlite object │ │
|
|
|
|
|
│ │ at 0x7ed67cb3f8c0> │ │
|
|
|
|
|
│ │ distilled_parameters = [{'pk_1': 26}] │ │
|
|
|
|
|
│ │ elem = <sqlalchemy.sql.selectable.Select object at 0x7ed67cad73e0> │ │
|
|
|
|
|
│ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │
|
|
|
|
|
│ │ default_load_options(\_invoke_all_eagers=False, │ │
|
|
|
|
|
│ │ \_lazy_loaded_from=<sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>), '\_result_disable_adapt_to_context': True}) │ │
|
|
|
|
|
│ │ extracted_params = [AnnotatedBindParameter('pk_1', None, type*=Integer())] │ │
|
|
|
|
|
│ │ for*executemany = False │ │
|
|
|
|
|
│ │ has_events = False │ │
|
|
|
|
|
│ │ keys = ['pk_1'] │ │
|
|
|
|
|
│ │ schema_translate_map = None │ │
|
|
|
|
|
│ │ self = <sqlalchemy.engine.base.Connection object at 0x7ed67ca85480> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1846 │
|
|
|
|
|
│ in \_execute_context │
|
|
|
|
|
│ │
|
|
|
|
|
│ 1843 │ │ if context.execute_style is ExecuteStyle.INSERTMANYVALUES: │
|
|
|
|
|
│ 1844 │ │ │ return self.\_exec_insertmany_context(dialect, context) │
|
|
|
|
|
│ 1845 │ │ else: │
|
|
|
|
|
│ ❱ 1846 │ │ │ return self.\_exec_single_context( │
|
|
|
|
|
│ 1847 │ │ │ │ dialect, context, statement, parameters │
|
|
|
|
|
│ 1848 │ │ │ ) │
|
|
|
|
|
│ 1849 │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ args = ( │ │
|
|
|
|
|
│ │ │ <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at │ │
|
|
|
|
|
│ │ 0x7ed67cae5950>, │ │
|
|
|
|
|
│ │ │ [{'pk_1': 26}], │ │
|
|
|
|
|
│ │ │ <sqlalchemy.sql.selectable.Select object at 0x7ed67cad73e0>, │ │
|
|
|
|
|
│ │ │ [AnnotatedBindParameter('pk_1', None, type*=Integer())] │ │
|
|
|
|
|
│ │ ) │ │
|
|
|
|
|
│ │ conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7ed67b61daf0> │ │
|
|
|
|
|
│ │ constructor = <bound method DefaultExecutionContext.\_init_compiled of <class │ │
|
|
|
|
|
│ │ 'sqlalchemy.dialects.sqlite.aiosqlite.SQLiteExecutionContext_aiosqlite'… │ │
|
|
|
|
|
│ │ context = <sqlalchemy.dialects.sqlite.aiosqlite.SQLiteExecutionContext_aiosqlite │ │
|
|
|
|
|
│ │ object at 0x7ed67ca85260> │ │
|
|
|
|
|
│ │ dialect = <sqlalchemy.dialects.sqlite.aiosqlite.SQLiteDialect_aiosqlite object at │ │
|
|
|
|
|
│ │ 0x7ed67cb3f8c0> │ │
|
|
|
|
|
│ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │
|
|
|
|
|
│ │ default_load_options(\_invoke_all_eagers=False, │ │
|
|
|
|
|
│ │ \_lazy_loaded_from=<sqlalchemy.orm.state.InstanceState object at │ │
|
|
|
|
|
│ │ 0x7ed67b61e330>), '\_result_disable_adapt_to_context': True}) │ │
|
|
|
|
|
│ │ kw = {'cache_hit': <CacheStats.CACHE_MISS: 1>} │ │
|
|
|
|
|
│ │ parameters = [{'pk_1': 26}] │ │
|
|
|
|
|
│ │ self = <sqlalchemy.engine.base.Connection object at 0x7ed67ca85480> │ │
|
|
|
|
|
│ │ statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at │ │
|
|
|
|
|
│ │ 0x7ed67cae5950> │ │
|
|
|
|
|
│ │ yp = None │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1986 │
|
|
|
|
|
│ in \_exec_single_context │
|
|
|
|
|
│ │
|
|
|
|
|
│ 1983 │ │ │ result = context.\_setup_result_proxy() │
|
|
|
|
|
│ 1984 │ │ │
|
|
|
|
|
│ 1985 │ │ except BaseException as e: │
|
|
|
|
|
│ ❱ 1986 │ │ │ self.\_handle_dbapi_exception( │
|
|
|
|
|
│ 1987 │ │ │ │ e, str_statement, effective_parameters, cursor, context │
|
|
|
|
|
│ 1988 │ │ │ ) │
|
|
|
|
|
│ 1989 │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ context = <sqlalchemy.dialects.sqlite.aiosqlite.SQLiteExecutionContext_aiosqli… │ │
|
|
|
|
|
│ │ object at 0x7ed67ca85260> │ │
|
|
|
|
|
│ │ cursor = <sqlalchemy.dialects.sqlite.aiosqlite.AsyncAdapt_aiosqlite_cursor │ │
|
|
|
|
|
│ │ object at 0x7ed67b667be0> │ │
|
|
|
|
|
│ │ dialect = <sqlalchemy.dialects.sqlite.aiosqlite.SQLiteDialect_aiosqlite object │ │
|
|
|
|
|
│ │ at 0x7ed67cb3f8c0> │ │
|
|
|
|
|
│ │ effective_parameters = (26,) │ │
|
|
|
|
|
│ │ evt_handled = False │ │
|
|
|
|
|
│ │ parameters = [(26,)] │ │
|
|
|
|
|
│ │ self = <sqlalchemy.engine.base.Connection object at 0x7ed67ca85480> │ │
|
|
|
|
|
│ │ statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at │ │
|
|
|
|
|
│ │ 0x7ed67cae5950> │ │
|
|
|
|
|
│ │ str_statement = 'SELECT episodes.id AS episodes_id, episodes.series_id AS │ │
|
|
|
|
|
│ │ episodes_series_id, epi'+335 │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/engine/base.py:2358 │
|
|
|
|
|
│ in \_handle_dbapi_exception │
|
|
|
|
|
│ │
|
|
|
|
|
│ 2355 │ │ │ │ raise sqlalchemy_exception.with_traceback(exc_info[2]) from e │
|
|
|
|
|
│ 2356 │ │ │ else: │
|
|
|
|
|
│ 2357 │ │ │ │ assert exc_info[1] is not None │
|
|
|
|
|
│ ❱ 2358 │ │ │ │ raise exc_info[1].with_traceback(exc_info[2]) │
|
|
|
|
|
│ 2359 │ │ finally: │
|
|
|
|
|
│ 2360 │ │ │ del self.\_reentrant_error │
|
|
|
|
|
│ 2361 │ │ │ if self.\_is_disconnect: │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ context = <sqlalchemy.dialects.sqlite.aiosqlite.SQLiteExecutionContex… │ │
|
|
|
|
|
│ │ object at 0x7ed67ca85260> │ │
|
|
|
|
|
│ │ cursor = <sqlalchemy.dialects.sqlite.aiosqlite.AsyncAdapt_aiosqlite_… │ │
|
|
|
|
|
│ │ object at 0x7ed67b667be0> │ │
|
|
|
|
|
│ │ e = MissingGreenlet("greenlet*spawn has not been called; can't │ │
|
|
|
|
|
│ │ call await_only() here. Was IO attempted in an unexpected │ │
|
|
|
|
|
│ │ place?") │ │
|
|
|
|
|
│ │ exc_info = ( │ │
|
|
|
|
|
│ │ │ <class 'sqlalchemy.exc.MissingGreenlet'>, │ │
|
|
|
|
|
│ │ │ MissingGreenlet("greenlet_spawn has not been called; │ │
|
|
|
|
|
│ │ can't call await_only() here. Was IO attempted in an │ │
|
|
|
|
|
│ │ unexpected place?"), │ │
|
|
|
|
|
│ │ │ <traceback object at 0x7ed67b687240> │ │
|
|
|
|
|
│ │ ) │ │
|
|
|
|
|
│ │ invalidate_pool_on_disconnect = True │ │
|
|
|
|
|
│ │ is_exit_exception = False │ │
|
|
|
|
|
│ │ is_sub_exec = False │ │
|
|
|
|
|
│ │ ismulti = False │ │
|
|
|
|
|
│ │ newraise = None │ │
|
|
|
|
|
│ │ parameters = (26,) │ │
|
|
|
|
|
│ │ self = <sqlalchemy.engine.base.Connection object at 0x7ed67ca85480> │ │
|
|
|
|
|
│ │ should_wrap = False │ │
|
|
|
|
|
│ │ sqlalchemy_exception = None │ │
|
|
|
|
|
│ │ statement = 'SELECT episodes.id AS episodes_id, episodes.series_id AS │ │
|
|
|
|
|
│ │ episodes_series_id, epi'+335 │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1967 │
|
|
|
|
|
│ in \_exec_single_context │
|
|
|
|
|
│ │
|
|
|
|
|
│ 1964 │ │ │ │ │ │ │ evt_handled = True │
|
|
|
|
|
│ 1965 │ │ │ │ │ │ │ break │
|
|
|
|
|
│ 1966 │ │ │ │ if not evt_handled: │
|
|
|
|
|
│ ❱ 1967 │ │ │ │ │ self.dialect.do_execute( │
|
|
|
|
|
│ 1968 │ │ │ │ │ │ cursor, str_statement, effective_parameters, context │
|
|
|
|
|
│ 1969 │ │ │ │ │ ) │
|
|
|
|
|
│ 1970 │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ context = <sqlalchemy.dialects.sqlite.aiosqlite.SQLiteExecutionContext_aiosqli… │ │
|
|
|
|
|
│ │ object at 0x7ed67ca85260> │ │
|
|
|
|
|
│ │ cursor = <sqlalchemy.dialects.sqlite.aiosqlite.AsyncAdapt_aiosqlite_cursor │ │
|
|
|
|
|
│ │ object at 0x7ed67b667be0> │ │
|
|
|
|
|
│ │ dialect = <sqlalchemy.dialects.sqlite.aiosqlite.SQLiteDialect_aiosqlite object │ │
|
|
|
|
|
│ │ at 0x7ed67cb3f8c0> │ │
|
|
|
|
|
│ │ effective_parameters = (26,) │ │
|
|
|
|
|
│ │ evt_handled = False │ │
|
|
|
|
|
│ │ parameters = [(26,)] │ │
|
|
|
|
|
│ │ self = <sqlalchemy.engine.base.Connection object at 0x7ed67ca85480> │ │
|
|
|
|
|
│ │ statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at │ │
|
|
|
|
|
│ │ 0x7ed67cae5950> │ │
|
|
|
|
|
│ │ str_statement = 'SELECT episodes.id AS episodes_id, episodes.series_id AS │ │
|
|
|
|
|
│ │ episodes_series_id, epi'+335 │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/engine/default.py:9 │
|
|
|
|
|
│ 51 in do_execute │
|
|
|
|
|
│ │
|
|
|
|
|
│ 948 │ │ cursor.executemany(statement, parameters) │
|
|
|
|
|
│ 949 │ │
|
|
|
|
|
│ 950 │ def do_execute(self, cursor, statement, parameters, context=None): │
|
|
|
|
|
│ ❱ 951 │ │ cursor.execute(statement, parameters) │
|
|
|
|
|
│ 952 │ │
|
|
|
|
|
│ 953 │ def do_execute_no_params(self, cursor, statement, context=None): │
|
|
|
|
|
│ 954 │ │ cursor.execute(statement) │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ context = <sqlalchemy.dialects.sqlite.aiosqlite.SQLiteExecutionContext_aiosqlite object │ │
|
|
|
|
|
│ │ at 0x7ed67ca85260> │ │
|
|
|
|
|
│ │ cursor = <sqlalchemy.dialects.sqlite.aiosqlite.AsyncAdapt_aiosqlite_cursor object at │ │
|
|
|
|
|
│ │ 0x7ed67b667be0> │ │
|
|
|
|
|
│ │ parameters = (26,) │ │
|
|
|
|
|
│ │ self = <sqlalchemy.dialects.sqlite.aiosqlite.SQLiteDialect_aiosqlite object at │ │
|
|
|
|
|
│ │ 0x7ed67cb3f8c0> │ │
|
|
|
|
|
│ │ statement = 'SELECT episodes.id AS episodes_id, episodes.series_id AS episodes_series_id, │ │
|
|
|
|
|
│ │ epi'+335 │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/aio │
|
|
|
|
|
│ sqlite.py:180 in execute │
|
|
|
|
|
│ │
|
|
|
|
|
│ 177 │ │ │ else: │
|
|
|
|
|
│ 178 │ │ │ │ self.\_cursor = \_cursor # type: ignore[misc] │
|
|
|
|
|
│ 179 │ │ except Exception as error: │
|
|
|
|
|
│ ❱ 180 │ │ │ self.\_adapt_connection.\_handle_exception(error) │
|
|
|
|
|
│ 181 │ │
|
|
|
|
|
│ 182 │ def executemany( │
|
|
|
|
|
│ 183 │ │ self, │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ operation = 'SELECT episodes.id AS episodes_id, episodes.series_id AS episodes_series_id, │ │
|
|
|
|
|
│ │ epi'+335 │ │
|
|
|
|
|
│ │ parameters = (26,) │ │
|
|
|
|
|
│ │ self = <sqlalchemy.dialects.sqlite.aiosqlite.AsyncAdapt_aiosqlite_cursor object at │ │
|
|
|
|
|
│ │ 0x7ed67b667be0> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/aio │
|
|
|
|
|
│ sqlite.py:340 in \_handle_exception │
|
|
|
|
|
│ │
|
|
|
|
|
│ 337 │ │ │ │ "no active connection" │
|
|
|
|
|
│ 338 │ │ │ ) from error │
|
|
|
|
|
│ 339 │ │ else: │
|
|
|
|
|
│ ❱ 340 │ │ │ raise error │
|
|
|
|
|
│ 341 │
|
|
|
|
|
│ 342 │
|
|
|
|
|
│ 343 class AsyncAdaptFallback_aiosqlite_connection(AsyncAdapt_aiosqlite_connection): │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ error = MissingGreenlet("greenlet_spawn has not been called; can't call await_only() here. │ │
|
|
|
|
|
│ │ Was IO attempted in an unexpected place?") │ │
|
|
|
|
|
│ │ self = <AdaptedConnection <Connection(Thread-1, started daemon 139459678959296)>> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/aio │
|
|
|
|
|
│ sqlite.py:157 in execute │
|
|
|
|
|
│ │
|
|
|
|
|
│ 154 │ ) -> Any: │
|
|
|
|
|
│ 155 │ │ │
|
|
|
|
|
│ 156 │ │ try: │
|
|
|
|
|
│ ❱ 157 │ │ │ \_cursor: AsyncIODBAPICursor = self.await*(self._connection.cursor()) # type │
|
|
|
|
|
│ 158 │ │ │ │
|
|
|
|
|
│ 159 │ │ │ if parameters is None: │
|
|
|
|
|
│ 160 │ │ │ │ self.await_(\_cursor.execute(operation)) │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ operation = 'SELECT episodes.id AS episodes_id, episodes.series_id AS episodes_series_id, │ │
|
|
|
|
|
│ │ epi'+335 │ │
|
|
|
|
|
│ │ parameters = (26,) │ │
|
|
|
|
|
│ │ self = <sqlalchemy.dialects.sqlite.aiosqlite.AsyncAdapt_aiosqlite_cursor object at │ │
|
|
|
|
|
│ │ 0x7ed67b667be0> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/sqlalchemy/util/\_concurrency_p │
|
|
|
|
|
│ y3k.py:123 in await_only │
|
|
|
|
|
│ │
|
|
|
|
|
│ 120 │ if not getattr(current, "**sqlalchemy_greenlet_provider**", False): │
|
|
|
|
|
│ 121 │ │ \_safe_cancel_awaitable(awaitable) │
|
|
|
|
|
│ 122 │ │ │
|
|
|
|
|
│ ❱ 123 │ │ raise exc.MissingGreenlet( │
|
|
|
|
|
│ 124 │ │ │ "greenlet_spawn has not been called; can't call await_only() " │
|
|
|
|
|
│ 125 │ │ │ "here. Was IO attempted in an unexpected place?" │
|
|
|
|
|
│ 126 │ │ ) │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ awaitable = <aiosqlite.context.Result object at 0x7ed67b63fa60> │ │
|
|
|
|
|
│ │ current = <greenlet.greenlet object at 0x7ed68264fb40 (otid=0x7ed68263b960) current active │ │
|
|
|
|
|
│ │ started main> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
|
|
|
MissingGreenlet: greenlet_spawn has not been called; can't call await_only() here. Was IO attempted
|
|
|
|
|
in an unexpected place? (Background on this error at: https://sqlalche.me/e/20/xd2s)
|
|
|
|
|
|
|
|
|
|
The above exception was the direct cause of the following exception:
|
|
|
|
|
|
|
|
|
|
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
|
|
|
|
|
│ /home/lukas/Volume/repo/Aniworld/src/server/services/download_service.py:135 in initialize │
|
|
|
|
|
│ │
|
|
|
|
|
│ 132 │ │ │ │
|
|
|
|
|
│ 133 │ │ │ # Load all items from database - they all start as PENDING │
|
|
|
|
|
│ 134 │ │ │ # since status is now managed in-memory only │
|
|
|
|
|
│ ❱ 135 │ │ │ all_items = await repository.get_all_items() │
|
|
|
|
|
│ 136 │ │ │ for item in all_items: │
|
|
|
|
|
│ 137 │ │ │ │ # All items from database are treated as pending │
|
|
|
|
|
│ 138 │ │ │ │ item.status = DownloadStatus.PENDING │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ e = QueueRepositoryError("Failed to get all items: greenlet_spawn has not been │ │
|
|
|
|
|
│ │ called; can't call await_only() here. Was IO attempted in an unexpected place? │ │
|
|
|
|
|
│ │ (Background on this error at: https://sqlalche.me/e/20/xd2s)") │ │
|
|
|
|
|
│ │ repository = <src.server.services.queue_repository.QueueRepository object at 0x7ed67ca6b770> │ │
|
|
|
|
|
│ │ self = <src.server.services.download_service.DownloadService object at 0x7ed67ca6b620> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
│ │
|
|
|
|
|
│ /home/lukas/Volume/repo/Aniworld/src/server/services/queue_repository.py:296 in get_all_items │
|
|
|
|
|
│ │
|
|
|
|
|
│ 293 │ │ │
|
|
|
|
|
│ 294 │ │ except Exception as e: │
|
|
|
|
|
│ 295 │ │ │ logger.error("Failed to get all items: %s", e) │
|
|
|
|
|
│ ❱ 296 │ │ │ raise QueueRepositoryError(f"Failed to get all items: {e}") from e │
|
|
|
|
|
│ 297 │ │ finally: │
|
|
|
|
|
│ 298 │ │ │ if manage_session: │
|
|
|
|
|
│ 299 │ │ │ │ await session.close() │
|
|
|
|
|
│ │
|
|
|
|
|
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
|
|
|
|
|
│ │ db = None │ │
|
|
|
|
|
│ │ db_items = [ │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=8, series_id=146, episode_id=26)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=9, series_id=146, episode_id=27)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=10, series_id=146, episode_id=28)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=11, series_id=146, episode_id=29)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=12, series_id=146, episode_id=30)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=13, series_id=146, episode_id=31)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=14, series_id=146, episode_id=32)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=15, series_id=146, episode_id=33)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=16, series_id=83, episode_id=1)>, │ │
|
|
|
|
|
│ │ │ <DownloadQueueItem(id=17, series_id=83, episode_id=2)>, │ │
|
|
|
|
|
│ │ │ ... +21 │ │
|
|
|
|
|
│ │ ] │ │
|
|
|
|
|
│ │ manage_session = True │ │
|
|
|
|
|
│ │ self = <src.server.services.queue_repository.QueueRepository object at │ │
|
|
|
|
|
│ │ 0x7ed67ca6b770> │ │
|
|
|
|
|
│ │ session = <sqlalchemy.ext.asyncio.session.AsyncSession object at 0x7ed67caf4e90> │ │
|
|
|
|
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
|
|
|
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
|
|
|
QueueRepositoryError: Failed to get all items: greenlet_spawn has not been called; can't call
|
|
|
|
|
await_only() here. Was IO attempted in an unexpected place? (Background on this error at:
|
|
|
|
|
https://sqlalche.me/e/20/xd2s)
|
|
|
|
|
|
|
|
|
|
2. fix add issue
|
|
|
|
|
|
|
|
|
|
INFO: 127.0.0.1:54956 - "POST /api/anime/add HTTP/1.1" 500
|
|
|
|
|
ERROR: Exception in ASGI application
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/anyio/streams/memory.py", line 98, in receive
|
|
|
|
|
return self.receive_nowait()
|
|
|
|
|
|
|
|
|
|
```^^
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/anyio/streams/memory.py", line 93, in receive_nowait
|
|
|
|
|
raise WouldBlock
|
|
|
|
|
anyio.WouldBlock
|
|
|
|
|
|
|
|
|
|
During handling of the above exception, another exception occurred:
|
|
|
|
|
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/base.py", line 78, in call_next
|
|
|
|
|
message = await recv_stream.receive()
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/anyio/streams/memory.py", line 118, in receive
|
|
|
|
|
raise EndOfStream
|
|
|
|
|
anyio.EndOfStream
|
|
|
|
|
|
|
|
|
|
During handling of the above exception, another exception occurred:
|
|
|
|
|
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/uvicorn/protocols/http/httptools_impl.py", line 426, in run_asgi
|
|
|
|
|
result = await app( # type: ignore[func-returns-value]
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
self.scope, self.receive, self.send
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
)
|
|
|
|
|
^
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in **call**
|
|
|
|
|
return await self.app(scope, receive, send)
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/fastapi/applications.py", line 1106, in **call**
|
|
|
|
|
await super().**call**(scope, receive, send)
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/applications.py", line 122, in **call**
|
|
|
|
|
await self.middleware_stack(scope, receive, send)
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/errors.py", line 184, in **call**
|
|
|
|
|
raise exc
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/errors.py", line 162, in **call**
|
|
|
|
|
await self.app(scope, receive, \_send)
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/base.py", line 108, in **call**
|
|
|
|
|
response = await self.dispatch_func(request, call_next)
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
File "/home/lukas/Volume/repo/Aniworld/src/server/middleware/auth.py", line 209, in dispatch
|
|
|
|
|
return await call_next(request)
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/base.py", line 84, in call_next
|
|
|
|
|
raise app_exc
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/base.py", line 70, in coro
|
|
|
|
|
await self.app(scope, receive_or_disconnect, send_no_error)
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/base.py", line 108, in **call**
|
|
|
|
|
response = await self.dispatch_func(request, call_next)
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
File "/home/lukas/Volume/repo/Aniworld/src/server/middleware/setup_redirect.py", line 147, in dispatch
|
|
|
|
|
return await call_next(request)
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/base.py", line 84, in call_next
|
|
|
|
|
raise app_exc
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/base.py", line 70, in coro
|
|
|
|
|
await self.app(scope, receive_or_disconnect, send_no_error)
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/cors.py", line 91, in **call**
|
|
|
|
|
await self.simple_response(scope, receive, send, request_headers=headers)
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/cors.py", line 146, in simple_response
|
|
|
|
|
await self.app(scope, receive, send)
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/exceptions.py", line 79, in **call**
|
|
|
|
|
raise exc
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/starlette/middleware/exceptions.py", line 68, in **call**
|
|
|
|
|
await self.app(scope, receive, sender)
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/site-packages/fastapi/middleware/asyncexitstack.py", line 14, in **call**
|
|
|
|
|
async with AsyncExitStack() as stack:
|
|
|
|
|
~~~~~~~~~~~~~~^^
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/contextlib.py", line 768, in **aexit**
|
|
|
|
|
raise exc
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/contextlib.py", line 751, in **aexit**
|
|
|
|
|
cb_suppress = await cb(\*exc_details)
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
File "/home/lukas/miniconda3/envs/AniWorld/lib/python3.13/contextlib.py", line 271, in **aexit**
|
|
|
|
|
raise RuntimeError("generator didn't stop after athrow()")
|
|
|
|
|
RuntimeError: generator didn't stop after athrow()
|
|
|
|
|
|
|
|
|
|
3. transactions
|
|
|
|
|
go throw code and add transactions. so that application stops the db is not curropted
|
|
|
|
|
|
|
|
|
|
4. filter
|
|
|
|
|
|
|
|
|
|
make a series filter to filter all series with no episodes found in folder
|
|
|
|
|
```
|
|
|
|
|
(No planned tasks)
|
|
|
|
|
|