Files
MediaManager/alembic/env.py
natarelli22 d8a0ec66c3 Support for handling Single Episode Torrents (#331)
**Description**
As explained on #322, MediaManager currently only matches torrents that
represent full seasons or season packs.
As a result, valid episode-based releases — commonly returned by
indexers such as EZTV — are filtered out during scoring and never
considered for download.

Initial changes to the season parsing logic allow these torrents to be
discovered.
However, additional changes are required beyond season parsing to
properly support single-episode imports.

This PR is intended as a work-in-progress / RFC to discuss the required
changes and align on the correct approach before completing the
implementation.

**Things planned to do**
[X] Update Web UI to better display episode-level details
[ ] Update TV show import logic to handle single episode files, instead
of assuming full season files (to avoid integrity errors when episodes
are missing)
[ ] Create episode file tables to store episode-level data, similar to
season files
[ ] Implement fetching and downloading logic for single-episode torrents

**Notes / current limitations**
At the moment, the database and import logic assume one file per season
per quality, which works for season packs but not for episode-based
releases.

These changes are intentionally not completed yet and are part of the
discussion this PR aims to start.

**Request for feedback**
This represents a significant change in how TV content is handled in
MediaManager.
Before proceeding further, feedback from @maxdorninger on the overall
direction and next steps would be greatly appreciated.

Once aligned, the remaining tasks can be implemented incrementally.

---------

Co-authored-by: Maximilian Dorninger <97409287+maxdorninger@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-22 15:21:19 +01:00

141 lines
3.6 KiB
Python

import sys
sys.path = ["", "..", *sys.path[1:]]
from logging.config import fileConfig # noqa: E402
from sqlalchemy import ( # noqa: E402
engine_from_config,
pool,
)
from alembic import context # noqa: E402
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from media_manager.auth.db import OAuthAccount, User # noqa: E402
from media_manager.config import MediaManagerConfig # noqa: E402
from media_manager.database import Base # noqa: E402
from media_manager.indexer.models import IndexerQueryResult # noqa: E402
from media_manager.movies.models import Movie, MovieFile, MovieRequest # noqa: E402
from media_manager.notification.models import Notification # noqa: E402
from media_manager.torrent.models import Torrent # noqa: E402
from media_manager.tv.models import ( # noqa: E402
Episode,
EpisodeFile,
Season,
SeasonRequest,
Show,
)
target_metadata = Base.metadata
# this is to keep pycharm from complaining about/optimizing unused imports
# noinspection PyStatementEffect
__all__ = [
"Episode",
"EpisodeFile",
"IndexerQueryResult",
"Movie",
"MovieFile",
"MovieRequest",
"Notification",
"OAuthAccount",
"Season",
"SeasonRequest",
"Show",
"Torrent",
"User",
]
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
db_config = MediaManagerConfig().database
db_url = f"postgresql+psycopg://{db_config.user}:{db_config.password}@{db_config.host}:{db_config.port}/{db_config.dbname}"
config.set_main_option("sqlalchemy.url", db_url)
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
def include_object(
_object: object | None,
name: str | None,
type_: str | None,
_reflected: bool | None,
_compare_to: object | None,
) -> bool:
if type_ == "table" and name == "apscheduler_jobs":
return False
return True
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
include_object=include_object,
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()