python – Safari in Big Sur does not label active downloads with “.download”

I use a custom python script to actively organize my downloads. Since moving to Big Sur, it still works 100% of the time, but about 5-10% of the time on a big download I’ll end up with multiple copies of the same file.

This is due to safari writing the file to the incoming directory, but without any indication that it is an incomplete file. (Previously it would have a .download extension, which indicates it’s incomplete).

This application is not constantly running, it is only triggered by a folder action when a file is created in the incoming directory. Previously the script would have detected the .download extension, and skips those files, allowing those to be retriggered later.

Is there a way to turn that back on? Or using python detect it is actively being downloaded?

Since the script isn’t being run continuously there isn’t a good method to compare file sizes, to see if it’s increasing. Plus, what would be the finished file size? (eg. It stopped increasing, but does that mean it’s finished? How long do we wait after it’s stopped increasing to assume it’s done?) — My gut instinct is that file sizing monitoring could be made to work, but there might be a better method to solve this.

So I’m open to any suggestions that could solve this minor pita problem.

This code was written back in python 2.2 days? I’ve been thinking about updating it to use path lib, but it works.

#!/usr/bin/env python
"""
:Module: sort_downloads
:Date: 20150-20
:Platforms: Mac (Tested under Mac OS X)
:Version: 1
:Authors:
    - Benjamin Schollnick
:Copyright (C) 2015, Benjamin Schollnick
:License - MIT,

:Description:
    This will take a starting folder (e.g. Downloads folder), and move any
    files found in the directory, to a different folder
    (e.g. Sorted_Downloads), and organize by file types (based off extensions).
    Any file type not identified by a file type will be placed into a folder
    that is dated in the mm-dd-yyyy format.

    This allows better organization, and groups together files downloaded
    (generally) in the same day.

    Under MOSX, I use launchd to run this script, any time my download folder
    is updated.

**Modules Used (Batteries Included)**:

   * os
   * sys
   * datetime
   * time
   * shutil
   * logging

**Required 3rd Party Modules**:

   * None

"""
import os, os.path
import sys
import datetime
import shutil
import logging

INBOUND_DIR = r'/Volumes/3TBDrive/support/incoming_downloads'
SORTED_DIRECTORY = r'/Volumes/4TB_Drive/sorted_downloads'


def return_datestamp(include_time=None):
    """
    Return the formatted time date string
    """
    datestamp = datetime.datetime.now()
    if not include_time:
        datestamp = str(datestamp).split(" ")(0)
    else:
        datestamp = str(datestamp).replace(":", "-")
    return datestamp


LOOKUP_TABLE = {'.TORRENT'  : "Torrents",
                '.DOWNLOAD' : None,
                '.DMG'      : "Disk Images",
                '.ISO'      : "Disk Images",
                '.ZIP'      : "Zip Files",
                '.JAR'      : "Java",
                '.SWF'      : "Flash",
                '.PDF'       : "PDF",
                '.PY'       : "Python",
                '.PYC'      : "Python",
                '.WEBLOC'   : "BookMarks",
                '.EPUB'     : "Ebooks",
                '.RAR'      : "RAR",
                '.7Z'       : "7Z",
                '.TXT'      : "Text",
                '.PAGES'    : "Pages",
                '.HTM'      : "HTML",
                '.HTML'     : "HTML",
                '.CSS'      : "StyleSheet",
                '.JS'       : "JavaScript",
                '.CSV'      : "CSV",
                '.MPG'      : "Movie",
                '.MPEG'     : "Movie",
                '.M4V'      : "Movie",
                '.MOV'      : "Movie",
                '.AVI'      : "Movie",
                '.MP4'      : "Movie",
                '.EXE'      : "Windows",
                '.MSI'      : "Windows",
                '.DOC'      : "MS Word",
                '.DOCX'     : "MS Word",
                '.XLS'      : "Excel",
                '.XLSX'     : "Excel",
                '.PKG'      : "Macintosh",
                '.APP'      : "Macintosh",
               }

def return_new_path(filename):
    """
    Args:
        filename - the Fully qualified filename

    Returns:
        The name of the folder to place the file in

    """
#    print(filename)
    output = None
    if os.path.isfile(filename):
        extension = os.path.splitext(filename)(1)
        if extension.upper() in LOOKUP_TABLE:
            output = LOOKUP_TABLE(extension.upper())
    else:
        output = None
#    print(output)
    if output == None:
        output = return_datestamp() #+ os.sep + os.path.split(filename)(1)
    return output

def main():
    """
    Args:
        None

    Returns:
        None

    """
    logger = logging.getLogger("FVS")
    loghandler = logging.FileHandler(sys.argv(0) + ".log")
    formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
    loghandler.setFormatter(formatter)
    logger.addHandler(loghandler)
    logger.setLevel(logging.WARNING)

    #
    #   Get the contents of the inbound_directory
    #
    contents_of_inbound = os.listdir(INBOUND_DIR)

    for file_x in contents_of_inbound:
        complete_filename = os.path.join(INBOUND_DIR, file_x.strip())
        if os.path.isfile(complete_filename) and not file_x.startswith(".") and 
            not file_x.lower().endswith(".part"):
            #       print return_new_path ( complete_filename )
            if os.path.getsize(complete_filename) <= 0:
                # if Filesize is zero or less, skip.  Incomplete file.
                continue
            new_path = SORTED_DIRECTORY + 
                r"/%s" % return_new_path(complete_filename)
            logger.info("Moving ... %s to %s", file_x, new_path)
            if not os.path.exists(new_path):
                logger.warning("Making sorted directory - %s", new_path)
                os.mkdir(new_path)

            if os.path.exists(os.path.join(new_path, file_x)):
                #
                #   File Already exists, rename it to prevent naming conflicts
                #
                dstamp = return_datestamp(include_time=True)
                logger.warning(
                    "%s already exists, renaming to %s-%s",
                    file_x, file_x, dstamp)
                fname, extension = os.path.splitext(complete_filename)
                new_filename = "%s-%s%s" % (fname, dstamp, extension)
                os.rename(complete_filename, new_filename)
                shutil.move(os.path.join(INBOUND_DIR, new_filename), new_path)
            else:
                #   Move the file, as is.
                if new_path.strip().upper() != SORTED_DIRECTORY.strip().upper():
                    shutil.move(complete_filename, new_path)

    if os.path.exists(INBOUND_DIR + os.sep + ".DS_Store"):
        os.remove(INBOUND_DIR + os.sep + ".DS_Store")


if __name__ == "__main__":
    main()#