92d6e07f3ed621522e9a74a708210bd718f2f0c7
2 # -*- coding: utf-8 -*-
4 __author__
= ("Dylan Lloyd <dylan@psu.edu>")
8 'notifications' : 'true',
9 # NOTIFICATIONS must be a string due to issues noted here:
10 # http://bugs.python.org/issue974019
11 # ConfigParser.getboolean fails when falling back to the default value
12 # if the value type is bool.
13 'youtube-dl' : '/usr/bin/youtube-dl',
14 'default_icon' : '/usr/share/icons/gnome/48x48/mimetypes/gnome-mime-application-x-shockwave-flash.png',
15 'youtube-dl_options' : '--no-progress --ignore-errors --continue --max-quality=22 -o "%(stitle)s---%(id)s.%(ext)s"'
18 import ConfigParser
# This module has been renamed to configparser in python 3.0
22 CONFIG_FILE
= os
.path
.join(os
.path
.expanduser('~'), '.i_like_pandora.config')
23 config
= ConfigParser
.ConfigParser(default_options
)
24 loaded_files
= config
.read(CONFIG_FILE
) # config.read returns an empty array if it fails.
25 if len(loaded_files
) == 0:
26 print 'Can\'t find a configuration file at', CONFIG_FILE
29 USER
= config
.get('settings', 'username')
30 DIR
= os
.path
.expanduser(config
.get('settings', 'download_folder'))
31 NOTIFICATIONS
= config
.getboolean('settings', 'notifications')
32 YT_DL
= config
.get('settings', 'youtube-dl')
33 DEFAULT_ICON
= config
.get('settings', 'default_icon')
34 YT_OPT
= default_options
['youtube-dl_options']
36 print 'There is a formatting error in the configuration file at', CONFIG_FILE
39 from BeautifulSoup
import BeautifulSoup
44 import shlex
, subprocess
52 def fetch_stations(user
):
53 """ This takes a pandora username and returns the a list of the station tokens that the user is subscribed to. """
55 page
= urllib
.urlopen('http://www.pandora.com/favorites/profile_tablerows_station.vm?webname=' + USER
)
56 page
= BeautifulSoup(page
)
57 table
= page
.findAll('div', attrs
={'class':'station_table_row'})
60 for attr
, value
in row
.find('a').attrs
:
62 stations
.append(value
[10:])
65 def fetch_tracks(stations
):
66 """ Takes a list of station tokens and returns a list of Title + Artist strings.
69 for station
in stations
:
70 page
= urllib
.urlopen('http://www.pandora.com/favorites/station_tablerows_thumb_up.vm?token=' + station
+ '&sort_col=thumbsUpDate')
71 page
= BeautifulSoup(page
)
74 for span
in page
.findAll('span', attrs
={'class':'track_title'}):
75 for attr
, value
in span
.attrs
:
76 if attr
== 'tracktitle':
78 for anchor
in page
.findAll('a'):
79 artists
.append(anchor
.string
)
80 if len(titles
) == len(artists
):
83 search_string
= title
+ ' ' + artists
[i
]
84 search_strings
.append(search_string
)
87 # This would mean something strange has happened: there
88 # aren't the same number of titles and artist names on a
93 def search_youtube(search_strings
):
94 """ This takes a list of search strings and tries to find the first result. It returns a list of the youtube video ids of those results.
97 for search_string
in search_strings
:
98 search_url
= 'http://youtube.com/results?search_query=' + urllib
.quote_plus(search_string
)
99 page
= urllib
.urlopen(search_url
)
100 page
= BeautifulSoup(page
)
101 result
= page
.find('div', attrs
={'class':'video-main-content'})
103 print 'odd feedback for search, could not find div at ', search_url
105 for attr
, value
in result
.attrs
:
106 if attr
== 'id' and len(value
[19:]) == 11:
107 video_list
.append(value
[19:])
109 print 'odd feedback for search', search_url
, " : ", value
[19:]
113 def check_for_existing(video_list
):
114 """ Checks the download-folder for existing videos with same id and removes from video_list. """
115 filelist
= os
.listdir(DIR
)
117 for video
in copy
.deepcopy(video_list
):
118 for files
in filelist
:
119 if re
.search(video
,files
):
125 def fetch_videos(video_list
):
126 """ Uses subprocess to trigger a download using youtube-dl of the list created earlier, and triggers notifications if enabled. """
128 args
= shlex
.split(YT_DL
+ ' ' + YT_OPT
)
129 if NOTIFICATIONS
: regex
= re
.compile("\[download\] Destination: (.+)")
130 for video
in video_list
:
132 thread
= subprocess
.Popen(args
+ ["http://youtube.com/watch?v=" + video
], stdout
=subprocess
.PIPE
)
133 output
= thread
.stdout
.read()
135 video_file
= regex
.findall(output
)
136 if len(video_file
) == 0:
138 thumbnail
= hashlib
.md5('file://' + DIR
+ video_file
[0]).hexdigest() + '.png'
139 # Two '/'s instead of three because the path is
140 # absolute; I'm not sure how this'd work on windows.
141 title
, sep
, vid_id
= video_file
[0].rpartition('---')
142 title
= string
.replace(title
, '_', ' ')
143 thumbnail
= os
.path
.join(os
.path
.expanduser('~/.thumbnails/normal'), thumbnail
)
144 if not os
.path
.isfile(thumbnail
):
145 opener
= urllib2
.build_opener()
147 page
= opener
.open('http://img.youtube.com/vi/' + video
+ '/1.jpg')
149 # The thumbnail really should be saved to
150 # ~/.thumbnails/normal (Thumbnail Managing
152 # [http://jens.triq.net/thumbnail-spec/]
153 # As others have had problems anyway
154 # (http://mail.gnome.org/archives/gnome-list/2010-October/msg00009.html)
155 # I decided not to bother at the moment.
156 temp
= tempfile
.NamedTemporaryFile(suffix
='.jpg')
159 note
= pynotify
.Notification(title
, 'video downloaded', temp
.name
)
161 note
= pynotify
.Notification(title
, 'video downloaded', DEFAULT_ICON
)
163 # Generally, this will never happen, because the
164 # video is a new file.
165 note
= pynotify
.Notification(title
, 'video downloaded', thumbnail
)
169 stations
= fetch_stations(USER
)
170 if len(stations
) == 0:
171 print 'Are you sure your pandora profile is public? Can\'t seem to find any stations listed with your account.'
172 search_strings
= fetch_tracks(stations
)
173 videos
= search_youtube(search_strings
)
174 videos
= check_for_existing(videos
)
177 if __name__
== "__main__":