|
4 | 4 | # Any time a folder is added to the monitored parent directory, that folder needs
|
5 | 5 | # to be monitored as well [recursive monitoring is not fully supported]. This
|
6 | 6 | # script will add to incrontab when a new folder is added.
|
7 |
| -# Be sure to chmod +x this script! |
| 7 | +# This script will also convert any files uploaded to a monitored folder |
| 8 | +# to a VP8 [webm] and mobile version to be put into an .alternates folder. |
| 9 | +# This script will also handle arbitrary file/folder renaming, at any depth. |
8 | 10 | # '''
|
9 | 11 |
|
10 | 12 | import os,sys,pwd,subprocess, time
|
11 | 13 | from datetime import datetime
|
12 | 14 |
|
13 | 15 | # CFG Variables
|
14 |
| -scriptPath = '/path/to/this/update.py' # the path to THIS script. |
15 |
| -changedDir = sys.argv[1] |
16 |
| -workingDir = sys.argv[2] |
17 |
| -event = sys.argv[3] |
| 16 | +scriptPath = '/var/www/soundtrack/update.py' # the path to THIS script. |
| 17 | +changed = sys.argv[1].strip().replace(' ','\\ ') # spaces. |
| 18 | +if len(sys.argv) > 4: |
| 19 | + workingDir = '\\ '.join(sys.argv[2:-1]) # spaces |
| 20 | +else: |
| 21 | + workingDir = sys.argv[2] # no spaces |
| 22 | +event = sys.argv[-1] |
18 | 23 | incrontemp = workingDir + '/temp'
|
19 |
| - |
20 | 24 | curUser = 'thang' # ensure this runs under the correct user, for incrontab
|
21 | 25 | os.setuid(pwd.getpwnam(curUser)[2])
|
22 | 26 |
|
23 | 27 | # Log to a timestamped log file
|
24 |
| -def log(out, err): |
| 28 | +def log(out, err, cmd): |
25 | 29 | curTime = datetime.time(datetime.now()).isoformat()
|
26 |
| - f = open(workingDir + '/error.log' + curTime, 'w') |
27 |
| - f.write('Could not update incrontab for %s' % changedDir + '\n') |
| 30 | + f = open(workingDir + '/error.%s.log' % curTime , 'w') |
| 31 | + f.write('Could not update incrontab for %s' % changed + '\n') |
28 | 32 | f.write('Failed output: \n')
|
| 33 | + f.write('Attempted: %s \n' % cmd) |
29 | 34 | f.write(out + '\n')
|
30 | 35 | f.write(err + '\n')
|
| 36 | + f.write('Parameters: %s, %s, %s' % (changed,workingDir,event) + '\n') |
31 | 37 | f.close()
|
32 | 38 |
|
33 |
| -def processCmd(cmdList): |
34 |
| - |
35 |
| - # Copy the current incrontab, & modify the copy accordingly. |
36 |
| - cmdList = ['/usr/bin/incrontab -l > %s' % incrontemp] + cmdList |
37 |
| - # Replace with the changed incrontemp. |
38 |
| - cmdList += ['/usr/bin/incrontab %s' % incrontemp] |
39 |
| - |
| 39 | +# Execute a list of shell commands, return the output as a list. |
| 40 | +def runCmd(cmdList): |
| 41 | + outList = [] |
40 | 42 | for cmd in cmdList:
|
41 | 43 | o = subprocess.Popen(cmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
42 | 44 | out, err = o.communicate()
|
| 45 | + outList += [out] |
43 | 46 | if o.returncode != 0:
|
44 |
| - log(out,err) |
| 47 | + log(out,err,cmd) |
45 | 48 | sys.exit('Process failed..' )
|
| 49 | + |
| 50 | + return outList |
| 51 | + |
| 52 | +# Update Incrontab after modifying the current incrontab. |
| 53 | +def updateIncron(cmdList): |
| 54 | + # Copy the current incrontab, & modify the copy accordingly. |
| 55 | + cmdList = ['/usr/bin/incrontab -l > %s' % incrontemp] + cmdList |
| 56 | + # Replace with the changed incrontemp. |
| 57 | + cmdList += ['/usr/bin/incrontab %s' % incrontemp] |
| 58 | + runCmd(cmdList) |
46 | 59 | os.remove(incrontemp)
|
47 | 60 |
|
48 | 61 | # Process an uploaded file.
|
49 | 62 | def processFile():
|
50 |
| - pass |
| 63 | + # full path to the file.. |
| 64 | + in_file = workingDir + '/' + changed |
| 65 | + # Pop off the extension. |
| 66 | + out_file = '.'.join(changed.split('.')[:-1]) |
| 67 | + # Put the file in the .alternates folder in the watched directory |
| 68 | + out_file = workingDir+'/.alternates/' + out_file |
51 | 69 |
|
52 |
| -def main(): |
| 70 | + # Make an iOS & Firefox playable version. |
| 71 | + cmds = ["HandBrakeCLI -i %s -o %s --preset=\"iPad\"" % ( in_file, out_file + '_ipad.mov' )] |
| 72 | + cmds += ["ffmpeg -y -i %s -threads 4 -f webm -vcodec libvpx -deinterlace -g 120 -level 216 -profile 0 -qmax 42 -qmin 10 -rc_buf_aggressivity 0.95 -vb 2M -acodec libvorbis -aq 70 -ac 2 %s" % (in_file, out_file + '_ffox.webm' )] |
| 73 | + runCmd(cmds) |
53 | 74 |
|
| 75 | +def main(): |
54 | 76 | # A Sub-Directory is added.
|
55 | 77 | if 'IN_CREATE,IN_ISDIR' in event:
|
56 |
| - cmds = ["echo '%s IN_CREATE,IN_DELETE,IN_MODIFY %s $# $@ $%%' >> %s" % (workingDir+'/'+changedDir, scriptPath, incrontemp)] |
57 |
| - processCmd(cmds) |
| 78 | + cmds = ["echo '%s IN_CREATE,IN_DELETE,IN_CLOSE_WRITE,IN_MOVED_TO %s $# $@ $%%' >> %s" % (workingDir+'/'+changed, scriptPath, incrontemp)] |
| 79 | + # Add the .alternates folder, but don't monitor it. |
| 80 | + cmds += ["mkdir %s" % (workingDir+'/'+changed+'/.alternates/')] |
| 81 | + # update incrontab to add/remove watches on the changed Directory |
| 82 | + updateIncron(cmds) |
58 | 83 |
|
59 | 84 | # A Sub-Directory is deleted.
|
60 | 85 | elif 'IN_DELETE,IN_ISDIR' in event:
|
61 |
| - cmds = ["sed -i '/%s/d' %s" % (changedDir.strip('/'), incrontemp)] |
62 |
| - processCmd(cmds) |
63 |
| - |
64 |
| - # A File is added... |
65 |
| - # We may need to watch when files are moved to the watched directory as well. |
66 |
| - elif 'IN_CREATE' in event or 'IN_MODIFY' in event: |
67 |
| - processFile() |
68 |
| - sys.exit() |
| 86 | + deletions = (workingDir+'/'+changed).replace('\\','\\\\').replace('/','\\/') |
| 87 | + # First we remove the exact entry, so something like foo/testx doesn't |
| 88 | + # get removed when foo/test is deleted. |
| 89 | + # Then, we remove instances of foo/test/ to take care of subdirs. |
| 90 | + |
| 91 | + cmds = ["sed -i '/%s\\ /d' %s" % (deletions,incrontemp)] |
| 92 | + cmds += ["sed -i '/%s\//d' %s" % (deletions, incrontemp)] |
| 93 | + updateIncron(cmds) |
| 94 | + |
| 95 | + # A File is added. |
| 96 | + elif 'IN_CLOSE_WRITE' in event: |
| 97 | + ext = changed.split('.')[-1] |
| 98 | + # Only process valid movie files. |
| 99 | + if 'mov' in ext: |
| 100 | + processFile() |
| 101 | + |
| 102 | + # A directory was renamed. |
| 103 | + elif 'IN_MOVED_TO,IN_ISDIR' in event: |
| 104 | + |
| 105 | + # Find the original name of the directory. |
| 106 | + # Compare with the directories in incrontab |
| 107 | + cmds = ['/usr/bin/incrontab -l'] |
| 108 | + cmdOut = runCmd(cmds)[0] |
| 109 | + oldName = '' |
| 110 | + |
| 111 | + for x in cmdOut.split('\n')[:-1]: |
| 112 | + tempDir = x.split(' IN_CREATE,')[0] |
| 113 | + # Ensure the basepath matches, and then check if it exists. |
| 114 | + # If it doesn't, we can assume that it was the renamed directory. |
| 115 | + # This does introduce a race condition, if a folder is deleted |
| 116 | + # while this script is running for a MOVED_TO event. |
| 117 | + # This race condition should be very rare. |
| 118 | + # I am not a doctor. Use at your own risk. |
| 119 | + |
| 120 | + if workingDir in tempDir and not os.path.exists(tempDir.replace('\\ ', ' ')): |
| 121 | + oldName = tempDir |
| 122 | + break |
| 123 | + |
| 124 | + # Now we need to pass this to sed, and {forward|back}slashes need to be |
| 125 | + # escaped. |
| 126 | + # Python needs backslashes escaped, and sed is weird about backslashes. |
| 127 | + # So I'm pretty sure this is the right amount of backslashes. |
| 128 | + |
| 129 | + oldName = oldName.replace('\\','\\\\').replace('/','\\/') |
| 130 | + newName = (workingDir+'/'+changed).replace('\\','\\\\').replace('/','\\/') |
| 131 | + cmds = ["sed -i 's/%s /%s /g' %s" % (oldName,newName,incrontemp)] |
| 132 | + updateIncron(cmds) |
| 133 | + |
| 134 | + # A file was renamed. |
| 135 | + elif 'IN_MOVED_TO' in event: |
| 136 | + changed = ''.join(sys.argv[1].split('.')[:-1]) # remove the extension |
| 137 | + wd = workingDir.replace('\\ ', ' ') |
| 138 | + altFolder = wd + '/.alternates/' |
| 139 | + files = os.walk(altFolder).next()[2] |
| 140 | + # Pop off extensions & remove dupes. |
| 141 | + files = list(set(map(lambda x: '_'.join(x.split('_')[:-1]),files))) |
| 142 | + moved = '' |
| 143 | + for f in files: |
| 144 | + if not os.path.exists(wd+'/'+f+'.*'): |
| 145 | + moved = f |
| 146 | + break |
| 147 | + |
| 148 | + try: |
| 149 | + os.rename(altFolder + moved + '_ffox.webm',altFolder + changed + '_ffox.webm') |
| 150 | + os.rename(altFolder + moved + '_ipad.mov',altFolder + changed + '_ipad.mov') |
| 151 | + |
| 152 | + # If we can't rename the files, so either one of these happened: |
| 153 | + # 1 - Only one of the alternates was created, in which case we still |
| 154 | + # need to make the alternates again |
| 155 | + # 2 - The file was deleted while this script was running, and we still |
| 156 | + # need to make the alternates again. |
| 157 | + except: |
| 158 | + processFile() |
| 159 | + |
| 160 | + |
| 161 | + # A file was removed. |
| 162 | + elif 'IN_DELETE' in event: |
| 163 | + altFolder = workingDir + '/.alternates/' |
69 | 164 |
|
| 165 | + # Delete the alternates |
| 166 | + try: |
| 167 | + os.remove(altFolder + changed + '_ffox.webm') |
| 168 | + os.remove(altFolder + changed + '_ipad.webm') |
| 169 | + # Don't sweat if they don't exist. |
| 170 | + except: |
| 171 | + pass |
70 | 172 |
|
71 | 173 | if __name__ == "__main__":
|
72 | 174 | main()
|
0 commit comments