Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request - Flag to output files that failed #64

Open
toblaroni opened this issue Nov 28, 2024 · 1 comment
Open

Request - Flag to output files that failed #64

toblaroni opened this issue Nov 28, 2024 · 1 comment

Comments

@toblaroni
Copy link

Would be nice to have the option to save a list of files that failed to download. I'm aware this is probably doable using the --on-complete flag.

@MXC1
Copy link

MXC1 commented Dec 7, 2024

I wrote a Python script to write failed downloads to a file and call it using the on-complete argument.

Here is the code if you want to do the same.

Edit 27/12/2024: The script will now also remove the track from the list if it is successfully downloaded at a future date. It also handles concurrent writing better using a file lock.

//log_failed_message.py
import sys
import csv
import os
import portalocker
import traceback
from datetime import datetime
from log_error_to_file import log_error_to_file

def log_debug(message):
    """Logs debug messages to the console."""
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    print(f"[{timestamp}] {message}")

def process_download(file_details):
    try:
        # Extract details
        path = file_details.get('path', '')
        title = file_details.get('title', '')
        artist = file_details.get('artist', '')
        album = file_details.get('album', '')
        uri = file_details.get('uri', '')
        length = file_details.get('length', '')
        failure_reason = file_details.get('failure-reason', '')
        state = file_details.get('state', '')

        log_debug(f"Extracted details: {file_details}")

        # Path to the CSV log file
        log_dir = 'E:/Music/sldl'
        failed_downloads_csv = os.path.join(log_dir, 'failed_downloads.csv')

        # Ensure the directory exists
        os.makedirs(log_dir, exist_ok=True)
        log_debug(f"Ensured directory exists: {log_dir}")

        # Create the file if it doesn't exist
        open(failed_downloads_csv, 'a', encoding='utf-8').close()

        # Open the file with portalocker to enforce cross-process locking
        with open(failed_downloads_csv, 'r+', newline='', encoding='utf-8') as f:
            # try:
                portalocker.lock(f, portalocker.LOCK_EX)  # Exclusive lock
                remove_successful_download_from_failed_downloads_csv(
                    f, state, title, artist, failed_downloads_csv
                )
                append_failed_download_if_not_already_in_file(
                    f, path, title, artist, album, uri, length, failure_reason, state, failed_downloads_csv
                )
            # finally:
            #     portalocker.unlock(f)  # Explicitly release lock in case of error
    except Exception as e:
        exception_details = traceback.format_exc()
        error_message = "File details:\n" + str(file_details) + "\n" + exception_details
        log_error_to_file(__file__, f"Error in log_failed_downloads: {error_message}")

        
def remove_successful_download_from_failed_downloads_csv(f,state,title,artist,failed_downloads_csv):
    # Read all rows into a list
    f.seek(0)
    reader = csv.reader(f)
    rows = list(reader)

    # Only filter and update rows if the current state is 'Downloaded' or 'Exists'
    if state in ['Downloaded', 'Exists']:
        updated_rows = [
            row for row in rows
            if not (row[1] == title and row[2] == artist)
        ]

        # Move file pointer to the beginning and truncate the file
        f.seek(0)
        f.truncate()

        # Write updated rows back to the file
        writer = csv.writer(f)
        writer.writerows(updated_rows)
        log_debug(f"Removed successful download from {failed_downloads_csv}")
        
def append_failed_download_if_not_already_in_file(f, path, title, artist, album, uri, length, failure_reason, state, failed_downloads_csv):
    f.seek(0)
    reader = csv.reader(f)

    # Check if the file already exists in the log
    file_exists = any(row[1] == title and row[2] == artist for row in reader)

    # If the file doesn't exist, append the details
    if not file_exists and state in ['Failed', 'NotFoundLastTime']:
        writer = csv.writer(f)
        writer.writerow([path, title, artist, album, uri, length, failure_reason, state])
        log_debug(f"Logged details to {failed_downloads_csv}")

    else:
        log_debug(f"Skipped logging for file: {path}, state: {state}")

if __name__ == "__main__":
    try:
        # Log the raw arguments for debugging
        log_debug(f"Raw arguments: {sys.argv}")

        # The file details are passed as arguments
        file_details = dict(zip(
            ['path', 'title', 'artist', 'album', 'uri', 'length', 'failure-reason', 'state'],
            sys.argv[1:]
        ))

        process_download(file_details)
    except Exception as e:
        log_error_to_file(__file__,f"Fatal error in main: {e}")
    finally:
        # input()
        pass  # No need for interactive input
on-complete = s:pythonw E:/Music/sldl/scripts/log_failed_download.py "{path}" "{title}" "{artist}" "{album}" "{uri}" "{length}" "{failure-reason}" "{state}"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants