##  backupMusic.py version 1.00

##  Copyright (c) 2007 Bruce Vaughan, BV Detailing & Design, Inc.

##  All rights reserved.

##  NOT FOR SALE. The software is provided "as is" without any warranty.

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

'''

I wrote this script to backup my iTunes music library files (over 17000 files)

from the root folder location to another location on a backup drive. The script

writes all actions taken to a log file and to stdout for feedback to the user.

All new and modified files are copied. Folders are created where required. This

script was written in Python 2.3. I usually execute this script in the

interactive window of Pythonwin.

'''

 

import os, time, shutil

 

class dirListing(object):

    ''' dirListing(dir_name, subdir, *args)

        Create a list of file names found in directory 'dir_name'

        If 'subdir' is True, recursively access subdirectories under 'dir_name'.

        Additional arguments, if any, are file extensions to match filenames. Matched

            file names are added to the list.

        If there are no additional arguments, all files found in the directory are

            added to the list.

        Example usage: fileList = dirListing(r'H:\TEMP', False, 'txt', 'py')

            Only files with 'txt' and 'py' extensions will be added to the list.

        Example usage: fileList = dirListing(r'H:\TEMP', True)

            All files and all the files in subdirectories under H:\TEMP will be added

            to the list.

        Attributes are self.fileList, self.dirList

    '''

 

    def __init__(self, dir_name, subdir, *args):

        self.dir_name = dir_name

        if subdir:

            self.accessDirs = True

        else:

            self.accessDirs = False

        self.extList = args

 

        self.fileList, self.dirList = dirListing.dirEntries(dir_name, subdir, *args)

 

    def dirEntries(dir_name, subdir, *args):   

        fileList = []

        dirList = []

        for fileName in os.listdir(dir_name):

            dirfile = os.path.join(dir_name, fileName)

            if os.path.isfile(dirfile):

                if len(args) == 0:

                    fileList.append(dirfile)

                else:

                    if os.path.splitext(dirfile)[1][1:] in args:

                        fileList.append(dirfile)

            # recursively access file names in subdirectories

            elif os.path.isdir(dirfile) and subdir:

                # print 'Accessing directory %s' % dirfile

                dirList.append(dirfile)

                f, d = dirListing.dirEntries(dirfile, subdir, *args)

                fileList.extend(f)

                dirList.extend(d)

        return fileList, dirList

 

    dirEntries = staticmethod(dirEntries)   

 

 

def time_in_seconds(time_str):

    str_list = (time_str.split(":"))

    return float(str_list[0])*3600 + float(str_list[1])*60 + float(str_list[2])

 

def formatSec(seconds):

    milli = int(seconds%1*1000)

    days = float(seconds)/86400

    hours = days%1*24

    minutes = hours%1*60

    seconds = minutes%1*60

    if int(days) > 0:

        outStr = '%d days,' % days

    else:

        outStr = ''

    outStr = ''.join([outStr, '%02d:%02d:%02d' % (hours, minutes, seconds)])

    if milli > 0:

        outStr += ' and %d milliseconds' % milli

    return outStr

 

class SongName(str):

 

    def __new__(cls, seq=''):

        return super(SongName, cls).__new__(cls, SongName.name(seq))

 

    def __init__(self, seq):

        # original string argument

        self.data = seq

        self.drive, self.dirs = [p.lstrip('\\') for p in os.path.splitdrive(os.path.split(seq)[0])]

        try:

            self.album = self.dirs.split('\\')[-1]

        except:

            self.album = None

        try:

            self.artist = self.dirs.split('\\')[-2]

        except:

            self.artist = None

        self.exists = os.path.isfile(seq)

        if self.exists:

            self.size = os.path.getsize(seq)

            self.sec = os.path.getmtime(seq)

            self.date = time.ctime(self.sec)

       

    def name(s):

        return os.path.split(s)[1]

 

    name = staticmethod(name)

 

    def backupfolder(self, d='U:\\'):

        return os.path.join('%s' % d, self.dirs)

 

    def backuppath(self, d='U:\\'):

        return os.path.join('%s' % d, self.dirs, self)

 

