diff --git a/docs/instructions.md b/docs/instructions.md index 1df7ce1..339dc47 100644 --- a/docs/instructions.md +++ b/docs/instructions.md @@ -119,5 +119,800 @@ For each task completed: ## TODO List: -✅ **COMPLETED:** anime not showing issue - Fixed by loading series from database on every startup -✅ **COMPLETED:** Load missing episodes for newly added series - Implemented episode scanning and database sync after NFO creation +1. fix issue: + 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 = [ │ │ + │ │ │ , │ │ + │ │ │ , │ │ + │ │ │ , │ │ + │ │ │ , │ │ + │ │ │ , │ │ + │ │ │ , │ │ + │ │ │ , │ │ + │ │ │ , │ │ + │ │ │ , │ │ + │ │ │ , │ │ + │ │ │ ... +21 │ │ + │ │ ] │ │ + │ │ manage_session = True │ │ + │ │ self = │ │ + │ │ session = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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 = │ │ + │ │ item_id = None │ │ + │ │ self = │ │ + │ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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': , │ │ + │ │ │ '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 = │ │ + │ │ self = │ │ + │ │ state = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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': , │ │ + │ │ │ '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 = │ │ + │ │ state = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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 = │ │ + │ │ state = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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 = │ │ + │ │ session = │ │ + │ │ state = │ │ + │ │ 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=, primary_key=True, nullable=False), │ │ + │ │ Column('series_id', Integer(), ForeignKey('anime_series.id'), │ │ + │ │ table=, nullable=False), Column('season', Integer(), │ │ + │ │ table=, nullable=False), Column('episode_number', │ │ + │ │ Integer(), table=, nullable=False), Column('title', │ │ + │ │ String(length=500), table=), Column('file_path', │ │ + │ │ String(length=1000), table=), Column('is_downloaded', │ │ + │ │ Boolean(), table=, nullable=False, │ │ + │ │ default=ScalarElementColumnDefault(False)), Column('created_at', │ │ + │ │ DateTime(timezone=True), table=, nullable=False, │ │ + │ │ server_default=DefaultClause(, for_update=False)), Column('updated_at', │ │ + │ │ DateTime(timezone=True), table=, nullable=False, │ │ + │ │ onupdate=ColumnElementColumnDefault(), │ │ + │ │ server_default=DefaultClause(, for_update=False)), schema=None) │ │ + │ │ effective_path = PropRegistry((, │ │ + │ │ <\_RelationshipDeclared at 0x7ed681866df0; episode>)) │ │ + │ │ execution_options = immutabledict({}) │ │ + │ │ extra_criteria = () │ │ + │ │ extra_options = () │ │ + │ │ load_options = default_load_options(\_invoke_all_eagers=False, │ │ + │ │ \_lazy_loaded_from=) │ │ + │ │ loadopt = None │ │ + │ │ opts = ( │ │ + │ │ │ , │ │ + │ │ ) │ │ + │ │ passive = symbol('PASSIVE_OFF') │ │ + │ │ pending = False │ │ + │ │ primary_key_identity = [26] │ │ + │ │ self = │ │ + │ │ session = │ │ + │ │ state = │ │ + │ │ stmt = │ │ + │ │ strategy_options = │ │ + │ │ \_get_params = { │ │ + │ │ │ Column('id', Integer(), table=, primary_key=True, │ │ + │ │ nullable=False): BindParameter('pk_1', None, type*=Integer()) │ │ + │ │ } │ │ + │ │ bind*arguments = immutabledict({}) │ │ + │ │ compile_options = default_compile_options(\_current_path=PropRegistry((, <\_RelationshipDeclared at │ │ + │ │ 0x7ed681866df0; episode>))) │ │ + │ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │ + │ │ default_load_options(\_invoke_all_eagers=False, │ │ + │ │ \_lazy_loaded_from=)}) │ │ + │ │ identity_token = None │ │ + │ │ is_user_refresh = False │ │ + │ │ load_options = default_load_options(\_invoke_all_eagers=False, │ │ + │ │ \_lazy_loaded_from=) │ │ + │ │ mapper = │ │ + │ │ new_compile_options = default_compile_options(\_current_path=PropRegistry((, <\_RelationshipDeclared at │ │ + │ │ 0x7ed681866df0; episode>))) │ │ + │ │ no_autoflush = False │ │ + │ │ only_load_props = None │ │ + │ │ params = {'pk_1': 26} │ │ + │ │ primary_key_identity = [26] │ │ + │ │ q = │ │ + │ │ query = │ │ + │ │ refresh_state = None │ │ + │ │ require_pk_cols = False │ │ + │ │ session = │ │ + │ │ statement = │ │ + │ │ 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=)}) │ │ + │ │ params = {'pk_1': 26} │ │ + │ │ self = │ │ + │ │ statement = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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': , │ │ + │ │ │ 'mapper': │ │ + │ │ } │ │ + │ │ conn = │ │ + │ │ events_todo = │ │ + │ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │ + │ │ default_load_options(\_invoke_all_eagers=False, │ │ + │ │ \_lazy_loaded_from=), '\_result_disable_adapt_to_context': True}) │ │ + │ │ params = {'pk_1': 26} │ │ + │ │ self = │ │ + │ │ statement = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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': , │ │ + │ │ │ 'mapper': │ │ + │ │ } │ │ + │ │ conn = │ │ + │ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │ + │ │ default_load_options(\_invoke_all_eagers=False, │ │ + │ │ \_lazy_loaded_from=), '\_result_disable_adapt_to_context': True}) │ │ + │ │ params = {'pk_1': 26} │ │ + │ │ session = │ │ + │ │ statement = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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=), '\_result_disable_adapt_to_context': True}) │ │ + │ │ meth = > │ │ + │ │ parameters = {'pk_1': 26} │ │ + │ │ self = │ │ + │ │ statement = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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 = │ │ + │ │ distilled_params = [{'pk_1': 26}] │ │ + │ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │ + │ │ default_load_options(\_invoke_all_eagers=False, │ │ + │ │ \_lazy_loaded_from=), '\_result_disable_adapt_to_context': True}) │ │ + │ │ self = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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 = │ │ + │ │ compiled_cache = │ │ + │ │ compiled_sql = │ │ + │ │ dialect = │ │ + │ │ distilled_parameters = [{'pk_1': 26}] │ │ + │ │ elem = │ │ + │ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │ + │ │ default_load_options(\_invoke_all_eagers=False, │ │ + │ │ \_lazy_loaded_from=), '\_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 = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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 = ( │ │ + │ │ │ , │ │ + │ │ │ [{'pk_1': 26}], │ │ + │ │ │ , │ │ + │ │ │ [AnnotatedBindParameter('pk_1', None, type*=Integer())] │ │ + │ │ ) │ │ + │ │ conn = │ │ + │ │ constructor = │ │ + │ │ dialect = │ │ + │ │ execution_options = immutabledict({'\_sa_orm_load_options': │ │ + │ │ default_load_options(\_invoke_all_eagers=False, │ │ + │ │ \_lazy_loaded_from=), '\_result_disable_adapt_to_context': True}) │ │ + │ │ kw = {'cache_hit': } │ │ + │ │ parameters = [{'pk_1': 26}] │ │ + │ │ self = │ │ + │ │ statement = │ │ + │ │ 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 = │ │ + │ │ cursor = │ │ + │ │ dialect = │ │ + │ │ effective_parameters = (26,) │ │ + │ │ evt_handled = False │ │ + │ │ parameters = [(26,)] │ │ + │ │ self = │ │ + │ │ statement = │ │ + │ │ 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 = │ │ + │ │ cursor = │ │ + │ │ e = MissingGreenlet("greenlet*spawn has not been called; can't │ │ + │ │ call await_only() here. Was IO attempted in an unexpected │ │ + │ │ place?") │ │ + │ │ exc_info = ( │ │ + │ │ │ , │ │ + │ │ │ MissingGreenlet("greenlet_spawn has not been called; │ │ + │ │ can't call await_only() here. Was IO attempted in an │ │ + │ │ unexpected place?"), │ │ + │ │ │ │ │ + │ │ ) │ │ + │ │ invalidate_pool_on_disconnect = True │ │ + │ │ is_exit_exception = False │ │ + │ │ is_sub_exec = False │ │ + │ │ ismulti = False │ │ + │ │ newraise = None │ │ + │ │ parameters = (26,) │ │ + │ │ self = │ │ + │ │ 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 = │ │ + │ │ cursor = │ │ + │ │ dialect = │ │ + │ │ effective_parameters = (26,) │ │ + │ │ evt_handled = False │ │ + │ │ parameters = [(26,)] │ │ + │ │ self = │ │ + │ │ statement = │ │ + │ │ 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 = │ │ + │ │ cursor = │ │ + │ │ parameters = (26,) │ │ + │ │ self = │ │ + │ │ 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 = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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 = > │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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 = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + │ │ + │ /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 = │ │ + │ │ current = │ │ + │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ + ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ + 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 = │ │ +│ │ self = │ │ +│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ +│ │ +│ /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 = [ │ │ +│ │ │ , │ │ +│ │ │ , │ │ +│ │ │ , │ │ +│ │ │ , │ │ +│ │ │ , │ │ +│ │ │ , │ │ +│ │ │ , │ │ +│ │ │ , │ │ +│ │ │ , │ │ +│ │ │ , │ │ +│ │ │ ... +21 │ │ +│ │ ] │ │ +│ │ manage_session = True │ │ +│ │ self = │ │ +│ │ session = │ │ +│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ +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 diff --git a/src/server/database/service.py b/src/server/database/service.py index 96628f3..d774478 100644 --- a/src/server/database/service.py +++ b/src/server/database/service.py @@ -610,7 +610,7 @@ class DownloadQueueService: Args: db: Database session - with_series: Whether to eagerly load series data + with_series: Whether to eagerly load series and episode data Returns: List of all DownloadQueueItem instances @@ -618,7 +618,11 @@ class DownloadQueueService: query = select(DownloadQueueItem) if with_series: - query = query.options(selectinload(DownloadQueueItem.series)) + # Eagerly load both series and episode relationships + query = query.options( + selectinload(DownloadQueueItem.series), + selectinload(DownloadQueueItem.episode) + ) query = query.order_by( DownloadQueueItem.created_at.asc(),