1206 lines
33 KiB
Python
1206 lines
33 KiB
Python
#(c) 2004-2008 Thunderstone Media, LLC
|
|
#Creative Commons Attribution-Noncommercial 3.0 United States License
|
|
#
|
|
#Python Developyment By:
|
|
#
|
|
#Ray Slakinski
|
|
#August Trometer
|
|
|
|
import iPXSettings
|
|
import sys, difflib
|
|
from types import *
|
|
from time import *
|
|
|
|
if sys.platform == 'darwin':
|
|
from objc import *
|
|
from Foundation import *
|
|
|
|
class SM (difflib.SequenceMatcher):
|
|
def __helper(self, alo, ahi, blo, bhi, answer):
|
|
# slightly modified from difflib to ignore blocks of one elt
|
|
# this prevents weird ones like <del>Whe</><ins>Ocasio</>n<ins>ally</>
|
|
i, j, k = x = self.find_longest_match(alo, ahi, blo, bhi)
|
|
# a[alo:i] vs b[blo:j] unknown
|
|
# a[i:i+k] same as b[j:j+k]
|
|
# a[i+k:ahi] vs b[j+k:bhi] unknown
|
|
if k:
|
|
if alo < i and blo < j:
|
|
self.__helper(alo, i, blo, j, answer)
|
|
if k > 1: answer.append(x)
|
|
if i+k < ahi and j+k < bhi:
|
|
self.__helper(i+k, ahi, j+k, bhi, answer)
|
|
|
|
_SequenceMatcher__helper = _SM__helper # python name mangling funniness
|
|
|
|
|
|
class HTTPHead:
|
|
import urllib2, iPXSettings
|
|
def __init__(self, url, username=None, password=None):
|
|
self.reply = 302
|
|
self.headers = []
|
|
self.msg = ''
|
|
setOpener()
|
|
url = url.strip()
|
|
url = url.replace(' ', '%20')
|
|
req = urllib2.Request(url)
|
|
req.add_header('User-Agent', iPXSettings.USER_AGENT)
|
|
|
|
try:
|
|
h = urllib2.urlopen(req)
|
|
self.headers = h.info()
|
|
except Exception, e:
|
|
logIt(e)
|
|
|
|
def getType(self):
|
|
if self.headers.has_key('Content-Type') and self.reply == 200:
|
|
return self.headers['Content-Type']
|
|
else:
|
|
return None
|
|
|
|
def getLength(self):
|
|
if self.headers.has_key('Content-Length') and self.reply == 200:
|
|
return self.headers['Content-Length']
|
|
else:
|
|
return None
|
|
|
|
def getName(self):
|
|
import re
|
|
from string import split
|
|
|
|
newName = ''
|
|
if self.headers.has_key('Content-Disposition') and self.reply == 200:
|
|
if re.search('filename=', self.headers['Content-Disposition'], re.IGNORECASE):
|
|
textSplit = self.headers['Content-Disposition'].split(';')
|
|
for text in textSplit:
|
|
if re.search('filename=', text, re.IGNORECASE):
|
|
newNameSplit = text.split('=')
|
|
newName = newNameSplit[len(newNameSplit) -1]
|
|
newName = newName.replace('"', '')
|
|
|
|
return newName
|
|
|
|
def stringCleaning(text):
|
|
import re
|
|
|
|
text = re.sub('[\\\\/?*:<>|;"\']','', text)
|
|
text = text.strip()
|
|
|
|
return text
|
|
|
|
def isTag(x): return x[0] == "<" and x[-1] == ">"
|
|
|
|
def stringDiff(a, b):
|
|
from string import join
|
|
"""@@@"""
|
|
out = ''
|
|
chars = 0
|
|
a, b = ' '.join(html2list(a)), ' '.join(html2list(b))
|
|
s = SM(None, a, b)
|
|
a = ' '.join(html2list(a, b=1))
|
|
for e in s.get_opcodes():
|
|
if e[0] == "replace":
|
|
out += "<del>" + a[e[1]:e[2]] + "</del>"
|
|
out += "<ins>" + b[e[3]:e[4]] + "</ins>"
|
|
elif e[0] == "delete":
|
|
out += "<del>"+ a[e[1]:e[2]] +"</del>"
|
|
elif e[0] == "insert":
|
|
out += "<ins>"+b[e[3]:e[4]] +"</ins>"
|
|
elif e[0] == "equal":
|
|
chunk = b[e[3]:e[4]]
|
|
if len(chunk) > chars: chars = len(chunk)
|
|
out += chunk
|
|
else:
|
|
raise "Um, something's broken. I didn't expect a '" + `e[0]` + "'."
|
|
return out
|
|
|
|
def textDiff(a, b):
|
|
"""Takes in strings a and b and returns a human-readable HTML diff."""
|
|
from string import join
|
|
|
|
out = []
|
|
a, b = html2list(a), html2list(b)
|
|
s = difflib.SequenceMatcher(isTag, a, b)
|
|
for e in s.get_opcodes():
|
|
if e[0] == "replace":
|
|
# @@ need to do something more complicated here
|
|
# call textDiff but not for html, but for some html... ugh
|
|
# gonna cop-out for now
|
|
out.append("<span class='modified diff'>"+' '.join(b[e[3]:e[4]])+"</span>")
|
|
elif e[0] == "delete":
|
|
out.append("<del class='diff'>"+ ' '.join(a[e[1]:e[2]]) + "</del>")
|
|
elif e[0] == "insert":
|
|
out.append("<ins class='diff'>"+' '.join(b[e[3]:e[4]]) + "</ins>")
|
|
elif e[0] == "equal":
|
|
out.append(' '.join(b[e[3]:e[4]]))
|
|
else:
|
|
raise "Um, something's broken. I didn't expect a '" + `e[0]` + "'."
|
|
return ' '.join(out)
|
|
|
|
def html2list(x, b=0):
|
|
import string
|
|
|
|
mode = 'char'
|
|
cur = ''
|
|
out = []
|
|
for c in x:
|
|
if mode == 'tag':
|
|
if c == '>':
|
|
if b: cur += ']'
|
|
else: cur += c
|
|
out.append(cur); cur = ''; mode = 'char'
|
|
else: cur += c
|
|
elif mode == 'char':
|
|
if c == '<':
|
|
out.append(cur)
|
|
if b: cur = '['
|
|
else: cur = c
|
|
mode = 'tag'
|
|
elif c in string.whitespace: out.append(cur); cur = ''
|
|
else: cur += c
|
|
out.append(cur)
|
|
return filter(lambda x: x is not '', out)
|
|
|
|
def htmlDiff(a, b):
|
|
f1, f2 = a.find('</head>'), a.find('</body>')
|
|
ca = a[f1+len('</head>'):f2]
|
|
|
|
f1, f2 = b.find('</head>'), b.find('</body>')
|
|
cb = b[f1+len('</head>'):f2]
|
|
|
|
r = textDiff(ca, cb)
|
|
hdr = '<style type="text/css"><!-- ins{color: green} del{color:red}--></style></head>'
|
|
return b[:f1] + hdr + r + b[f2:]
|
|
|
|
def getDirSize(dir):
|
|
import os
|
|
|
|
sizes = []
|
|
os.path.walk(dir, calcDirSize, sizes)
|
|
total = 0
|
|
for size in sizes:
|
|
total = total + size
|
|
|
|
return (total)
|
|
|
|
def calcDirSize(arg, dir, files):
|
|
import os
|
|
|
|
for file in files:
|
|
stats = os.stat(os.path.join(dir, file))
|
|
size = stats[6]
|
|
arg.append(size)
|
|
|
|
def HTML2Text(text):
|
|
import htmlentitydefs
|
|
flag = [1]
|
|
cleanText = ''
|
|
|
|
def stripfunc(c):
|
|
if not flag[0]:
|
|
if c == '>':
|
|
flag[0] = 1
|
|
return 0
|
|
elif c == '<':
|
|
flag[0] = 0
|
|
return flag[0]
|
|
|
|
cleanText = filter(stripfunc,text)
|
|
|
|
# 039 isn't in the defs table for some reason.
|
|
htmlentitydefs.codepoint2name[39] = 'quot'
|
|
|
|
for key in htmlentitydefs.name2codepoint:
|
|
if ('&%s;' % key) in cleanText:
|
|
try:
|
|
cleanText = cleanText.replace('&%s;' % key, unichr(htmlentitydefs.name2codepoint[key]))
|
|
except:
|
|
pass
|
|
|
|
|
|
for key in htmlentitydefs.codepoint2name:
|
|
if key < 100:
|
|
if ('�%d;' % key) in cleanText:
|
|
try:
|
|
cleanText = cleanText.replace('�%d;' % key, unichr(key))
|
|
except:
|
|
pass
|
|
if ('&#%d;' % key) in cleanText:
|
|
try:
|
|
cleanText = cleanText.replace('&#%d;' % key, unichr(key))
|
|
except:
|
|
pass
|
|
|
|
cleanText = cleanText.strip()
|
|
return cleanText
|
|
|
|
def text2Audio(text, saveDir, saveName):
|
|
import os
|
|
|
|
printMSG('Converting Text Entry To Audio...')
|
|
logIt('Converting Text Entry To Audio...')
|
|
cleanText = HTML2Text(text)
|
|
cleanText = cleanText.replace('"', '')
|
|
if sys.platform == 'darwin':
|
|
try:
|
|
f = open('/tmp/iPX-textToAudio.applescript', 'w')
|
|
|
|
f.write('set theText to "%s"\r\n' % cleanText)
|
|
f.write('set theFile to POSIX file "%s/%s\r\n' % (saveDir,saveName))
|
|
f.write('say theText saving to theFile')
|
|
f.close()
|
|
|
|
os.popen('/usr/bin/osascript /tmp/iPX-textToAudio.applescript')
|
|
return True
|
|
except Exception, msg:
|
|
logIt('text2Audio Error: %s' % msg)
|
|
return False
|
|
elif sys.platform == 'win32':
|
|
try:
|
|
import win32com.client
|
|
import shutil
|
|
|
|
speech = win32com.client.Dispatch('SAPI.SpVoice')
|
|
file_stream = win32com.client.Dispatch('SAPI.SpFileStream')
|
|
except Exception, msg:
|
|
logIt('text2Audio Error: %s' % msg)
|
|
return False
|
|
|
|
logIt('Speech Modules Loaded...')
|
|
|
|
try:
|
|
file_stream.Format.Type = 6
|
|
file_stream.Open(saveName, 3, True)
|
|
speech.AudioOutputStream = file_stream
|
|
if not speech.Speak(cleanText) == 1:
|
|
return False
|
|
file_stream.Close()
|
|
if os.path.isfile('.\\%s' % saveName):
|
|
shutil.move('.\\' + saveName, saveDir + '\\' + saveName)
|
|
else:
|
|
logIt('text2Audio Error: File was not properly created')
|
|
return False
|
|
except Exception, msg:
|
|
logIt('text2Audio Error: %s' % msg)
|
|
return False
|
|
|
|
return True
|
|
|
|
def prepURL(url):
|
|
url = url.replace('%3A', ':')
|
|
url = url.replace('%2F', '/')
|
|
|
|
if (url[:8] != 'https://'):
|
|
if (url[:7] != 'http://'):
|
|
url = 'http://%s' % url
|
|
return url
|
|
|
|
def trimLog():
|
|
import os, iPXSettings
|
|
|
|
if os.path.isfile(iPXSettings.logFile):
|
|
os.unlink(iPXSettings.logFile)
|
|
|
|
def genHash(data):
|
|
import md5
|
|
|
|
try:
|
|
msg = data.encode('utf8')
|
|
except:
|
|
from binascii import hexlify
|
|
|
|
msg = hexlify(data)
|
|
msg = msg.encode('utf8')
|
|
|
|
item = md5.new()
|
|
item.update(msg)
|
|
|
|
return item.hexdigest()
|
|
|
|
def logIt(incomingMsg):
|
|
import os, iPXSettings
|
|
|
|
try:
|
|
if type(incomingMsg) is UnicodeType:
|
|
incomingMsg = incomingMsg.encode('utf8')
|
|
else:
|
|
incomingMsg = str(incomingMsg)
|
|
|
|
msg = '%s %s' % (strftime('[%H:%M:%S]',localtime()), incomingMsg)
|
|
|
|
if iPXSettings.DEBUG:
|
|
printMSG(msg)
|
|
|
|
if os.path.isfile(iPXSettings.logFile):
|
|
f = open(iPXSettings.logFile, 'a')
|
|
f.write(msg + '\r\n')
|
|
f.close()
|
|
else:
|
|
f = open(iPXSettings.logFile, 'w')
|
|
f.write(msg + '\r\n')
|
|
f.close()
|
|
except Exception, msg:
|
|
if iPXSettings.DEBUG:
|
|
printMSG('Error Saving Log: %s' % msg)
|
|
if os.path.isfile(iPXSettings.logFile):
|
|
if iPXSettings.DEBUG:
|
|
printMSG('Attempting to deleting old log file...')
|
|
try:
|
|
os.unlink(iPXSettings.logFile)
|
|
except Exception, msg:
|
|
if iPXSettings.DEBUG:
|
|
printMSG('Error Deleting Log: ' + msg)
|
|
|
|
def latin1_to_ascii(unicrap):
|
|
"""This takes a UNICODE string and replaces Latin-1 characters with
|
|
something equivalent in 7-bit ASCII. It returns a plain ASCII string.
|
|
This function makes a best effort to convert Latin-1 characters into
|
|
ASCII equivalents. It does not just strip out the Latin-1 characters.
|
|
All characters in the standard 7-bit ASCII range are preserved.
|
|
In the 8th bit range all the Latin-1 accented letters are converted
|
|
to unaccented equivalents. Most symbol characters are converted to
|
|
something meaningful. Anything not converted is deleted.
|
|
"""
|
|
xlate={0xc0:'A', 0xc1:'A', 0xc2:'A', 0xc3:'A', 0xc4:'A', 0xc5:'A',
|
|
0xc6:'Ae', 0xc7:'C',
|
|
0xc8:'E', 0xc9:'E', 0xca:'E', 0xcb:'E',
|
|
0xcc:'I', 0xcd:'I', 0xce:'I', 0xcf:'I',
|
|
0xd0:'Th', 0xd1:'N',
|
|
0xd2:'O', 0xd3:'O', 0xd4:'O', 0xd5:'O', 0xd6:'O', 0xd8:'O',
|
|
0xd9:'U', 0xda:'U', 0xdb:'U', 0xdc:'U',
|
|
0xdd:'Y', 0xde:'th', 0xdf:'ss',
|
|
0xe0:'a', 0xe1:'a', 0xe2:'a', 0xe3:'a', 0xe4:'a', 0xe5:'a',
|
|
0xe6:'ae', 0xe7:'c',
|
|
0xe8:'e', 0xe9:'e', 0xea:'e', 0xeb:'e',
|
|
0xec:'i', 0xed:'i', 0xee:'i', 0xef:'i',
|
|
0xf0:'th', 0xf1:'n',
|
|
0xf2:'o', 0xf3:'o', 0xf4:'o', 0xf5:'o', 0xf6:'o', 0xf8:'o',
|
|
0xf9:'u', 0xfa:'u', 0xfb:'u', 0xfc:'u',
|
|
0xfd:'y', 0xfe:'th', 0xff:'y',
|
|
0xa1:'!', 0xa2:'{cent}', 0xa3:'{pound}', 0xa4:'{currency}',
|
|
0xa5:'{yen}', 0xa6:'|', 0xa7:'{section}', 0xa8:'{umlaut}',
|
|
0xa9:'{C}', 0xaa:'{^a}', 0xab:'<<', 0xac:'{not}',
|
|
0xad:'-', 0xae:'{R}', 0xaf:'_', 0xb0:'{degrees}',
|
|
0xb1:'{+/-}', 0xb2:'{^2}', 0xb3:'{^3}', 0xb4:"'",
|
|
0xb5:'{micro}', 0xb6:'{paragraph}', 0xb7:'*', 0xb8:'{cedilla}',
|
|
0xb9:'{^1}', 0xba:'{^o}', 0xbb:'>>',
|
|
0xbc:'{1/4}', 0xbd:'{1/2}', 0xbe:'{3/4}', 0xbf:'?',
|
|
0xd7:'*', 0xf7:'/'
|
|
}
|
|
|
|
r = ''
|
|
for i in unicrap:
|
|
if xlate.has_key(ord(i)):
|
|
r += xlate[ord(i)]
|
|
elif ord(i) >= 0x80:
|
|
pass
|
|
else:
|
|
r += str(i)
|
|
return r
|
|
|
|
def updatePlaylist(saveLocation, saveName, playList, customGenre, convertToAAC, makeBookmarkable):
|
|
import os, iPXSettings, random
|
|
|
|
location = ''
|
|
iPXID = ''
|
|
|
|
if sys.platform == 'darwin':
|
|
if (convertToAAC):
|
|
printMSG('Exporting to iTunes and converting to AAC...')
|
|
logIt('Exporting to iTunes and converting to AAC...')
|
|
else:
|
|
printMSG('Exporting to iTunes...')
|
|
logIt('Exporting to iTunes...')
|
|
trackID = 0
|
|
fullPath = saveLocation + '/' + saveName
|
|
song = os.path.abspath(fullPath)
|
|
while True:
|
|
scriptName = '/tmp/iPX-iTunes-%d.scpt' % random.randint(1,90000)
|
|
|
|
if not os.path.isfile(scriptName):
|
|
break
|
|
|
|
f = open(scriptName, 'w')
|
|
|
|
# initial settings
|
|
f.write('set thePlaylist to "%s"\r\n' % playList)
|
|
f.write('set theFile to "%s"\r\n' % song)
|
|
f.write('set theGenre to "%s"\r\n' % customGenre)
|
|
f.write('set theTrackID to 0\r\n')
|
|
f.write('property required_version : "4.0"\r\n')
|
|
|
|
# if iTunes isn't open, open it in the background
|
|
f.write('tell application "System Events"\r\n')
|
|
f.write('if (not (exists process "iTunes")) then\r\n')
|
|
f.write('tell application "iTunes"\r\n')
|
|
f.write('set visible of front window to false\r\n')
|
|
f.write('end tell\r\n')
|
|
f.write('end if\r\n')
|
|
f.write('end tell\r\n')
|
|
|
|
f.write('set theFile to the POSIX file theFile\r\n')
|
|
|
|
f.write('tell application "iTunes"\r\n')
|
|
|
|
#
|
|
# IMPORT TO ITUNES LIBRARY
|
|
#
|
|
f.write('with timeout of 1800 seconds\r\n')
|
|
|
|
# move track to Library and get track object
|
|
f.write('try\r\n')
|
|
f.write('set theTrack to add theFile to library playlist 1 of source 1\r\n')
|
|
f.write('on error theError\r\n')
|
|
f.write('set theTrack to add theFile to library playlist 1\r\n')
|
|
f.write('end try\r\n')
|
|
f.write('end timeout\r\n')
|
|
|
|
#
|
|
# SET CUSTOM GENRE
|
|
#
|
|
if len(customGenre) > 0:
|
|
f.write('try\r\n')
|
|
f.write('set genre of theTrack to theGenre\r\n')
|
|
f.write('end try\r\n')
|
|
|
|
#
|
|
# SET IPXID IN THE COMMENTS FIELD
|
|
#
|
|
iPXID = strftime('%M%Y%m%H%d%S',gmtime())
|
|
|
|
f.write('set theComment to the comment of theTrack\r\n')
|
|
|
|
f.write('set theCharacters to every character in theComment\r\n')
|
|
f.write('set commentString to ""\r\n')
|
|
f.write('repeat with aCharacter in theCharacters\r\n')
|
|
f.write('if (count commentString) is less than 160 then\r\n')
|
|
f.write('set commentString to commentString & aCharacter\r\n')
|
|
f.write('end if\r\n')
|
|
f.write('end repeat\r\n')
|
|
f.write('set the comment of theTrack to commentString & "\r\r" & "[iPXID:%s]"\r\n' % iPXID)
|
|
|
|
#
|
|
# MAKE BOOKMARKABLE
|
|
#
|
|
f.write('with timeout of 1800 seconds\r\n')
|
|
f.write('try\r\n')
|
|
f.write('set this_version to the version as string\r\n')
|
|
f.write('if this_version is greater than or equal to the required_version then\r\n')
|
|
f.write('set bookmarkable of theTrack to true\r\n')
|
|
f.write('end if\r\n')
|
|
f.write('end try\r\n')
|
|
f.write('end timeout\r\n')
|
|
|
|
#
|
|
# MOVE TO PLAYLIST
|
|
#
|
|
# Create playlist if it doesn't exist
|
|
f.write('try\r\n')
|
|
f.write('if (not (exists user playlist thePlaylist)) then\r\n')
|
|
f.write('make new playlist with properties {name:thePlaylist}\r\n')
|
|
f.write('end if\r\n')
|
|
|
|
# move to the playlist
|
|
f.write('duplicate theTrack to the playlist thePlaylist\r\n')
|
|
f.write('end try\r\n')
|
|
|
|
"""
|
|
# update connected iPod
|
|
f.write('try\r\n')
|
|
f.write('set myPod to (name of some source whose kind is iPod)\r\n')
|
|
f.write('update myPod\r\n')
|
|
f.write('end try\r\n')
|
|
"""
|
|
|
|
f.write('return theTrack\r\n')
|
|
f.write('end tell\r\n')
|
|
|
|
f.close()
|
|
|
|
ret_pipe = os.popen('/usr/bin/osascript %s' % scriptName)
|
|
returnValue = ''
|
|
returnValue = ret_pipe.readline()
|
|
if len(returnValue) > 0:
|
|
location = "iTunes"
|
|
|
|
if (iPXSettings.deleteAudio > 0):
|
|
logIt('Deleting: %s...' % fullPath)
|
|
if os.path.isfile(fullPath):
|
|
os.unlink(fullPath)
|
|
else:
|
|
printMSG('iTunes failed to update. Track left in Library')
|
|
logIt('iTunes failed to update. Track left in Library')
|
|
iPXID = ''
|
|
if not iPXSettings.DEBUG:
|
|
if os.path.isfile(scriptName):
|
|
os.unlink(scriptName)
|
|
else:
|
|
logIt('Keeping script: %s' % scriptName)
|
|
elif sys.platform == 'win32':
|
|
import win32com.client
|
|
import shutil
|
|
if iPXSettings.Prefs['exportApp'] == 1:
|
|
try:
|
|
printMSG('Exporting to Windows Media Player...')
|
|
logIt('Exporting to Windows Media Player...')
|
|
|
|
wmp = win32com.client.Dispatch('WMPlayer.OCX.7')
|
|
playlists = wmp.playlistCollection.getByName(playList)
|
|
if playlists.count == 0:
|
|
pl = wmp.playlistCollection.newPlaylist(playList)
|
|
else:
|
|
pl = playlists.item(0)
|
|
media = wmp.newMedia('%s\\%s' % (saveLocation, saveName))
|
|
if len(customGenre) > 0:
|
|
# Can not set Genre on some media files
|
|
try:
|
|
media.setItemInfo('WM/Genre', customGenre)
|
|
except:
|
|
pass
|
|
logIt('Adding Media File...')
|
|
pl.appendItem(media)
|
|
wmp.close()
|
|
location = "WMP"
|
|
except Exception, msg:
|
|
logIt('Failed to export to Windows Media Player')
|
|
logIt('ERRMSG: %s' % msg)
|
|
else:
|
|
try:
|
|
if (convertToAAC):
|
|
printMSG('Exporting to iTunes and converting to AAC...')
|
|
logIt('Exporting to iTunes and converting to AAC...')
|
|
else:
|
|
printMSG('Exporting to iTunes...')
|
|
logIt('Exporting to iTunes...')
|
|
|
|
itunes = win32com.client.Dispatch('iTunes.Application')
|
|
playlists = itunes.LibrarySource.Playlists
|
|
pl = playlists.ItemByName(playList)
|
|
if not pl:
|
|
pl = itunes.CreatePlaylist(playList)
|
|
|
|
if (convertToAAC):
|
|
oldEncoder = itunes.CurrentEncoder
|
|
itunes.CurrentEncoder = itunes.Encoders.ItemByName('AAC Encoder')
|
|
handle = itunes.ConvertFile2('%s\\%s' % (saveLocation, saveName))
|
|
while handle.InProgress:
|
|
sleep(3)
|
|
itunes.CurrentEncoder = oldEncoder
|
|
for track in handle.Tracks:
|
|
aacLocation = str(track.Location)
|
|
if os.path.isfile(aacLocation):
|
|
newAacLocation = aacLocation.replace('.m4a', '.m4b')
|
|
shutil.copy(aacLocation, newAacLocation)
|
|
track.Delete()
|
|
os.unlink(aacLocation)
|
|
handle = pl.AddFile(newAacLocation)
|
|
try:
|
|
while handle.InProgress:
|
|
sleep(3)
|
|
except:
|
|
pass
|
|
else:
|
|
handle = pl.AddFile('%s\\%s' % (saveLocation, saveName))
|
|
try:
|
|
while handle.InProgress:
|
|
sleep(3)
|
|
except:
|
|
pass
|
|
for track in handle.Tracks:
|
|
#
|
|
# SET IPXID IN THE COMMENTS FIELD
|
|
#
|
|
iPXID = strftime('\r\n[iPXID:%M%Y%m%H%d%S]\r\n',gmtime())
|
|
track.Comment = track.Comment + iPXID
|
|
if len(customGenre) > 0:
|
|
track.Genre = customGenre
|
|
|
|
location = 'iTunes'
|
|
except Exception, msg:
|
|
logIt('Failed to export to iTunes')
|
|
logIt('ERRMSG: %s' % msg)
|
|
|
|
return iPXID, location
|
|
|
|
|
|
def updateiPhoto(saveLocation, saveName, photoAlbum, type):
|
|
import os, iPXSettings, random
|
|
|
|
printMSG( 'Exporting to iPhoto...')
|
|
|
|
location = ''
|
|
iPhotoID = 0
|
|
iPXID = ''
|
|
|
|
if sys.platform == 'darwin':
|
|
fullPath = saveLocation + '/' + saveName
|
|
|
|
while True:
|
|
scriptName = '/tmp/iPX-iPhoto-%d.scpt' % random.randint(1,90000)
|
|
|
|
if not os.path.isfile(scriptName):
|
|
break
|
|
|
|
f = open(scriptName, 'w')
|
|
|
|
# base values
|
|
f.write('set theAlbum to "%s"\r\n' % photoAlbum)
|
|
f.write('set thePath to "%s"\r\n' % fullPath)
|
|
|
|
f.write('tell application "iPhoto"\r\n')
|
|
|
|
#create album if it doesn't exist
|
|
f.write('if not (exists album theAlbum) then\r\n')
|
|
f.write('new album name theAlbum\r\n')
|
|
f.write('end if\r\n')
|
|
|
|
#import the photo
|
|
f.write('set thePhoto to thePath\r\n')
|
|
f.write('import from thePhoto to album theAlbum\r\n')
|
|
|
|
#get the photo location
|
|
f.write('set importedPhoto to the last item in (every photo in the last import album)\r\n')
|
|
|
|
#
|
|
# SET IPXID IN THE COMMENTS FIELD
|
|
#
|
|
iPXID = strftime('%m%M%Y%H%d%S',gmtime())
|
|
|
|
f.write('set theComment to the comment of importedPhoto\r\n')
|
|
f.write('set the comment of importedPhoto to theComment & "\r\r" & "[iPXID:%s]"\r\n' % iPXID)
|
|
|
|
# return the final Track Location
|
|
f.write('return the (id of importedPhoto) mod (8 ^ 8) as integer\r\n')
|
|
f.write('end tell\r\n')
|
|
|
|
f.close()
|
|
|
|
try:
|
|
ret_pipe = os.popen('/usr/bin/osascript %s' % scriptName)
|
|
iPhotoID = int(ret_pipe.readline())
|
|
except:
|
|
pass
|
|
|
|
if (iPhotoID > 3):
|
|
location = "iPhoto"
|
|
|
|
if type == 0:
|
|
if (iPXSettings.deleteImages > 0):
|
|
logIt('Deleting: %s...' % fullPath)
|
|
if os.path.isfile(fullPath):
|
|
os.unlink(fullPath)
|
|
elif type == 1:
|
|
if (iPXSettings.deleteVideo > 0):
|
|
logIt('Deleting: %s...' % fullPath)
|
|
if os.path.isfile(fullPath):
|
|
os.unlink(fullPath)
|
|
|
|
else:
|
|
printMSG('iPhoto failed to update. Image left in iPodderX Library')
|
|
iPXID = ''
|
|
if not iPXSettings.DEBUG:
|
|
if os.path.isfile(scriptName):
|
|
os.unlink(scriptName)
|
|
|
|
# return the photoID of the file just added
|
|
return iPXID, location
|
|
|
|
def detectFileType(fileName):
|
|
import typeFile, re
|
|
|
|
logIt('Detecting file type...')
|
|
try:
|
|
fileType = typeFile.file(fileName)
|
|
except Exception, msg:
|
|
logIt('Failed to detect type, using data')
|
|
logIt(msg)
|
|
fileType = 'data'
|
|
|
|
if re.search('ascii', fileType, re.IGNORECASE):
|
|
f = open(fileName, 'r')
|
|
for line in f.readlines():
|
|
if re.search('<html>', line, re.IGNORECASE):
|
|
type = 'html'
|
|
break
|
|
if re.search('xml', fileType, re.IGNORECASE):
|
|
f = open(fileName, 'r')
|
|
for line in f.readlines():
|
|
if re.search('<rss', line, re.IGNORECASE):
|
|
type = 'rss'
|
|
break
|
|
elif re.search('<opml', line, re.IGNORECASE):
|
|
type = 'opml'
|
|
break
|
|
|
|
logIt('File type is %s' % fileType)
|
|
return fileType
|
|
|
|
def readplist(pList):
|
|
if sys.platform == 'darwin':
|
|
try:
|
|
plistObj = NSMutableDictionary.dictionary()
|
|
plistObj = NSMutableDictionary.dictionaryWithContentsOfFile_(pList)
|
|
except Exception, msg:
|
|
logIt('Error reading plist: %s' % msg)
|
|
plistObj = {}
|
|
elif sys.platform == 'win32':
|
|
import plistlib
|
|
|
|
try:
|
|
plistObj = plistlib.Plist.fromFile(file(pList))
|
|
except:
|
|
plistObj = plistlib.Plist()
|
|
|
|
return plistObj
|
|
|
|
def writeplist(dictObj, plist):
|
|
import os
|
|
|
|
if sys.platform == 'darwin':
|
|
try:
|
|
dictObj.writeToFile_atomically_(plist, True)
|
|
except Exception, msg:
|
|
logIt('Error saving %s file' % plist)
|
|
logIt('ERRMSG: %s' % msg)
|
|
if os.path.isfile(plist):
|
|
os.unlink(plist)
|
|
elif sys.platform == 'win32':
|
|
import plistlib
|
|
|
|
dictObj.write(plist)
|
|
|
|
def checkReg():
|
|
return 1
|
|
|
|
def checkDir(dir):
|
|
import os
|
|
|
|
if not os.path.isdir(dir):
|
|
os.mkdir(dir)
|
|
|
|
def delTree(dir):
|
|
import os
|
|
|
|
for root, dirs, files in os.walk(dir, topdown=False):
|
|
for name in files:
|
|
os.remove(os.path.join(root, name))
|
|
for name in dirs:
|
|
os.rmdir(os.path.join(root, name))
|
|
|
|
def getHistory():
|
|
import os, iPXSettings
|
|
|
|
logIt('Getting history from history.dat...')
|
|
guids = []
|
|
|
|
if os.path.isfile(iPXSettings.historyFile):
|
|
logIt('Found history.plist, converting to history.dat...')
|
|
try:
|
|
import plistlib
|
|
|
|
tempHist = plistlib.Plist.fromFile(file(iPXSettings.historyFile))
|
|
except Exception, msg:
|
|
logIt('History file could not be read')
|
|
logIt('ERRORMSG: %s' % msg)
|
|
tempHist['downloadHistory'] = list('')
|
|
|
|
try:
|
|
if (len(tempHist['downloadHistory']) > 0):
|
|
for historyItem in tempHist['downloadHistory']:
|
|
if historyItem.has_key('encGUID'):
|
|
guids.append(historyItem['encGUID'])
|
|
|
|
try:
|
|
os.unlink(iPXSettings.historyFile)
|
|
except:
|
|
pass
|
|
|
|
for guid in guids:
|
|
saveHistory(guid)
|
|
except Exception, msg:
|
|
logIt('ERRMSG: %s' % msg)
|
|
else:
|
|
import pickle
|
|
|
|
try:
|
|
if os.path.isfile(iPXSettings.newHistoryFile):
|
|
guids = pickle.load(open(iPXSettings.newHistoryFile))
|
|
except:
|
|
pass
|
|
|
|
return guids
|
|
|
|
def saveHistory(encGUID):
|
|
import pickle, iPXSettings
|
|
|
|
if iPXSettings.ranHistCheck == False:
|
|
iPXSettings.histGUIDs = getHistory()
|
|
iPXSettings.ranHistCheck = True
|
|
|
|
if not encGUID in iPXSettings.histGUIDs:
|
|
logIt('Adding to history.dat: %s' % encGUID)
|
|
iPXSettings.histGUIDs.append(encGUID)
|
|
pickle.dump(iPXSettings.histGUIDs, open(iPXSettings.newHistoryFile,'w'))
|
|
else:
|
|
logIt('Entry already found in history.dat: %s' % encGUID)
|
|
|
|
def doPing(encURL, feedURL):
|
|
import urllib, iPXSettings
|
|
|
|
logIt('Sending Anonymous download feedback...')
|
|
pingURL = 'http://directory.iPodderX.com/feedData/survey/files?url=%s&feed=%s' % (encURL, feedURL)
|
|
|
|
try:
|
|
class AppURLopener(urllib.FancyURLopener):
|
|
version = iPXSettings.USER_AGENT
|
|
|
|
urllib._urlopener = AppURLopener()
|
|
handle = urllib.urlopen(pingURL)
|
|
except Exception, msg:
|
|
logIt('ERRMSG: %s' % msg)
|
|
|
|
def printMSG(msg):
|
|
import os
|
|
try:
|
|
print msg.encode('utf8')
|
|
sys.stdout.flush()
|
|
except Exception, msg:
|
|
logIt('Failed to print console message')
|
|
logIt('ERRMSG: %s' % msg)
|
|
|
|
def checkForScript():
|
|
import iPXSettings, os
|
|
|
|
if sys.platform == 'darwin':
|
|
if not iPXSettings.progName == 'iPodderX':
|
|
if (int(os.popen('ps auxww | grep -i iPodderX.py | grep -i "progName==' + iPXSettings.progName +'" |grep -v /bin/sh | grep -w -v ps | wc -l','r').readline().strip()) > 1):
|
|
printMSG('CHECK_ALREADY_RUNNING')
|
|
for line in os.popen('ps auxww | grep -i iPodderX.py | grep -v /bin/sh | grep -w -v ps','r').readlines():
|
|
logIt(line)
|
|
return True
|
|
else:
|
|
return False
|
|
else:
|
|
if (int(os.popen('ps auxww | grep -i iPodderX.py |grep -v /bin/sh | grep -w -v ps | wc -l','r').readline().strip()) > 2):
|
|
printMSG('CHECK_ALREADY_RUNNING')
|
|
for line in os.popen('ps auxww | grep -i iPodderX.py | grep -v /bin/sh | grep -w -v ps','r').readlines():
|
|
logIt(line)
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
elif sys.platform == 'win32':
|
|
pass
|
|
|
|
def getOpmlFeeds(url):
|
|
import opmlparser, urllib, re
|
|
|
|
ompl_parser = opmlparser.OPMLParser()
|
|
|
|
try:
|
|
data = urllib.urlopen(url).read()
|
|
ompl_parser.feed(data)
|
|
except Exception, msg:
|
|
logIt('OPML Connect Error: %s' % msg)
|
|
|
|
opmlFeeds = {}
|
|
|
|
for node in ompl_parser:
|
|
try:
|
|
if re.search('rss', str(node.get('type')), re.IGNORECASE) or re.search('atom', str(node.get('type')), re.IGNORECASE):
|
|
title = 'Unknown'
|
|
if not str(node.get('title')) == 'None':
|
|
title = str(node.get('title'))
|
|
elif not str(node.get('text')) == 'None':
|
|
title = str(node.get('text'))
|
|
|
|
if not str(node.get('xmlUrl')) == 'None':
|
|
opmlFeeds[title] = str(node.get('xmlUrl'))
|
|
elif not str(node.get('url')) == 'None':
|
|
opmlFeeds[title] = str(node.get('url'))
|
|
|
|
except Exception, msg:
|
|
logIt('OPML Error: %s' % msg)
|
|
|
|
return opmlFeeds
|
|
|
|
def checkForUI():
|
|
import os, iPXSettings
|
|
|
|
if sys.platform == 'darwin':
|
|
if (int(os.popen('ps auxww | grep -i /Contents/MacOS/%s | grep -w -v ps | wc -l' % iPXSettings.progName,'r').readline().strip()) > 1):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
elif sys.platform == 'win32':
|
|
pass
|
|
|
|
def encrypt(plain):
|
|
import pyDes, iPXSettings
|
|
from binascii import hexlify
|
|
|
|
key = iPXSettings.getVar('3DESKey')
|
|
key = key.decode('rot-13')
|
|
|
|
k = pyDes.triple_des(key)
|
|
d = k.encrypt(plain.strip(), ' ')
|
|
|
|
return hexlify(d)
|
|
|
|
def decrypt(ciph):
|
|
import pyDes, iPXSettings
|
|
from binascii import unhexlify
|
|
|
|
ciph = unhexlify(ciph)
|
|
key = iPXSettings.getVar('3DESKey')
|
|
key = key.decode('rot-13')
|
|
|
|
k = pyDes.triple_des(key)
|
|
d = k.decrypt(ciph, ' ')
|
|
|
|
return d
|
|
|
|
def isWhole(x):
|
|
return x == math.floor(x)
|
|
|
|
def setProxy():
|
|
import os, iPXSettings
|
|
from string import split
|
|
|
|
proxy = ''
|
|
if sys.platform == 'darwin':
|
|
import ic
|
|
try:
|
|
inetConfig = ic.IC()
|
|
if 'UseHTTPProxy' in inetConfig and inetConfig['UseHTTPProxy']:
|
|
if inetConfig.has_key('HTTPProxyHost'):
|
|
proxy = 'http://%s' % inetConfig['HTTPProxyHost']
|
|
iPXSettings.useProxyServer = inetConfig['HTTPProxyHost']
|
|
except Exception, msg:
|
|
logIt('Failed to detect OSX Proxy: %s' % msg)
|
|
elif sys.platform == 'win32':
|
|
if iPXSettings.useProxyServer:
|
|
if iPXSettings.useProxyIE:
|
|
try:
|
|
import _winreg as winreg
|
|
|
|
host_port = None
|
|
|
|
# Try to grab current proxy settings from the registry
|
|
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
|
|
'Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings')
|
|
regval = winreg.QueryValueEx(regkey, 'ProxyServer')
|
|
proxyEnabled = winreg.QueryValueEx(regkey, 'ProxyEnable')
|
|
regkey.Close()
|
|
regval = str(regval[0])
|
|
proxyEnabled = proxyEnabled[0]
|
|
if proxyEnabled:
|
|
# Regval can be of two types:
|
|
# - 'myproxy:3128' if one proxy for all protocols
|
|
# - 'ftp=myftpproxy:3128;http=myhttpproxy:3128;...' if several different proxies
|
|
values = regval.split(';')
|
|
if len(values) > 1:
|
|
for s in values:
|
|
scheme, p = s.split('=')
|
|
if scheme == 'http':
|
|
host_port = p
|
|
break
|
|
else:
|
|
host_port = values[0]
|
|
|
|
# Split host and port
|
|
if host_port is not None:
|
|
t = host_port.split(':')
|
|
host = t[0].strip()
|
|
if host:
|
|
try:
|
|
port = int(t[1])
|
|
except:
|
|
port = 80
|
|
|
|
proxy = '%s:%d' % (host, port)
|
|
iPXSettings.proxyServer = host
|
|
iPXSettings.proxyPort = str(port)
|
|
|
|
except Exception, e:
|
|
logIt('Proxy Detection Error: %s' % e)
|
|
else:
|
|
if iPXSettings.useProxyAuth:
|
|
proxy = iPXSettings.proxyUsername + ':' + decrypt(iPXSettings.proxyPassword) + '@' + iPXSettings.proxyServer
|
|
|
|
if len(iPXSettings.proxyPort) > 0:
|
|
proxy = proxy + ':' + iPXSettings.proxyPort
|
|
else:
|
|
proxy = iPXSettings.proxyServer
|
|
if len(iPXSettings.proxyPort) > 0:
|
|
proxy = proxy + ':' + iPXSettings.proxyPort
|
|
if len(proxy) > 0:
|
|
proxy = 'http://' + proxy
|
|
|
|
if len(proxy) > 0:
|
|
os.environ["http_proxy"] = proxy
|
|
os.environ["https_proxy"] = proxy
|
|
else:
|
|
iPXSettings.proxyServer = ''
|
|
iPXSettings.proxyPort = ''
|
|
|
|
return proxy
|
|
|
|
def setOpener(url='', uName=None, pWord=''):
|
|
import urlparse, base64, socket, urllib2, iPXSettings
|
|
|
|
socket.setdefaulttimeout(30)
|
|
|
|
headers = []
|
|
host = urlparse.urlparse(url)[1]
|
|
path = urlparse.urlparse(url)[2]
|
|
|
|
headers.append(('User-Agent', iPXSettings.USER_AGENT))
|
|
if iPXSettings.useProxyServer:
|
|
if iPXSettings.useProxyAuth:
|
|
user_pass = base64.encodestring('%s:%s' %(iPXSettings.proxyUsername,decrypt(iPXSettings.proxyPassword)))
|
|
headers.append(('Proxy-authorization', 'Basic %s' % user_pass))
|
|
|
|
opener = urllib2.build_opener()
|
|
|
|
if not uName == None:
|
|
webAuth = base64.encodestring('%s:%s' % (uName, pWord))
|
|
headers.append(('Authorization', 'Basic ' + webAuth))
|
|
|
|
opener.addheaders = headers
|
|
urllib2.install_opener(opener)
|
|
|
|
return opener
|
|
|
|
def getFileViaProxySSL(url, uName, pWord, justHead=False, dlProgress=False):
|
|
import urlparse, base64, socket, httplib, urllib, os, iPXSettings, re
|
|
from string import split
|
|
|
|
host = urlparse.urlparse(url)[1]
|
|
port=443
|
|
filePath = urlparse.urlparse(url)[2]
|
|
|
|
phost=iPXSettings.proxyServer
|
|
pport=int(iPXSettings.proxyPort)
|
|
|
|
if iPXSettings.SUPERDEBUG:
|
|
logIt('Using Proxy: %s:%d' % (phost, pport))
|
|
|
|
webAuth = base64.encodestring('%s:%s' % (uName, pWord))
|
|
|
|
proxy_authorization = ''
|
|
if iPXSettings.useProxyAuth:
|
|
user=iPXSettings.proxyUsername
|
|
passwd=decrypt(iPXSettings.proxyPassword)
|
|
user_pass=base64.encodestring(user + ':' + passwd)
|
|
proxy_authorization='Proxy-authorization: Basic '+user_pass+'\r\n'
|
|
|
|
proxy_connect='CONNECT %s:%s HTTP/1.0\r\n'%(host,port)
|
|
proxy_pieces=proxy_connect+proxy_authorization+'\r\n'
|
|
if iPXSettings.SUPERDEBUG:
|
|
print proxy_pieces
|
|
|
|
try:
|
|
#now connect, very simple recv and error checking
|
|
proxy=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
|
proxy.settimeout(60)
|
|
proxy.connect((phost,pport))
|
|
proxy.sendall(proxy_pieces+'\r\n')
|
|
response=proxy.recv(8192)
|
|
status=response.split()[1]
|
|
if status!='200': raise Exception, 'Recieved HTTP Status Code: %d' % status
|
|
|
|
#trivial setup for ssl socket
|
|
ssl = socket.ssl(proxy, None, None)
|
|
sock = httplib.FakeSocket(proxy, ssl)
|
|
|
|
#initalize httplib and replace with your socket
|
|
params = urllib.urlencode({})
|
|
headers = {'Authorization':'Basic ' + webAuth, 'User-Agent':iPXSettings.USER_AGENT}
|
|
h=httplib.HTTPConnection('localhost')
|
|
h.sock=sock
|
|
|
|
if justHead:
|
|
saveName = None
|
|
h.request("HEAD", url,headers=headers)
|
|
respObj = h.getresponse()
|
|
url = urlparse.urljoin(url, respObj.getheader('location', ''))
|
|
saveName = url.split('/')[len(url.split('/'))-1]
|
|
logIt(saveName)
|
|
if respObj.status in (301,302,):
|
|
url = urlparse.urljoin(url, respObj.getheader('location', ''))
|
|
#filePath = urlparse.urlparse(url)[2]
|
|
logIt('Redirecting to url: %s' % url)
|
|
try:
|
|
if not respObj.getheader('Content-Disposition') == None:
|
|
if re.search('filename=', respObj.getheader('Content-Disposition'), re.IGNORECASE):
|
|
textSplit = respObj.getheader('Content-Disposition')
|
|
textSplit = textSplit.split(';')
|
|
for text in textSplit:
|
|
if re.search('filename=', text, re.IGNORECASE):
|
|
logIt('Detected New Filename To Use:')
|
|
newSaveNameSplit = text.split('=')
|
|
newSaveName = newSaveNameSplit[len(newSaveNameSplit) -1]
|
|
newSaveName = newSaveName.replace('"', '')
|
|
logIt(newSaveName)
|
|
saveName = newSaveName
|
|
except Exception, msg:
|
|
logIt('Content-Disposition Error: %s' % msg)
|
|
|
|
return respObj.status, url, saveName
|
|
else:
|
|
h.request('GET', url, headers=headers)
|
|
except Exception, msg:
|
|
logIt('Connection Failed')
|
|
logIt('ERRMSG: %s' % msg)
|
|
return 0, None, None
|
|
|
|
if dlProgress:
|
|
import tempfile
|
|
try:
|
|
count = 0
|
|
lastPercentDone = 0
|
|
n = 1024 # number of bytes to read at a time
|
|
fileSize = 0
|
|
r=h.getresponse()
|
|
if r.status == 200:
|
|
fileSize = float(r.getheader('Content-Length', 0.00)) / 1024
|
|
printMSG(';;1;;1;;100.00;;0.00')
|
|
tmpFile = tempfile.mkstemp()
|
|
tmpFileName = tmpFile[1]
|
|
f = open(tmpFileName, 'ab')
|
|
while True:
|
|
a = r.read(n)
|
|
f.write(a)
|
|
if not a: break
|
|
count += len(a) # len(a) may not be same as n for final read
|
|
|
|
percentDone = ((float(count) /1024) / fileSize) * 100
|
|
if percentDone >= lastPercentDone + 1:
|
|
lastPercentDone = percentDone
|
|
printMSG(';;1;;1;;100.00;;%.2f' % percentDone)
|
|
|
|
printMSG(';;1;;1;;100.00;;100.00')
|
|
f.close()
|
|
os.close(tmpFile[0])
|
|
|
|
return 200, tmpFileName, r
|
|
else:
|
|
logIt('Recieved HTTP Code: %s' % r.status)
|
|
return 0, None, None
|
|
|
|
except Exception, msg:
|
|
logIt('Connection Failed')
|
|
logIt('ERRMSG: %s' % msg)
|
|
return 0, None, None
|
|
else:
|
|
try:
|
|
r=h.getresponse()
|
|
data = r.read()
|
|
if iPXSettings.SUPERDEBUG:
|
|
print data
|
|
return 200, data, None
|
|
except Exception, msg:
|
|
logIt('Connection Failed')
|
|
logIt('ERRMSG: %s' % msg)
|
|
return 0, '', None |