1 | #!/usr/bin/python |
---|
2 | |
---|
3 | # vim:ts=4 sw=4 nowrap: |
---|
4 | |
---|
5 | # system imports |
---|
6 | import os |
---|
7 | import sys |
---|
8 | import shlex |
---|
9 | import code |
---|
10 | import getopt |
---|
11 | from datetime import datetime |
---|
12 | # MythTV imports |
---|
13 | from MythLog import * |
---|
14 | |
---|
15 | |
---|
16 | # create logging object |
---|
17 | log = MythLog(INFO, '%(levelname)s - %(message)s', 'MythDB') |
---|
18 | |
---|
19 | # check for dependency |
---|
20 | try: |
---|
21 | import MySQLdb |
---|
22 | except: |
---|
23 | log.Msg(CRITICAL, "MySQLdb (python-mysqldb) is required but is not found.") |
---|
24 | sys.exit(1) |
---|
25 | |
---|
26 | |
---|
27 | |
---|
28 | class MythDB: |
---|
29 | """ |
---|
30 | A connection to the mythtv database. |
---|
31 | """ |
---|
32 | def __init__(self, args): |
---|
33 | # Setup connection variables |
---|
34 | dbconn = { |
---|
35 | 'host' : None, |
---|
36 | 'name' : None, |
---|
37 | 'user' : None, |
---|
38 | 'pass' : None |
---|
39 | } |
---|
40 | |
---|
41 | # Try to read the mysql.txt file used by MythTV. |
---|
42 | # Order taken from libs/libmyth/mythcontext.cpp |
---|
43 | config_files = [ |
---|
44 | '/usr/local/share/mythtv/mysql.txt', |
---|
45 | '/usr/share/mythtv/mysql.txt', |
---|
46 | '/usr/local/etc/mythtv/mysql.txt', |
---|
47 | '/etc/mythtv/mysql.txt', |
---|
48 | os.path.expanduser('~/.mythtv/mysql.txt'), |
---|
49 | ] |
---|
50 | if 'MYTHCONFDIR' in os.environ: |
---|
51 | config_locations.append('%s/mysql.txt' % os.environ['MYTHCONFDIR']) |
---|
52 | |
---|
53 | found_config = False |
---|
54 | for config_file in config_files: |
---|
55 | try: |
---|
56 | config = shlex.shlex(open(config_file)) |
---|
57 | config.wordchars += "." |
---|
58 | except: |
---|
59 | continue |
---|
60 | |
---|
61 | dbconn['host'] = None |
---|
62 | dbconn['name'] = None |
---|
63 | dbconn['user'] = None |
---|
64 | dbconn['pass'] = None |
---|
65 | token = config.get_token() |
---|
66 | while token != config.eof and not found_config: |
---|
67 | if token == "DBHostName": |
---|
68 | if config.get_token() == "=": |
---|
69 | dbconn['host'] = config.get_token() |
---|
70 | elif token == "DBName": |
---|
71 | if config.get_token() == "=": |
---|
72 | dbconn['name'] = config.get_token() |
---|
73 | elif token == "DBUserName": |
---|
74 | if config.get_token() == "=": |
---|
75 | dbconn['user'] = config.get_token() |
---|
76 | elif token == "DBPassword": |
---|
77 | if config.get_token() == "=": |
---|
78 | dbconn['pass'] = config.get_token() |
---|
79 | token = config.get_token() |
---|
80 | if dbconn['host'] != None and dbconn['name'] != None and dbconn['user'] != None and dbconn['pass'] != None: |
---|
81 | log.Msg(INFO, 'Using config %s', config_file) |
---|
82 | found_config = True |
---|
83 | break |
---|
84 | |
---|
85 | # Overrides from command line parameters |
---|
86 | try: |
---|
87 | opts, args = getopt.getopt(args, '', ['dbhost=', 'user=', 'pass=', 'database=']) |
---|
88 | for o, a in opts: |
---|
89 | if o == '--dbhost': |
---|
90 | dbconn['host'] = a |
---|
91 | if o == '--user': |
---|
92 | dbconn['user'] = a |
---|
93 | if o == '--pass': |
---|
94 | dbconn['pass'] = a |
---|
95 | if o == '--database': |
---|
96 | dbconn['name'] = a |
---|
97 | except: |
---|
98 | pass |
---|
99 | |
---|
100 | if not dbconn['host'] and not found_config: |
---|
101 | raise MythError('Unable to find MythTV configuration file') |
---|
102 | |
---|
103 | try: |
---|
104 | self.db = MySQLdb.connect(user=dbconn['user'], host=dbconn['host'], passwd=dbconn['pass'], db=dbconn['name']) |
---|
105 | log.Msg(INFO, 'DB Connection info (host:%s, name:%s, user:%s, pass:%s)', dbconn['host'], dbconn['name'], dbconn['user'], dbconn['pass']) |
---|
106 | except: |
---|
107 | raise MythError('Connection failed for \'%s\'@\'%s\' to database %s using password %s' % (dbconn['user'], dbconn['host'], dbconn['name'], dbconn['pass'])) |
---|
108 | |
---|
109 | |
---|
110 | |
---|
111 | def getAllSettings(self, hostname=None): |
---|
112 | """ |
---|
113 | Returns values for all settings. |
---|
114 | |
---|
115 | Returns None if there are no settings. If multiple rows are |
---|
116 | found (multiple hostnames), returns the value of the first one. |
---|
117 | """ |
---|
118 | log.Msg(DEBUG, 'Retrieving all setting for host %s', hostname) |
---|
119 | c = self.db.cursor() |
---|
120 | if hostname is None: |
---|
121 | c.execute(""" |
---|
122 | SELECT value, data |
---|
123 | FROM settings |
---|
124 | WHERE hostname IS NULL""") |
---|
125 | else: |
---|
126 | c.execute(""" |
---|
127 | SELECT value, data |
---|
128 | FROM settings |
---|
129 | WHERE hostname LIKE('%s%%')""" % |
---|
130 | (hostname) |
---|
131 | ) |
---|
132 | rows = c.fetchall() |
---|
133 | c.close() |
---|
134 | |
---|
135 | if rows: |
---|
136 | return rows |
---|
137 | else: |
---|
138 | return None |
---|
139 | |
---|
140 | |
---|
141 | |
---|
142 | def getSetting(self, value, hostname=None): |
---|
143 | """ |
---|
144 | Returns the value for the given MythTV setting. |
---|
145 | |
---|
146 | Returns None if the setting was not found. If multiple rows are |
---|
147 | found (multiple hostnames), returns the value of the first one. |
---|
148 | """ |
---|
149 | log.Msg(DEBUG, 'Looking for setting %s for host %s', value, hostname) |
---|
150 | c = self.db.cursor() |
---|
151 | if hostname is None: |
---|
152 | c.execute(""" |
---|
153 | SELECT data |
---|
154 | FROM settings |
---|
155 | WHERE value LIKE('%s') AND hostname IS NULL LIMIT 1""" % |
---|
156 | (value)) |
---|
157 | else: |
---|
158 | c.execute(""" |
---|
159 | SELECT data |
---|
160 | FROM settings |
---|
161 | WHERE value LIKE('%s') AND hostname LIKE('%s%%') LIMIT 1""" % |
---|
162 | (value, hostname)) |
---|
163 | row = c.fetchone() |
---|
164 | c.close() |
---|
165 | |
---|
166 | if row: |
---|
167 | return row[0] |
---|
168 | else: |
---|
169 | return None |
---|
170 | |
---|
171 | |
---|
172 | |
---|
173 | def cursor(self): |
---|
174 | return self.db.cursor() |
---|
175 | |
---|
176 | |
---|
177 | |
---|
178 | if __name__ == '__main__': |
---|
179 | banner = "'mdb' is a MythDB instance." |
---|
180 | try: |
---|
181 | import readline, rlcompleter |
---|
182 | except: |
---|
183 | pass |
---|
184 | else: |
---|
185 | readline.parse_and_bind("tab: complete") |
---|
186 | banner = banner + " TAB completion is available." |
---|
187 | mdb = MythDB(sys.argv[1:]) |
---|
188 | namespace = globals().copy() |
---|
189 | namespace.update(locals()) |
---|
190 | code.InteractiveConsole(namespace).interact(banner) |
---|