Hello to all!

I've made this script the other day, and I need some input from you telling me how could I improve the code / style. There aren't problems with the code (it does the job) but I want it more organized and "simple" so that in 1 week I still remember what the hell was I thinking!

The problem was: I needed a program that moves for me some files, particularly, some video files, from one directory to another. All the video files are about Tv Shows, so they have this format:

Showname.SxxExx.Episode Title.mp4 (where S = season and E = episode)

But that's not it. I wanted also to create the apropriate folders for every Tv Show i moved in another directory. So if the directory "Show Name" doesn't exists, my script creates it and then checks if the season folder exists inside. If it doesn't, it creates that too and finaly it moves the file.

Here is the code:

import string
import glob
import os
import shutil
import re
import sys
import hashlib
#############################################
from colorama import Fore, Back, init

init(autoreset=True)

#print(Fore.RED + 'some red text')
#print(Back.GREEN + 'and with a green background')
#print(Style.DIM + 'and in dim text')
#print(Fore.RESET + Back.RESET + Style.RESET_ALL)
#print('back to normal now')

#Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
#Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
#Style: DIM, NORMAL, BRIGHT, RESET_ALL

# https://pypi.python.org/pypi/colorama#downloads

#############################################


class TvShow(object):
    def __init__(self, path):
        self.path = path  # The complete path to the video file. Example: D:\Movies\Somefolder\showname.showseason.avi
        self.show_name = None  # This will hold the show's name. Example: "xFactor" (shit program btw)
        self.show_season = None  # This will hold the show's season number. Example: "S06" for season 6.
        self.show_video_file_name = None  # The complete file name. Example: "Showname.SxxExx.Xvid.avi"

    def create(self):
        #First we split the path by "\" and put it in a list. REMEMBER: Linux uses slash and not backslash.
        splitted_path_by_slash = string.split(self.path, "\\")

        #Then we get the *last* item from the list, which will hold the complete name of our video file
        #and put it into the TvShow's variable.
        self.show_video_file_name = splitted_path_by_slash[-1]

        #Now we split the name by dots (.) to get the show's name and season
        splitted_video_file_name_by_dot = string.split(self.show_video_file_name, ".")

        #Here we finaly assign the show name to our variable.
        self.show_name = splitted_video_file_name_by_dot[0]

        #Here we get the "SxxExx" section and put it in a string
        tvshow_season_and_episode = splitted_video_file_name_by_dot[1]

        #Now we need to split that string to extract only the "Sxx" part.
        temp = string.split(tvshow_season_and_episode, "E")

        #Finaly we assign the value of season to it's variable.
        self.show_season = temp[0]


def checkIfValidFolder(destination, source):
    err = 0
    if os.path.exists(destination) is True and os.path.exists(source) is True:
        if os.listdir(source) == []:
            print("The source folder exists, but directory is empty. No videos?")
        else:
            for tvshowname in os.listdir(source):
                if re.match(r'(?P<name>[^.]+)\.S(?P<S>\d+)E(?P<E>\d+)\.(?P<title>[^.]+)\.(?P<ext>.*)', tvshowname) is None: #Regex explanation: http://regex101.com/r/gG9nK2
                    print("The following file is not valid: " + tvshowname)
                    err += 1

            if err == 0:
                print("Source and Destination folders exist! Nice one!")
                return True
            else:
                print("Errors were found")
                return False
                #do stuff...
    else:
        if os.path.exists(source) is False:
            print "The " + Fore.RED + "SOURCE" + Fore.RESET + " folder, " + str(source) + ", does not appear to exist."
        if os.path.exists(destination) is False:
            print "The " + Fore.RED + "DESTINATION" + Fore.RESET + " folder, " + str(
                destination) + ", does not appear to exist."



def md5_for_file(path, block_size=256*128, hr=True):
    '''
    Block size directly depends on the block size of your filesystem
    to avoid performances issues
    Here I have blocks of 4096 octets (Default NTFS)
    '''
    md5 = hashlib.md5()
    with open(path,'rb') as f:
        for chunk in iter(lambda: f.read(block_size), b''):
             md5.update(chunk)
    if hr:
        return md5.hexdigest()
    return md5.digest()


