4

This is probably a simple question, but I'm brand new to python and programming in general.

I'm working on a simple program to copy/move .mp3 files from on location to another while mirroring the directory structure of the source location. What I have so far works, however it also creates new folders in the destination location even if the source folder contained no mp3 files. I only want to create the new directories if the source contains .mp3s, otherwise it could lead to a bunch of empty folders in the destination.

Here is what I have so far:

import os
import shutil #Used for copying files

##CONFIG
source_dir = "C:\Users\username\Desktop\iTunes\\" #set the root folder that you want to     scan and move files from.  This script will scan recursively.
destPath = "C:\Users\username\Desktop\converted From iTunes" #set the destination root that you want to move files to.  Any non-existing sub directories will be created.
ext = ".mp3" #set the type of file you want to search for.
count = 0 #initialize counter variable to count number of files moved
##

##FIND FILES
for dirName, subdirList, fileList in os.walk(source_dir):

    #set the path for the destination folder(s)
    dest = destPath + dirName.replace(source_dir, '\\') 

    #if the source directory doesn't exist in the destination folder
    #then create a new folder
    if not os.path.isdir(dest):
        os.mkdir(dest)
        print('Directory created at: ' + dest)

    for fname in fileList:
        if fname.endswith(ext) :
            #determine source & new file locations
            oldLoc = dirName + '\\' + fname
            newLoc = dest + '\\' + fname

            if os.path.isfile(newLoc): # check to see if the file already exists.  If it does print out a message saying so.
                print ('file "' + newLoc + fname + '" already exists')

            if not os.path.isfile(newLoc): #if the file doesnt exist then copy it and print out confirmation that is was copied/moved
                try:
                    shutil.move(oldLoc, newLoc)
                    print('File ' + fname + ' copied.')
                    count = count + 1
                except IOError:
                    print('There was an error copying the file:  "' + fname + '"')
                    print 'error'            

print "\n"
print str(count) + " files were moved."
print "\n"

so if the folder structure is something like:

root->
 band 1->
  album name->
   song.m4a,
   song2.m4a

right now it will create all those folders in the destination driectory, even though there are no .mp3s to copy.....

Any help is appreciated!

mgilson
  • 300,191
  • 65
  • 633
  • 696
dkehler
  • 43
  • 1
  • 4

3 Answers3

1

I think I would create my own wrapper around copy for this sort of thing:

def fcopy(src,dest):
    """
    Copy file from source to dest.  dest can include an absolute or relative path
    If the path doesn't exist, it gets created
    """
    dest_dir = os.path.dirname(dest)
    try:
        os.makedirs(dest_dir)
    except os.error as e:
        pass #Assume it exists.  This could fail if you don't have permissions, etc...
    shutil.copy(src,dest)

Now you can just walk the tree calling this function on any .mp3 file.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • I will take some time to read over this an understand how it works. It looks promising. Like I said, I am really new to programming and most concepts are new to me. thank you. – dkehler Feb 15 '13 at 05:10
0

Would shutils.copytree not do what you want in fewer lines?

hd1
  • 33,938
  • 5
  • 80
  • 91
  • using the `ignore=...` keyword seems elegant here, but couldn't OP still end up with a bunch of empty directories? – mgilson Feb 15 '13 at 03:28
  • As I understand it shutils.copytree would copy the entire folder structure. I only want a folder copied if it contains .mp3 files. – dkehler Feb 15 '13 at 05:00
0

The simplest thing to do I can think of for your existing code would be to just make it skip over any folders that don't have any .mp3 files in them. This can easily be done by adding the following items and if statement to the top of your loop. The itertools.ifilter() and fnmatch.fnmatch() functions can be used together to simplify checking for files with the proper extension.

from itertools import ifilter
from fnmatch import fnmatch

ext = '.mp3'
fnPattern = '*'+ext

for dirName, subdirList, fileList in os.walk(source_dir):
    if not any(ifilter(lambda fname: fnmatch(fname, fnPattern), fileList)):
        print '  skipping "{}"'.format(dirName)
        continue
    ...

You will also have to change the os.mkdir(dest) to os.makedirs(dest) in the code further down to ensure that any subdirectories skipped by earlier iterations get created when there's a need to copy files to a corresponding subbranch of the destination directory.

You could optimize things a bit by creating and saving a possibly empty iterator of matching files that have the extension, and then use it again later to to determine what files to copy:

from itertools import ifilter
from fnmatch import fnmatch

ext = '.mp3'
fnPattern = '*'+ext

for dirName, subdirList, fileList in os.walk(source_dir):

    # generate list of files in directory with desired extension
    matches = ifilter(lambda fname: fnmatch(fname, fnPattern), fileList)

    # skip subdirectory if it does not contain any files of interest
    if not matches:
        continue
    ...
    ... create destination directory with os.makedirs()
    ...
    #  copy each file to destination directory
    for fname in matches:
      ... copy file
martineau
  • 119,623
  • 25
  • 170
  • 301
  • But how do you know that it doesn't have any subdirectories with `.mp3` in them? – mgilson Feb 15 '13 at 11:20
  • I tried altering my code so it looked for mp3s before it created directories, but that would fail if there was a folder that held no mp3s itself, but contained a subfolder that held mp3s. This looks like it would just skip a folder (and its subfolders?) if it does not contain mp3s? – dkehler Feb 15 '13 at 15:26
  • @mgilson: `os.walk` yields each directory of the tree rooted at directory `source_dir` (including `source_dir` itself) regardless of whether any processing is done for each one in the `for`. This means that subdirectories of a directory in the tree will be processed even if the directory itself is skipped over by the `if/continue`. – martineau Feb 15 '13 at 17:13
  • @martineau -- Yeah, I get that. The problem is when you go to copy a file to a destination directory that doesn't exist. – mgilson Feb 15 '13 at 17:15
  • @mgilson: A simple way to handle that would be to apply `os.makedirs()` to each destination directory before copying any files into it -- which admittedly does require a little bit more to be changed in the OP's code. – martineau Feb 15 '13 at 17:22
  • @martineau -- Right -- which is basically what I say in my solution. – mgilson Feb 15 '13 at 17:27
  • @mgilson: Indeed it does, but it says nothing about how to skip directories that don't contain any .mp3 files -- which was the primary reason I added my own answer, since it actually addresses what the OP asked about. – martineau Feb 15 '13 at 17:37
  • Excellent. that worked very well! I think the big thing I was missing in my attempts was not knowing about os.makedirs(). I think now that I have a working version, I will go back to the start and look at reworking things to clean it up. I thank you all for your responses. – dkehler Feb 15 '13 at 20:45
  • @user2070735: Good to hear. Please consider accepting the answer that helped you most -- as well as up-voting _all_ those you consider worthy of it. When you're "cleaning up" your code, be sure to consider switching to the `os.path` functions as much as possible for manipulating source and destination directory and file paths, as they can be very useful for extracting pieces of them and joining them back together. – martineau Feb 15 '13 at 21:47
  • Thank for you for your advice regarding os.path, I will keep that in mind. I have accepted an answer, however am unable to up-vote due to my newbie-ness here. If possible I will return here once I have earned enough points and give up-votes where up-votes are due. :-) – dkehler Feb 16 '13 at 01:58