Check-in [ce0e9149db]
Overview
Comment: | Add conf.nothreads flag and --nt cmdline flag, to prevent Gtk3 idle update race conditions if need be. (Still flaky for initial startups.) |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
ce0e9149db46cf2296f451c2168e19d2 |
User & Date: | mario on 2015-04-24 00:00:44 |
Other Links: | manifest | tags |
Context
2015-04-24
| ||
04:59 | Disable update_streams_partially_done() for Gtk3, as that's the main cause of memory corruption (despite being run in idle loop). check-in: 1edde401ce user: mario tags: trunk | |
00:00 | Add conf.nothreads flag and --nt cmdline flag, to prevent Gtk3 idle update race conditions if need be. (Still flaky for initial startups.) check-in: ce0e9149db user: mario tags: trunk | |
2015-04-23
| ||
19:31 | Use distinct /tmp/streamtuner2/ directory for temporary pls/m3u/xspf files (also for DND). And have action. module reuse them, based on numeric row{} hash. check-in: 7411543862 user: mario tags: trunk | |
Changes
Modified channels/__init__.py from [bddc9139b6] to [7050519949].
︙ | ︙ | |||
442 443 444 445 446 447 448 449 450 | # display outside of this non-main thread uikit.do(self.display_categories) # insert content into gtk category list def display_categories(self): # rebuild gtk.TreeView | > < < | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 | # display outside of this non-main thread uikit.do(self.display_categories) # insert content into gtk category list def display_categories(self): log.UI(self.module+".display_categories()", "mk tree, expand_all, select first path, currentcat") # rebuild gtk.TreeView uikit.tree(self.gtk_cat, self.categories, title="Category", icon=gtk.STOCK_OPEN) # if it's a short list of categories, there's probably subfolders if len(self.categories) < 20: self.gtk_cat.expand_all() # select any first element self.gtk_cat.get_selection().select_path("0") #set_cursor self.currentcat() |
︙ | ︙ |
Modified channels/jamendo.py from [c57db677e5] to [e32838ce56].
1 2 3 4 5 6 7 8 9 10 | # encoding: UTF-8 # api: streamtuner2 # title: Jamendo # description: A license-free music collection and artist hub. # type: channel # version: 2.2 # category: radio # url: http://jamendo.com/ # depends: json # config: | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # encoding: UTF-8 # api: streamtuner2 # title: Jamendo # description: A license-free music collection and artist hub. # type: channel # version: 2.2 # category: radio # url: http://jamendo.com/ # depends: json # config: # { name: jamendo_stream_format, value: ogg, type: select, select: "ogg=Ogg Vorbis, 112kbit/s|mp32=MP3, 192kbit/s VBR|mp31=MP3, 96kbit/s|flac=Xiph FLAC, β³600kbit/s", description: "Audio format for tracks, albums, playlists." } # { name: jamendo_image_size, value: 50, type: select, select: "25=25px|35=35px|50=50px|55=55px|60=60px|65=65px|70=70px|75=75px|85=85px|100=100px|130=130px|150=150px|200=200px|300=300px", description: "Preview images size (height and width) for albums or tracks." } # { name: jamendo_count, value: 1, type:text, description: "How many result sets (200 entries each) to retrieve." } # priority: default # png: # iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAwNJREFUOI1lk0toXGUYhp/v/8+ZzDQzmdZMZ8YSkxIQCWTTYsALLsQsUsWFQgJeEJQqIigUvBK0FBQULIJKMEKrFjdOW9xoW1AbRdRCEYtd2GCxxUQbk8bMrTNnzpn//1xMLYLv6l2877N7oBcB0GMY # IAUY/p9w/glCrVzby3+LVqaxM4dx+inQV7KIHcKYAbzG4FYgqcmudYBAF+jKnT2QAExPYysVHCdKRQgewwRT0B1FfQboYsLLeE7hk4Oo/iD3rFit4GQGRCsYwJMt7yQVfrS6Go3HUQtvskiwCWM82qlijGdbaUsbTV5G/X65+y+rCzgBRI+VbsSmvvltqVkuX78t6rvpKWtvuMsgOfHtJia66OPz # 86564UtDkA2Lm/VJYF6mLgXo50X0xNAHy4cK2jk+diVe+1FVVX/57oIuL66qqmriVF23rZ1TjyTLH2YT/WJ4TY+XRgAMKqNgJ41vebbvTgeFneo6Tb567zPmHn+LxdPnCIxHTFrt2CtB/8BWqtVOAewDWgGDmHG0u1VtztjC7SLqRSUkM5Clsd5ibs/7nDzyLd51xfQPq82PKS5SsHfQAEMsIZG3 |
︙ | ︙ |
Modified config.py from [9fc88f2a2d] to [1bfcbd774c].
1 2 3 4 5 6 7 8 9 10 11 12 13 | # # encoding: UTF-8 # api: streamtuner2 # type: class # title: global config object # description: reads ~/.config/streamtuner/*.json files # config: # { arg: -d, type: str, name: disable[], description: Omit plugin from initialization. } # { arg: -e, type: str, name: enable[], description: Add channel plugin. } # { arg: --gtk3, type: boolean, name: gtk3, description: Start with Gtk3 interface. } # { arg: -D, type: boolean, name: debug, description: Enable debug messages on console } # { arg: action, type: str *, name: action[], description: CLI interface commands. } # { arg: -x, type: boolean, name: exit, hidden: 1 } | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # # encoding: UTF-8 # api: streamtuner2 # type: class # title: global config object # description: reads ~/.config/streamtuner/*.json files # config: # { arg: -d, type: str, name: disable[], description: Omit plugin from initialization. } # { arg: -e, type: str, name: enable[], description: Add channel plugin. } # { arg: --gtk3, type: boolean, name: gtk3, description: Start with Gtk3 interface. } # { arg: -D, type: boolean, name: debug, description: Enable debug messages on console } # { arg: action, type: str *, name: action[], description: CLI interface commands. } # { arg: -x, type: boolean, name: exit, hidden: 1 } # { arg: --nt, type: boolean, name: nothreads, description: Disable threading/gtk_idle UI. } # version: 2.7 # priority: core # # In the main application or module files which need access # to a global conf.* object, just import this module as follows: # # from config import * # |
︙ | ︙ | |||
116 117 118 119 120 121 122 123 124 125 126 127 128 129 | # core plugins, cannot be disabled anyway "bookmarks": 1, "search": 1, "streamedit": 1, "configwin": 1, } self.tmp = os.environ.get("TEMP", "/tmp") + "/streamtuner2" self.max_streams = "500" self.show_bookmarks = 1 self.show_favicons = 1 self.load_favicon = 1 self.heuristic_bookmark_update = 0 self.retain_deleted = 0 self.auto_save_appstate = 1 | > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | # core plugins, cannot be disabled anyway "bookmarks": 1, "search": 1, "streamedit": 1, "configwin": 1, } self.tmp = os.environ.get("TEMP", "/tmp") + "/streamtuner2" self.nothreads = 0 self.max_streams = "500" self.show_bookmarks = 1 self.show_favicons = 1 self.load_favicon = 1 self.heuristic_bookmark_update = 0 self.retain_deleted = 0 self.auto_save_appstate = 1 |
︙ | ︙ | |||
283 284 285 286 287 288 289 290 291 292 293 294 295 296 | ap.add_argument(*kwargs.pop("args"), **kwargs) return ap.parse_args() # Copy args fields into conf. dict def apply_args(self, args): self.debug = args.debug if args.exit: sys.exit(1) for p_id in (args.disable or []): self.plugins[p_id] = 0 for p_id in (args.enable or []): self.plugins[p_id] = 1 | > | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | ap.add_argument(*kwargs.pop("args"), **kwargs) return ap.parse_args() # Copy args fields into conf. dict def apply_args(self, args): self.debug = args.debug self.nothreads = args.nothreads if args.exit: sys.exit(1) for p_id in (args.disable or []): self.plugins[p_id] = 0 for p_id in (args.enable or []): self.plugins[p_id] = 1 |
︙ | ︙ | |||
485 486 487 488 489 490 491 | def log_print(self, *args, **kwargs): # debug level method = self.method.upper() if method != "ERR": if "debug" in conf and not conf.debug: return # color/prefix | | > | 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 | def log_print(self, *args, **kwargs): # debug level method = self.method.upper() if method != "ERR": if "debug" in conf and not conf.debug: return # color/prefix method = r"[{}[{}][0m".format(self.colors.get(method.split("_")[0], "47m"), method) # output print(method + " " + " ".join([str(a) for a in args]), file=sys.stderr) # Colors colors = { "ERR": "31m", # red ERROR "INIT": "31m", # red INIT ERROR "PROC": "32m", # green PROCESS "CONF": "33m", # brown CONFIG DATA "UI": "34m", # blue USER INTERFACE BEHAVIOUR "UIKIT":"38;5;222;48;5;235m", # THREAD/UIKIT/IDLE TASKS "HTTP": "35m", # magenta HTTP REQUEST "DATA": "36m", # cyan DATA "INFO": "37m", # gray INFO "STAT": "37m", # gray CONFIG STATE } # instantiate right away |
︙ | ︙ |
Modified gtk3.xml.gz from [b485718e2f] to [540b2b2ee4].
cannot compute difference between binary files
Modified st2.py from [9776e658be] to [d0ecae043d].
︙ | ︙ | |||
292 293 294 295 296 297 298 299 300 301 302 303 304 305 | self.thread( #@TODO: should get a wrapper, for HTTP errors, and optionalize bookamrks lambda: ( self.channel().load(category,reload), reload and self.bookmarks.heuristic_update(self.current_channel,category) ) ) # Thread a function, add to worker pool (for utilizing stop button) def thread(self, target, *args): thread = Thread(target=target, args=args) thread.start() self.working.append(thread) # Click in category list def on_category_clicked(self, widget, event, *more): | > > | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | self.thread( #@TODO: should get a wrapper, for HTTP errors, and optionalize bookamrks lambda: ( self.channel().load(category,reload), reload and self.bookmarks.heuristic_update(self.current_channel,category) ) ) # Thread a function, add to worker pool (for utilizing stop button) def thread(self, target, *args): if conf.nothreads: return target(*args) thread = Thread(target=target, args=args) thread.start() self.working.append(thread) # Click in category list def on_category_clicked(self, widget, event, *more): |
︙ | ︙ |
Modified uikit.py from [2a34911ea4] to [cf9953748a].
︙ | ︙ | |||
400 401 402 403 404 405 406 | c.set_current_name(re.sub(r"\.(m3u|pls|xspf|jspf|asx|json|smil|wpl)$", ext.strip("*"), fn)) # Spool gtk update calls from non-main threads (optional immediate=1 flag to run task next, not last) @staticmethod def do(callback, *args, **kwargs): | > | < > > > | | > | | | | | | | | 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | c.set_current_name(re.sub(r"\.(m3u|pls|xspf|jspf|asx|json|smil|wpl)$", ext.strip("*"), fn)) # Spool gtk update calls from non-main threads (optional immediate=1 flag to run task next, not last) @staticmethod def do(callback, *args, **kwargs): name = inspect.getsource(callback).strip() if callback.__name__=='<lambda>' else str(callback) if kwargs.get("immediate"): del kwargs["immediate"] pos = 0 else: pos = -1 # Run callback right away if uikit.in_idle or conf.nothreads: log.UIKIT_RUN_NOW(name) callback(*args, **kwargs) # Spool them for Gtk idle handling else: log.UIKIT_SPOOL(name) uikit.idle_tasks.insert(pos, [lambda: callback(*args, **kwargs), name]) gobject.idle_add(uikit.idle_do) # Collect tasks to perform in gtk.main loop idle_tasks = [] in_idle = False # Execute UI updating tasks in order @staticmethod def idle_do(): uikit.in_idle = True if uikit.idle_tasks: task, name = uikit.idle_tasks.pop(0) log.UIKIT_EXEC(name) task() uikit.in_idle = False return len(uikit.idle_tasks) > 0 # adds background color to widget, # eventually wraps it into a gtk.Window, if it needs a container @staticmethod |
︙ | ︙ |