had a crack at fixing the initial migration

This commit is contained in:
Joe Thomas
2025-07-04 12:31:07 +02:00
parent 7dff3111bf
commit 361288e6bd
5 changed files with 213 additions and 6 deletions

2
.gitignore vendored
View File

@@ -9,6 +9,8 @@ tv/*
log.txt
res/*
media_manager/indexer/indexers/prowlarr.http
*.egg-info
.env
web/cache/

View File

@@ -56,6 +56,23 @@ docker compose up db -d
uv run alembic upgrade head
```
## Get the frontend up and running
TODO: provide an env.example to copy
```bash
cd /web && npm install
```
## Now start the backend and frontend
```bash
fastapi dev /media_manager/main.py --reload --host
```
```bash
cd /web && npm run dev
```
### [View the docs for installation instructions and more](https://maxdorninger.github.io/MediaManager/configuration-overview.html#configuration-overview)

View File

@@ -28,6 +28,8 @@ from media_manager.auth.db import User, OAuthAccount # noqa: E402
from media_manager.indexer.models import IndexerQueryResult # noqa: E402
from media_manager.torrent.models import Torrent # noqa: E402
from media_manager.tv.models import Show, Season, Episode, SeasonFile, SeasonRequest # 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.database import Base # noqa: E402
@@ -45,6 +47,10 @@ target_metadata = Base.metadata
Episode,
SeasonFile,
SeasonRequest,
Movie,
MovieFile,
MovieRequest,
Notification,
)

View File

@@ -8,6 +8,9 @@ Create Date: 2025-05-27 21:36:18.532068
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = "93fb07842385"
@@ -19,12 +22,191 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
# Create user table
op.create_table('user',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('email', sa.String(length=320), nullable=False),
sa.Column('hashed_password', sa.String(length=1024), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('is_superuser', sa.Boolean(), nullable=False),
sa.Column('is_verified', sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True)
# Create oauth account table
op.create_table('oauthaccount',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('oauth_name', sa.String(length=100), nullable=False),
sa.Column('access_token', sa.String(length=1024), nullable=False),
sa.Column('expires_at', sa.Integer(), nullable=True),
sa.Column('refresh_token', sa.String(length=1024), nullable=True),
sa.Column('account_id', sa.String(length=320), nullable=False),
sa.Column('account_email', sa.String(length=320), nullable=False),
sa.Column('user_id', sa.UUID(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='cascade'),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_oauthaccount_account_id'), 'oauthaccount', ['account_id'], unique=False)
op.create_index(op.f('ix_oauthaccount_oauth_name'), 'oauthaccount', ['oauth_name'], unique=False)
# Create torrent table
op.create_table('torrent',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('status', sa.Enum('finished', 'downloading', 'error', 'unknown', name='torrentstatus'), nullable=False),
sa.Column('title', sa.String(), nullable=False),
sa.Column('quality', sa.Enum('uhd', 'fullhd', 'hd', 'sd', 'unknown', name='quality'), nullable=False),
sa.Column('imported', sa.Boolean(), nullable=False),
sa.Column('hash', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
# Create indexer query result table
op.create_table('indexer_query_result',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('title', sa.String(), nullable=False),
sa.Column('download_url', sa.String(), nullable=False),
sa.Column('seeders', sa.Integer(), nullable=False),
sa.Column('flags', postgresql.ARRAY(sa.String()), nullable=True),
sa.Column('quality', sa.Enum('uhd', 'fullhd', 'hd', 'sd', 'unknown', name='quality'), nullable=False),
sa.Column('season', postgresql.ARRAY(sa.Integer()), nullable=True),
sa.Column('size', sa.BigInteger(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
# Create notification table
op.create_table('notification',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('message', sa.String(), nullable=False),
sa.Column('read', sa.Boolean(), nullable=False),
sa.Column('timestamp', sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
# Create show table
op.create_table('show',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('external_id', sa.Integer(), nullable=False),
sa.Column('metadata_provider', sa.String(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('overview', sa.String(), nullable=False),
sa.Column('year', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('external_id', 'metadata_provider')
)
# Create movie table
op.create_table('movie',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('external_id', sa.Integer(), nullable=False),
sa.Column('metadata_provider', sa.String(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('overview', sa.String(), nullable=False),
sa.Column('year', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('external_id', 'metadata_provider')
)
# Create season table
op.create_table('season',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('show_id', sa.UUID(), nullable=False),
sa.Column('number', sa.Integer(), nullable=False),
sa.Column('external_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('overview', sa.String(), nullable=False),
sa.ForeignKeyConstraint(['show_id'], ['show.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('show_id', 'number')
)
# Create movie file table
op.create_table('movie_file',
sa.Column('movie_id', sa.UUID(), nullable=False),
sa.Column('file_path_suffix', sa.String(), nullable=False),
sa.Column('quality', sa.Enum('uhd', 'fullhd', 'hd', 'sd', 'unknown', name='quality'), nullable=False),
sa.Column('torrent_id', sa.UUID(), nullable=True),
sa.ForeignKeyConstraint(['movie_id'], ['movie.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['torrent_id'], ['torrent.id'], ondelete='SET NULL'),
sa.PrimaryKeyConstraint('movie_id', 'file_path_suffix')
)
# Create movie request table
op.create_table('movie_request',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('movie_id', sa.UUID(), nullable=False),
sa.Column('wanted_quality', sa.Enum('uhd', 'fullhd', 'hd', 'sd', 'unknown', name='quality'), nullable=False),
sa.Column('min_quality', sa.Enum('uhd', 'fullhd', 'hd', 'sd', 'unknown', name='quality'), nullable=False),
sa.Column('authorized', sa.Boolean(), nullable=False),
sa.Column('requested_by_id', sa.UUID(), nullable=True),
sa.Column('authorized_by_id', sa.UUID(), nullable=True),
sa.ForeignKeyConstraint(['authorized_by_id'], ['user.id'], ondelete='SET NULL'),
sa.ForeignKeyConstraint(['movie_id'], ['movie.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['requested_by_id'], ['user.id'], ondelete='SET NULL'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('movie_id', 'wanted_quality')
)
# Create episode table
op.create_table('episode',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('season_id', sa.UUID(), nullable=False),
sa.Column('number', sa.Integer(), nullable=False),
sa.Column('external_id', sa.Integer(), nullable=False),
sa.Column('title', sa.String(), nullable=False),
sa.ForeignKeyConstraint(['season_id'], ['season.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('season_id', 'number')
)
# Create season file table
op.create_table('season_file',
sa.Column('season_id', sa.UUID(), nullable=False),
sa.Column('torrent_id', sa.UUID(), nullable=True),
sa.Column('file_path_suffix', sa.String(), nullable=False),
sa.Column('quality', sa.Enum('uhd', 'fullhd', 'hd', 'sd', 'unknown', name='quality'), nullable=False),
sa.ForeignKeyConstraint(['season_id'], ['season.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['torrent_id'], ['torrent.id'], ondelete='SET NULL'),
sa.PrimaryKeyConstraint('season_id', 'file_path_suffix')
)
# Create season request table
op.create_table('season_request',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('season_id', sa.UUID(), nullable=False),
sa.Column('wanted_quality', sa.Enum('uhd', 'fullhd', 'hd', 'sd', 'unknown', name='quality'), nullable=False),
sa.Column('min_quality', sa.Enum('uhd', 'fullhd', 'hd', 'sd', 'unknown', name='quality'), nullable=False),
sa.Column('requested_by_id', sa.UUID(), nullable=True),
sa.Column('authorized', sa.Boolean(), nullable=False),
sa.Column('authorized_by_id', sa.UUID(), nullable=True),
sa.ForeignKeyConstraint(['authorized_by_id'], ['user.id'], ondelete='SET NULL'),
sa.ForeignKeyConstraint(['requested_by_id'], ['user.id'], ondelete='SET NULL'),
sa.ForeignKeyConstraint(['season_id'], ['season.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('season_id', 'wanted_quality')
)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('season_request')
op.drop_table('season_file')
op.drop_table('episode')
op.drop_table('movie_request')
op.drop_table('movie_file')
op.drop_table('season')
op.drop_table('movie')
op.drop_table('show')
op.drop_table('notification')
op.drop_table('indexer_query_result')
op.drop_table('torrent')
op.drop_index(op.f('ix_oauthaccount_oauth_name'), table_name='oauthaccount')
op.drop_index(op.f('ix_oauthaccount_account_id'), table_name='oauthaccount')
op.drop_table('oauthaccount')
op.drop_index(op.f('ix_user_email'), table_name='user')
op.drop_table('user')
# ### end Alembic commands ###

View File

@@ -5,10 +5,10 @@ from pydantic_settings import BaseSettings
class BasicConfig(BaseSettings):
image_directory: Path = "/data/images"
tv_directory: Path = "/data/tv"
movie_directory: Path = "/data/movies"
torrent_directory: Path = "/data/torrents"
image_directory: Path = Path(__file__).parent.parent / "data" / "images"
tv_directory: Path = Path(__file__).parent.parent / "data" / "tv"
movie_directory: Path = Path(__file__).parent.parent / "data" / "movies"
torrent_directory: Path = Path(__file__).parent.parent / "data" / "torrents"
FRONTEND_URL: AnyHttpUrl = "http://localhost:3000/"
CORS_URLS: list[str] = []
DEVELOPMENT: bool = False