def summary(folders_created, subfolders_created):
    print("")
    print("------------------------------------------------------------------")
    if not folders_created:
        print ("No new show has been added to your collection master!")
        print("")
    else:
        print ("")
        print("New show(s) added:")
        for item in folders_created:
            print item

    print("")
    print("------------------------------------------------------------------")
    if not subfolders_created:
        print ("No new season has been added master!")
        print("")
    else:
        print ("")
        print("New season(s) added:")
        for item in subfolders_created:
            print item


def copyThisShit(from_dir, to_dir):
    i = 0
    j = 0
    new_fold = []
    new_sub_folder = []

    for filename in sorted(glob.glob(os.path.join(from_dir + "\\", '*.*'))):
        show = TvShow(filename)
        show.create()
        dest_show_folder = to_dir + "\\" + show.show_name
        if os.path.exists(dest_show_folder):
            dest_show_folder_season = dest_show_folder + "\\" + show.show_season
            if os.path.exists(dest_show_folder_season):
                if os.path.isfile(dest_show_folder_season + "\\" + show.show_video_file_name):
                    print(
                    "The file " + dest_show_folder + "\\" + show.show_season + "\\" + Back.GREEN + show.show_video_file_name + Back.RESET + " already exists. I'll SKIP it master")

                else:
                    print(
                    "The file " + dest_show_folder + "\\" + Back.RED + show.show_video_file_name + Back.RESET + " DOESN'T exist. I'll COPY it master")
                    #shutil.copy(filename, dest_show_folder_season)
                    copy_with_prog(filename, dest_show_folder_season + "\\" + show.show_video_file_name)
                    print("")
            else:
                print(
                "The subfolder " + dest_show_folder + "\\" + Back.RED + show.show_season + Back.RESET + " DOESN'T exist. I'll create it for you master")

                new_sub_folder.insert(i, dest_show_folder + "\\" + Fore.MAGENTA + show.show_season + Fore.RESET)
                i += 1

                os.mkdir(dest_show_folder_season)
                print ("The file " + dest_show_folder_season + "\\" + Back.RED + show.show_video_file_name + Back.RESET + " DOESN'T exist. I'll COPY it master")
                copy_with_prog(filename, dest_show_folder_season + "\\" + show.show_video_file_name)
                print("")
                #shutil.copy(filename, dest_show_folder_season)
        else:
            print (
            "The directory " + to_dir + "\\" + Back.RED + show.show_name + Back.RESET + " DOESN'T exists. I'll create it for you master")
            new_fold.insert(j, to_dir + "\\" + Fore.MAGENTA + show.show_name + Fore.RESET)
            j += 1
            i += 1
            new_sub_folder.insert(i, dest_show_folder + "\\" + Fore.MAGENTA + show.show_season + Fore.RESET)
            os.mkdir(dest_show_folder)
            print("The subfolder " + dest_show_folder + "\\" + Back.RED + show.show_season + Back.RESET + " DOESN'T exist. I'll create it for you master")
            os.mkdir(dest_show_folder + "\\" + show.show_season)
            dest_show_folder_season = dest_show_folder + "\\" + show.show_season
            print ("The file " + dest_show_folder_season + "\\" + Back.RED + show.show_video_file_name + Back.RESET + " DOESN'T exist. I'll COPY it master")
            copy_with_prog(filename, dest_show_folder_season + "\\" + show.show_video_file_name)
            print("")
            #shutil.copy(filename, dest_show_folder + "\\" + show.show_season)

    summary(new_fold, new_sub_folder)


def checkInput():
    valid = False
    while valid is False:
        source_folder = raw_input("Source Video Directory?")
        destination_folder = raw_input("Destination Video Directory?")
        if checkIfValidFolder(destination_folder, source_folder) is True:
            valid = True
            print ("Everything Looks good!")
            copyThisShit(source_folder, destination_folder)



