1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- 2### BEGIN LICENSE 3# Copyright (c) 2012, Peter Levi <peterlevi@peterlevi.com> 4# This program is free software: you can redistribute it and/or modify it 5# under the terms of the GNU General Public License version 3, as published 6# by the Free Software Foundation. 7# 8# This program is distributed in the hope that it will be useful, but 9# WITHOUT ANY WARRANTY; without even the implied warranties of 10# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 11# PURPOSE. See the GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License along 14# with this program. If not, see <http://www.gnu.org/licenses/>. 15### END LICENSE 16import hashlib 17import logging 18import os 19 20from configobj import ConfigObj, DuplicateError 21from variety.profile import get_profile_path 22from variety.Util import Util 23from variety_lib import varietyconfig 24 25logger = logging.getLogger("variety") 26 27TRUTH_VALUES = ["enabled", "1", "true", "on", "yes"] 28 29 30class Options: 31 OUTDATED_HASHES = {"clock_filter": ["dca6bd2dfa2b8c4e2db8801e39208f7f"]} 32 SIMPLE_DOWNLOADERS = [] # set by VarietyWindow at start 33 IMAGE_SOURCES = [] # set by VarietyWindow at start 34 CONFIGURABLE_IMAGE_SOURCES = [] # set by VarietyWindow at start 35 CONFIGURABLE_IMAGE_SOURCES_MAP = {} # set by VarietyWindow at start 36 37 class SourceType: 38 # local files and folders 39 IMAGE = "image" 40 FOLDER = "folder" 41 ALBUM_FILENAME = "album (by filename)" 42 ALBUM_DATE = "album (by date)" 43 44 # special local folders 45 FAVORITES = "favorites" 46 FETCHED = "fetched" 47 48 # predefined configurable sources 49 FLICKR = "flickr" 50 51 BUILTIN_SOURCE_TYPES = { 52 IMAGE, 53 FOLDER, 54 ALBUM_FILENAME, 55 ALBUM_DATE, 56 FAVORITES, 57 FETCHED, 58 FLICKR, 59 } 60 61 LOCAL_PATH_TYPES = {IMAGE, FOLDER, ALBUM_FILENAME, ALBUM_DATE} 62 63 LOCAL_TYPES = {IMAGE, FOLDER, ALBUM_FILENAME, ALBUM_DATE, FAVORITES, FETCHED} 64 65 DL_TYPES = {FLICKR} 66 67 EDITABLE_DL_TYPES = {FLICKR} 68 69 REMOVABLE_TYPES = {FOLDER, IMAGE, ALBUM_FILENAME, ALBUM_DATE} | EDITABLE_DL_TYPES 70 71 class LightnessMode: 72 DARK = 0 73 LIGHT = 1 74 75 def __init__(self): 76 self.configfile = os.path.join(get_profile_path(), "variety.conf") 77 78 def read(self): 79 self.set_defaults() 80 81 try: 82 config = self.read_config() 83 needs_writing = self.fix_outdated(config) 84 85 try: 86 self.change_enabled = config["change_enabled"].lower() in TRUTH_VALUES 87 except Exception: 88 pass 89 90 try: 91 self.change_on_start = config["change_on_start"].lower() in TRUTH_VALUES 92 except Exception: 93 pass 94 95 try: 96 self.change_interval = int(config["change_interval"]) 97 if self.change_interval < 5: 98 self.change_interval = 5 99 except Exception: 100 pass 101 102 try: 103 self.safe_mode = config["safe_mode"].lower() in TRUTH_VALUES 104 except Exception: 105 pass 106 107 try: 108 self.download_folder = os.path.expanduser(config["download_folder"]) 109 except Exception: 110 pass 111 112 try: 113 self.download_preference_ratio = max( 114 0, min(1, float(config["download_preference_ratio"])) 115 ) 116 except Exception: 117 pass 118 119 try: 120 self.quota_enabled = config["quota_enabled"].lower() in TRUTH_VALUES 121 except Exception: 122 pass 123 124 try: 125 self.quota_size = max(50, int(config["quota_size"])) 126 except Exception: 127 pass 128 129 try: 130 self.favorites_folder = os.path.expanduser(config["favorites_folder"]) 131 except Exception: 132 pass 133 134 try: 135 favorites_ops_text = config["favorites_operations"] 136 self.favorites_operations = list( 137 [x.strip().split(":") for x in favorites_ops_text.split(";") if x] 138 ) 139 except Exception: 140 pass 141 142 try: 143 self.fetched_folder = os.path.expanduser(config["fetched_folder"]) 144 except Exception: 145 pass 146 147 try: 148 self.clipboard_enabled = config["clipboard_enabled"].lower() in TRUTH_VALUES 149 except Exception: 150 pass 151 152 try: 153 self.clipboard_use_whitelist = ( 154 config["clipboard_use_whitelist"].lower() in TRUTH_VALUES 155 ) 156 except Exception: 157 pass 158 159 try: 160 self.clipboard_hosts = config["clipboard_hosts"].lower().split(",") 161 except Exception: 162 pass 163 164 try: 165 icon = config["icon"] 166 if icon in ["Light", "Dark", "Current", "1", "2", "3", "4", "None"] or ( 167 os.access(icon, os.R_OK) and Util.is_image(icon) 168 ): 169 self.icon = icon 170 except Exception: 171 pass 172 173 try: 174 self.desired_color_enabled = config["desired_color_enabled"].lower() in TRUTH_VALUES 175 except Exception: 176 pass 177 178 try: 179 self.desired_color = list(map(int, config["desired_color"].split())) 180 for i, x in enumerate(self.desired_color): 181 self.desired_color[i] = max(0, min(255, x)) 182 except Exception: 183 self.desired_color = None 184 185 try: 186 self.min_size_enabled = config["min_size_enabled"].lower() in TRUTH_VALUES 187 except Exception: 188 pass 189 190 try: 191 self.min_size = int(config["min_size"]) 192 self.min_size = max(0, min(100, self.min_size)) 193 except Exception: 194 pass 195 196 try: 197 self.use_landscape_enabled = config["use_landscape_enabled"].lower() in TRUTH_VALUES 198 except Exception: 199 pass 200 201 try: 202 self.lightness_enabled = config["lightness_enabled"].lower() in TRUTH_VALUES 203 except Exception: 204 pass 205 206 try: 207 self.lightness_mode = int(config["lightness_mode"]) 208 self.lightness_mode = max(0, min(1, self.lightness_mode)) 209 except Exception: 210 pass 211 212 try: 213 self.min_rating_enabled = config["min_rating_enabled"].lower() in TRUTH_VALUES 214 except Exception: 215 pass 216 217 try: 218 self.min_rating = int(config["min_rating"]) 219 self.min_rating = max(1, min(5, self.min_rating)) 220 except Exception: 221 pass 222 223 try: 224 self.smart_notice_shown = config["smart_notice_shown"].lower() in TRUTH_VALUES 225 except Exception: 226 pass 227 228 try: 229 self.smart_register_shown = config["smart_register_shown"].lower() in TRUTH_VALUES 230 except Exception: 231 pass 232 233 try: 234 self.stats_notice_shown = config["stats_notice_shown"].lower() in TRUTH_VALUES 235 except Exception: 236 pass 237 238 try: 239 self.smart_enabled = config["smart_enabled"].lower() in TRUTH_VALUES 240 except Exception: 241 pass 242 243 try: 244 self.sync_enabled = config["sync_enabled"].lower() in TRUTH_VALUES 245 except Exception: 246 pass 247 248 try: 249 self.stats_enabled = config["stats_enabled"].lower() in TRUTH_VALUES 250 except Exception: 251 pass 252 253 try: 254 self.copyto_enabled = config["copyto_enabled"].lower() in TRUTH_VALUES 255 except Exception: 256 pass 257 258 try: 259 self.copyto_folder = os.path.expanduser(config["copyto_folder"]) 260 except Exception: 261 pass 262 263 try: 264 self.clock_enabled = config["clock_enabled"].lower() in TRUTH_VALUES 265 except Exception: 266 pass 267 268 try: 269 self.clock_filter = config["clock_filter"].strip() 270 except Exception: 271 pass 272 273 try: 274 self.clock_font = config["clock_font"] 275 except Exception: 276 pass 277 278 try: 279 self.clock_date_font = config["clock_date_font"] 280 except Exception: 281 pass 282 283 try: 284 self.quotes_enabled = config["quotes_enabled"].lower() in TRUTH_VALUES 285 except Exception: 286 pass 287 288 try: 289 self.quotes_font = config["quotes_font"] 290 except Exception: 291 pass 292 293 try: 294 self.quotes_text_color = list(map(int, config["quotes_text_color"].split())) 295 for i, x in enumerate(self.quotes_text_color): 296 self.quotes_text_color[i] = max(0, min(255, x)) 297 except Exception: 298 pass 299 300 try: 301 self.quotes_bg_color = list(map(int, config["quotes_bg_color"].split())) 302 for i, x in enumerate(self.quotes_bg_color): 303 self.quotes_bg_color[i] = max(0, min(255, x)) 304 except Exception: 305 pass 306 307 try: 308 self.quotes_bg_opacity = int(float(config["quotes_bg_opacity"])) 309 self.quotes_bg_opacity = max(0, min(100, self.quotes_bg_opacity)) 310 except Exception: 311 pass 312 313 try: 314 self.quotes_text_shadow = config["quotes_text_shadow"].lower() in TRUTH_VALUES 315 except Exception: 316 pass 317 318 try: 319 self.quotes_text_color = list(map(int, config["quotes_text_color"].split())) 320 for i, x in enumerate(self.quotes_text_color): 321 self.quotes_text_color[i] = max(0, min(255, x)) 322 except Exception: 323 pass 324 325 try: 326 self.quotes_disabled_sources = config["quotes_disabled_sources"].strip().split("|") 327 except Exception: 328 pass 329 330 try: 331 self.quotes_tags = config["quotes_tags"] 332 except Exception: 333 pass 334 335 try: 336 self.quotes_authors = config["quotes_authors"] 337 except Exception: 338 pass 339 340 try: 341 self.quotes_change_enabled = config["quotes_change_enabled"].lower() in TRUTH_VALUES 342 except Exception: 343 pass 344 345 try: 346 self.quotes_change_interval = int(config["quotes_change_interval"]) 347 if self.quotes_change_interval < 10: 348 self.quotes_change_interval = 10 349 except Exception: 350 pass 351 352 try: 353 self.quotes_width = int(float(config["quotes_width"])) 354 self.quotes_width = max(0, min(100, self.quotes_width)) 355 except Exception: 356 pass 357 358 try: 359 self.quotes_hpos = int(float(config["quotes_hpos"])) 360 self.quotes_hpos = max(0, min(100, self.quotes_hpos)) 361 except Exception: 362 pass 363 364 try: 365 self.quotes_vpos = int(float(config["quotes_vpos"])) 366 self.quotes_vpos = max(0, min(100, self.quotes_vpos)) 367 except Exception: 368 pass 369 370 try: 371 self.quotes_max_length = int(config["quotes_max_length"]) 372 self.quotes_max_length = max(0, self.quotes_max_length) 373 except Exception: 374 pass 375 376 try: 377 self.quotes_favorites_file = os.path.expanduser(config["quotes_favorites_file"]) 378 except Exception: 379 pass 380 381 try: 382 self.slideshow_sources_enabled = ( 383 config["slideshow_sources_enabled"].lower() in TRUTH_VALUES 384 ) 385 except Exception: 386 pass 387 388 try: 389 self.slideshow_favorites_enabled = ( 390 config["slideshow_favorites_enabled"].lower() in TRUTH_VALUES 391 ) 392 except Exception: 393 pass 394 395 try: 396 self.slideshow_downloads_enabled = ( 397 config["slideshow_downloads_enabled"].lower() in TRUTH_VALUES 398 ) 399 except Exception: 400 pass 401 402 try: 403 self.slideshow_custom_enabled = ( 404 config["slideshow_custom_enabled"].lower() in TRUTH_VALUES 405 ) 406 except Exception: 407 pass 408 409 try: 410 custom_path = config["slideshow_custom_folder"] 411 if custom_path in ("None", "Default") or not os.path.isdir(custom_path): 412 self.slideshow_custom_folder = Util.get_xdg_pictures_folder() 413 else: 414 self.slideshow_custom_folder = custom_path 415 except Exception: 416 pass 417 418 try: 419 slideshow_sort_order = config["slideshow_sort_order"] 420 if slideshow_sort_order in [ 421 "Random", 422 "Name, asc", 423 "Name, desc", 424 "Date, asc", 425 "Date, desc", 426 ]: 427 self.slideshow_sort_order = slideshow_sort_order 428 except Exception: 429 pass 430 431 try: 432 self.slideshow_monitor = config["slideshow_monitor"] 433 except Exception: 434 pass 435 436 try: 437 slideshow_mode = config["slideshow_mode"] 438 if slideshow_mode in ["Fullscreen", "Desktop", "Maximized", "Window"]: 439 self.slideshow_mode = slideshow_mode 440 except Exception: 441 pass 442 443 try: 444 self.slideshow_seconds = float(config["slideshow_seconds"]) 445 self.slideshow_seconds = max(0.5, self.slideshow_seconds) 446 except Exception: 447 pass 448 449 try: 450 self.slideshow_fade = float(config["slideshow_fade"]) 451 self.slideshow_fade = max(0, min(1, self.slideshow_fade)) 452 except Exception: 453 pass 454 455 try: 456 self.slideshow_zoom = float(config["slideshow_zoom"]) 457 self.slideshow_zoom = max(0, min(1, self.slideshow_zoom)) 458 except Exception: 459 pass 460 461 try: 462 self.slideshow_pan = float(config["slideshow_pan"]) 463 self.slideshow_pan = max(0, min(0.20, self.slideshow_pan)) 464 except Exception: 465 pass 466 467 self.sources = [] 468 if "sources" in config: 469 sources = config["sources"] 470 for v in sources.values(): 471 try: 472 self.sources.append(Options.parse_source(v)) 473 except Exception: 474 logger.debug(lambda: "Cannot parse source: " + v, exc_info=True) 475 logger.info("Ignoring no longer supported source %s", v) 476 477 # automatically append sources for all simple downloaders we have 478 source_types = set(s[1] for s in self.sources) 479 for downloader in sorted(self.SIMPLE_DOWNLOADERS, key=lambda dl: dl.get_source_type()): 480 if downloader.get_source_type() not in source_types: 481 self.sources.append( 482 [True, downloader.get_source_type(), downloader.get_description()] 483 ) 484 485 self.parse_autosources() 486 487 if "filters" in config: 488 self.filters = [] 489 filters = config["filters"] 490 for v in filters.values(): 491 try: 492 self.filters.append(Options.parse_filter(v)) 493 except Exception: 494 logger.exception(lambda: "Cannot parse filter: " + str(v)) 495 496 self.parse_autofilters() 497 498 if needs_writing: 499 logger.info(lambda: "Some outdated settings were updated, writing the changes") 500 self.write() 501 502 except Exception: 503 logger.exception(lambda: "Could not read configuration:") 504 505 def fix_outdated(self, config): 506 changed = False 507 for key, outdated_hashes in Options.OUTDATED_HASHES.items(): 508 if key in config: 509 current_hash = hashlib.md5(config[key].encode()).hexdigest() 510 if current_hash in outdated_hashes: 511 # entry is outdated: delete it and use the default 512 logger.warning( 513 lambda: "Option " + key + " has an outdated value, using the new default" 514 ) 515 changed = True 516 del config[key] 517 return changed 518 519 def parse_autosources(self): 520 try: 521 with open(varietyconfig.get_data_file("config", "sources.txt"), encoding="utf8") as f: 522 for line in f: 523 if not line.strip() or line.strip().startswith("#"): 524 continue 525 try: 526 s = Options.parse_source(line.strip()) 527 if s[1] in [src[1] for src in self.sources]: 528 continue 529 self.sources.append(s) 530 except Exception: 531 logger.exception(lambda: "Cannot parse source in sources.txt: " + line) 532 except Exception: 533 logger.exception(lambda: "Cannot open sources.txt") 534 535 def parse_autofilters(self): 536 try: 537 with open(varietyconfig.get_data_file("config", "filters.txt"), encoding="utf8") as f: 538 for line in f: 539 if not line.strip() or line.strip().startswith("#"): 540 continue 541 try: 542 s = Options.parse_filter(line.strip()) 543 if not s[1].lower() in [f[1].lower() for f in self.filters]: 544 self.filters.append(s) 545 except Exception: 546 logger.exception(lambda: "Cannot parse filter in filters.txt: " + line) 547 except Exception: 548 logger.exception(lambda: "Cannot open filters.txt") 549 550 @staticmethod 551 def parse_source(v): 552 s = v.strip().split("|") 553 enabled = s[0].lower() in TRUTH_VALUES 554 return [enabled, s[1], s[2]] 555 556 @staticmethod 557 def parse_filter(v): 558 s = v.strip().split("|") 559 enabled = s[0].lower() in TRUTH_VALUES 560 return [enabled, s[1], s[2]] 561 562 @staticmethod 563 def get_all_supported_source_types(): 564 return Options.SourceType.BUILTIN_SOURCE_TYPES | Options.get_plugin_source_types() 565 566 @staticmethod 567 def get_downloader_source_types(): 568 return Options.SourceType.DL_TYPES | Options.get_plugin_source_types() 569 570 @staticmethod 571 def get_editable_source_types(): 572 return Options.SourceType.EDITABLE_DL_TYPES | Options.get_configurable_plugin_source_types() 573 574 @staticmethod 575 def get_removable_source_types(): 576 return Options.SourceType.REMOVABLE_TYPES | Options.get_editable_source_types() 577 578 @staticmethod 579 def get_plugin_source_types(): 580 return set(dl.get_source_type() for dl in Options.IMAGE_SOURCES) 581 582 @staticmethod 583 def get_configurable_plugin_source_types(): 584 return set(dl.get_source_type() for dl in Options.CONFIGURABLE_IMAGE_SOURCES) 585 586 def set_defaults(self): 587 self.change_enabled = True 588 self.change_on_start = False 589 self.change_interval = 300 590 self.safe_mode = False 591 592 self.download_folder = os.path.join(get_profile_path(), "Downloaded") 593 self.download_preference_ratio = 0.9 594 self.quota_enabled = True 595 self.quota_size = 1000 596 597 self.favorites_folder = os.path.join(get_profile_path(), "Favorites") 598 self.favorites_operations = [ 599 ["Downloaded", "Copy"], 600 ["Fetched", "Move"], 601 ["Others", "Copy"], 602 ] 603 604 self.fetched_folder = os.path.join(get_profile_path(), "Fetched") 605 self.clipboard_enabled = False 606 self.clipboard_use_whitelist = True 607 self.clipboard_hosts = "wallhaven.cc,ns223506.ovh.net,wallpapers.net,flickr.com,imgur.com,deviantart.com,interfacelift.com,vladstudio.com".split( 608 "," 609 ) 610 611 self.icon = "Light" 612 613 self.desired_color_enabled = False 614 self.desired_color = None 615 self.min_size_enabled = False 616 self.min_size = 80 617 self.use_landscape_enabled = True 618 self.lightness_enabled = False 619 self.lightness_mode = Options.LightnessMode.DARK 620 self.min_rating_enabled = False 621 self.min_rating = 4 622 623 self.smart_notice_shown = False 624 self.smart_register_shown = False 625 self.stats_notice_shown = False 626 627 self.smart_enabled = False 628 self.sync_enabled = False 629 self.stats_enabled = False 630 631 self.copyto_enabled = False 632 self.copyto_folder = "Default" 633 634 self.clock_enabled = False 635 self.clock_font = "Ubuntu Condensed, 70" 636 self.clock_date_font = "Ubuntu Condensed, 30" 637 self.clock_filter = "-density 100 -font `fc-match -f '%{file[0]}' '%CLOCK_FONT_NAME'` -pointsize %CLOCK_FONT_SIZE -gravity SouthEast -fill '#00000044' -annotate 0x0+[%HOFFSET+58]+[%VOFFSET+108] '%H:%M' -fill white -annotate 0x0+[%HOFFSET+60]+[%VOFFSET+110] '%H:%M' -font `fc-match -f '%{file[0]}' '%DATE_FONT_NAME'` -pointsize %DATE_FONT_SIZE -fill '#00000044' -annotate 0x0+[%HOFFSET+58]+[%VOFFSET+58] '%A, %B %d' -fill white -annotate 0x0+[%HOFFSET+60]+[%VOFFSET+60] '%A, %B %d'" 638 639 self.quotes_enabled = False 640 self.quotes_font = "Bitstream Charter 30" 641 self.quotes_text_color = (255, 255, 255) 642 self.quotes_bg_color = (80, 80, 80) 643 self.quotes_bg_opacity = 55 644 self.quotes_text_shadow = False 645 self.quotes_disabled_sources = [] 646 self.quotes_tags = "" 647 self.quotes_authors = "" 648 self.quotes_change_enabled = False 649 self.quotes_change_interval = 300 650 self.quotes_width = 70 651 self.quotes_hpos = 100 652 self.quotes_vpos = 40 653 self.quotes_max_length = 250 654 self.quotes_favorites_file = os.path.join(get_profile_path(), "favorite_quotes.txt") 655 656 self.slideshow_sources_enabled = True 657 self.slideshow_favorites_enabled = True 658 self.slideshow_downloads_enabled = False 659 self.slideshow_custom_enabled = False 660 self.slideshow_custom_folder = Util.get_xdg_pictures_folder() 661 self.slideshow_sort_order = "Random" 662 self.slideshow_monitor = "All" 663 self.slideshow_mode = "Fullscreen" 664 self.slideshow_seconds = 6 665 self.slideshow_fade = 0.4 666 self.slideshow_zoom = 0.2 667 self.slideshow_pan = 0.05 668 669 self.sources = [ 670 [True, Options.SourceType.FAVORITES, "The Favorites folder"], 671 [True, Options.SourceType.FETCHED, "The Fetched folder"], 672 [True, Options.SourceType.FOLDER, "/usr/local/share/backgrounds/"], 673 [ 674 True, 675 Options.SourceType.FLICKR, 676 "user:www.flickr.com/photos/peter-levi/;user_id:93647178@N00;", 677 ], 678 ] 679 680 self.filters = [ 681 [False, "Keep original", ""], 682 [False, "Grayscale", "-type Grayscale"], 683 [False, "Heavy blur", "-blur 120x40"], 684 [False, "Oil painting", "-paint 6"], 685 [False, "Charcoal painting", "-charcoal 3"], 686 [False, "Pointilism", "-spread 10 -noise 3"], 687 [False, "Pixellate", "-scale 3% -scale 3333%"], 688 ] 689 690 def write(self): 691 try: 692 config = ConfigObj(self.configfile, encoding="utf8", default_encoding="utf8") 693 except Exception: 694 config = ConfigObj(encoding="utf8", default_encoding="utf8") 695 config.filename = self.configfile 696 697 try: 698 config["change_enabled"] = str(self.change_enabled) 699 config["change_on_start"] = str(self.change_on_start) 700 config["change_interval"] = str(self.change_interval) 701 config["safe_mode"] = str(self.safe_mode) 702 703 config["download_folder"] = Util.collapseuser(self.download_folder) 704 config["download_preference_ratio"] = str(self.download_preference_ratio) 705 706 config["quota_enabled"] = str(self.quota_enabled) 707 config["quota_size"] = str(self.quota_size) 708 709 config["favorites_folder"] = Util.collapseuser(self.favorites_folder) 710 config["favorites_operations"] = ";".join( 711 ":".join(x) for x in self.favorites_operations 712 ) 713 714 config["fetched_folder"] = Util.collapseuser(self.fetched_folder) 715 config["clipboard_enabled"] = str(self.clipboard_enabled) 716 config["clipboard_use_whitelist"] = str(self.clipboard_use_whitelist) 717 config["clipboard_hosts"] = ",".join(self.clipboard_hosts) 718 719 config["icon"] = self.icon 720 721 config["desired_color_enabled"] = str(self.desired_color_enabled) 722 config["desired_color"] = ( 723 " ".join(map(str, self.desired_color)) if self.desired_color else "None" 724 ) 725 config["min_size_enabled"] = str(self.min_size_enabled) 726 config["min_size"] = str(self.min_size) 727 config["use_landscape_enabled"] = str(self.use_landscape_enabled) 728 config["lightness_enabled"] = str(self.lightness_enabled) 729 config["lightness_mode"] = str(self.lightness_mode) 730 config["min_rating_enabled"] = str(self.min_rating_enabled) 731 config["min_rating"] = str(self.min_rating) 732 733 config["smart_notice_shown"] = str(self.smart_notice_shown) 734 config["smart_register_shown"] = str(self.smart_register_shown) 735 config["stats_notice_shown"] = str(self.stats_notice_shown) 736 737 config["smart_enabled"] = str(self.smart_enabled) 738 config["sync_enabled"] = str(self.sync_enabled) 739 config["stats_enabled"] = str(self.stats_enabled) 740 741 config["copyto_enabled"] = str(self.copyto_enabled) 742 config["copyto_folder"] = Util.collapseuser(self.copyto_folder) 743 744 config["clock_enabled"] = str(self.clock_enabled) 745 config["clock_filter"] = self.clock_filter 746 config["clock_font"] = self.clock_font 747 config["clock_date_font"] = self.clock_date_font 748 749 config["quotes_enabled"] = str(self.quotes_enabled) 750 config["quotes_font"] = self.quotes_font 751 config["quotes_text_color"] = " ".join(map(str, self.quotes_text_color)) 752 config["quotes_bg_color"] = " ".join(map(str, self.quotes_bg_color)) 753 config["quotes_bg_opacity"] = str(self.quotes_bg_opacity) 754 config["quotes_text_shadow"] = str(self.quotes_text_shadow) 755 config["quotes_disabled_sources"] = "|".join(self.quotes_disabled_sources) 756 config["quotes_tags"] = self.quotes_tags 757 config["quotes_authors"] = self.quotes_authors 758 config["quotes_change_enabled"] = str(self.quotes_change_enabled) 759 config["quotes_change_interval"] = str(self.quotes_change_interval) 760 config["quotes_width"] = str(self.quotes_width) 761 config["quotes_hpos"] = str(self.quotes_hpos) 762 config["quotes_vpos"] = str(self.quotes_vpos) 763 config["quotes_max_length"] = str(self.quotes_max_length) 764 config["quotes_favorites_file"] = Util.collapseuser(self.quotes_favorites_file) 765 766 config["slideshow_sources_enabled"] = str(self.slideshow_sources_enabled) 767 config["slideshow_favorites_enabled"] = str(self.slideshow_favorites_enabled) 768 config["slideshow_downloads_enabled"] = str(self.slideshow_downloads_enabled) 769 config["slideshow_custom_enabled"] = str(self.slideshow_custom_enabled) 770 config["slideshow_custom_folder"] = Util.collapseuser(self.slideshow_custom_folder) 771 config["slideshow_sort_order"] = self.slideshow_sort_order 772 config["slideshow_monitor"] = self.slideshow_monitor 773 config["slideshow_mode"] = self.slideshow_mode 774 config["slideshow_seconds"] = str(self.slideshow_seconds) 775 config["slideshow_fade"] = str(self.slideshow_fade) 776 config["slideshow_zoom"] = str(self.slideshow_zoom) 777 config["slideshow_pan"] = str(self.slideshow_pan) 778 779 config["sources"] = {} 780 for i, s in enumerate(self.sources): 781 config["sources"]["src" + str(i + 1)] = str(s[0]) + "|" + str(s[1]) + "|" + s[2] 782 783 config["filters"] = {} 784 for i, f in enumerate(self.filters): 785 config["filters"]["filter" + str(i + 1)] = str(f[0]) + "|" + f[1] + "|" + f[2] 786 787 config.write() 788 789 except Exception: 790 logger.exception(lambda: "Could not write configuration:") 791 792 @staticmethod 793 def set_options(opts): 794 config = Options().read_config() 795 for key, value in opts: 796 config[key] = value 797 config.write() 798 799 def read_config(self): 800 config = ConfigObj(raise_errors=False, encoding="utf8", default_encoding="utf8") 801 config.filename = self.configfile 802 try: 803 config.reload() 804 except DuplicateError: 805 logger.warning(lambda: "Duplicate keys in config file, please fix this") 806 return config 807 808 809if __name__ == "__main__": 810 formatter = logging.Formatter("%(levelname)s:%(name)s: %(funcName)s() '%(message)s'") 811 812 logger = logging.getLogger("variety") 813 logger_sh = logging.StreamHandler() 814 logger_sh.setFormatter(formatter) 815 logger.addHandler(logger_sh) 816 817 o = Options() 818 o.read() 819 print(o.sources) 820 print(o.filters) 821 o.write() 822