1 | #!/usr/bin/python
|
---|
2 | # -*- coding: utf-8 -*-
|
---|
3 |
|
---|
4 | from appscript import *
|
---|
5 | from itertools import izip
|
---|
6 | import htmlentitydefs
|
---|
7 | import re
|
---|
8 |
|
---|
9 | # based on <http://sebsauvage.net/python/snyppets/>
|
---|
10 | RE_ENTITY_CHR = re.compile(u'&(%s);' % u'|'.join(htmlentitydefs.name2codepoint))
|
---|
11 | RE_ENTITY_DEC = re.compile(u'&#(\d+);')
|
---|
12 | RE_ENTITY_HEX = re.compile(u'&#x(\w+);')
|
---|
13 | def decode_entities(s):
|
---|
14 | def entity2char(m):
|
---|
15 | entity = m.group(1)
|
---|
16 | if entity in htmlentitydefs.name2codepoint:
|
---|
17 | return unichr(htmlentitydefs.name2codepoint[entity])
|
---|
18 | return u' ' # Unknown entity: We replace with a space.
|
---|
19 | replaced = 0
|
---|
20 | s, n = RE_ENTITY_CHR.subn(entity2char, s)
|
---|
21 | replaced += n
|
---|
22 | s, n = RE_ENTITY_DEC.subn(lambda x: unichr(int(x.group(1))), s)
|
---|
23 | replaced += n
|
---|
24 | s, n = RE_ENTITY_HEX.subn(lambda x: unichr(int(x.group(1),16)), s)
|
---|
25 | replaced += n
|
---|
26 | return s, replaced
|
---|
27 |
|
---|
28 | OPEN_P = r'<[Pp][^>]*>'
|
---|
29 | RE_OPEN_P = re.compile(OPEN_P)
|
---|
30 | RE_MATCHED_P = re.compile(OPEN_P + r'(?!<[Pp])(.*)</[Pp]>')
|
---|
31 | RE_TOO_MANY_CR = re.compile(r'\s*\n\s*\n\s*\n+', re.U)
|
---|
32 | RE_TAG = re.compile(r'<[^>]*>')
|
---|
33 | RE_WHITESPACE = re.compile(r'\s+', re.U)
|
---|
34 | RE_LEADING_WHITESPACE = re.compile(r'^\s+(.*)', re.U)
|
---|
35 | RE_TRAILING_WHITESPACE = re.compile(r'(.*)\s+$', re.U)
|
---|
36 |
|
---|
37 | def html_to_text(s):
|
---|
38 | s, replaced = decode_entities(s)
|
---|
39 | s = s.replace('\r\n', '\n')
|
---|
40 | s = s.replace('\r', '\n')
|
---|
41 | if replaced > 0 or RE_TAG.search(s): # HTML
|
---|
42 | s = RE_WHITESPACE.sub(' ', s)
|
---|
43 | s = RE_MATCHED_P.sub(r'\1\n\n', s)
|
---|
44 | s = RE_OPEN_P.sub(r'\n\n', s)
|
---|
45 | s = RE_TAG.sub('', s)
|
---|
46 | s = RE_TOO_MANY_CR.sub(r'\n\n', s)
|
---|
47 | s = RE_LEADING_WHITESPACE.sub(r'\1', s)
|
---|
48 | s = RE_TRAILING_WHITESPACE.sub(r'\1', s)
|
---|
49 | return s
|
---|
50 |
|
---|
51 | def podcasts_to_lyrics():
|
---|
52 | iTunes = app('iTunes')
|
---|
53 |
|
---|
54 | podcasts = iTunes.tracks[its.podcast == True]
|
---|
55 |
|
---|
56 | ids = podcasts.id()
|
---|
57 | descs = podcasts.description()
|
---|
58 | longdescs = podcasts.long_description()
|
---|
59 | lyricses = podcasts.lyrics()
|
---|
60 |
|
---|
61 | for id_, desc, longdesc, lyrics in izip(ids, descs, longdescs, lyricses):
|
---|
62 | if lyrics == k.missing_value: # video
|
---|
63 | continue
|
---|
64 |
|
---|
65 | if longdesc == k.missing_value:
|
---|
66 | if desc == k.missing_value:
|
---|
67 | continue
|
---|
68 | longdesc = desc
|
---|
69 | else:
|
---|
70 | longdesc = html_to_text(longdesc)
|
---|
71 |
|
---|
72 | if lyrics == longdesc:
|
---|
73 | continue
|
---|
74 |
|
---|
75 | iTunes.tracks[its.id == id_].lyrics.set(longdesc)
|
---|
76 |
|
---|
77 |
|
---|
78 |
|
---|
79 | if __name__ == '__main__':
|
---|
80 | # sync_ipod()
|
---|
81 | # - poll whether 'Sync' button is enabled
|
---|
82 | # update_podcasts()
|
---|
83 | # - poll '[queued for download]' on last podcast, then any remaining
|
---|
84 | podcasts_to_lyrics()
|
---|
85 | # sync_ipod()
|
---|
86 |
|
---|
87 | # write podcast entry replacing http://njr.sabi.net/2007/03/04/an-applescript-to-update-podcasts-and-your-ipod/ - now we can use accessibility
|
---|