| 1 |
#!/usr/bin/env python |
| 2 |
|
| 3 |
''' |
| 4 |
danbooru.py (http://untu.ms/danbooru/) |
| 5 |
====================================== |
| 6 |
A content retrieval tool for danbooru (http://danbooru.donmai.us/). The |
| 7 |
requirements are Python 2.5 (http://python.org/) and a little console-fu. |
| 8 |
|
| 9 |
usage examples |
| 10 |
============== |
| 11 |
* danbooru.py negima “cat ears” |
| 12 |
Download content tagged negima and cat_ears to the default folder |
| 13 |
(negima+cat_ears) |
| 14 |
* danbooru.py -x himm -r safe “sawatari izumi” |
| 15 |
Download content tagged sawatari_izumi and rated safe (-r or –rating) to |
| 16 |
a folder named himm (-f or –folder) |
| 17 |
* danbooru.py -l 50 gif |
| 18 |
Download content tagged as gif, limiting it to 50 posts (-l or –limit) |
| 19 |
* danbooru.py -i -n -s 8 flash |
| 20 |
Download content tagged flash, ignoring the youngest local file (or last |
| 21 |
id, refers to the -i or –no-last-id option), ignore whether the file |
| 22 |
exists in the local database (-n or –no-db), and use server 8 (-s or |
| 23 |
–server option, use -L or –list to see a list of available servers) |
| 24 |
* danbooru.py -c * -x * |
| 25 |
Catalogue (-c or –catalogue) and rename (-x or –fix) all files in all |
| 26 |
subfolders in the current path |
| 27 |
* danbooru.py -h |
| 28 |
View a list of available commands |
| 29 |
|
| 30 |
version history |
| 31 |
=============== |
| 32 |
* 0.2: 09.01.2007 |
| 33 |
* 0.1: 01.01.2007 |
| 34 |
|
| 35 |
copyright |
| 36 |
========= |
| 37 |
danbooru.py is made by Reinis Ivanovs (dabas@untu.ms) and is released |
| 38 |
to the public domain. |
| 39 |
''' |
| 40 |
|
| 41 |
import re |
| 42 |
import os |
| 43 |
import urllib |
| 44 |
import shelve |
| 45 |
import sqlite3 |
| 46 |
import pickle |
| 47 |
|
| 48 |
from glob import glob, iglob |
| 49 |
from hashlib import md5 |
| 50 |
from sys import platform, stderr |
| 51 |
from time import time |
| 52 |
from xml.dom import minidom |
| 53 |
|
| 54 |
# time.clock is more granual than time.time on win32 |
| 55 |
if platform == 'win32': |
| 56 |
from time import clock as xtime, sleep |
| 57 |
else: |
| 58 |
from time import time as xtime, sleep |
| 59 |
|
| 60 |
# A tender age |
| 61 |
__version__ = '0.2' |
| 62 |
__build__ = '119' |
| 63 |
|
| 64 |
case = lambda count, word: word if count == 1 else word + 's' |
| 65 |
cases = lambda count, singular, plural: singular if count == 1 else plural |
| 66 |
|
| 67 |
|
| 68 |
# Identify as danbooru.py/0.x (change this if you want to go ninja) |
| 69 |
class Opener(urllib.FancyURLopener): |
| 70 |
version = 'danbooru.py/%s' % (__version__,) |
| 71 |
urllib._urlopener = Opener() |
| 72 |
|
| 73 |
|
| 74 |
class ServerIdError(KeyError): |
| 75 |
'''No such server ID''' |
| 76 |
|
| 77 |
|
| 78 |
class Robot(dict): |
| 79 |
api_url = 'http://danbooru.donmai.us/api/' |
| 80 |
posts_path = 'find_posts?tags=%(tags)s%(last_id)s%(rating)s&limit=\ |
| 81 |
%(limit)d&offset=%(offset)d' |
| 82 |
last_id = '+after_id:%d' |
| 83 |
rating_path = '+rating:%s' |
| 84 |
servers_path = 'find_servers' |
| 85 |
md5_path = 'find_posts?md5=%s' |
| 86 |
settings_filename = os.path.join(os.path.expanduser('~'), '.danboorudata') |
| 87 |
db_filename = os.path.join(os.path.expanduser('~'), '.danboorudb') |
| 88 |
namepattern = re.compile(r'(?:\d+_)?([a-f\d]{32})') |
| 89 |
idpattern = re.compile(r'(\d+)_[a-f\d]{32}') |
| 90 |
logfile = 'error.log' |
| 91 |
|
| 92 |
def __init__(self, args, limit, offset, **kwargs): |
| 93 |
for key, value in kwargs.iteritems(): |
| 94 |
self[key] = value |
| 95 |
self.end = lambda text, start: '%s (%.2fs)' % (text, time()-start) |
| 96 |
self.tags = self.parse_tags(args) |
| 97 |
self.settings = self.load_settings() |
| 98 |
self.servers = self.load_servers() |
| 99 |
self.db, self.cur = self.load_db() |
| 100 |
self.folder = self.tags |
| 101 |
self.limit = limit |
| 102 |
self.offset = offset |
| 103 |
self.dl = Downloader() |
| 104 |
|
| 105 |
def get_last_id(self, pathname): |
| 106 |
'''Get the youngest file by its danbooru id''' |
| 107 |
if self['refresh']: |
| 108 |
return '' |
| 109 |
filenames = self.get_filenames(pathname) |
| 110 |
filenames = sorted(filenames)[::-1] |
| 111 |
for item in filenames: |
| 112 |
folder, name, ext = self.split_path(item) |
| 113 |
match = re.match(self.idpattern, name) |
| 114 |
if match: |
| 115 |
id = int(match.group(1).lstrip('0')) |
| 116 |
break |
| 117 |
else: id = 1 |
| 118 |
return self.last_id % (int(id),) |
| 119 |
|
| 120 |
def error(self, message): |
| 121 |
print >> stderr, 'Error: %s' % (message,) |
| 122 |
|
| 123 |
def retrieve_content(self): |
| 124 |
'''Start downloading''' |
| 125 |
print 'Downloading to %s…' % (self.folder,) |
| 126 |
if not os.path.exists(self.folder): |
| 127 |
os.mkdir(self.folder) |
| 128 |
last_id = self.get_last_id(self.folder) |
| 129 |
step, limit, offset = 100, self.limit, self.offset |
| 130 |
for i in xrange(offset, limit, step): |
| 131 |
j = i+step if i+step < limit else limit |
| 132 |
params = { 'tags': self.tags, 'last_id': last_id, 'limit': j, |
| 133 |
'offset': i, 'rating': self.rating_path % self['rating'] \ |
| 134 |
if self['rating'] else ''} |
| 135 |
path, start = self.posts_path % params, time() |
| 136 |
url = self.api_url+path |
| 137 |
print 'API:', path+'…', |
| 138 |
data = self.get_data(url, 'post', 'id') |
| 139 |
print self.end('done', start) |
| 140 |
if self['nodb'] or not len(data): |
| 141 |
print '%d posts returned' % (len(data),) |
| 142 |
if not len(data): break |
| 143 |
else: |
| 144 |
before = len(data) |
| 145 |
self.filter_data(data) |
| 146 |
values = (before, case(before, 'post'), |
| 147 |
len(data), cases(len(data), 'wasn\'t', 'weren\'t')) |
| 148 |
print '%d %s returned, %d %s in the local database' % values |
| 149 |
if self['simulate']: continue |
| 150 |
if len(data): |
| 151 |
for key, value in data.iteritems(): |
| 152 |
self.get_post(key, value) |
| 153 |
self.update_db(data) |
| 154 |
self.db.commit() |
| 155 |
try: |
| 156 |
if before < step: break |
| 157 |
except UnboundLocalError, e: |
| 158 |
print e |
| 159 |
else: |
| 160 |
print 'Post limit (%d) met' % (limit,) |
| 161 |
if not glob(os.path.join(self.folder, '*')): |
| 162 |
print '%s is empty: removing' % (self.folder,) |
| 163 |
os.rmdir(self.folder) |
| 164 |
|
| 165 |
def get_post(self, id, post): |
| 166 |
'''Download an individual post''' |
| 167 |
filename = post['file_name'] |
| 168 |
# Figure out the local name (id is padded with zeroes) |
| 169 |
localname = os.path.join(self.folder, '%07d_%s' % (id, filename)) |
| 170 |
if os.path.exists(localname): |
| 171 |
self.error('File already exists') |
| 172 |
return |
| 173 |
server = self.servers[self.server] |
| 174 |
server = 'http://' + server['host'] + server['path'] |
| 175 |
url = server + '/'.join((filename[0:2], filename[2:4], filename)) |
| 176 |
print url |
| 177 |
print self.dl.retrieve(url, localname, self.exit), \ |
| 178 |
'KiB retrieved in %s' % (self.folder,) |
| 179 |
|
| 180 |
def filter_data(self, data): |
| 181 |
'''Filter out the data that already exists in the local db''' |
| 182 |
values = '”,”'.join([str(key) for key in data.keys()]) |
| 183 |
query = self.cur.execute(self.by_id_command % values) |
| 184 |
for row in query: |
| 185 |
id, = row |
| 186 |
if id in data: |
| 187 |
del data[id] |
| 188 |
return data |
| 189 |
|
| 190 |
def log(self, message): |
| 191 |
'''Unused''' |
| 192 |
print >> open(self.logfile, 'a+'), message |
| 193 |
|
| 194 |
def use_server(self, id): |
| 195 |
'''Set the server for this instance to use''' |
| 196 |
if id not in self.servers: |
| 197 |
raise ServerIdError, id |
| 198 |
self.server = id |
| 199 |
|
| 200 |
def load_servers(self): |
| 201 |
'''Load the servers from stored data''' |
| 202 |
if 'servers' in self.settings: |
| 203 |
servers = self.settings['servers'] |
| 204 |
else: |
| 205 |
servers = self.update_servers() |
| 206 |
return servers |
| 207 |
|
| 208 |
# TODO: remove redundant parts and use the get_data() method instead |
| 209 |
def update_servers(self): |
| 210 |
'''Download and parse the server list from the api''' |
| 211 |
if 'servers_fresh' in dir(self): |
| 212 |
return self.servers |
| 213 |
print 'Updating servers list…', |
| 214 |
start = time() |
| 215 |
url = self.api_url + self.servers_path |
| 216 |
data = minidom.parse(urllib.urlopen(url)) |
| 217 |
results = {} |
| 218 |
for server in data.getElementsByTagName('server'): |
| 219 |
attributes = dict(server.attributes.items()) |
| 220 |
results[int(attributes.pop('id'))] = attributes |
| 221 |
if not len(results): |
| 222 |
print self.end('done', start) |
| 223 |
self.error('danbooro seems to be down') |
| 224 |
self.exit() |
| 225 |
data.unlink() |
| 226 |
self.servers_fresh = True |
| 227 |
self.save_settings(servers=results) |
| 228 |
print self.end('done', start) |
| 229 |
return results |
| 230 |
|
| 231 |
def list_servers(self): |
| 232 |
'''Print servers''' |
| 233 |
print 'Listing servers…' |
| 234 |
row = lambda id, host: '%s %s' % (str(id).rjust(2), host) |
| 235 |
print row('ID', 'Host') |
| 236 |
for id in self.servers: |
| 237 |
print row(id, self.servers[id]['host']), '[default]' \ |
| 238 |
if id == self.settings['default'] else '' |
| 239 |
|
| 240 |
def load_settings(self): |
| 241 |
'''Connect to the persistent settings''' |
| 242 |
return shelve.open(self.settings_filename) |
| 243 |
|
| 244 |
def save_settings(self, **kwargs): |
| 245 |
'''Save and flush settings''' |
| 246 |
for key in kwargs: |
| 247 |
self.settings[key] = kwargs[key] |
| 248 |
self.settings.sync() |
| 249 |
|
| 250 |
def get_data(self, url, elementname, keyname): |
| 251 |
'''Fetch and parse data from the api (would be many lines longer if \ |
| 252 |
this had to be actually spidered)''' |
| 253 |
data = minidom.parse(urllib.urlopen(url)) |
| 254 |
results = {} |
| 255 |
for server in data.getElementsByTagName(elementname): |
| 256 |
attributes = dict(server.attributes.items()) |
| 257 |
results[int(attributes.pop(keyname))] = attributes |
| 258 |
data.unlink() |
| 259 |
return results |
| 260 |
|
| 261 |
def get_serverlist(self): |
| 262 |
url, start = self.api_url + self.servers_path, time() |
| 263 |
print 'Getting servers list…', |
| 264 |
results = self.get_data(url, 'server', 'id') |
| 265 |
print self.end(start) |
| 266 |
return results |
| 267 |
|
| 268 |
def get_content_data(self, tags=None, limit=None, offset=None, hashes=None): |
| 269 |
if tags and limit and offset: |
| 270 |
url = self.api_url + self.posts_path % (tags, limit, offset) |
| 271 |
count = limit - offset |
| 272 |
elif hashes: |
| 273 |
url = self.api_url + self.md5_path % ','.join(hashes) |
| 274 |
count = len(hashes) |
| 275 |
print 'Getting content data for %d %s…' % (count, case(count, 'file')), |
| 276 |
start = time() |
| 277 |
results = self.get_data(url, 'post', 'id') |
| 278 |
print self.end('done', start) |
| 279 |
if len(results) < count: |
| 280 |
missing = count - len(results) |
| 281 |
print '%d %s not found' % (missing, case(missing, 'file')) |
| 282 |
return results |
| 283 |
|
| 284 |
def parse_tags(self, args): |
| 285 |
'''Parse script arguments''' |
| 286 |
tags = [urllib.quote(item.replace(' ', '_')).replace('%2B', '+') \ |
| 287 |
for item in args] |
| 288 |
tags = '+'.join(tags) |
| 289 |
return tags |
| 290 |
|
| 291 |
def exit(self): |
| 292 |
'''Say bye and report db changes''' |
| 293 |
changes = self.db.total_changes |
| 294 |
if changes: |
| 295 |
print '%d %s to the local database in this session' % \ |
| 296 |
(changes, case(changes, 'change')) |
| 297 |
print 'Bye~!' |
| 298 |
exit() |
| 299 |
|
| 300 |
def load_db(self): |
| 301 |
'''Connect to the sqlite db''' |
| 302 |
self.init_db_command ='''CREATE TABLE IF NOT EXISTS content \ |
| 303 |
(id INTEGER PRIMARY KEY, md5 TEXT, tags TEXT, misc BLOB);''' |
| 304 |
self.update_db_command ='''INSERT OR IGNORE into content \ |
| 305 |
(id, md5, tags, misc) values (%d, “%s“, “%s“, “%s“);''' |
| 306 |
self.by_md5_command ='''SELECT md5, id FROM content \ |
| 307 |
WHERE md5 IN (“%s“);''' |
| 308 |
self.by_id_command ='''SELECT id FROM content WHERE id IN (“%s“);''' |
| 309 |
db = sqlite3.connect(self.db_filename) |
| 310 |
db.text_factory = lambda text: unicode(text, 'utf-8', 'ignore') |
| 311 |
cur = db.cursor() |
| 312 |
cur.execute(self.init_db_command) |
| 313 |
return db, cur |
| 314 |
|
| 315 |
def hash_in_filename(self, filename): |
| 316 |
'''Try to avoid hashing the file''' |
| 317 |
name, ext = os.path.splitext(os.path.basename(filename)) |
| 318 |
results = re.search(self.namepattern, name) |
| 319 |
return results.groups()[0] if results else None |
| 320 |
|
| 321 |
def filter_hashes(self, hashes): |
| 322 |
'''Remove hashes that exist in the local database''' |
| 323 |
values = '”,”'.join(hashes.values()) |
| 324 |
query = self.cur.execute(self.by_md5_command % values) |
| 325 |
for hash in query: |
| 326 |
hash, id = hash |
| 327 |
for key, value in hashes.copy().iteritems(): |
| 328 |
if hash != value: continue |
| 329 |
del hashes[key] |
| 330 |
return hashes |
| 331 |
|
| 332 |
def get_hashes(self, names, source, filter=True): |
| 333 |
'''Get hashes for files in a path''' |
| 334 |
print 'Getting hashes for %d %s in %s…' % \ |
| 335 |
(len(names), case(len(names), 'file'), source), |
| 336 |
results, start = {}, time() |
| 337 |
for item in names: |
| 338 |
hash = self.hash_in_filename(item) \ |
| 339 |
or md5(open(item, 'rb').read()).hexdigest() |
| 340 |
results[item] = hash |
| 341 |
if filter: |
| 342 |
results = self.filter_hashes(results) |
| 343 |
print self.end('done', start) |
| 344 |
return results |
| 345 |
|
| 346 |
def catalogue_content(self, pathname): |
| 347 |
'''Add files to the local database''' |
| 348 |
print 'Starting to catalogue %s…' % (pathname,) |
| 349 |
filenames = self.get_filenames(pathname) |
| 350 |
hashes = self.get_hashes(filenames, pathname) |
| 351 |
message = '%d of %d files already in local database' |
| 352 |
print message % (len(filenames)-len(hashes), len(filenames)) |
| 353 |
count, step = 0, 100 |
| 354 |
for i in xrange(0, len(hashes), step): |
| 355 |
data = self.get_content_data(hashes=hashes.values()[i:i+step]) |
| 356 |
count += len(data) |
| 357 |
if self['simulate']: continue |
| 358 |
self.update_db(data) |
| 359 |
self.db.commit() |
| 360 |
print '%d %s added to database' % (count, cases(count, 'entry', 'entries')) |
| 361 |
|
| 362 |
def split_path(self, pathname): |
| 363 |
'''Split the path in a tuple of three''' |
| 364 |
folder, name = os.path.split(pathname) |
| 365 |
name, ext = os.path.splitext(name) |
| 366 |
return folder, name, ext |
| 367 |
|
| 368 |
def fix_filenames(self, pathname): |
| 369 |
'''Rename files to id_hash''' |
| 370 |
print 'Fixing filenames in %s…' % (pathname,) |
| 371 |
filenames = self.get_filenames(pathname) |
| 372 |
start, count = time(), 0 |
| 373 |
hashes = self.get_hashes(filenames, pathname, filter=False) |
| 374 |
values = '”,”'.join(hashes.values()) |
| 375 |
query = self.cur.execute(self.by_md5_command % values) |
| 376 |
query = dict(query.fetchall()) |
| 377 |
for filename, hash in hashes.iteritems(): |
| 378 |
if hash not in query.keys(): |
| 379 |
continue |
| 380 |
folder, oldname, ext = self.split_path(filename) |
| 381 |
# Figure out the new name (id is padded with zeroes) |
| 382 |
newname = '%07d_%s%s' % (query[hash], hash, ext) |
| 383 |
newname = os.path.join(folder, newname) |
| 384 |
if filename == newname: |
| 385 |
continue |
| 386 |
if os.path.exists(newname): |
| 387 |
os.remove(filename) |
| 388 |
else: |
| 389 |
try: os.rename(filename, newname) |
| 390 |
except WindowsError, e: |
| 391 |
print e |
| 392 |
count -= 1 |
| 393 |
count += 1 |
| 394 |
print '%d %s fixed' % (count, case(count, 'filename')) |
| 395 |
|
| 396 |
def expand_paths(self, source): |
| 397 |
'''Does exactly what the name says''' |
| 398 |
names = set() |
| 399 |
for item in source.split(): |
| 400 |
names.update(glob(item)) |
| 401 |
return filter(os.path.isdir, names) |
| 402 |
|
| 403 |
def get_filenames(self, pathname): |
| 404 |
'''Again, does just what the name says''' |
| 405 |
names = glob(os.path.join(pathname, '*')) |
| 406 |
return filter(os.path.isfile, names) |
| 407 |
|
| 408 |
def update_db(self, data): |
| 409 |
'''Write data to the transaction (has to be committed to the db explicitly)''' |
| 410 |
for key, value in data.iteritems(): |
| 411 |
value['author'] = value['author'].encode('utf-8') |
| 412 |
values = (key, value.pop('md5'), value.pop('tags'), pickle.dumps(value)) |
| 413 |
try: |
| 414 |
self.cur.execute(self.update_db_command % values) |
| 415 |
except sqlite3.OperationalError, e: |
| 416 |
print e |
| 417 |
|
| 418 |
|
| 419 |
class Downloader(object): |
| 420 |
'''Shows a progress bar for downloads. this is actually useful outside the |
| 421 |
scope of danbooru.py''' |
| 422 |
|
| 423 |
before = .0 |
| 424 |
history = [] |
| 425 |
cycles = 0 |
| 426 |
average = lambda self: sum(self.history) / (len(self.history) or 1) |
| 427 |
|
| 428 |
def __init__(self, width=55): |
| 429 |
self.width = width |
| 430 |
self.kibi = lambda bits: bits / 2 ** 10 |
| 431 |
self.proc = lambda a, b: a / (b * 0.01) |
| 432 |
|
| 433 |
def retrieve(self, url, destination, callback=None): |
| 434 |
self.size = 0 |
| 435 |
xtime() |
| 436 |
try: urllib.urlretrieve(url, destination, self.progress) |
| 437 |
except KeyboardInterrupt: |
| 438 |
print '\nDownload cancelled' |
| 439 |
for i in range(5): |
| 440 |
try: |
| 441 |
os.remove(destination) |
| 442 |
break |
| 443 |
except: |
| 444 |
sleep(.1) |
| 445 |
else: raise |
| 446 |
if callback: callback() |
| 447 |
exit() |
| 448 |
print |
| 449 |
return self.size |
| 450 |
|
| 451 |
def progress(self, blocks, blocksize, filesize): |
| 452 |
self.cycles += 1 |
| 453 |
bits = min(blocks*blocksize, filesize) |
| 454 |
done = self.proc(bits, filesize) if bits != filesize else 100 |
| 455 |
bar = self.bar(done) |
| 456 |
if not self.cycles % 3 and bits != filesize: |
| 457 |
now = xtime() |
| 458 |
elapsed = now-self.before |
| 459 |
if elapsed: |
| 460 |
speed = self.kibi(blocksize * 3 / elapsed) |
| 461 |
self.history.append(speed) |
| 462 |
self.history = self.history[-4:] |
| 463 |
self.before = now |
| 464 |
average = round(sum(self.history[-4:]) / 4, 1) |
| 465 |
self.size = self.kibi(bits) |
| 466 |
print '\r[%s] %s KiB/s ' % (bar, str(average)), |
| 467 |
|
| 468 |
def bar(self, done): |
| 469 |
span = self.width * done * 0.01 |
| 470 |
offset = len(str(int(done))) - .99 |
| 471 |
result = ('%d%%' % (done,)).center(self.width) |
| 472 |
return result.replace(' ', '-', int(span - offset)) |
| 473 |
|
| 474 |
|
| 475 |
def parse_options(): |
| 476 |
'''Parse arguments passed to the script''' |
| 477 |
help = { 'limit': 'set how many posts (not files) to get from the api \ |
| 478 |
[default: %default]', |
| 479 |
'offset': 'set the position to start downloading from \ |
| 480 |
[default: %default]', |
| 481 |
'server': 'which server to use (takes an index, see -L for a list of \ |
| 482 |
available servers)', |
| 483 |
'refresh': 'allow retrieving posts older than the highest id \ |
| 484 |
of the local files in the destination folder', |
| 485 |
'nodb': 'allow downloading posts that are already present \ |
| 486 |
in the local database', |
| 487 |
'catalogue': 'add local files to the database \ |
| 488 |
(queries the api with their hashes)', |
| 489 |
'fixnames': 'change filenames to _.* format', |
| 490 |
'folder': 'override the download destination (default is same as tags)', |
| 491 |
'update': 'update the serverlist', |
| 492 |
'list': 'see a list of available servers', |
| 493 |
'set_default': 'set a default server', |
| 494 |
'rating': 'convenience shortcut to the rating: tag', |
| 495 |
'simulate': 'don\'t download files or add posts to the database', |
| 496 |
} |
| 497 |
usage = '%prog [-l NUM] [-o NUM] [-s NUM] [-r safe|questionable|explicit] \ |
| 498 |
[-f PATH] [-i] [-n] [-c PATH] [-x PATH] [-u] [-L] [-d] ' |
| 499 |
from optparse import OptionParser |
| 500 |
parser = OptionParser(usage=usage, version='%s.%s' % (__version__, __build__), |
| 501 |
description='A tool for retrieving content from danbooru.donmai.us') |
| 502 |
parser.add_option('-l', '–limit', dest='limit', help=help['limit'], \ |
| 503 |
metavar='NUM', default=1000, type='int') |
| 504 |
parser.add_option('-o', '–offset', dest='offset', help=help['offset'], \ |
| 505 |
metavar='NUM', default=0, type='int') |
| 506 |
parser.add_option('-s', '–server', dest='server', help=help['server'], \ |
| 507 |
metavar='NUM', default=None, type='int') |
| 508 |
parser.add_option('-r', '–rating', dest='rating', help=help['rating'], \ |
| 509 |
metavar='NAME', default=None, type='string') |
| 510 |
parser.add_option('-f', '–folder', dest='folder', help=help['folder'], \ |
| 511 |
metavar='PATH', default=None) |
| 512 |
parser.add_option('-i', '–no-last-id', dest='refresh', \ |
| 513 |
help=help['refresh'], action='store_true', default=False) |
| 514 |
parser.add_option('-n', '–no-db', dest='nodb', help=help['nodb'], \ |
| 515 |
action='store_true', default=False) |
| 516 |
parser.add_option('-c', '–catalogue', dest='catalogue', \ |
| 517 |
help=help['catalogue'], metavar='PATH', default=None) |
| 518 |
parser.add_option('-x', '–fix', dest='fixnames', help=help['fixnames'], \ |
| 519 |
metavar='PATH', default=None) |
| 520 |
parser.add_option('-u', '–update', dest='update', help=help['update'], \ |
| 521 |
action='store_true', default=False) |
| 522 |
parser.add_option('-L', '–list', dest='list', help=help['list'], \ |
| 523 |
action='store_true', default=False) |
| 524 |
parser.add_option('-d', '–default', dest='set_default', \ |
| 525 |
help=help['set_default'], metavar='ID', default=None, type='int') |
| 526 |
parser.add_option('-e', '–simulate', dest='simulate', \ |
| 527 |
help=help['simulate'], action='store_true', default=False) |
| 528 |
options, args = parser.parse_args() |
| 529 |
return options, args, parser |
| 530 |
|
| 531 |
|
| 532 |
def main(): |
| 533 |
'''Decide what to do based on the options returned by optparse''' |
| 534 |
options, args, parser = parse_options() |
| 535 |
robot = Robot(args, options.limit, options.offset, rating=options.rating, |
| 536 |
refresh=options.refresh, nodb=options.nodb, simulate=options.simulate) |
| 537 |
if options.rating: |
| 538 |
values = ('safe', 'explicit', 'questionable') |
| 539 |
if options.rating not in values: |
| 540 |
parser.error('only %r are valid ratings' % (values,)) |
| 541 |
else: |
| 542 |
robot.rating = options.rating |
| 543 |
if options.refresh: |
| 544 |
print 'Using No Last ID mode…' |
| 545 |
if options.folder: |
| 546 |
robot.folder = options.folder |
| 547 |
if options.catalogue: |
| 548 |
for name in robot.expand_paths(options.catalogue): |
| 549 |
robot.catalogue_content(name) |
| 550 |
if options.fixnames: |
| 551 |
for name in robot.expand_paths(options.fixnames): |
| 552 |
robot.fix_filenames(name) |
| 553 |
if options.update: |
| 554 |
robot.update_servers() |
| 555 |
if options.set_default: |
| 556 |
server_id = options.set_default |
| 557 |
robot.use_server(server_id) |
| 558 |
robot.save_settings(default=server_id) |
| 559 |
print 'Default server set to %d (%s)' % \ |
| 560 |
(server_id, robot.servers[server_id]['host']) |
| 561 |
if 'default' not in robot.settings: |
| 562 |
server_id = robot.servers.keys()[0] |
| 563 |
robot.use_server(server_id) |
| 564 |
robot.save_settings(default=server_id) |
| 565 |
if options.list: |
| 566 |
robot.list_servers() |
| 567 |
elif options.server: |
| 568 |
robot.use_server(options.server) |
| 569 |
else: |
| 570 |
robot.use_server(robot.settings['default']) |
| 571 |
if robot.tags: |
| 572 |
if 'server' not in dir(robot): |
| 573 |
robot.use_server(robot.settings['default']) |
| 574 |
print 'Using server %d (%s)' % \ |
| 575 |
(robot.server, robot.servers[robot.server]['host']) |
| 576 |
robot.retrieve_content() |
| 577 |
robot.exit() |
| 578 |
|
| 579 |
|
| 580 |
if __name__ == '__main__': |
| 581 |
main() |
0 Comments »
No comments yet.
RSS feed for comments on this post. TrackBack URI