@@ -3943,6 +3943,145 @@ def migration_037_fix_shared_episodes_schema(conn, db_type: str):
39433943 cursor .close ()
39443944
39453945
3946+ @register_migration ("107" , "fix_gpodder_episode_actions_antennapod" , "Fix existing GPodder episode actions to include Started and Total fields for AntennaPod compatibility" , requires = ["103" ])
3947+ def migration_107_fix_gpodder_episode_actions (conn , db_type : str ):
3948+ """
3949+ Fix existing GPodder episode actions to be compatible with AntennaPod.
3950+ AntennaPod requires all play actions to have Started, Position, and Total fields.
3951+ This migration adds those fields by joining with the Episodes table to get duration.
3952+ """
3953+ cursor = conn .cursor ()
3954+
3955+ try :
3956+ logger .info ("Starting GPodder episode actions fix for AntennaPod compatibility..." )
3957+
3958+ if db_type == "postgresql" :
3959+ # First, count how many actions need fixing
3960+ cursor .execute ("""
3961+ SELECT COUNT(*)
3962+ FROM "GpodderSyncEpisodeActions"
3963+ WHERE action = 'play'
3964+ AND (started IS NULL OR total IS NULL OR started < 0 OR total <= 0)
3965+ """ )
3966+ count_result = cursor .fetchone ()
3967+ actions_to_fix = count_result [0 ] if count_result else 0
3968+
3969+ logger .info (f"Found { actions_to_fix } play actions that need fixing (PostgreSQL)" )
3970+
3971+ if actions_to_fix > 0 :
3972+ # Update from Episodes table join
3973+ logger .info ("Updating episode actions with duration from Episodes table..." )
3974+ cursor .execute ("""
3975+ UPDATE "GpodderSyncEpisodeActions" AS gsa
3976+ SET
3977+ started = 0,
3978+ total = e.episodeduration
3979+ FROM "Episodes" e
3980+ WHERE gsa.action = 'play'
3981+ AND gsa.episodeurl = e.episodeurl
3982+ AND e.episodeduration IS NOT NULL
3983+ AND e.episodeduration > 0
3984+ AND (gsa.started IS NULL OR gsa.total IS NULL OR gsa.started < 0 OR gsa.total <= 0)
3985+ """ )
3986+ conn .commit ()
3987+
3988+ # Fallback: use Position as Total for episodes not in Episodes table
3989+ logger .info ("Updating remaining actions using Position as fallback for Total..." )
3990+ cursor .execute ("""
3991+ UPDATE "GpodderSyncEpisodeActions"
3992+ SET
3993+ started = 0,
3994+ total = COALESCE(position, 1)
3995+ WHERE action = 'play'
3996+ AND (started IS NULL OR total IS NULL OR started < 0 OR total <= 0)
3997+ AND position IS NOT NULL
3998+ AND position > 0
3999+ """ )
4000+ conn .commit ()
4001+
4002+ # Final cleanup: set minimal valid values for any remaining invalid actions
4003+ logger .info ("Final cleanup: setting minimal valid values for remaining invalid actions..." )
4004+ cursor .execute ("""
4005+ UPDATE "GpodderSyncEpisodeActions"
4006+ SET
4007+ started = 0,
4008+ total = 1
4009+ WHERE action = 'play'
4010+ AND (started IS NULL OR total IS NULL OR started < 0 OR total <= 0)
4011+ """ )
4012+ conn .commit ()
4013+
4014+ # Verify the fix
4015+ cursor .execute ("""
4016+ SELECT COUNT(*)
4017+ FROM "GpodderSyncEpisodeActions"
4018+ WHERE action = 'play'
4019+ AND (started IS NULL OR total IS NULL OR started < 0 OR total <= 0 OR position <= 0)
4020+ """ )
4021+ remaining_result = cursor .fetchone ()
4022+ remaining_broken = remaining_result [0 ] if remaining_result else 0
4023+
4024+ logger .info (f"Fixed { actions_to_fix - remaining_broken } episode actions (PostgreSQL)" )
4025+ if remaining_broken > 0 :
4026+ logger .warning (f"{ remaining_broken } actions still have invalid fields - these may need manual review" )
4027+ else :
4028+ logger .info ("No actions need fixing (PostgreSQL)" )
4029+
4030+ else : # MySQL/MariaDB
4031+ # First, count how many actions need fixing
4032+ cursor .execute ("""
4033+ SELECT COUNT(*)
4034+ FROM GpodderSyncEpisodeActions
4035+ WHERE Action = 'play'
4036+ AND (Started IS NULL OR Total IS NULL OR Started < 0 OR Total <= 0)
4037+ """ )
4038+ count_result = cursor .fetchone ()
4039+ actions_to_fix = count_result [0 ] if count_result else 0
4040+
4041+ logger .info (f"Found { actions_to_fix } play actions that need fixing (MySQL)" )
4042+
4043+ if actions_to_fix > 0 :
4044+ # MySQL: Update using JOIN
4045+ logger .info ("Updating episode actions with duration from Episodes table..." )
4046+ cursor .execute ("""
4047+ UPDATE GpodderSyncEpisodeActions AS gsa
4048+ LEFT JOIN Episodes e ON gsa.EpisodeURL = e.EpisodeURL
4049+ AND e.EpisodeDuration IS NOT NULL
4050+ AND e.EpisodeDuration > 0
4051+ SET
4052+ gsa.Started = 0,
4053+ gsa.Total = COALESCE(e.EpisodeDuration, gsa.Position, 1)
4054+ WHERE gsa.Action = 'play'
4055+ AND (gsa.Started IS NULL OR gsa.Total IS NULL OR gsa.Started < 0 OR gsa.Total <= 0)
4056+ """ )
4057+ conn .commit ()
4058+
4059+ # Verify the fix
4060+ cursor .execute ("""
4061+ SELECT COUNT(*)
4062+ FROM GpodderSyncEpisodeActions
4063+ WHERE Action = 'play'
4064+ AND (Started IS NULL OR Total IS NULL OR Started < 0 OR Total <= 0 OR Position <= 0)
4065+ """ )
4066+ remaining_result = cursor .fetchone ()
4067+ remaining_broken = remaining_result [0 ] if remaining_result else 0
4068+
4069+ logger .info (f"Fixed { actions_to_fix - remaining_broken } episode actions (MySQL)" )
4070+ if remaining_broken > 0 :
4071+ logger .warning (f"{ remaining_broken } actions still have invalid fields - these may need manual review" )
4072+ else :
4073+ logger .info ("No actions need fixing (MySQL)" )
4074+
4075+ logger .info ("GPodder episode actions fix migration completed successfully" )
4076+ logger .info ("AntennaPod should now be able to sync episode actions correctly" )
4077+
4078+ except Exception as e :
4079+ logger .error (f"Error in migration 107: { e } " )
4080+ raise
4081+ finally :
4082+ cursor .close ()
4083+
4084+
39464085if __name__ == "__main__" :
39474086 # Register all migrations and run them
39484087 register_all_migrations ()
0 commit comments