[4] | 1 | #!/usr/bin/env python |
---|
| 2 | |
---|
| 3 | # Module for retrieving CDDB v1 data from CDDB servers via HTTP |
---|
| 4 | |
---|
| 5 | # Written 17 Nov 1999 by Ben Gertzfield <che@debian.org> |
---|
| 6 | # This work is released under the GNU GPL, version 2 or later. |
---|
| 7 | |
---|
| 8 | # Release version 1.3 |
---|
| 9 | # CVS ID: $Id: CDDB.py,v 1.4 2004/11/14 19:26:38 dischi Exp $ |
---|
| 10 | |
---|
| 11 | import urllib, string, socket, os, struct, re |
---|
| 12 | |
---|
| 13 | name = 'CDDB.py' |
---|
| 14 | version = 1.3 |
---|
| 15 | |
---|
| 16 | if os.environ.has_key('EMAIL'): |
---|
| 17 | (default_user, hostname) = string.split(os.environ['EMAIL'], '@') |
---|
| 18 | else: |
---|
| 19 | try: |
---|
| 20 | default_user = os.geteuid() or os.environ['USER'] or 'user' |
---|
| 21 | except: |
---|
| 22 | default_user = 'user' |
---|
| 23 | hostname = socket.gethostname() or 'host' |
---|
| 24 | |
---|
| 25 | proto = 4 |
---|
| 26 | default_server = 'http://freedb.freedb.org/~cddb/cddb.cgi' |
---|
| 27 | |
---|
| 28 | def query(track_info, server_url=default_server, |
---|
| 29 | user=default_user, host=hostname, client_name=name, |
---|
| 30 | client_version=version, trying=0): |
---|
| 31 | |
---|
| 32 | disc_id = track_info[0] |
---|
| 33 | num_tracks = track_info[1] |
---|
| 34 | |
---|
| 35 | query_str = (('%08lx %d ') % (long(disc_id), num_tracks)) |
---|
| 36 | |
---|
| 37 | for i in track_info[2:]: |
---|
| 38 | query_str = query_str + ('%d ' % i) |
---|
| 39 | |
---|
| 40 | query_str = urllib.quote_plus(string.rstrip(query_str)) |
---|
| 41 | |
---|
| 42 | url = "%s?cmd=cddb+query+%s&hello=%s+%s+%s+%s&proto=%i" % \ |
---|
| 43 | (server_url, query_str, user, host, client_name, |
---|
| 44 | client_version, proto) |
---|
| 45 | |
---|
| 46 | response = urllib.urlopen(url) |
---|
| 47 | |
---|
| 48 | # Four elements in header: status, category, disc-id, title |
---|
| 49 | header = string.split(string.rstrip(response.readline()), ' ', 3) |
---|
| 50 | |
---|
| 51 | try: |
---|
| 52 | header[0] = string.atoi(header[0]) |
---|
| 53 | except: |
---|
| 54 | if trying > 10: |
---|
| 55 | return [ 900, None ] |
---|
| 56 | return query(track_info, default_server, |
---|
| 57 | default_user, hostname, name, version, trying+1) |
---|
| 58 | |
---|
| 59 | if header[0] == 200: # OK |
---|
| 60 | result = { 'category': header[1], 'disc_id': header[2], 'title': |
---|
| 61 | header[3] } |
---|
| 62 | |
---|
| 63 | return [ header[0], result ] |
---|
| 64 | |
---|
| 65 | elif header[0] == 211 or header[0] == 210: # multiple matches |
---|
| 66 | result = [] |
---|
| 67 | |
---|
| 68 | for line in response.readlines(): |
---|
| 69 | line = string.rstrip(line) |
---|
| 70 | |
---|
| 71 | if line == '.': # end of matches |
---|
| 72 | break |
---|
| 73 | # otherwise: |
---|
| 74 | # split into 3 pieces, not 4 |
---|
| 75 | # (thanks to bgp for the fix!) |
---|
| 76 | match = string.split(line, ' ', 2) |
---|
| 77 | |
---|
| 78 | result.append({ 'category': match[0], 'disc_id': match[1], 'title': |
---|
| 79 | match[2] }) |
---|
| 80 | |
---|
| 81 | return [ header[0], result ] |
---|
| 82 | |
---|
| 83 | else: |
---|
| 84 | return [ header[0], None ] |
---|
| 85 | |
---|
| 86 | def read(category, disc_id, server_url=default_server, |
---|
| 87 | user=default_user, host=hostname, client_name=name, |
---|
| 88 | client_version=version, trying=0): |
---|
| 89 | |
---|
| 90 | url = "%s?cmd=cddb+read+%s+%s&hello=%s+%s+%s+%s&proto=%i" % \ |
---|
| 91 | (server_url, category, disc_id, user, host, client_name, |
---|
| 92 | client_version, proto) |
---|
| 93 | |
---|
| 94 | response = urllib.urlopen(url) |
---|
| 95 | |
---|
| 96 | header = string.split(string.rstrip(response.readline()), ' ', 3) |
---|
| 97 | |
---|
| 98 | try: |
---|
| 99 | header[0] = string.atoi(header[0]) |
---|
| 100 | except: |
---|
| 101 | if trying > 10: |
---|
| 102 | return [ 900, None ] |
---|
| 103 | return read(category, disc_id, default_server, |
---|
| 104 | user, host, client_name, client_version, trying+1) |
---|
| 105 | |
---|
| 106 | if header[0] == 210 or header[0] == 417: # success or access denied |
---|
| 107 | reply = [] |
---|
| 108 | |
---|
| 109 | for line in response.readlines(): |
---|
| 110 | line = string.rstrip(line) |
---|
| 111 | |
---|
| 112 | if line == '.': |
---|
| 113 | break; |
---|
| 114 | |
---|
| 115 | line = string.replace(line, r'\t', "\t") |
---|
| 116 | line = string.replace(line, r'\n', "\n") |
---|
| 117 | line = string.replace(line, r'\\', "\\") |
---|
| 118 | |
---|
| 119 | reply.append(line) |
---|
| 120 | |
---|
| 121 | if header[0] == 210: # success, parse the reply |
---|
| 122 | return [ header[0], parse_read_reply(reply) ] |
---|
| 123 | else: # access denied. :( |
---|
| 124 | return [ header[0], reply ] |
---|
| 125 | else: |
---|
| 126 | return [ header[0], None ] |
---|
| 127 | |
---|
| 128 | def parse_read_reply(comments): |
---|
| 129 | |
---|
| 130 | len_re = re.compile(r'#\s*Disc length:\s*(\d+)\s*seconds') |
---|
| 131 | revis_re = re.compile(r'#\s*Revision:\s*(\d+)') |
---|
| 132 | submit_re = re.compile(r'#\s*Submitted via:\s*(.+)') |
---|
| 133 | keyword_re = re.compile(r'([^=]+)=(.*)') |
---|
| 134 | |
---|
| 135 | result = {} |
---|
| 136 | |
---|
| 137 | for line in comments: |
---|
| 138 | keyword_match = keyword_re.match(line) |
---|
| 139 | if keyword_match: |
---|
| 140 | (keyword, data) = keyword_match.groups() |
---|
| 141 | |
---|
| 142 | if result.has_key(keyword): |
---|
| 143 | result[keyword] = result[keyword] + data |
---|
| 144 | else: |
---|
| 145 | result[keyword] = data |
---|
| 146 | continue |
---|
| 147 | |
---|
| 148 | len_match = len_re.match(line) |
---|
| 149 | if len_match: |
---|
| 150 | result['disc_len'] = int(len_match.group(1)) |
---|
| 151 | continue |
---|
| 152 | |
---|
| 153 | revis_match = revis_re.match(line) |
---|
| 154 | if revis_match: |
---|
| 155 | result['revision'] = int(revis_match.group(1)) |
---|
| 156 | continue |
---|
| 157 | |
---|
| 158 | submit_match = submit_re.match(line) |
---|
| 159 | if submit_match: |
---|
| 160 | result['submitted_via'] = submit_match.group(1) |
---|
| 161 | continue |
---|
| 162 | |
---|
| 163 | return result |
---|