| 1 | #!/usr/bin/python |
|---|
| 2 | |
|---|
| 3 | import sys |
|---|
| 4 | import datetime |
|---|
| 5 | import time |
|---|
| 6 | import re |
|---|
| 7 | import getpass |
|---|
| 8 | import os.path |
|---|
| 9 | from optparse import OptionParser |
|---|
| 10 | from rockfm import rockbox |
|---|
| 11 | from rockfm import Scrobbler |
|---|
| 12 | from rockfm import rockfm |
|---|
| 13 | |
|---|
| 14 | current_version = '0.4' |
|---|
| 15 | |
|---|
| 16 | def setup_args(): |
|---|
| 17 | parser = OptionParser(usage='%prog [options]\nrock.fm -- ' + |
|---|
| 18 | 'submit your songs played in rockbox to last.fm', |
|---|
| 19 | version='rock.fm %s' % current_version) |
|---|
| 20 | |
|---|
| 21 | parser.add_option('-c', '--config', action='store', type='string', |
|---|
| 22 | dest='conf_file', help='Location of the configuration file') |
|---|
| 23 | parser.add_option('-n', '--no-config', action='store_true', |
|---|
| 24 | dest='no_conf', help='Do not use a configuration file') |
|---|
| 25 | parser.add_option('-d', '--directory', action='store', type='string', |
|---|
| 26 | dest='log_dir', help='Directory of the scrobbler log file', |
|---|
| 27 | default=None) |
|---|
| 28 | parser.add_option('-D', '--dry-run', '--pretend', action='store_true', |
|---|
| 29 | dest='pretend', help="Don't actually contact the servers") |
|---|
| 30 | parser.add_option('-f', '--file', action='store', type='string', |
|---|
| 31 | dest='log_file', help='Scrobbler log file', default=None) |
|---|
| 32 | parser.add_option('-u', '--username', action='store', type='string', |
|---|
| 33 | dest='username', help='Last.fm username') |
|---|
| 34 | parser.add_option('-P', '--password', action='store', type='string', |
|---|
| 35 | dest='password', help='Last.fm password (plaintext)') |
|---|
| 36 | parser.add_option('-p', '--prompt', action='store_true', |
|---|
| 37 | dest='prompt_pw', help='Prompt for a password to be entered') |
|---|
| 38 | parser.add_option('-U', '--utc-offset', action='store', type='float', |
|---|
| 39 | default=100.0, dest='utc_offset', |
|---|
| 40 | help='Override UTC offset autodetection') |
|---|
| 41 | parser.add_option('-m', '--max', action='store', type='int', default=1, |
|---|
| 42 | dest='max_subs', help='Maximum songs to submit in one request') |
|---|
| 43 | parser.add_option('-b', '--backup-dir', action='store', type='string', |
|---|
| 44 | dest='backup_dir', help='Create a backup of the log file ' + |
|---|
| 45 | 'in the given directory') |
|---|
| 46 | parser.add_option('-g', '--gui', action='store_true', default=True, |
|---|
| 47 | dest='use_gui', help='Use the GUI interface') |
|---|
| 48 | parser.add_option('-N', '--no-gui', action='store_false', |
|---|
| 49 | dest='use_gui', help="Don't use the GUI interface") |
|---|
| 50 | parser.add_option('-v', '--verbose', action='count', |
|---|
| 51 | dest='verbose', help='Print status updates to the screen. ' + |
|---|
| 52 | 'Use twice for extra verbosity') |
|---|
| 53 | |
|---|
| 54 | return parser |
|---|
| 55 | |
|---|
| 56 | def show_gui(scrobbler,conf,args): |
|---|
| 57 | """Processes the logic of the GUI""" |
|---|
| 58 | # if we can't load PyQt4, exit |
|---|
| 59 | try: |
|---|
| 60 | import rockfm.ui.RockApp |
|---|
| 61 | except ImportError, error: |
|---|
| 62 | sys.stderr.write('ImportError: %s' % (error)) |
|---|
| 63 | sys.exit(6) |
|---|
| 64 | |
|---|
| 65 | # calling the RockApp exits the app when closed |
|---|
| 66 | app = rockfm.ui.RockApp(scrobbler,conf,args) |
|---|
| 67 | |
|---|
| 68 | if __name__ == '__main__': |
|---|
| 69 | # parse command line options, and exit if given a bad one |
|---|
| 70 | try: |
|---|
| 71 | parser = setup_args() |
|---|
| 72 | (options, args) = parser.parse_args() |
|---|
| 73 | except Exception, strerr: |
|---|
| 74 | sys.stderr.write('Error parsing arguments: %s' % strerr) |
|---|
| 75 | sys.exit(1) |
|---|
| 76 | |
|---|
| 77 | # create the new scrobbler object |
|---|
| 78 | scrobbler = Scrobbler.Scrobbler('rb2', '0.1', options.username, |
|---|
| 79 | options.password) |
|---|
| 80 | |
|---|
| 81 | # look for a conf file in standard locations |
|---|
| 82 | if options.no_conf is not False and options.conf_file is None: |
|---|
| 83 | for conf_loc in ('~/.config/rockfm', '~/.rockfm', |
|---|
| 84 | '~/rockfm/rockfm.conf', '/usr/share/rockfm/rockfm.conf', |
|---|
| 85 | '/usr/local/share/rockfm/rockfm.conf', '/etc/rockfm.conf'): |
|---|
| 86 | if os.path.exists(os.path.expanduser(conf_loc)): |
|---|
| 87 | options.conf_file = os.path.expanduser(conf_loc) |
|---|
| 88 | break |
|---|
| 89 | |
|---|
| 90 | scrobbler.pretend = options.pretend |
|---|
| 91 | conf_settings = {} |
|---|
| 92 | if options.log_file is not None or options.log_dir is not None: |
|---|
| 93 | forced_log = True |
|---|
| 94 | else: |
|---|
| 95 | forced_log = False |
|---|
| 96 | |
|---|
| 97 | if not options.no_conf and options.conf_file is not None: |
|---|
| 98 | conf_settings = rockfm.read_config(options.conf_file) |
|---|
| 99 | |
|---|
| 100 | # set the required arguments |
|---|
| 101 | if options.username is None: |
|---|
| 102 | try: |
|---|
| 103 | scrobbler.username = conf_settings['username'] |
|---|
| 104 | except KeyError: pass |
|---|
| 105 | |
|---|
| 106 | if options.password is None and options.prompt_pw is not True: |
|---|
| 107 | try: |
|---|
| 108 | scrobbler.set_md5_pw(conf_settings['password']) |
|---|
| 109 | except KeyError: pass |
|---|
| 110 | |
|---|
| 111 | if options.log_file is None and options.log_dir is None: |
|---|
| 112 | if not conf_settings.has_key('log_file'): |
|---|
| 113 | try: |
|---|
| 114 | options.log_file = conf_settings['log_dir'] + \ |
|---|
| 115 | '/.scrobbler.log' |
|---|
| 116 | except KeyError: pass |
|---|
| 117 | else: |
|---|
| 118 | options.log_file = conf_settings['log_file'] |
|---|
| 119 | |
|---|
| 120 | # try the rest of them. pass on exceptions, since we expect |
|---|
| 121 | # them if the key has not been set in the config function |
|---|
| 122 | if options.utc_offset is None: |
|---|
| 123 | try: |
|---|
| 124 | options.utc_offset = conf_settings['utc_offset'] |
|---|
| 125 | except: pass |
|---|
| 126 | |
|---|
| 127 | if options.max_subs is None: |
|---|
| 128 | try: |
|---|
| 129 | options.max_subs = int(conf_settings['max_submit']) |
|---|
| 130 | except: pass |
|---|
| 131 | |
|---|
| 132 | if options.use_gui is None: |
|---|
| 133 | try: |
|---|
| 134 | if int(conf_settings['gui']) > 0: |
|---|
| 135 | options.use_gui = True |
|---|
| 136 | else: |
|---|
| 137 | options.use_gui = False |
|---|
| 138 | except: pass |
|---|
| 139 | |
|---|
| 140 | if options.verbose is None: |
|---|
| 141 | try: |
|---|
| 142 | if int(conf_settings['verbose']) == 1: |
|---|
| 143 | scrobbler.set_verbose(True, False, use_gui) |
|---|
| 144 | elif int(conf_settings['verbose']) > 1: |
|---|
| 145 | scrobbler.set_verbose(True, True, use_gui) |
|---|
| 146 | except: pass |
|---|
| 147 | |
|---|
| 148 | if options.log_dir is not None and options.log_file is None: |
|---|
| 149 | options.log_file = options.log_dir + '/.scrobbler.log' |
|---|
| 150 | |
|---|
| 151 | # backup the log file |
|---|
| 152 | if options.backup_dir is not None and options.log_file is not None and \ |
|---|
| 153 | options.use_gui is not True: |
|---|
| 154 | try: |
|---|
| 155 | if scrobbler.verbose: |
|---|
| 156 | sys.stdout.write('Copying scrobbler log file from %s to %s' % |
|---|
| 157 | (options.log_file, options.backup_dir)) |
|---|
| 158 | rockbox.backup_log(options.log_file, options.backup_dir) |
|---|
| 159 | except IOError, err: |
|---|
| 160 | sys.stderr.write('Could not backup log file: %s\n' % err) |
|---|
| 161 | |
|---|
| 162 | # exit gracefully if this was the only intention |
|---|
| 163 | if scrobbler.username == None or options.prompt_pw is None: |
|---|
| 164 | sys.exit(0) |
|---|
| 165 | |
|---|
| 166 | if options.use_gui: |
|---|
| 167 | conf_settings['log_file'] = options.log_file |
|---|
| 168 | conf_settings['file'] = options.conf_file |
|---|
| 169 | conf_settings['forced_log'] = forced_log |
|---|
| 170 | show_gui(scrobbler,conf_settings,[]) |
|---|
| 171 | |
|---|
| 172 | if options.prompt_pw: |
|---|
| 173 | while scrobbler.password == '' or scrobbler.password is None: |
|---|
| 174 | scrobbler.password = getpass.getpass('Password: ') |
|---|
| 175 | |
|---|
| 176 | # show usage if log file, username, or password are not present |
|---|
| 177 | # and no configuration file exists |
|---|
| 178 | if (options.log_file is None or scrobbler.username is None or |
|---|
| 179 | scrobbler.password is None) and options.use_gui is False: |
|---|
| 180 | parser.print_help() |
|---|
| 181 | sys.exit(2) |
|---|
| 182 | |
|---|
| 183 | scrobbler.set_max_submit(options.max_subs) |
|---|
| 184 | if options.verbose == 1: |
|---|
| 185 | scrobbler.set_verbose(True, False) |
|---|
| 186 | elif options.verbose > 1: |
|---|
| 187 | scrobbler.set_verbose(True, True) |
|---|
| 188 | |
|---|
| 189 | # compute the utc offset, if not given |
|---|
| 190 | if options.utc_offset == 100.0: |
|---|
| 191 | local = time.time() |
|---|
| 192 | utc = time.mktime(time.gmtime(local)) |
|---|
| 193 | options.utc_offset = local - utc |
|---|
| 194 | if scrobbler.verbose: |
|---|
| 195 | print 'Guessing UTC offset:', options.utc_offset, 'seconds:', \ |
|---|
| 196 | options.utc_offset / 3600, 'hours' |
|---|
| 197 | else: |
|---|
| 198 | # set the utc offset to seconds |
|---|
| 199 | if options.utc_offset > -12.0 and options.utc_offset < 13.0: |
|---|
| 200 | options.utc_offset = options.utc_offset * 3600 |
|---|
| 201 | |
|---|
| 202 | (header,log_array) = rockbox.read_log(options.log_file) |
|---|
| 203 | for entry in log_array: |
|---|
| 204 | artist, album, track, track_num, length, status, time = entry |
|---|
| 205 | if status == 'L' and int(length) > 30: |
|---|
| 206 | corrected_time = float(time) - float(options.utc_offset) |
|---|
| 207 | if scrobbler.vv: |
|---|
| 208 | print 'changed from', datetime.datetime.utcfromtimestamp( |
|---|
| 209 | float(time)), |
|---|
| 210 | print 'to', datetime.datetime.utcfromtimestamp(float( |
|---|
| 211 | corrected_time)) |
|---|
| 212 | scrobbler.queue_song(artist, track, album, '', length, |
|---|
| 213 | corrected_time) |
|---|
| 214 | |
|---|
| 215 | handshake_succeeded = False |
|---|
| 216 | while not handshake_succeeded: |
|---|
| 217 | try: |
|---|
| 218 | handshake_succeeded = rockfm.handshake(scrobbler) |
|---|
| 219 | except Scrobbler.HandshakeFrequencyError, error: |
|---|
| 220 | sys.stderr.write('Handshake frequency error: %s\n' % error) |
|---|
| 221 | sys.exit(3) |
|---|
| 222 | except Scrobbler.UnknownHandshakeError, error: |
|---|
| 223 | sys.stderr.write('Unknown handshake error: %s\n' % error) |
|---|
| 224 | sys.exit(3) |
|---|
| 225 | |
|---|
| 226 | while len(scrobbler.queue) > 0: |
|---|
| 227 | try: |
|---|
| 228 | scrobbler.submit_queue() |
|---|
| 229 | except Scrobbler.BadAuthError: |
|---|
| 230 | try: |
|---|
| 231 | sys.stderr.write('BadAuth error: attempting re-handshake\n') |
|---|
| 232 | rockfm.handshake(scrobbler) |
|---|
| 233 | except Scrobbler.HandshakeFrequencyError, error: |
|---|
| 234 | sys.stderr.write('%s\n' % error) |
|---|
| 235 | sys.exit(3) |
|---|
| 236 | except Scrobbler.UnknownHandshakeError, error: |
|---|
| 237 | sys.stderr.write('%s\n' % error) |
|---|
| 238 | sys.exit(3) |
|---|
| 239 | except Scrobbler.SubmitError, error: |
|---|
| 240 | sys.stderr.write('Error during submit phase: %s\n' % (error)) |
|---|
| 241 | except IOError, error: |
|---|
| 242 | sys.stderr.write('IOError: %s\n' % (error)) |
|---|
| 243 | |
|---|
| 244 | if not scrobbler.pretend: |
|---|
| 245 | rockbox.reset_log(options.log_file, header, scrobbler, |
|---|
| 246 | options.utc_offset) |
|---|