2 # -*- coding: utf-8 -*-
4 __author__
= ("Dylan Lloyd <dylan@psu.edu>")
9 USER
= 'alphabethos' # pandora account name http://pandora.com/people<USER>
10 DIR
= '/home/dylan/pandora/' # where to download the videos - will not be automatically created
11 YT_DL
= '/usr/bin/youtube-dl' # Path to youtube-dl
12 NOTIFICATIONS
= True # False
13 DEFAULT_ICON
='/usr/share/icons/gnome/48x48/mimetypes/gnome-mime-application-x-shockwave-flash.png' # for notifications
14 YT_OPT
= '--no-progress --ignore-errors --continue --max-quality=22 -o "%(stitle)s---%(id)s.%(ext)s"'
17 from BeautifulSoup
import BeautifulSoup
23 import shlex
, subprocess
31 def fetch_stations(user
):
32 """ This takes a pandora username and returns the a list of the station tokens that the user is subscribed to. """
34 page
= urllib
.urlopen('http://www.pandora.com/favorites/profile_tablerows_station.vm?webname=' + USER
)
35 page
= BeautifulSoup(page
)
36 table
= page
.findAll('div', attrs
={'class':'station_table_row'})
39 for attr
, value
in row
.find('a').attrs
:
41 stations
.append(value
[10:])
44 def fetch_tracks(stations
):
45 """ Takes a list of station tokens and returns a list of youtube search urls.
46 What this should really do is just return the Title + Artist strings.
49 for station
in stations
:
50 page
= urllib
.urlopen('http://www.pandora.com/favorites/station_tablerows_thumb_up.vm?token=' + station
+ '&sort_col=thumbsUpDate')
51 page
= BeautifulSoup(page
)
54 for span
in page
.findAll('span', attrs
={'class':'track_title'}):
55 for attr
, value
in span
.attrs
:
56 if attr
== 'tracktitle':
58 for anchor
in page
.findAll('a'):
59 artists
.append(anchor
.string
)
60 if len(titles
) == len(artists
):
63 search_string
= title
+ ' ' + artists
[i
]
64 search_strings
.append(search_string
)
70 def fetch_search_video_ids(search_strings
):
71 """ This takes a list of youtube search urls and tries to find the first result. It returns a list of youtube video ids.
72 It really should take a list of ids instead.
75 for search_string
in search_strings
:
76 search_url
= 'http://youtube.com/results?search_query=' + urllib
.quote_plus(search_string
)
77 page
= urllib
.urlopen(search_url
)
78 page
= BeautifulSoup(page
)
79 result
= page
.find('div', attrs
={'class':'video-main-content'})
81 print 'odd feedback for search, could not find div at ', search_url
83 for attr
, value
in result
.attrs
:
84 if attr
== 'id' and len(value
[19:]) == 11:
85 video_list
.append(value
[19:])
87 print 'odd feedback for search', search_url
, " : ", value
[19:]
91 def check_for_existing(video_list
):
92 """ Checks the download-folder for existing videos with same id and removes from video_list. """
93 filelist
= os
.listdir(DIR
)
95 for video
in copy
.deepcopy(video_list
):
96 for files
in filelist
:
97 if re
.search(video
,files
):
103 def fetch_videos(videolist
):
104 """ Uses subprocess to trigger a download using youtube-dl of the list created earlier, and triggers notifications if enabled. """
106 args
= shlex
.split(YT_DL
+ ' ' + YT_OPT
)
107 if NOTIFICATIONS
: regex
= re
.compile("\[download\] Destination: (.+)")
108 for item
in videolist
:
110 thread
= subprocess
.Popen(args
+ ["http://youtube.com/watch?v=" + item
], stdout
=subprocess
.PIPE
)
111 output
= thread
.stdout
.read()
113 video_file
= regex
.findall(output
)
114 if len(video_file
) == 0:
116 thumbnail
= hashlib
.md5('file://' + DIR
+ video_file
[0]).hexdigest() + '.png'
117 # Two '/'s instead of three because the path is
118 # absolute; I'm not sure how this'd work on windows.
119 title
, sep
, vid_id
= video_file
[0].rpartition('---')
120 title
= string
.replace(title
, '_', ' ')
121 thumbnail
= os
.path
.join(os
.path
.expanduser('~/.thumbnails/normal'), thumbnail
)
122 if not os
.path
.isfile(thumbnail
):
123 opener
= urllib2
.build_opener()
125 page
= opener
.open('http://img.youtube.com/vi/' + item
+ '/1.jpg')
127 # The thumbnail really should be saved to
128 # ~/.thumbnails/normal (Thumbnail Managing
130 # [http://jens.triq.net/thumbnail-spec/]
131 # As others have had problems anyway
132 # (http://mail.gnome.org/archives/gnome-list/2010-October/msg00009.html)
133 # I decided not to bother at the moment.
134 temp
= tempfile
.NamedTemporaryFile(suffix
='.jpg')
137 note
= pynotify
.Notification(title
, 'video downloaded', temp
.name
)
139 note
= pynotify
.Notification(title
, 'video downloaded', DEFAULT_ICON
)
141 # Generally, this will never happen, because the
142 # video is a new file.
143 note
= pynotify
.Notification(title
, 'video downloaded', thumbnail
)
148 stations
= fetch_stations(USER
)
149 if len(stations
) == 0:
150 print 'are you sure your pandora profile is public?'
151 search_urls
= fetch_tracks(stations
)
152 video_list
= fetch_search_video_ids(search_urls
)
153 video_list
= check_for_existing(video_list
)
154 fetch_videos(video_list
)
156 if __name__
== "__main__":