Now stores user data in ~/.i_like_pandora.config
[i_like_pandora.git] / likes_pandora.py
old mode 100644 (file)
new mode 100755 (executable)
index c787863..181cda7
@@ -1,27 +1,52 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 __author__ = ("Dylan Lloyd <dylan@psu.edu>")
 __license__ = "BSD"
 
-# SETTINGS
+default_options = {
+    'notifications' : 'true',
+    # NOTIFICATIONS must be a string due to issues noted here:
+    # http://bugs.python.org/issue974019
+    # ConfigParser.getboolean fails when falling back to the default value
+    # if the value type is bool.
+    'youtube-dl' : '/usr/bin/youtube-dl',
+    'default_icon' : '/usr/share/icons/gnome/48x48/mimetypes/gnome-mime-application-x-shockwave-flash.png',
+    'youtube-dl_options' : '--no-progress --ignore-errors --continue --max-quality=22 -o "%(stitle)s---%(id)s.%(ext)s"'
+}
+
+import ConfigParser # This module has been renamed to configparser in python  3.0
+import sys
+import os
 
-USER = 'alphabethos'
-DIR = '/home/dylan/pandora/'
-YT_DL = '/usr/bin/youtube-dl' # Path to youtube-dl
-NOTIFICATIONS = True # False
-DEFAULT_ICON ='/usr/share/icons/gnome/48x48/mimetypes/gnome-mime-application-x-shockwave-flash.png'
-YT_OPT = '--no-progress --ignore-errors --continue --max-quality=22 -o "%(stitle)s---%(id)s.%(ext)s"'
-# END OF SETTINGS
+CONFIG_FILE= os.path.join(os.path.expanduser('~'), '.i_like_pandora.config')
+config = ConfigParser.ConfigParser(default_options)
+loaded_files = config.read(CONFIG_FILE) # config.read returns an empty array if it fails.
+if len(loaded_files) == 0:
+    print 'Can\'t find a configuration file at', CONFIG_FILE
+    sys.exit()
+try:
+    USER = config.get('settings', 'username')
+    DIR = os.path.expanduser(config.get('settings', 'download_folder'))
+    NOTIFICATIONS = config.getboolean('settings', 'notifications')
+    YT_DL = config.get('settings', 'youtube-dl')
+    DEFAULT_ICON = config.get('settings', 'default_icon')
+except:
+    print 'There is a formatting error in the configuration file at', CONFIG_FILE
+    sys.exit()
 
 from BeautifulSoup import BeautifulSoup
 import urllib
 import urllib2
-import pynotify
-import tempfile
-import string
+import re
+import copy
+import shlex, subprocess
+
 if NOTIFICATIONS:
+    import pynotify
     import hashlib
-    import os
-    import shlex, subprocess
-    import re
+    import tempfile
+    import string
 
 def fetch_stations(user):
     """ This takes a pandora username and returns the a list of the station tokens that the user is subscribed to. """
@@ -37,10 +62,9 @@ def fetch_stations(user):
     return stations
 
 def fetch_tracks(stations):
-    """ Takes a list of station tokens and returns a list of youtube search urls.
-        What this should really do is just return the Title + Artist strings.
+    """ Takes a list of station tokens and returns a list of Title + Artist strings.
     """
-    search_urls = []
+    search_strings = []
     for station in stations:
         page = urllib.urlopen('http://www.pandora.com/favorites/station_tablerows_thumb_up.vm?token=' + station + '&sort_col=thumbsUpDate')
         page = BeautifulSoup(page)
@@ -55,48 +79,56 @@ def fetch_tracks(stations):
         if len(titles) == len(artists):
             i = 0
             for title in titles:
-                search_url = 'http://youtube.com/results?search_query=' + urllib.quote_plus(title + ' ' + artists[i])
-                search_urls.append(search_url)
+                search_string = title + ' ' + artists[i]
+                search_strings.append(search_string)
                 i += 1
         else:
-           pass  ## ERROR
-    return search_urls
+            # This would mean something strange has happened: there
+            # aren't the same number of titles and artist names on a
+            # station page.
+            pass
+    return search_strings
 
-def fetch_search_video_ids(search_urls):
-    """ This takes a list of youtube search urls and tries to find the first result. It returns a list of youtube video ids.
-        It really should take a list of ids instead.
+def search_youtube(search_strings):
+    """ 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.
     """
     video_list = []
-    for url in search_urls:
-        page = urllib.urlopen(url)
+    for search_string in search_strings:
+        search_url = 'http://youtube.com/results?search_query=' + urllib.quote_plus(search_string)
+        page = urllib.urlopen(search_url)
         page = BeautifulSoup(page)
         result = page.find('div', attrs={'class':'video-main-content'})
+        if result == None:
+            print 'odd feedback for search, could not find div at ', search_url
+            continue
         for attr, value in result.attrs:
             if attr == 'id' and len(value[19:]) == 11:
                 video_list.append(value[19:])
             elif attr == 'id':
-                print 'odd feedback for url', url, " : ", value[19:]
+                print 'odd feedback for search', search_url, " : ", value[19:]
     return video_list
 
 
-def check_for_existing():
-    """ Checks the download-folder for existing videos with same id and removes from videolist. """
-    videolist = get_video_ids()
+def check_for_existing(video_list):
+    """ Checks the download-folder for existing videos with same id and removes from video_list. """
     filelist = os.listdir(DIR)
-    for video in copy.deepcopy(videolist):
+    i = 0
+    for video in copy.deepcopy(video_list):
         for files in filelist:
             if re.search(video,files):
-                del videolist[video]
-    return videolist
+                del video_list[i]
+                i -= 1
+        i += 1
+    return video_list
 
-def fetch_videos(videolist):
+def fetch_videos(video_list):
     """ Uses subprocess to trigger a download using youtube-dl of the list created earlier, and triggers notifications if enabled. """
     os.chdir(DIR)
     args = shlex.split(YT_DL + ' ' + YT_OPT)
     if NOTIFICATIONS: regex = re.compile("\[download\] Destination: (.+)")
-    for item in videolist:
-        if item:
-            thread = subprocess.Popen(args + [item], stdout=subprocess.PIPE)
+    for video in video_list:
+        if video:
+            thread = subprocess.Popen(args + ["http://youtube.com/watch?v=" + video], stdout=subprocess.PIPE)
             output = thread.stdout.read()
             if NOTIFICATIONS:
                 video_file = regex.findall(output)
@@ -111,7 +143,7 @@ def fetch_videos(videolist):
                 if not os.path.isfile(thumbnail):
                     opener = urllib2.build_opener()
                     try:
-                        page = opener.open('http://img.youtube.com/vi/' + item + '/1.jpg')
+                        page = opener.open('http://img.youtube.com/vi/' + video + '/1.jpg')
                         thumb = page.read()
                         # The thumbnail really should be saved to
                         # ~/.thumbnails/normal (Thumbnail Managing
@@ -132,12 +164,14 @@ def fetch_videos(videolist):
                     note = pynotify.Notification(title, 'video downloaded', thumbnail)
                 note.show()
 
-
 def main():
     stations = fetch_stations(USER)
-    search_urls = fetch_tracks(stations)
-    video_list = fetch_search_video_ids(search_urls)
-    fetch_videos(video_list)
+    if len(stations) == 0:
+        print 'Are you sure your pandora profile is public? Can\'t seem to find any stations listed with your account.'
+    search_strings = fetch_tracks(stations)
+    videos = search_youtube(search_strings)
+    videos = check_for_existing(videos)
+    fetch_videos(videos)
 
 if __name__ ==  "__main__":
     main()