def backupMusic(src=r'F:\Music', dst='E:\\', log=r'E:\backup_music1.log'):

    '''

    Backup music library files from src to dst and write data to log file.

    '''

    music = dirListing(src, 1)

    print 'Opening log file: %s' % time.ctime()

    outList = ['Opening log file: %s' % time.ctime(), ]

    for song in music.fileList:

        a = SongName(song)

        if not os.path.isfile(a.backuppath(dst)):

            if not os.path.isdir(a.backupfolder(dst)):

                outList.append('  Create directory %s' % a.backupfolder(dst))

                print '\nCreate directory %s' % a.backupfolder(dst)

                os.makedirs(a.backupfolder(dst))

            print '\nCopy file %s to %s' % (a.data, a.backuppath(dst))

            outList.append('    Copy file %s to %s' % (a.data, a.backuppath(dst)))

            shutil.copy2(a.data, a.backuppath(dst))

        else:

            b = SongName(a.backuppath(dst))

            if a.sec > b.sec:

                outList.append('        Copy file %s - the backup file is older.' % a.data)

                print '\nCopy file %s - the backup file is older.' % a.data

                shutil.copy2(a.data, a.backuppath(dst))

    outList.append('Closing log file: %s\n\n\n\n' % time.ctime())

    f = open(log, 'a')

    f.write('\n'.join(outList))

    f.close()

    return True

           

'''

s1 = r'R:\Music\Ace Of Base\The Sign\01 All That She Wants.m4a'

s2 = r'R:\Music\Compilations\Celtic Circle 2\2-01 Tranquility.m4a'

s3 = r'R:\Music\Compilations\Country Roads #07 He Stopped Loving Her\7-01 Hey Good Looking.mp3'

s4 = r'C:\Documents and Settings\All Users\Documents\My Music\Sample Music\New Stories (Highway Blues).wma'

 

a = SongName(s1)

b = SongName(s2)

c = SongName(s3)

d = SongName(s4)

 

print a, a.data

print a.sec

print a.date

print "Song file '%s' age = %s" % (a, formatSec(time.time()-a.sec))

print

print b, b.data

print b.sec

print b.date

print "Song file '%s' age = %s" % (b, formatSec(time.time()-b.sec))

print

print c, c.data

print c.sec

print c.date

print "Song file '%s' age = %s" % (c, formatSec(time.time()-c.sec))

 

print

print d, d.data

print d.sec

print d.date

print "Song file '%s' age = %s" % (d, formatSec(time.time()-d.sec))

'''

'''

>>> 01 All That She Wants.m4a R:\Music\Ace Of Base\The Sign\01 All That She Wants.m4a

1140476054

Mon Feb 20 16:54:14 2006

Song file '01 All That She Wants.m4a' age = 509 days,17:58:30 and 46 milliseconds

 

2-01 Tranquility.m4a R:\Music\Compilations\Celtic Circle 2\2-01 Tranquility.m4a

1125107226

Fri Aug 26 20:47:06 2005

Song file '2-01 Tranquility.m4a' age = 687 days,15:05:38 and 78 milliseconds

 

7-01 Hey Good Looking.mp3 R:\Music\Compilations\Country Roads #7 He Stopped Loving Her T\7-01 Hey Good Looking.mp3

1160245440

Sat Oct 07 13:24:00 2006

Song file '7-01 Hey Good Looking.mp3' age = 280 days,22:28:44 and 92 milliseconds

>>>

'''

 

'''

f = open(c.data, 'rb')

f.seek(-128, 2)

# print f.tell()

data = f.read(128)

f.close()

'''

 

'''

from struct import *

print unpack('h'*64, data)

'''

