From 2ba4ff31134a9916d6dc0673d66629b30c473a26 Mon Sep 17 00:00:00 2001 From: Junwei Zhao Date: Thu, 30 Oct 2025 22:54:20 +1100 Subject: [PATCH] Fix docker --- .../01209c730b33_initial_migration.py | 76 +++++++++++++++---- ..._populate_file_location_and_file_format.py | 51 ------------- .../c1fd223a3556_add_file_tracking_columns.py | 44 ----------- backend/app/db/session.py | 9 ++- backend/main.py | 5 +- docker-compose.yml | 1 + 6 files changed, 74 insertions(+), 112 deletions(-) delete mode 100644 backend/alembic/versions/49793394b596_populate_file_location_and_file_format.py delete mode 100644 backend/alembic/versions/c1fd223a3556_add_file_tracking_columns.py diff --git a/backend/alembic/versions/01209c730b33_initial_migration.py b/backend/alembic/versions/01209c730b33_initial_migration.py index fd294ec..19fb9fb 100644 --- a/backend/alembic/versions/01209c730b33_initial_migration.py +++ b/backend/alembic/versions/01209c730b33_initial_migration.py @@ -21,6 +21,46 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: """Upgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### + # Create all tables + op.create_table('music', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('title', sa.String(), nullable=True), + sa.Column('artist', sa.String(), nullable=True), + sa.Column('album', sa.String(), nullable=True), + sa.Column('duration', sa.Float(), nullable=True), + sa.Column('file_path', sa.String(), nullable=True), + sa.Column('file_size', sa.Integer(), nullable=True), + sa.Column('file_format', sa.String(), nullable=True), + sa.Column('file_location', sa.String(), nullable=True), + sa.Column('file_exists', sa.Boolean(), nullable=True), + sa.Column('source_url', sa.String(), nullable=True), + sa.Column('source_type', sa.String(), nullable=True), + sa.Column('thumbnail', sa.String(), nullable=True), + sa.Column('lyrics', sa.Text(), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('last_scanned_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + with op.batch_alter_table('music', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_music_artist'), ['artist'], unique=False) + batch_op.create_index(batch_op.f('ix_music_id'), ['id'], unique=False) + batch_op.create_index(batch_op.f('ix_music_title'), ['title'], unique=False) + batch_op.create_unique_constraint('uq_music_file_path', ['file_path']) + + op.create_table('playlists', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(), nullable=True), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('thumbnail', sa.String(), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + with op.batch_alter_table('playlists', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_playlists_id'), ['id'], unique=False) + batch_op.create_index(batch_op.f('ix_playlists_name'), ['name'], unique=True) + op.create_table('app_settings', sa.Column('id', sa.Integer(), nullable=False), sa.Column('key', sa.String(), nullable=True), @@ -32,27 +72,37 @@ def upgrade() -> None: batch_op.create_index(batch_op.f('ix_app_settings_id'), ['id'], unique=False) batch_op.create_index(batch_op.f('ix_app_settings_key'), ['key'], unique=True) - with op.batch_alter_table('music', schema=None) as batch_op: - batch_op.add_column(sa.Column('file_format', sa.String(), nullable=True)) - batch_op.add_column(sa.Column('file_location', sa.String(), nullable=True)) - batch_op.add_column(sa.Column('file_exists', sa.Boolean(), nullable=True)) - batch_op.add_column(sa.Column('last_scanned_at', sa.DateTime(), nullable=True)) - + op.create_table('playlist_music', + sa.Column('playlist_id', sa.Integer(), nullable=False), + sa.Column('music_id', sa.Integer(), nullable=False), + sa.Column('position', sa.Integer(), nullable=True), + sa.Column('added_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['music_id'], ['music.id'], ), + sa.ForeignKeyConstraint(['playlist_id'], ['playlists.id'], ), + sa.PrimaryKeyConstraint('playlist_id', 'music_id') + ) # ### end Alembic commands ### def downgrade() -> None: """Downgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('music', schema=None) as batch_op: - batch_op.drop_column('last_scanned_at') - batch_op.drop_column('file_exists') - batch_op.drop_column('file_location') - batch_op.drop_column('file_format') - + op.drop_table('playlist_music') + with op.batch_alter_table('app_settings', schema=None) as batch_op: batch_op.drop_index(batch_op.f('ix_app_settings_key')) batch_op.drop_index(batch_op.f('ix_app_settings_id')) - op.drop_table('app_settings') + + with op.batch_alter_table('playlists', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_playlists_name')) + batch_op.drop_index(batch_op.f('ix_playlists_id')) + op.drop_table('playlists') + + with op.batch_alter_table('music', schema=None) as batch_op: + batch_op.drop_constraint('uq_music_file_path', type_='unique') + batch_op.drop_index(batch_op.f('ix_music_title')) + batch_op.drop_index(batch_op.f('ix_music_id')) + batch_op.drop_index(batch_op.f('ix_music_artist')) + op.drop_table('music') # ### end Alembic commands ### diff --git a/backend/alembic/versions/49793394b596_populate_file_location_and_file_format.py b/backend/alembic/versions/49793394b596_populate_file_location_and_file_format.py deleted file mode 100644 index 4f4d629..0000000 --- a/backend/alembic/versions/49793394b596_populate_file_location_and_file_format.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Populate file_location and file_format - -Revision ID: 49793394b596 -Revises: c1fd223a3556 -Create Date: 2025-10-30 21:56:40.131970 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '49793394b596' -down_revision: Union[str, Sequence[str], None] = 'c1fd223a3556' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # Populate file_location from MUSIC_DIR + file_path for existing records - # Note: This uses SQLite-specific syntax - op.execute(""" - UPDATE music - SET file_location = './data/music/' || file_path - WHERE file_location IS NULL OR file_location = '' - """) - - # Populate file_format from file_path extension - op.execute(""" - UPDATE music - SET file_format = LOWER( - CASE - WHEN file_path LIKE '%.mp3' THEN 'mp3' - WHEN file_path LIKE '%.m4a' THEN 'm4a' - WHEN file_path LIKE '%.flac' THEN 'flac' - WHEN file_path LIKE '%.wav' THEN 'wav' - WHEN file_path LIKE '%.ogg' THEN 'ogg' - WHEN file_path LIKE '%.opus' THEN 'opus' - ELSE SUBSTR(file_path, INSTR(file_path, '.') + 1) - END - ) - WHERE file_format IS NULL OR file_format = '' - """) - - -def downgrade() -> None: - """Downgrade schema.""" - pass diff --git a/backend/alembic/versions/c1fd223a3556_add_file_tracking_columns.py b/backend/alembic/versions/c1fd223a3556_add_file_tracking_columns.py deleted file mode 100644 index e7ba1f7..0000000 --- a/backend/alembic/versions/c1fd223a3556_add_file_tracking_columns.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Add file tracking columns - -Revision ID: c1fd223a3556 -Revises: 01209c730b33 -Create Date: 2025-10-30 21:48:22.894324 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = 'c1fd223a3556' -down_revision: Union[str, Sequence[str], None] = '01209c730b33' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('music', schema=None) as batch_op: - batch_op.add_column(sa.Column('file_format', sa.String(), nullable=True)) - batch_op.add_column(sa.Column('file_location', sa.String(), nullable=True)) - batch_op.add_column(sa.Column('file_exists', sa.Boolean(), nullable=True)) - batch_op.add_column(sa.Column('last_scanned_at', sa.DateTime(), nullable=True)) - - # Set default value for existing records - op.execute("UPDATE music SET file_exists = 1 WHERE file_exists IS NULL") - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('music', schema=None) as batch_op: - batch_op.drop_column('last_scanned_at') - batch_op.drop_column('file_exists') - batch_op.drop_column('file_location') - batch_op.drop_column('file_format') - - # ### end Alembic commands ### diff --git a/backend/app/db/session.py b/backend/app/db/session.py index b1c302d..9254a0e 100644 --- a/backend/app/db/session.py +++ b/backend/app/db/session.py @@ -29,5 +29,12 @@ async def get_db(): async def init_db(): + """Initialize database connection pool + + Note: Table creation is handled by Alembic migrations. + This function just ensures the connection pool is ready. + """ + # Just test the connection async with engine.begin() as conn: - await conn.run_sync(Base.metadata.create_all) + # Connection pool is ready + pass diff --git a/backend/main.py b/backend/main.py index 11063ee..0b920d1 100644 --- a/backend/main.py +++ b/backend/main.py @@ -80,9 +80,8 @@ async def lifespan(app: FastAPI): os.makedirs(settings.LOCAL_MUSIC_DIR, exist_ok=True) print(f"✅ LOCAL_MUSIC_DIR created: {settings.LOCAL_MUSIC_DIR}") - # Run database migrations - await run_migrations() - + # Note: Database migrations are run by start.sh before uvicorn starts + # We just initialize the connection pool here await init_db() # Start scheduler diff --git a/docker-compose.yml b/docker-compose.yml index 8907871..aab7006 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,7 @@ services: - "8000:8000" volumes: - ./data:/app/data + - /Users/junv/Music/网易云音乐:/app/data/local-music environment: - PYTHONUNBUFFERED=1 - PYTHONPATH=/app/backend