@@ -843,159 +843,116 @@ def process_completed_album(album_data, failed_grab):
843843
844844
845845def monitor_downloads (grab_list , failed_grab ):
846+ MAX_FILE_RETRIES = 4 # Max requeue attempts per file for hard errors (Errored, Cancelled, etc.)
847+
846848 def delete_album (reason ):
847849 cancel_and_delete (grab_list [album_id ]["files" ])
848850 logger .info (f"{ reason } Album: { grab_list [album_id ]['title' ]} Artist: { grab_list [album_id ]['artist' ]} " )
849851 del grab_list [album_id ]
850852 failed_grab .append (lidarr .get_album (album_id ))
851853
854+ def requeue_file (album_id , file ):
855+ """Requeue a single errored file. Returns True on success, False if enqueue failed."""
856+ data_dict = [{"filename" : file ["filename" ], "size" : file ["size" ]}]
857+ logger .info (f"Download error. Requeue file: { file ['filename' ]} " )
858+ requeue = slskd_do_enqueue (file ["username" ], data_dict , file ["file_dir" ])
859+ if requeue is not None :
860+ file ["id" ] = requeue [0 ]["id" ]
861+ time .sleep (1 )
862+ slskd_download_status (grab_list [album_id ]["files" ])
863+ return True
864+ return False
865+
866+ def handle_hard_error (album_id , file , problems ):
867+ """
868+ Handle Cancelled/TimedOut/Errored/Aborted files.
869+ Returns True if the album was deleted (caller should stop processing this album).
870+ """
871+ if len (problems ) == len (grab_list [album_id ]["files" ]):
872+ delete_album ("Failed grab of" )
873+ return True
874+ file .setdefault ("retry" , 0 )
875+ file ["retry" ] += 1
876+ if file ["retry" ] > MAX_FILE_RETRIES :
877+ delete_album ("Failed grab of" )
878+ return True
879+ if not requeue_file (album_id , file ):
880+ delete_album ("Failed grab of" )
881+ return True
882+ return False
883+
884+ def handle_rejected (album_id , file , problems ):
885+ """
886+ Handle Rejected files. Returns True if the album was deleted or a requeue was
887+ attempted (caller should stop processing this album this iteration).
888+ Rejected files often indicate grab limits; we wait for all other files to reach
889+ a stable state before requeueing.
890+ """
891+ files = grab_list [album_id ]["files" ]
892+ if len (problems ) == len (files ):
893+ delete_album ("Failed grab of" )
894+ return True
895+ # Only requeue once all non-problem files have settled (no files mid-transfer).
896+ stable_states = ("Completed, Succeeded" , "Queued, Remotely" , "Queued, Locally" )
897+ accounted = sum (1 for f in files if f ["status" ]["state" ] in stable_states ) + len (problems )
898+ if accounted < len (files ):
899+ return False
900+ grab_list [album_id ].setdefault ("rejected_retries" , 0 )
901+ if grab_list [album_id ]["rejected_retries" ] >= int (len (files ) * 1.2 ):
902+ delete_album ("Failed grab of" )
903+ return True
904+ if not requeue_file (album_id , file ):
905+ delete_album ("Failed grab of" )
906+ return True
907+ grab_list [album_id ]["rejected_retries" ] += 1
908+ return True # Requeued one file; wait for next monitoring iteration
909+
852910 while True :
853- total_albums = len (grab_list )
854- # Deal with the problems.
855- # "Completed, Cancelled", Abort album as failed
856- # "Completed, TimedOut", Abort album as failed
857- # "Completed, Errored", Abort album as failed
858- # "Completed, Aborted", Abort album as failed
859- # "Completed, Rejected", Retry. Some users have a max grab count. We need to check if ALL files are Rejected first.
860- # We're going to need to drop items out of the list. So we might have to resort to enumerating the keys so we don't hit issues.
861- done_count = 0
862911 for album_id in list (grab_list .keys ()):
863- if slskd_download_status (grab_list [album_id ]["files" ]):
864- album_done , problems , queued = downloads_all_done (grab_list [album_id ]["files" ]) # Lets check to see what status the files have
865- if "count_start" not in grab_list [album_id ]:
866- grab_list [album_id ]["count_start" ] = time .time ()
867- if (time .time () - grab_list [album_id ]["count_start" ]) >= stalled_timeout : # Album is taking too long. Bail out regardless
868- delete_album ("Timeout waiting for download of" )
869- continue
870- if queued == len (grab_list [album_id ]["files" ]): # Shorter time out for whole albums in "Queued, Remotely"
871- if (time .time () - grab_list [album_id ]["count_start" ]) >= remote_queue_timeout :
872- delete_album ("Timeout waiting for download of" )
873- continue
874- done_count += album_done
875- if problems is not None :
876- logger .debug ("We got problems!" )
877- for file in problems :
878- logger .debug (f"Checking { file ['filename' ]} " )
879- match file ["status" ]["state" ]:
880- case (
881- "Completed, Cancelled" | "Completed, TimedOut" | "Completed, Errored" | "Completed, Aborted"
882- ): # Normal errors. We'll retry a few times as sometumes the error is transient
883- abort = False
884- if len (problems ) == len (grab_list [album_id ]["files" ]):
885- delete_album ("Failed grab of" )
886- break
887- for download_file in grab_list [album_id ]["files" ]:
888- if file ["filename" ] == download_file ["filename" ]:
889- if "retry" not in download_file :
890- download_file ["retry" ] = 0
891- download_file ["retry" ] += 1
892- if download_file ["retry" ] < 5 :
893- retry = download_file ["retry" ]
894- size = file ["size" ]
895- data_dict = [{"filename" : file ["filename" ], "size" : size }]
896- logger .info (f"Download error. Requeue file: { file ['filename' ]} " )
897- requeue = slskd_do_enqueue (
898- file ["username" ],
899- data_dict ,
900- file ["file_dir" ],
901- )
902- if requeue is not None :
903- download_file ["id" ] = requeue [0 ]["id" ]
904- download_file ["retry" ] = retry
905- time .sleep (1 )
906- _ = slskd_download_status (grab_list [album_id ]["files" ]) # Refresh the status of the files to prevent issues.
907- else :
908- delete_album ("Failed grab of" )
909- abort = True # Move to the next album so we don't block or overload a remote user
910- break
911- else :
912- # Delete from album list add to failures
913- delete_album ("Failed grab of" )
914- abort = True # As above.
915- break
916- if abort :
917- break
918- case "Completed, Rejected" :
919- # Do a measured retry. This is often a soft failure due to grab limits. Check if any files worked then go from there.
920- # This needs a recode. But it works for now.
921- # In the recode we need to test to see if we are getting multiple albums from the same user and temper our retries based on
922- # those other album(s) completing.
923- # If we aren't in that condition we need to fall back to per file retry counts as files will also be rejected if the file is
924- # too long or too short based on the share record. This can happen when people re-tag media but don't rescan media.
925- # Also I've seen cases of single files out of a set being in the "not shared" category.
926- if len (problems ) == len (grab_list [album_id ]["files" ]):
927- delete_album ("Failed grab of" ) # They are all rejected. Usually this happens because of misconfigurations. Files appear in search but aren't shared.
928- break
929- else :
930- if "rejected_retries" not in grab_list [album_id ]:
931- grab_list [album_id ]["rejected_retries" ] = 0
932- working_count = len (grab_list [album_id ]["files" ]) - len (problems )
933- for gfile in grab_list [album_id ]["files" ]:
934- if gfile ["status" ]["state" ] in [
935- "Completed, Succeeded" ,
936- "Queued, Remotely" ,
937- "Queued, Locally" ,
938- ]:
939- working_count -= 1
940- if working_count == 0 :
941- if grab_list [album_id ]["rejected_retries" ] < int (len (grab_list [album_id ]["files" ]) * 1.2 ): # Little bit of wiggle room here
942- abort = False
943- for gfile in grab_list [album_id ]["files" ]:
944- if gfile ["filename" ] == file ["filename" ]:
945- size = file ["size" ]
946- data_dict = [
947- {
948- "filename" : file ["filename" ],
949- "size" : size ,
950- }
951- ]
952- logger .info (f"Download error. Requeue file: { file ['filename' ]} " )
953- requeue = slskd_do_enqueue (
954- file ["username" ],
955- data_dict ,
956- file ["file_dir" ],
957- )
958- if requeue is not None :
959- gfile ["id" ] = requeue [0 ]["id" ]
960- grab_list [album_id ]["rejected_retries" ] += 1
961- _ = slskd_download_status (grab_list [album_id ]["files" ])
962- abort = True
963- break
964- else :
965- cancel_and_delete (grab_list [album_id ]["files" ])
966- logger .info (f"Failed grab of Album: { grab_list [album_id ]['title' ]} Artist: { grab_list [album_id ]['artist' ]} " )
967- del grab_list [album_id ]
968- failed_grab .append (lidarr .get_album (album_id )) # Not sure if returns an array or not
969- abort = True
970- break
971- if abort :
972- break
973- else :
974- delete_album ("Failed grab of" )
975- break
976- case _:
977- logger .error (
978- "Not sure how I got here. This shouldn't be possible for problem files!"
979- ) # This really should be impossible to reach. But is required to round out the case statement.
980- else :
981- if album_done :
982- album_data = grab_list [album_id ]
983- album_data ["album_id" ] = album_id
984- logger .info (f"Completed download of Album: { album_data ['title' ]} Artist: { album_data ['artist' ]} " )
985- process_completed_album (album_data , failed_grab )
986- del grab_list [album_id ]
912+ if not slskd_download_status (grab_list [album_id ]["files" ]):
913+ grab_list [album_id ]["error_count" ] = grab_list [album_id ].get ("error_count" , 0 ) + 1
914+ continue
987915
988- else :
989- if "error_count" not in grab_list [album_id ]:
990- grab_list [album_id ]["error_count" ] = 0
991- grab_list [album_id ]["error_count" ] += 1
992- # I dunno. slskd might be broken? Or the user deleted things? I've never seen this so I have no idea what we should do here. It most likely would mean SLSKD is down.
993- # So we probably want to abort everything because cleanup would be impossible.
916+ album_done , problems , queued = downloads_all_done (grab_list [album_id ]["files" ])
917+
918+ grab_list [album_id ].setdefault ("count_start" , time .time ())
919+ elapsed = time .time () - grab_list [album_id ]["count_start" ]
994920
995- if len (grab_list ) < 1 : # We remove items from the grab list once they are downloaded or aborted. So when there are no grabs left, we are done!
921+ if elapsed >= stalled_timeout :
922+ delete_album ("Timeout waiting for download of" )
923+ continue
924+ if queued == len (grab_list [album_id ]["files" ]) and elapsed >= remote_queue_timeout :
925+ delete_album ("Timeout waiting for download of" )
926+ continue
927+
928+ if album_done :
929+ album_data = grab_list [album_id ]
930+ album_data ["album_id" ] = album_id
931+ logger .info (f"Completed download of Album: { album_data ['title' ]} Artist: { album_data ['artist' ]} " )
932+ process_completed_album (album_data , failed_grab )
933+ del grab_list [album_id ]
934+ continue
935+
936+ if problems :
937+ logger .debug ("Files with errors detected." )
938+ for file in problems :
939+ if album_id not in grab_list :
940+ break
941+ logger .debug (f"Checking { file ['filename' ]} " )
942+ state = file ["status" ]["state" ]
943+ if state in ("Completed, Cancelled" , "Completed, TimedOut" , "Completed, Errored" , "Completed, Aborted" ):
944+ if handle_hard_error (album_id , file , problems ):
945+ break
946+ elif state == "Completed, Rejected" :
947+ if handle_rejected (album_id , file , problems ):
948+ break
949+ else :
950+ logger .error (f"Unexpected file state in problem list: { state } " )
951+
952+ if not grab_list :
996953 break
997954
998- time .sleep (5 ) # Wait for things to progress and start the checks again.
955+ time .sleep (5 )
999956
1000957
1001958def grab_most_wanted (albums ):
0 commit comments