omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular
    1. Home
    2. ts

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    • Profile
    • Following 0
    • Followers 0
    • Topics 1
    • Posts 20
    • Best 2
    • Controversial 0
    • Groups 0

    ts

    @ts

    2
    Reputation
    610
    Profile views
    20
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    ts Unfollow Follow

    Best posts made by ts

    • RE: Something like applicationWillResignActive?

      Ok so far it’s working, though I’m a bit nervous on running .clear() at the wrong time (not sure if I can attach an observer to an object) or replace itself (the notification instance .object())

      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.clear()
      		
      	def clear(self):
      		self.ni = None
      		self.ni_name = None
      		self.ni_obj = None
      		
      	'''the objc block signature. do not modify'''
      	def _block(self, _cmd, notification):
      		try:
      			self.ni = objc_util.ObjCInstance(notification)
      			self.ni_name = self.ni.name()
      			self.ni_obj = self.ni.object()
      		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)
      

      Using it within my script (a portion of it), it does work just as I originally wanted to (thanks again! @JonB)

      from NotificationObserver import NotificationObserver
      
      observer_names = {
      	"AVPlayerItemDidPlayToEndTimeNotification": "itemDidPlay"
      }
      
      for on in observer_names:
      	name = observer_names[on]
      	setattr(NotificationObserver, name, NotificationObserver(on))
      	getattr(NotificationObserver, name).start()
      
      def app_active():
      	return not sa.applicationState() == 2 #1 skip
      	#return not sa.isSuspended() #1 skip
      	#return not sa._isResigningActive() #3 skips
      
      def should_do_loop():
      	if keyboard.is_keyboard() or app_active():
      		return True
      	else:
      		return False
      
      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 is NotificationObserver.itemDidPlay.ni_obj:
      				NotificationObserver.itemDidPlay.clear()
      				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)
      
      posted in Pythonista
      ts
      ts
    • RE: Something like applicationWillResignActive?

      So modified it a bit to allow other notifications

      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.state = False
      		
      	'''define your own callback here'''
      	@objc_util.ui.in_background
      	def callback(self, name, obj, userInfo):
      		print(time.asctime() + name)
      		self.state = True
      		
      	'''the objc block signature. do not modify'''
      	def _block(self, _cmd, notification):
      		try:
      			self.ni = objc_util.ObjCInstance(notification)
      			self.ni_name = self.ni.name()
      			self.ni_obj = self.ni.object()
      			self.ni_ui = self.ni.userInfo()
      			self.callback(self.ni_name.__str__(), self.ni_obj, self.ni_ui)
      		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)
      

      To test it, I did

      from NotificationObserver import NotificationObserver
      
      observer_names = {
      	"UIApplicationDidBecomeActiveNotification": "didBecomeActive",
      	"UIApplicationDidEnterBackgroundNotification": "didEnterBackground",
      	"UIApplicationWillEnterForegroundNotification": "willEnterForeground",
      	"UIApplicationWillResignActiveNotification": "willResignActive"
      }
      
      for on in observer_names:
      	name = observer_names[on]
      	setattr(NotificationObserver, name, NotificationObserver(on))
      	getattr(NotificationObserver, name).start()
      

      I also added applicationState() into the mix and got this in the console

      sa.applicationState() = 0
      ...
      sa.applicationState() = 1
      sa.applicationState() = 1
      willResignActive
      sa.applicationState() = 1
      ...
      sa.applicationState() = 2
      sa.applicationState() = 2
      didEnterBackground
      sa.applicationState() = 2
      ...
      willEnterForeground
      sa.applicationState() = 1
      ...
      sa.applicationState() = 0
      sa.applicationState() = 0
      didBecomeActive
      sa.applicationState() = 0
      ...

      So really it’s not much different than

      if not sa.applicationState():
      

      Correction: The notification is still good to run code when the app goes into the inactive state, but in my case I actually need something else. Also forgot to mention this (when you are done)

      for on in observer_names:
      	getattr(NotificationObserver, observer_names[on]).stop()
      
      posted in Pythonista
      ts
      ts

    Latest posts made by ts

    • RE: Video as a background

      @RocketBlaster05 If you still require the youtube_dl module, you can do it as suggested here or alternatively a small edit I did from his script years ago (in order to get it to work, also, let me know if this code should be removed from this post). Every other week or so, you can backup then download to keep it updated

      # coding: utf-8
      # author: Shaun Hevey
      # youtube-dl downloader is used to download youtube_dl and patch it to work in Pythonista.
      # Replace function came from http://stackoverflow.com/questions/39086/search-and-replace-a-line-in-a-file-in-python
      # Download file was adapted from Python file downloader (https://gist.github.com/89edf288a15fde45682a)
      
      import console
      import os
      import requests
      import shutil
      import tempfile
      import time
      import ui
      import zipfile
      
      youtubedl_dir = 'youtube_dl'
      youtubedl_location = './site-packages/'
      backup_location = './backup/youtube_dl/'
      youtubedl_downloadurl = 'https://yt-dl.org/downloads/latest/youtube-dl'
      youtubedl_unarchive_location = './temp/'
      files_to_change = [('utils.py','import ctypes','#import ctypes'),
                         ('utils.py','import pipes','#import pipes'),
                         ('YoutubeDL.py','self._err_file.isatty() and ',''),
                         ('downloader/common.py','(\'\\r\\x1b[K\' if sys.stderr.isatty() else \'\\r\')','\'\\r\''),
                         ('extractor/common.py',' and sys.stderr.isatty()','')]
      
      def backup_youtubedl(sender):
          console.show_activity('Checking for youtube-dl')
          if os.path.isdir(youtubedl_location+youtubedl_dir):
              console.show_activity('Backing up youtube-dl')
              if not os.path.exists(backup_location):
                  os.makedirs(backup_location)
              shutil.move(youtubedl_location+youtubedl_dir,backup_location+youtubedl_dir+ time.strftime('%Y%m%d%H%M%S'))
          console.hide_activity()
      
      @ui.in_background
      def restore_youtubedl_backup(sender):
          if not os.path.isdir(backup_location) or not os.listdir(backup_location):
              console.alert('Nothing to do', 'No backups found to restore')
          else:
              folders = os.listdir(backup_location)
              folder = folders[len(folders)-1]
              shutil.move(backup_location+folder,youtubedl_location+youtubedl_dir)
              console.alert('Success','Successfully restored '+folder)
      
      def downloadfile(url):
          localFilename = url.split('/')[-1] or 'download'
          with open(localFilename, 'wb') as f:
              r = requests.get(url, stream=True)
              total_length = r.headers.get('content-length')
              if total_length:
                  dl = 0
                  total_length = float(total_length)
                  for chunk in r.iter_content(1024):
                      dl += len(chunk)
                      f.write(chunk)
                      #.setprogress(dl/total_length*100.0)
              else:
                  f.write(r.content)
          return localFilename
      
      def process_file(path):
          os.mkdir(youtubedl_unarchive_location)
          if zipfile.is_zipfile(path):
              zipfile.ZipFile(path).extractall(path=youtubedl_unarchive_location)
      
      @ui.in_background
      def update_youtubedl(sender):
          if os.path.exists(youtubedl_location+youtubedl_dir):
              msg = 'Are you sure you want to update youtubedl exists in site-packages and will be overwritten'
              if not console.alert('Continue',msg,'OK'):
                  return
          console.show_activity('Downloading')
          file = downloadfile(youtubedl_downloadurl)
          console.show_activity('Extracting')
          process_file(file)
          console.show_activity('Moving')
          if os.path.exists(youtubedl_location+youtubedl_dir):
              shutil.rmtree(youtubedl_location+youtubedl_dir)
          shutil.move(youtubedl_unarchive_location+youtubedl_dir, youtubedl_location)
          console.show_activity('Cleaning Up Download Files')
          shutil.rmtree(youtubedl_unarchive_location)
          os.remove(file)
          console.show_activity('Making youtube-dl friendly')
          process_youtubedl_for_pythonista()
          console.hide_activity()
      
      def process_youtubedl_for_pythonista():
          for patch in files_to_change:
              filename, old_str, new_str = patch
              replace_in_file(youtubedl_location+youtubedl_dir+'/'+filename, old_str, new_str)
      
      def replace_in_file(file_path, old_str, new_str):
          with open(file_path, encoding='utf-8') as old_file:
              #Create temp file
              fh, abs_path = tempfile.mkstemp()
              os.close(fh)  # close low level and reopen high level
              with open(abs_path,'w', encoding='utf-8') as new_file:
                  for line in old_file:
                      new_file.write(line.replace(old_str, new_str))
          #Remove original file
          os.remove(file_path)
          #Move new file
          shutil.move(abs_path, file_path)
      
      def make_button(title, action):
          button = ui.Button(title=title)
          button.action = action
          button.background_color ='lightgrey'
          button.border_color = 'black'
          button.border_width = 1
          button.flex = 'WB'
          return button
      
      view = ui.View(frame=(0,0,172,132))
      view.background_color = 'white'
      
      backup_button = make_button(title='Backup YoutubeDL', action=backup_youtubedl)
      backup_button.center = (view.width * 0.5, view.y+backup_button.height)
      view.add_subview(backup_button)
      
      restore_button = make_button(title='Restore YoutubeDL', action=restore_youtubedl_backup)
      restore_button.center = (view.width * 0.5, backup_button.y+restore_button.height*1.75)
      view.add_subview(restore_button)
      
      download_button = make_button(title='Download YoutubeDL', action=update_youtubedl)
      download_button.center = (view.width * 0.5, restore_button.y+download_button.height*1.75)
      view.add_subview(download_button)
      
      view.present('sheet')
      
      posted in Pythonista
      ts
      ts
    • RE: Something like applicationWillResignActive?

      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)
      
      posted in Pythonista
      ts
      ts
    • RE: Something like applicationWillResignActive?

      Edited this post because the “.rate() method” I added along with comparing the item (as an OR) is actually worse (I forgot both threads were out of sync lol) than using the applicationState (meaning it kept restarting on a cycle). This leaves me with 2 (maybe more?) options

      1. Just use if sa.applicationState(): to control behavior
      2. Somehow hook an observer to observe an object (via my AVPlayer.init()) rather than any (share the same observer)

      Edit: I have another idea via same observer (I’ll try it later on)

      posted in Pythonista
      ts
      ts
    • RE: Something like applicationWillResignActive?

      Ok so far it’s working, though I’m a bit nervous on running .clear() at the wrong time (not sure if I can attach an observer to an object) or replace itself (the notification instance .object())

      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.clear()
      		
      	def clear(self):
      		self.ni = None
      		self.ni_name = None
      		self.ni_obj = None
      		
      	'''the objc block signature. do not modify'''
      	def _block(self, _cmd, notification):
      		try:
      			self.ni = objc_util.ObjCInstance(notification)
      			self.ni_name = self.ni.name()
      			self.ni_obj = self.ni.object()
      		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)
      

      Using it within my script (a portion of it), it does work just as I originally wanted to (thanks again! @JonB)

      from NotificationObserver import NotificationObserver
      
      observer_names = {
      	"AVPlayerItemDidPlayToEndTimeNotification": "itemDidPlay"
      }
      
      for on in observer_names:
      	name = observer_names[on]
      	setattr(NotificationObserver, name, NotificationObserver(on))
      	getattr(NotificationObserver, name).start()
      
      def app_active():
      	return not sa.applicationState() == 2 #1 skip
      	#return not sa.isSuspended() #1 skip
      	#return not sa._isResigningActive() #3 skips
      
      def should_do_loop():
      	if keyboard.is_keyboard() or app_active():
      		return True
      	else:
      		return False
      
      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 is NotificationObserver.itemDidPlay.ni_obj:
      				NotificationObserver.itemDidPlay.clear()
      				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)
      
      posted in Pythonista
      ts
      ts
    • RE: Something like applicationWillResignActive?

      So modified it a bit to allow other notifications

      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.state = False
      		
      	'''define your own callback here'''
      	@objc_util.ui.in_background
      	def callback(self, name, obj, userInfo):
      		print(time.asctime() + name)
      		self.state = True
      		
      	'''the objc block signature. do not modify'''
      	def _block(self, _cmd, notification):
      		try:
      			self.ni = objc_util.ObjCInstance(notification)
      			self.ni_name = self.ni.name()
      			self.ni_obj = self.ni.object()
      			self.ni_ui = self.ni.userInfo()
      			self.callback(self.ni_name.__str__(), self.ni_obj, self.ni_ui)
      		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)
      

      To test it, I did

      from NotificationObserver import NotificationObserver
      
      observer_names = {
      	"UIApplicationDidBecomeActiveNotification": "didBecomeActive",
      	"UIApplicationDidEnterBackgroundNotification": "didEnterBackground",
      	"UIApplicationWillEnterForegroundNotification": "willEnterForeground",
      	"UIApplicationWillResignActiveNotification": "willResignActive"
      }
      
      for on in observer_names:
      	name = observer_names[on]
      	setattr(NotificationObserver, name, NotificationObserver(on))
      	getattr(NotificationObserver, name).start()
      

      I also added applicationState() into the mix and got this in the console

      sa.applicationState() = 0
      ...
      sa.applicationState() = 1
      sa.applicationState() = 1
      willResignActive
      sa.applicationState() = 1
      ...
      sa.applicationState() = 2
      sa.applicationState() = 2
      didEnterBackground
      sa.applicationState() = 2
      ...
      willEnterForeground
      sa.applicationState() = 1
      ...
      sa.applicationState() = 0
      sa.applicationState() = 0
      didBecomeActive
      sa.applicationState() = 0
      ...

      So really it’s not much different than

      if not sa.applicationState():
      

      Correction: The notification is still good to run code when the app goes into the inactive state, but in my case I actually need something else. Also forgot to mention this (when you are done)

      for on in observer_names:
      	getattr(NotificationObserver, observer_names[on]).stop()
      
      posted in Pythonista
      ts
      ts
    • RE: Something like applicationWillResignActive?

      @JonB Woah! I just tried it out, that’s actually awesome! Thank you :)

      posted in Pythonista
      ts
      ts
    • RE: Something like applicationWillResignActive?

      @cvp Oh I didn’t add the colon at the end of the selector name, now I have a type_encoding. Also I see now, that’s actually cool, I didn’t think about the “original” + selector addition (c.class_addMethod). In the method, you did

      rtnval = self.originalinitWithURL_(url)
      return rtnval.ptr
      

      Is this common to return the pointer or?

      posted in Pythonista
      ts
      ts
    • RE: Something like applicationWillResignActive?

      @JonB So this is pretty new to me though I can sort of see what’s going on, however I’m not sure it will work (I’m not getting a a type_encoding), also can you still run the old method code and the new method code instead of replacing the old code?

      sad = objc_util.UIApplication.sharedApplication().delegate()
      cls_p = objc_util.c.object_getClass(sad.ptr)
      cls = objc_util.ObjCInstance(cls_p)
      type_encoding = cls.instanceMethodSignatureForSelector_(objc_util.sel("applicationWillResignActive"))
      
      posted in Pythonista
      ts
      ts
    • RE: Something like applicationWillResignActive?

      Oh lol, thanks @cvp

      posted in Pythonista
      ts
      ts
    • Something like applicationWillResignActive?

      Does anyone know a replacement for

      objc_util.UIApplication.sharedApplication().applicationState()
      

      I want to check right before you leave Pythonista or before the application is put under sleep mode, cause currently with applicationState, if you pull down the Notification Center, control center, about to switch apps, or in multitasking view, its value changes from 0 to 1

      Edit 1: Oh wait there’s another value, 2, for when you leave the app and enter sleep mode

      Edit 2: Still brings it back to my initial issue (for when I leave the app / enter sleep), as in it passes my logic when it’s not suppose to (its value is not 2)

      posted in Pythonista
      ts
      ts