class ProgressBar:
    def __init__(self, minValue = 0, maxValue = 10, totalWidth=12):
        self.progBar = "[]"   # This holds the progress bar string
        self.min = minValue
        self.max = maxValue
        self.span = maxValue - minValue
        self.width = totalWidth
        self.amount = 0       # When amount == max, we are 100% done
        self.updateAmount(0)  # Build progress bar string

    def updateAmount(self, newAmount = 0):
        if newAmount < self.min: newAmount = self.min
        if newAmount > self.max: newAmount = self.max
        self.amount = newAmount

        # Figure out the new percent done, round to an integer
        diffFromMin = float(self.amount - self.min)
        percentDone = (diffFromMin / float(self.span)) * 100.0
        percentDone = round(percentDone)
        percentDone = int(percentDone)

        # Figure out how many hash bars the percentage should be
        allFull = self.width - 2
        numHashes = (percentDone / 100.0) * allFull
        numHashes = int(round(numHashes))

        # build a progress bar with hashes and spaces
        self.progBar = "[" + '#'*numHashes + ' '*(allFull-numHashes) + "]"

        # figure out where to put the percentage, roughly centered
        percentPlace = (len(self.progBar) / 2) - len(str(percentDone))
        percentString = str(percentDone) + "%"

        # slice the percentage into the bar
        self.progBar = (self.progBar[0:percentPlace] + Back.BLUE + percentString
                        + Back.RESET + self.progBar[percentPlace+len(percentString):])

    def __str__(self):
        return str(self.progBar)

def copy_with_prog(src_file, dest_file, overwrite = False, block_size = 256*128):
    if not overwrite:
        if os.path.isfile(dest_file):
            raise IOError("File exists, not overwriting")

    # Open src and dest files, get src file size
    src = open(src_file, "rb")
    dest = open(dest_file, "wb")

    src_size = os.stat(src_file).st_size

    # Set progress bar
    prgb = ProgressBar(totalWidth = 79, maxValue = src_size)

    # Start copying file
    cur_block_pos = 0 # a running total of current position
    while True:
        cur_block = src.read(block_size)

        # Update progress bar
        prgb.updateAmount(cur_block_pos)
        cur_block_pos += block_size

        sys.stdout.write(
            '\r%s\r' % str(prgb)
        )

        # If it's the end of file
        if not cur_block:
            # ..write new line to prevent messing up terminal
            sys.stderr.write('\n')
            break
        else:
            # ..if not, write the block and continue
            dest.write(cur_block)
    #end while

    # Close files
    src.close()
    dest.close()

    #Check MD5 of the file to see if it was copied correctly.
    print(Back.YELLOW + Fore.BLACK + "Checking MD5 hash..." +  Fore.RESET + Back.RESET            ),
    if md5_for_file(src_file) == md5_for_file(dest_file):
        print (Back.GREEN + "MD5 GOOD!" + Back.RESET)
    else:
        print (Back.RED + "MD5 BAD!" + Back.RESET)
        raise IOError(
            "The two files don't have the same MD5 code!"
        )
     # Check output file is same size as input one!
    #dest_size = os.stat(dest_file).st_size
    #
    #if dest_size != src_size:
    #    raise IOError(
    #        "New file-size does not match original (src: %s, dest: %s)" % (
    #        src_size, dest_size)
    #    )


checkInput()

Recommended Answers

All 4 Replies

Do you really need that many classes? Will these classes get used more than once? For example, the class "ProgressBar" what is this for?

Actually there are "only" 2 classes. The progressbar one simply draws a text prograssbar in the terminal while a file is beeing copied.

I'll let the python experts comment on the code. What I would add (and this is independent of the language) is a header which describes the purpose of the code and perhaps some notes on its implementation. I would also add comment blocks to each class/method. The class/method names are not sufficient documentation.

That's a nice idea, thanks.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.