@@ -2563,6 +2563,59 @@ def migration_029_fix_people_episodes_table_schema(conn, db_type: str):
25632563 cursor .close ()
25642564
25652565
2566+ @register_migration ("030" , "add_user_language_preference" , "Add Language column to Users table for user-specific language preferences" , requires = ["001" ])
2567+ def migration_030_add_user_language_preference (conn , db_type : str ):
2568+ """Add Language column to Users table for user-specific language preferences"""
2569+ cursor = conn .cursor ()
2570+
2571+ try :
2572+ # Get the default language from environment variable, fallback to 'en'
2573+ default_language = os .environ .get ("DEFAULT_LANGUAGE" , "en" )
2574+
2575+ # Validate language code (basic validation)
2576+ if not default_language or len (default_language ) > 10 :
2577+ default_language = "en"
2578+
2579+ logger .info (f"Adding Language column to Users table with default '{ default_language } '" )
2580+
2581+ if db_type == 'postgresql' :
2582+ # Add Language column with default from environment variable
2583+ safe_execute_sql (cursor , f'''
2584+ ALTER TABLE "Users"
2585+ ADD COLUMN IF NOT EXISTS Language VARCHAR(10) DEFAULT '{ default_language } '
2586+ ''' , conn = conn )
2587+
2588+ # Add comment to document the column
2589+ safe_execute_sql (cursor , '''
2590+ COMMENT ON COLUMN "Users".Language IS 'ISO 639-1 language code for user interface language preference'
2591+ ''' , conn = conn )
2592+
2593+ else : # mysql/mariadb
2594+ # Check if column exists first
2595+ cursor .execute ("""
2596+ SELECT COUNT(*)
2597+ FROM INFORMATION_SCHEMA.COLUMNS
2598+ WHERE TABLE_SCHEMA = DATABASE()
2599+ AND TABLE_NAME = 'Users'
2600+ AND COLUMN_NAME = 'Language'
2601+ """ )
2602+
2603+ if cursor .fetchone ()[0 ] == 0 :
2604+ safe_execute_sql (cursor , f'''
2605+ ALTER TABLE Users
2606+ ADD COLUMN Language VARCHAR(10) DEFAULT '{ default_language } '
2607+ COMMENT 'ISO 639-1 language code for user interface language preference'
2608+ ''' , conn = conn )
2609+
2610+ logger .info (f"Successfully added Language column to Users table with default '{ default_language } '" )
2611+
2612+ except Exception as e :
2613+ logger .error (f"Error in migration 030: { e } " )
2614+ raise
2615+ finally :
2616+ cursor .close ()
2617+
2618+
25662619# ============================================================================
25672620# GPODDER SYNC MIGRATIONS
25682621# These migrations match the gpodder-api service migrations from Go code
@@ -3043,14 +3096,77 @@ def migration_105_optimize_episode_actions_performance(conn, db_type: str):
30433096 ''' , conn = conn )
30443097
30453098 logger .info ("Successfully added episode actions performance indexes" )
3046-
3099+
30473100 except Exception as e :
30483101 logger .error (f"Error in gpodder migration 105: { e } " )
30493102 raise
30503103 finally :
30513104 cursor .close ()
30523105
30533106
3107+ @register_migration ("106" , "optimize_subscription_sync_performance" , "Add missing indexes for subscription sync queries" , requires = ["103" ])
3108+ def migration_106_optimize_subscription_sync_performance (conn , db_type : str ):
3109+ """Add critical indexes for subscription sync performance to prevent AntennaPod timeouts"""
3110+ cursor = conn .cursor ()
3111+
3112+ try :
3113+ logger .info ("Adding performance indexes for subscription sync..." )
3114+
3115+ if db_type == 'postgresql' :
3116+ # Critical indexes for subscription sync performance
3117+ safe_execute_sql (cursor , '''
3118+ CREATE INDEX IF NOT EXISTS idx_gpodder_sync_subs_user_device_timestamp
3119+ ON "GpodderSyncSubscriptions"(UserID, DeviceID, Timestamp DESC)
3120+ ''' , conn = conn )
3121+
3122+ safe_execute_sql (cursor , '''
3123+ CREATE INDEX IF NOT EXISTS idx_gpodder_sync_subs_user_action_timestamp
3124+ ON "GpodderSyncSubscriptions"(UserID, Action, Timestamp DESC)
3125+ ''' , conn = conn )
3126+
3127+ safe_execute_sql (cursor , '''
3128+ CREATE INDEX IF NOT EXISTS idx_gpodder_sync_subs_podcast_url_user
3129+ ON "GpodderSyncSubscriptions"(UserID, PodcastURL, Timestamp DESC)
3130+ ''' , conn = conn )
3131+
3132+ # Optimize subscription change queries with compound index
3133+ safe_execute_sql (cursor , '''
3134+ CREATE INDEX IF NOT EXISTS idx_gpodder_sync_subs_complex_query
3135+ ON "GpodderSyncSubscriptions"(UserID, DeviceID, Action, Timestamp DESC, PodcastURL)
3136+ ''' , conn = conn )
3137+
3138+ else : # mysql/mariadb
3139+ # Critical indexes for subscription sync performance
3140+ safe_execute_sql (cursor , '''
3141+ CREATE INDEX idx_gpodder_sync_subs_user_device_timestamp
3142+ ON GpodderSyncSubscriptions(UserID, DeviceID, Timestamp DESC)
3143+ ''' , conn = conn )
3144+
3145+ safe_execute_sql (cursor , '''
3146+ CREATE INDEX idx_gpodder_sync_subs_user_action_timestamp
3147+ ON GpodderSyncSubscriptions(UserID, Action, Timestamp DESC)
3148+ ''' , conn = conn )
3149+
3150+ safe_execute_sql (cursor , '''
3151+ CREATE INDEX idx_gpodder_sync_subs_podcast_url_user
3152+ ON GpodderSyncSubscriptions(UserID, PodcastURL(255), Timestamp DESC)
3153+ ''' , conn = conn )
3154+
3155+ # Optimize subscription change queries with compound index
3156+ safe_execute_sql (cursor , '''
3157+ CREATE INDEX idx_gpodder_sync_subs_complex_query
3158+ ON GpodderSyncSubscriptions(UserID, DeviceID, Action, Timestamp DESC, PodcastURL(255))
3159+ ''' , conn = conn )
3160+
3161+ logger .info ("Successfully added subscription sync performance indexes" )
3162+
3163+ except Exception as e :
3164+ logger .error (f"Error in gpodder migration 106: { e } " )
3165+ raise
3166+ finally :
3167+ cursor .close ()
3168+
3169+
30543170if __name__ == "__main__" :
30553171 # Register all migrations and run them
30563172 register_all_migrations ()
0 commit comments