Fixed loading of pynotify and other modules when notifications are disabled.
[i_like_pandora.git] / likes_pandora.py
1 __author__ = ("Dylan Lloyd <dylan@psu.edu>")
2 __license__ = "BSD"
3
4 # SETTINGS
5
6 USER = 'alphabethos'
7 DIR = '/home/dylan/pandora/'
8 YT_DL = '/usr/bin/youtube-dl' # Path to youtube-dl
9 NOTIFICATIONS = True # False
10 DEFAULT_ICON ='/usr/share/icons/gnome/48x48/mimetypes/gnome-mime-application-x-shockwave-flash.png'
11 YT_OPT = '--no-progress --ignore-errors --continue --max-quality=22 -o "%(stitle)s---%(id)s.%(ext)s"'
12 # END OF SETTINGS
13
14 from BeautifulSoup import BeautifulSoup
15 import urllib
16 import urllib2
17 import pynotify
18 import tempfile
19 import string
20 if NOTIFICATIONS:
21 import hashlib
22 import os
23 import shlex, subprocess
24 import re
25
26 def fetch_stations(user):
27 """ This takes a pandora username and returns the a list of the station tokens that the user is subscribed to. """
28 stations = []
29 page = urllib.urlopen('http://www.pandora.com/favorites/profile_tablerows_station.vm?webname=' + USER)
30 page = BeautifulSoup(page)
31 table = page.findAll('div', attrs={'class':'station_table_row'})
32 for row in table:
33 if row.find('a'):
34 for attr, value in row.find('a').attrs:
35 if attr == 'href':
36 stations.append(value[10:])
37 return stations
38
39 def fetch_tracks(stations):
40 """ Takes a list of station tokens and returns a list of youtube search urls.
41 What this should really do is just return the Title + Artist strings.
42 """
43 search_urls = []
44 for station in stations:
45 page = urllib.urlopen('http://www.pandora.com/favorites/station_tablerows_thumb_up.vm?token=' + station + '&sort_col=thumbsUpDate')
46 page = BeautifulSoup(page)
47 titles = []
48 artists = []
49 for span in page.findAll('span', attrs={'class':'track_title'}):
50 for attr, value in span.attrs:
51 if attr == 'tracktitle':
52 titles.append(value)
53 for anchor in page.findAll('a'):
54 artists.append(anchor.string)
55 if len(titles) == len(artists):
56 i = 0
57 for title in titles:
58 search_url = 'http://youtube.com/results?search_query=' + urllib.quote_plus(title + ' ' + artists[i])
59 search_urls.append(search_url)
60 i += 1
61 else:
62 pass ## ERROR
63 return search_urls
64
65 def fetch_search_video_ids(search_urls):
66 """ This takes a list of youtube search urls and tries to find the first result. It returns a list of youtube video ids.
67 It really should take a list of ids instead.
68 """
69 video_list = []
70 for url in search_urls:
71 page = urllib.urlopen(url)
72 page = BeautifulSoup(page)
73 result = page.find('div', attrs={'class':'video-main-content'})
74 for attr, value in result.attrs:
75 if attr == 'id' and len(value[19:]) == 11:
76 video_list.append(value[19:])
77 elif attr == 'id':
78 print 'odd feedback for url', url, " : ", value[19:]
79 return video_list
80
81
82 def check_for_existing():
83 """ Checks the download-folder for existing videos with same id and removes from videolist. """
84 videolist = get_video_ids()
85 filelist = os.listdir(DIR)
86 for video in copy.deepcopy(videolist):
87 for files in filelist:
88 if re.search(video,files):
89 del videolist[video]
90 return videolist
91
92 def fetch_videos(videolist):
93 """ Uses subprocess to trigger a download using youtube-dl of the list created earlier, and triggers notifications if enabled. """
94 os.chdir(DIR)
95 args = shlex.split(YT_DL + ' ' + YT_OPT)
96 if NOTIFICATIONS: regex = re.compile("\[download\] Destination: (.+)")
97 for item in videolist:
98 if item:
99 thread = subprocess.Popen(args + [item], stdout=subprocess.PIPE)
100 output = thread.stdout.read()
101 if NOTIFICATIONS:
102 video_file = regex.findall(output)
103 if len(video_file) == 0:
104 break
105 thumbnail = hashlib.md5('file://' + DIR + video_file[0]).hexdigest() + '.png'
106 # Two '/'s instead of three because the path is
107 # absolute; I'm not sure how this'd work on windows.
108 title, sep, vid_id = video_file[0].rpartition('---')
109 title = string.replace(title, '_', ' ')
110 thumbnail = os.path.join(os.path.expanduser('~/.thumbnails/normal'), thumbnail)
111 if not os.path.isfile(thumbnail):
112 opener = urllib2.build_opener()
113 try:
114 page = opener.open('http://img.youtube.com/vi/' + item + '/1.jpg')
115 thumb = page.read()
116 # The thumbnail really should be saved to
117 # ~/.thumbnails/normal (Thumbnail Managing
118 # Standard)
119 # [http://jens.triq.net/thumbnail-spec/]
120 # As others have had problems anyway
121 # (http://mail.gnome.org/archives/gnome-list/2010-October/msg00009.html)
122 # I decided not to bother at the moment.
123 temp = tempfile.NamedTemporaryFile(suffix='.jpg')
124 temp.write(thumb)
125 temp.flush()
126 note = pynotify.Notification(title, 'video downloaded', temp.name)
127 except:
128 note = pynotify.Notification(title, 'video downloaded', DEFAULT_ICON)
129 else:
130 # Generally, this will never happen, because the
131 # video is a new file.
132 note = pynotify.Notification(title, 'video downloaded', thumbnail)
133 note.show()
134
135
136 def main():
137 stations = fetch_stations(USER)
138 search_urls = fetch_tracks(stations)
139 video_list = fetch_search_video_ids(search_urls)
140 fetch_videos(video_list)
141
142 if __name__ == "__main__":
143 main()