'''

>>> 760620

(-28827, -8609, -18248, 13390, -27948, -11099, 16199, -16985, 28785, 27036, 9641, -22453, 32654, -24246, 23563, -29099, 7650, -29921, -6989, 2139, 19112, -27450, 12581, 3330, 26940, -22589, -26226, 16644, 16651, 16941, 4944, -9452, -11332, 9972, 20546, -9662, -19776, 8200, 25216, -11155, 1317, 18529, -9370, -10593, -24824, 11382, -16010, 18212, -19546, -28290, -459, 17539, -20027, 25376, 30417, 1561, 3375, 30992, -9990, -28905, -10852, -26942, 1738, 0)

'''

 

'''

>>> data

'e\x8f_\xde\xb8\xb8N4\xd4\x92\xa5\xd4G?\xa7\xbdqp\x9ci\xa9%K\xa8\x8e\x7fJ\xa1\x0b\\U\x8e\xe2\x1d\x1f\x8b\xb3\xe4[\x08\xa8J\xc6\x94%1\x02\r<i\xc3\xa7\x8e\x99\x04A\x0bA-BP\x13\x14\xdb\xbc\xd3\xf4&BPB\xda\xc0\xb2\x08 \x80bm\xd4%\x05aHf\xdb\x9f\xd6\x08\x9fv,v\xc1$G\xa6\xb3~\x915\xfe\x83D\xc5\xb1 c\xd1v\x19\x06/\r\x10y\xfa\xd8\x17\x8f\x9c\xd5\xc2\x96\xca\x06\x00\x00'

>>> for item in data:

... print ord(item)

...

101

143

95

222

184

184

78

52

212

146

165

212

71

63

167

189

113

112

156

105

169

37

75

168

142

127

74

161

11

92

85

142

226

29

31

139

179

228

91

8

168

74

198

148

37

49

2

13

60

105

195

167

142

153

4

65

11

65

45

66

80

19

20

219

188

211

244

38

66

80

66

218

192

178

8

32

128

98

109

212

37

5

97

72

102

219

159

214

8

159

118

44

118

193

36

71

166

179

126

145

53

254

131

68

197

177

32

99

209

118

25

6

47

13

16

121

250

216

23

143

156

213

194

150

202

6

0

0

>>> len(data)

128

>>>

'''

 

'''

>>> a

'01 All That She Wants.m4a'

>>> b

'2-01 Tranquility.m4a'

>>> c

'7-01 Hey Good Looking.mp3'

>>> c.artist

'Compilations'

>>> c.album

'Country Roads #7 He Stopped Loving Her T'

>>> os.path.splitdrive(os.path.split(s3))

('', ('R:\\Music\\Compilations\\Country Roads #7 He Stopped Loving Her T', '7-01 Hey Good Looking.mp3'))

>>> os.path.split(s3)

('R:\\Music\\Compilations\\Country Roads #7 He Stopped Loving Her T', '7-01 Hey Good Looking.mp3')

>>> s3

'R:\\Music\\Compilations\\Country Roads #7 He Stopped Loving Her T\\7-01 Hey Good Looking.mp3'

>>> a.artist

'Ace Of Base'

>>> a.backuppath()

'R:\\Music\\Ace Of Base\\The Sign\\01 All That She Wants.m4a'

>>> a

'01 All That She Wants.m4a'

>>> '\n'.join([a,b,c])

'01 All That She Wants.m4a\n2-01 Tranquility.m4a\n7-01 Hey Good Looking.mp3'

>>> print '\n'.join([a,b,c])

01 All That She Wants.m4a

2-01 Tranquility.m4a

7-01 Hey Good Looking.mp3

>>>

'''

 

'''

>>> patt = re.compile(r'(\w|\s)+(?=\.[A-Z,a-z,0-9]{3,4})')

>>> patt.search('H:\\Music\\Free Bird.mp3').group(0)

'Free Bird'

>>>

'''

 

'''

>>> a

Music\Free Bird

>>> s = a

>>> type(s)

<class '__main__.Song'>

>>> s = str(a)

>>> type(s)

<type 'str'>

>>> print a.toMp3()

E:\Music\Free Bird.mp3

>>> '''