This seems about right. Instead of swapping out via same property, I just handle the item (one at a time) using a queue

import objc_util import time class NotificationObserver: def __init__(self, name): self.name = objc_util.ns(name) self.center = objc_util.ObjCClass("NSNotificationCenter").defaultCenter() self._observer = None self._blk = None self.queue = objc_util.ObjCClass('NSOperationQueue').new() self.queue.setName_(objc_util.ns('test')) self.ni_objs = [] '''the objc block signature. do not modify''' def _block(self, _cmd, notification): try: ni_obj = objc_util.ObjCInstance(notification).object() self.ni_objs.append(ni_obj) except Exception as ex: print(ex) '''start observing''' def start(self): #print(time.asctime() + ' starting') if self._observer: raise Exception('observer already started') self._blk = objc_util.ObjCBlock(self._block, restype=None, argtypes=[objc_util.c_void_p, objc_util.c_void_p]) self._observer = self.center.addObserverForName_object_queue_usingBlock_(self.name, None, self.queue, self._blk) objc_util.retain_global(self) def stop(self): #print(time.asctime() + ' stopping') if self._observer: self.center.removeObserver_(self._observer) self._observer = None objc_util.release_global(self)

Trying to make the seek call as quick as possible, also cleared the list (really should be removing the item that won’t exist in case you have other player objects) before the next item plays in case another (prior) notification(s) slips through

def looper(player): while v.on_screen: if not paused and not wait: #if hasattr(player, "item_load_time") and player.player.rate() == 0.0 and should_do_loop(): pi = player.item if pi and pi in NotificationObserver.itemDidPlay.ni_objs: NotificationObserver.itemDidPlay.ni_objs.remove(pi) player.seek(secs=0) player.player.play() player.playCount += 1 if player.layer: #console.hud_alert("vp.playCount = {}".format(player.playCount), duration=0.5) pass elif not inf: #Audio player has looped -> next item change_i_by_val(1) update_i_change() #console.hud_alert("ap.playCount = {}".format(player.playCount), duration=0.5) pass time.sleep(refresh_rate) else: time.sleep(refresh_rate_helper)