def main(): print(VERSION) data_dir = None config = client_config.ClientConfig() if getattr(config, 'USE_CUSTOM_DIR', False): print("Using custom directory") if (sys.platform == 'darwin' and os.path.dirname(sys.executable ).endswith('MacOS')): data_dir = os.path.join(get_mac_dot_app_dir(os.path.dirname( sys.executable)), '.update') else: data_dir = os.path.join(os.path.dirname(sys.executable), '.update') client = Client(config, refresh=True, progress_hooks=[cb], data_dir=data_dir) update = client.update_check(APPNAME, VERSION) if update is not None: print("We have an update") retry_count = 0 while retry_count < 5: success = update.download() if success is True: break print("Retry Download. Count {}".format(retry_count + 1)) retry_count += 1 if success: print('Update download successful') print('Restarting') update.extract_restart() else: print('Failed to download update') print("Leaving main()")
def main(): print(VERSION) data_dir = None config = client_config.ClientConfig() if getattr(config, 'USE_CUSTOM_DIR', False): if (sys.platform == 'darwin' and os.path.dirname(sys.executable ).endswith('MacOS')): data_dir = os.path.join(get_mac_dot_app_dir(os.path.dirname( sys.executable)), '.update') else: data_dir = os.path.join(os.path.dirname(os.path.dirname( sys.executable)), '.update') client = Client(config, refresh=True, progress_hooks=[cb], data_dir=data_dir) update = client.update_check(APPNAME, VERSION) if update is not None: success = update.download() print('') if success is True: print('Update download successful') print('Extracting & overwriting') update.extract_overwrite() else: print('Failed to download update') return VERSION
def test_bad_pub_key(self): t_config = TConfig() # If changed ensure it's a valid key format t_config.PUBLIC_KEY = '25RSdhJ+xCsxxTjY5jffilatipp29tnKp/D5BelSMJM' t_config.DATA_DIR = os.getcwd() client = Client(t_config, refresh=True, test=True) assert client.update_check(client.app_name, '0.0.0') is None
def test_manifest_filesystem(self): t_config = TConfig() t_config.PUBLIC_KEYS = ['bad key'] t_config.DATA_DIR = os.getcwd() client = Client(t_config, refresh=True, test=True) filesystem_data = client._get_manifest_filesystem() filesystem_data = json.loads(filesystem_data) del filesystem_data['sigs'] assert client.json_data == filesystem_data
def test_http(self): t_config = TConfig() t_config.DATA_DIR = os.getcwd() client = Client(t_config, refresh=True, test=True) update = client.update_check(client.app_name, '0.0.1') assert update is not None assert update.app_name == 'Acme' assert update.download() is True assert update.is_downloaded() is True
def test_http(client): t_config = TConfig() t_config.DATA_DIR = os.getcwd() t_config.VERIFY_SERVER_CERT = False client = Client(t_config, refresh=True, test=True) update = client.update_check(client.app_name, '0.0.1') assert update is not None assert update.app_name == 'jms' assert update.download() is True assert update.is_downloaded() is True
def test_manifest_filesystem(self): t_config = TConfig() t_config.DATA_DIR = os.getcwd() client = Client(t_config, refresh=True, test=True) filesystem_data = client._get_manifest_filesystem() assert filesystem_data is not None if six.PY3: filesystem_data = filesystem_data.decode() filesystem_data = json.loads(filesystem_data) del filesystem_data['signature'] assert client.json_data == filesystem_data
def test_callback(self): def cb(status): print(status) def cb2(status): raise IndexError t_config = TConfig() t_config.PUBLIC_KEY = '25RSdhJ+xCsxxTjY5jffilatipp29tnKp/D5BelSMJM' t_config.DATA_DIR = os.getcwd() client = Client(t_config, refresh=True, test=True, progress_hooks=[cb]) client.add_progress_hook(cb2) assert client.update_check(client.app_name, '0.0.0') is None
def test_callback(self): def cb(status): print(status) def cb2(status): raise IndexError t_config = TConfig() t_config.PUBLIC_KEYS = ['bad key'] t_config.DATA_DIR = os.getcwd() client = Client(t_config, refresh=True, test=True, call_back=cb, callbacks=[cb, cb2]) client.add_call_back(cb2) assert client.update_check('jms', '0.0.0') is None
def test_async_http(self): t_config = TConfig() t_config.DATA_DIR = os.getcwd() client = Client(t_config, refresh=True, test=True) update = client.update_check(client.app_name, '0.0.1') assert update is not None assert update.app_name == 'Acme' update.download(async=True) count = 0 while count < 61: if update.is_downloaded() is True: break time.sleep(1) count += 1 assert update.is_downloaded() is True
def test_multipule_async_calls(self, client): t_config = TConfig() t_config.DATA_DIR = os.getcwd() t_config.VERIFY_SERVER_CERT = False client = Client(t_config, refresh=True, test=True) update = client.update_check(client.app_name, '0.0.1') assert update is not None assert update.app_name == 'Acme' update.download(async=True) count = 0 assert update.download(async=True) is None assert update.download() is None while count < 61: if update.is_downloaded() is True: break time.sleep(1) count += 1 assert update.is_downloaded() is True
def main(): print(VERSION) data_dir = None config = client_config.ClientConfig() if getattr(config, "USE_CUSTOM_DIR", False): print("Using custom directory") if sys.platform == "darwin" and os.path.dirname( sys.executable).endswith("MacOS"): data_dir = os.path.join( get_mac_dot_app_dir(os.path.dirname(sys.executable)), ".update") else: data_dir = os.path.join(os.path.dirname(sys.executable), ".update") client = Client(config, refresh=True, progress_hooks=[cb], data_dir=data_dir) update = client.update_check(APPNAME, VERSION) if update is not None: print("We have an update") retry_count = 0 while retry_count < 5: success = update.download() if success is True: break print("Retry Download. Count {}".format(retry_count + 1)) retry_count += 1 if success: print("Update download successful") print("Restarting") update.extract_restart() else: print("Failed to download update") print("Leaving main()")
def __init__(self, mw=None, test_version=None, channel='stable', dev_channel=False): client_config = ClientConfig() self.client = Client(client_config, progress_hooks=[self.print_status_info]) # set dev channel to alpha internally based on bool if dev_channel: channel = 'alpha' warnings.simplefilter( 'ignore', DeprecationWarning) # pyupdater turns this on, annoying _version = VERSION if test_version is None else test_version update_available = False app_update = None status = 'initialized' self.url_changelog = 'https://raw.githubusercontent.com/jaymegordo/SMSEventLog/main/CHANGELOG.md' f.set_self(vars())
def client(): t_config = TConfig() t_config.DATA_DIR = os.getcwd() client = Client(t_config, refresh=True, test=True) client.FROZEN = True return client
def test_url_str_attr(self): t_config = TConfig() t_config.DATA_DIR = os.getcwd() t_config.UPDATE_URLS = 'http://acme.com/update' client = Client(t_config, test=True) assert isinstance(client.update_urls, list)
def test_failed_refresh(self, client): client = Client(None, refresh=True, test=True) client.data_dir = os.getcwd() assert client.ready is False
from pyupdater.client import Client from client_config import ClientConfig APP_NAME = 'Super App' APP_VERSION = '1.1.0' ASSET_NAME = 'ffmpeg' ASSET_VERSION = '2.3.2' def print_status_info(info): total = info.get(u'total') downloaded = info.get(u'downloaded') status = info.get(u'status') print(downloaded, total, status) client = Client(ClientConfig()) client.refresh() client.add_progress_hook(print_status_info)
click.echo(Scenario(p.plan).asyaml()) def print_status_info(info): total = info.get(u'total') downloaded = info.get(u'downloaded') status = info.get(u'status') # print("#", downloaded, total, status) click.echo("#", downloaded, total, status) if getattr(sys, 'frozen', False): sys.argv[0] = "kubectl-val" if "--debug-updater" in sys.argv: logging.getLogger("pyupdater").setLevel(logging.DEBUG) STDERR_HANDLER = logging.StreamHandler(sys.stderr) STDERR_HANDLER.setFormatter(logging.Formatter(logging.BASIC_FORMAT)) logging.getLogger("pyupdater").addHandler(STDERR_HANDLER) else: logging.getLogger("pyupdater").setLevel(logging.ERROR) import poodle poodle.log.setLevel(logging.ERROR) client = Client(ClientConfig()) client.refresh() app_update = client.update_check(APP_NAME, APP_VERSION) if app_update is not None: app_update.download() if app_update.is_downloaded(): app_update.extract_restart() run(sys.argv[1:]) # pylint: disable=no-value-for-parameter
class Updater: def __init__(self, version): self.version = version self.cfg = ClientConfig() self.cfg.UPDATE_URLS = UPDATE_URLS self.cli = Client(self.cfg, headers=AUTHORIZE_HEADER, refresh=True, data_dir=DOWNLOAD_FOLDER) self.app_update = None def if_latest_version(self): try: app_update = self.cli.update_check(self.cfg.APP_NAME, self.version) return app_update.version except AttributeError: return self.version def download(self, callback): self.cli.add_progress_hook(callback) self.app_update = self.cli.update_check(self.cfg.APP_NAME, self.version) if self.app_update is None: return None self.app_update.download() if self.app_update.is_downloaded(): return True def install(self): if not self.app_update: return None if self.app_update.is_downloaded(): self.app_update.extract_restart() return True # asset def get_asset_latest_version(self, asset_name, asset_version): try: lib_update = self.cli.update_check(asset_name, asset_version) return lib_update.version except AttributeError as e: print("check_asset_update_log error ", e) return None def download_asset(self, asset_name, asset_version): """ :param asset_name: :param asset_version: :return: """ lib_update = self.cli.update_check(asset_name, asset_version) if lib_update is None: return None lib_update.download() if lib_update.is_downloaded(): lib_update.extract() return lib_update.update_folder # asset-update log def check_asset_update_log(self): """ 检查update_log的版本,update_log版本需要和app版本一致 :return: 版本号 """ new_version = self.get_asset_latest_version(ASSET_UPDATE_LOG, self.version) return new_version if new_version else self.version def download_asset_update_log(self): return self.download_asset(ASSET_UPDATE_LOG, self.version)
def __init__(self, queue, app_name, app_version): # OS PLATFORM = platform.system() # -> 'Windows' / 'Linux' / 'Darwin' # SET WORKING DIRECTORY # This is required for the PATHS to work properly when app is frozen if PLATFORM == 'Windows': cwd = os.path.dirname(os.path.abspath(sys.argv[0])) elif PLATFORM == 'Linux': cwd = os.path.abspath( os.path.join(os.path.dirname(__file__), os.pardir)) else: logging.warning(('This app has not been yet ' 'tested for platform_system={}').format(PLATFORM)) cwd = os.path.abspath( os.path.join(os.path.dirname(__file__), os.pardir)) os.chdir(cwd) #A callback to print download progress. def downloadStatus(info): total = info.get(u'total') downloaded = info.get(u'downloaded') progress = str(round(int(downloaded) * 100 / int(total))) status = info.get(u'status') if status == "finished": logging.info("Download finished") elif status == "downloading": logging.debug("Download progress: %s" % progress + "%") queue.put(progress) else: logging.warning( "Unexpected download status: %s".format(status)) #Initialize client with ClientConfig & later call refresh to get latest update data. #This HTTP key is needed to access to my repository. logging.debug("Opening client...") #client = Client(ClientConfig(),http = "reuMxY8PxCnrHctZyh29") client = Client(ClientConfig()) client.refresh() #A hook to track download information client.add_progress_hook(downloadStatus) #Update_check returns an AppUpdate object if there is an update available logging.debug("Checking for updates...") app_update = client.update_check(app_name, app_version) #If we get an update object we can proceed to download the update. if app_update is not None: logging.info("A newer version was found") logging.debug("Downloading update...") app_update.download() else: logging.info("No update avalable") queue.put("NO_UPDATE") #Ensure file downloaded successfully, extract update, overwrite and restart current application if app_update is not None and app_update.is_downloaded(): logging.debug("Overwriting current application...") app_update.extract_overwrite() logging.info("Succefuly overwrited") queue.put("DONE")
APP_NAME = 'Google Photos Wallpaper' APP_VERSION = '0.0.1' # Pyupdater download status callback def print_status_info(info): total = info.get(u'total') downloaded = info.get(u'downloaded') status = info.get(u'status') print(downloaded, total, status) print('Initializing client') client = Client(ClientConfig(), refresh=True, progress_hooks=[print_status_info]) print('Client initialized') print('Checking for updates') app_update = client.update_check(APP_NAME, APP_VERSION) if app_update is not None: print('Update available, downloading') app_update.download(background=True) if app_update.is_downloaded(): print('Downloaded update, restarting') app_update.extract_restart() else: print("No updates")
APP_NAME = 'p2' APP_VERSION = '1.1.1' ASSET_NAME = 'ffmpeg' ASSET_VERSION = '2.3.2' def print_status_info(info): total = info.get(u'total') downloaded = info.get(u'downloaded') status = info.get(u'status') print(downloaded, total, status) client = Client(ClientConfig()) client.refresh() client.add_progress_hook(print_status_info) client = Client(ClientConfig(), refresh=True, progress_hooks=[print_status_info]) app_update = client.update_check(APP_NAME, APP_VERSION) app_update = client.update_check(APP_NAME, APP_VERSION, channel='beta') if app_update is not None: app_update.download() if app_update is not None: app_update.download(background=True) if app_update.is_downloaded(): app_update.extract_overwrite()
class Updater(Tk): HEIGHT = 600 WIDTH = 600 def __init__(self): super(Updater, self).__init__() self.title("Application updater") self.minsize(self.WIDTH, self.HEIGHT) self.wm_iconbitmap('./assets/icons/icon.ico') self.client = Client(ClientConfig(), progress_hooks=[self.print_status_info]) self.client.refresh() self.pb = None self.frame_updater = None self.label = None self.waiting_image = None self.lib_update = self.client.update_check('Application', application.__version__) def run(self): global_frame = tk.Frame(self, height=self.HEIGHT, width=self.WIDTH, bg="#222222") global_frame.place(x=0, y=0, relwidth=1, relheight=1) frame_top = tk.Frame(self, bg="#990033") frame_top.place(relx=0, rely=0, relwidth=1, relheight=0.2) attention_image = tk.PhotoImage(file='./assets/images/attention.png') label = tk.Label(frame_top, text=" Programme Updater", bg="#990033", foreground="#ffffff", image=attention_image, compound="left", font=60) label.config(font=(None, 21)) label.place(relx=0.1, rely=0.1, relwidth=0.8, relheight=0.8) self.frame_updater = tk.Frame(self, bg="#555555") self.frame_updater.place(relx=0.1, rely=0.4, relwidth=0.8, relheight=0.2) self.waiting_image = tk.PhotoImage(file='./assets/images/waiting.png') self.label = tk.Label(self.frame_updater, text=" Checking for update", bg="#999999", foreground="#ffffff", image=self.waiting_image, compound="left", font=60) self.label.config(font=(None, 12)) self.label.place(relx=0.1, rely=0.1, relwidth=0.8, relheight=0.4) self.pb = ttk.Progressbar(self.frame_updater, orient="horizontal", length=200, mode="determinate", value=0, maximum=100) self.pb.place(relx=0.1, rely=0.5, relwidth=0.8, relheight=0.2) self.after(500, self.update) def update(self): if self.lib_update is not None: self.lib_update.cleanup() self.client.refresh() self.label['text'] = ' Downloading update' self.after(100, self.download) self.lib_update.download() else: self.pb.destroy() self.waiting_image = tk.PhotoImage(file='./assets/images/ok.png') self.label['image'] = self.waiting_image self.label['text'] = ' No update needed' self.after(1000, self.execute_app) def download(self): if self.lib_update is not None and self.lib_update.is_downloaded(): self.label['text'] = ' Installing update & restart' # self.pb['value'] = 100 self.after(1000, self.restart) def restart(self): self.lib_update.extract_restart() def print_status_info(self, info): total = info.get(u'total') percent_complete = info.get(u'percent_complete') downloaded = info.get(u'downloaded') status = info.get(u'status') self.pb['value'] = percent_complete print(downloaded, total, status, percent_complete) def execute_app(self): self.destroy() app = Application() app.run() app.mainloop()
class VersionHandler(TasksThread): """Check for new version and update or notify.""" newVersion = pyqtSignal(str) newData = pyqtSignal(str) noNewVersion = pyqtSignal() progress = pyqtSignal(dict) updated_data = pyqtSignal(str) # Constants APP_NAME = ClientConfig.APP_NAME APP_VERSION = scctool.__version__ ASSET_NAME = 'SCCT-data' ASSET_VERSION = '0.0.0' client = Client(ClientConfig()) app_update = None asset_update = None def __init__(self, controller): """Init the thread.""" super().__init__() self.__controller = controller self.setTimeout(10) self.addTask('version_check', self.__version_check) self.addTask('update_data', self.__update_data) self.addTask('update_app', self.__update_app) self.updated_data.connect(controller.displayWarning) # self.disableCB.connect(controller.uncheckCB) def isCompatible(self): """Check if data update is needed.""" return compareVersions(self.asset_update.latest, self.APP_VERSION, 3) < 1 def update_progress(self, data): """Process progress updates.""" self.progress.emit(data) def __version_check(self): try: self.client.add_progress_hook(self.update_progress) self.client.refresh() self.ASSET_VERSION = getDataVersion() channel = getChannel(self.APP_VERSION) self.app_update = self.client.update_check(self.APP_NAME, self.APP_VERSION, channel=channel) if self.asset_update is not None: self.newData.emit(self.asset_update.latest) module_logger.info("Asset: " + self.asset_update.latest) if self.isCompatible(): self.activateTask("update_data") if self.app_update is not None: scctool.__latest_version__ = self.app_update.latest scctool.__new_version__ = True self.newVersion.emit(self.app_update.latest) module_logger.info("App: " + self.app_update.latest) else: self.noNewVersion.emit() except Exception: module_logger.exception("message") finally: self.deactivateTask('version_check') def __update_data(self): try: module_logger.info("Start to update data files!") if self.asset_update is None: self.deactivateTask('update_data') return self.asset_update.download() extractData(self.asset_update) module_logger.info("Updated data files!") self.updated_data.emit(_("Updated data files!")) except Exception: module_logger.exception("message") finally: self.deactivateTask('update_data') def __update_app(self): try: if self.app_update is None: self.deactivateTask('update_app') return if hasattr(sys, "frozen"): module_logger.info("Start to update app!") self.app_update.download(False) if self.app_update.is_downloaded(): module_logger.info("Download sucessfull.") self.__controller.cleanUp() setRestartFlag() module_logger.info("Restarting...") self.app_update.extract_restart() except Exception: module_logger.exception("message") finally: self.deactivateTask('update_app')
def test_bad_pub_key(self, client): t_config = TConfig() t_config.PUBLIC_KEYS = ['bad key'] t_config.DATA_DIR = os.getcwd() client = Client(t_config, refresh=True, test=True) assert client.update_check('jms', '0.0.0') is None
class MidiProjectController: def __init__(self, callback=None, options=None): self._sendMidiCallback = callback self._update_paths = None self._client_config = None self._isUpdating = False if options is not None: self._update_paths = options.update_paths self._client_config = options.client_config self._update_urls = options.update_urls # Client for pyupdate if self._client_config is None: self._client_config = client_config.ClientConfig() self.client = Client(self._client_config, downloader=self.createDownloader) self.client.add_progress_hook(print_status_info) def createDownloader(self, filename, urls, **kwargs): logger.debug("Create downloader for {}".format(filename)) # replace urls from serverconfig urls = self._update_urls d = PathAndUrlDownloader(filename, urls, callback=self.downloadCallback) return d def downloadCallback(self, file): if self._update_paths is not None: for updatePath in self._update_paths: for p in glob.iglob(updatePath): if os.path.isdir(p): logger.info("Checking directory {}/{}".format(p, file)) path = os.path.join(p, file) if os.path.isfile(path): logger.info( "Downloading from path {}".format(path)) return open(path, "rb").read() logger.debug("{} not found in {}".format(file, updatePath)) return None def handleMidiMsg(self, msg: mido.Message, serverconfig: serverconfiguration.ServerConfiguration, proj: project.Project): # of type mido.Message # channel 0..15 0 # frame_type 0..7 0 # frame_value 0..15 0 # control 0..127 0 # note 0..127 0 # program 0..127 0 # song 0..127 0 # value 0..127 0 # velocity 0..127 64 # data (0..127, 0..127, …) () (empty tuple) # pitch -8192..8191 0 # pos 0..16383 0 # time any integer or float 0 if msg.type == 'program_change': self._handleProgramChange(msg.program, proj) elif msg.type == 'control_change': self._handleControlChange(msg.control, msg.value, proj) elif msg.type == 'sysex': self._handleSysex(msg.data, serverconfig, proj) def _handleSysex(self, data, serverconfig: serverconfiguration.ServerConfiguration, proj: project.Project): if len(data) < 2: logger.error("Sysex message too short") return if data[0] == 0x00 and data[1] == 0x00: # Version logger.info("MIDI-BLE REQ Version") if self._sendMidiCallback is not None: self._sendMidiCallback(self._createVersionMsg()) elif data[0] == 0x00 and data[1] == 0x10: # Update logger.info("MIDI-BLE REQ Update") logger.info("Checking for update, current version is {}".format( version.get_version())) self._updatePathsAndUrls(serverconfig) if not self._isUpdating: self._isUpdating = True # New Downloaders will be created on request self.client.refresh() app_update = self.client.update_check('Molecole', version.get_version()) if app_update is not None: logger.info("Update {} available".format( app_update.current_version)) threading.Thread(target=self._updateApp, args=[app_update]).start() else: self._isUpdating = False logger.info("Update check returned no update") if self._sendMidiCallback is not None: self._sendMidiCallback( self._createUpdateNotAvailableMsg()) else: if self._sendMidiCallback is not None: self._sendMidiCallback(self._createUpdateBusyMsg()) elif data[0] == 0x00 and data[1] == 0x11: # Update check logger.info("MIDI-BLE REQ Update check") logger.info("Checking for update, current version is {}".format( version.get_version())) self._updatePathsAndUrls(serverconfig) if not self._isUpdating: self._isUpdating = True try: # Move version.gz to force a reload oldFile = os.path.join(self.client.data_dir, self.client.version_file) if os.path.exists(oldFile): shutil.move(oldFile, oldFile.replace('.gz', '.gz.bak')) oldFile = os.path.join(self.client.data_dir, self.client.version_file_compat) if os.path.exists(oldFile): shutil.move(oldFile, oldFile.replace('.gz', '.gz.bak')) self.client.refresh() app_update = self.client.update_check( 'Molecole', version.get_version()) self._isUpdating = False if app_update is not None: logger.info("Update {} available".format( app_update.version)) if self._sendMidiCallback is not None: self._sendMidiCallback( self._createUpdateVersionAvailableMsg( app_update.version)) else: logger.info("Update check returned no update") if self._sendMidiCallback is not None: self._sendMidiCallback( self._createUpdateNotAvailableMsg()) except Exception as e: logger.error("Error trying to update: {}".format(e)) self._isUpdating = False else: if self._sendMidiCallback is not None: self._sendMidiCallback(self._createUpdateBusyMsg()) elif data[0] == 0x00 and data[1] == 0x20: # Active project metadata if self._sendMidiCallback is not None: metadata = serverconfig.getProjectMetadata(proj.id) self._sendMidiCallback(self._createActiveProjectMsg(metadata)) elif data[0] == 0x00 and data[1] == 0x30: # Get projects if self._sendMidiCallback is not None: metadata = serverconfig.getProjectsMetadata() self._sendMidiCallback(self._createProjectsMsg(metadata)) elif data[0] == 0x00 and data[1] == 0x40: # Activate project projUid = str(bytes(sysex_data.decode(data[2:])), encoding='utf8') logger.info("MIDI-BLE REQ Activate project {}".format(projUid)) proj = serverconfig.getProject(projUid) if proj is not None: if self._sendMidiCallback is not None: self._sendMidiCallback( self._createActivateProjSuccessfulMsg()) else: if self._sendMidiCallback is not None: self._sendMidiCallback( self._createActivateProjNotFoundMsg()) elif data[0] == 0x00 and data[1] == 0x50: # Import project logger.info("MIDI-BLE REQ Import project") dec = sysex_data.decode(data[2:]) projGzip = zlib.decompress(bytes(dec)) projJson = str(projGzip, encoding='utf8') try: serverconfig.importProject(projJson) if self._sendMidiCallback is not None: self._sendMidiCallback( self._createImportProjSuccessfulMsg()) except Exception: if self._sendMidiCallback is not None: self._sendMidiCallback(self._createImportProjErrorMsg()) elif data[0] == 0x00 and data[1] == 0x60: # Export project byteArr = bytes(sysex_data.decode(data[2:])) logger.debug("Decoded {} to {}".format([hex(c) for c in data[2:]], byteArr)) projUid = str(byteArr, encoding='utf8') logger.info("MIDI-BLE REQ Export project {}".format(projUid)) proj = serverconfig.getProject(projUid) if proj is not None: if self._sendMidiCallback is not None: self._sendMidiCallback( self._createExportProjSuccessfulMsg(proj)) else: if self._sendMidiCallback is not None: self._sendMidiCallback(self._createExportProjNotFoundMsg()) elif data[0] == 0x00 and data[1] == 0x70: # Activate project projUid = str(bytes(sysex_data.decode(data[2:])), encoding='utf8') logger.info("MIDI-BLE REQ Delete project {}".format(projUid)) proj = serverconfig.getProject(projUid) if proj is not None: serverconfig.deleteProject(projUid) if self._sendMidiCallback is not None: self._sendMidiCallback(self._deleteProjSuccessfulMsg()) else: if self._sendMidiCallback is not None: self._sendMidiCallback(self._deleteProjNotFoundMsg()) elif data[0] == 0x01 and data[1] == 0x00: # Get active scene ID logger.info("MIDI-BLE REQ Active scene index") sceneId = serverconfig.getActiveProjectOrDefault().activeSceneId if self._sendMidiCallback is not None: self._sendMidiCallback( self._createGetActiveSceneIdMsg(sceneId)) elif data[0] == 0x01 and data[1] == 0x10: # Get active scene metadata logger.info("MIDI-BLE REQ Get active scene metadata") proj = serverconfig.getActiveProjectOrDefault() metadata = proj.getSceneMetadata(proj.activeSceneId) if self._sendMidiCallback is not None: self._sendMidiCallback( self._createActiveSceneMetadataMsg(metadata)) elif data[0] == 0x01 and data[1] == 0x20: # Get scenes metadata logger.info("MIDI-BLE REQ Get scenes") proj = serverconfig.getActiveProjectOrDefault() metadata = proj.sceneMetadata if self._sendMidiCallback is not None: self._sendMidiCallback(self._createScenesMetadataMsg(metadata)) elif data[0] == 0x01 and data[1] == 0x30: # Get enabled controllers for active scene logger.info("MIDI-BLE REQ Get controller enabled for active scene") proj = serverconfig.getActiveProjectOrDefault() if self._sendMidiCallback is not None: self._sendMidiCallback(self._createEnabledControllersMsg(proj)) elif data[0] == 0x01 and data[1] == 0x40: # Request controller values for active project logger.info("MIDI-BLE REQ Get controller values for active scene") proj = serverconfig.getActiveProjectOrDefault() self._sendControllerStatus(proj) elif data[0] == 0x01 and data[1] == 0x50: # Reset controller values for active project logger.info("MIDI-BLE REQ Reset controller for active scene") proj = serverconfig.getActiveProjectOrDefault() proj.resetControllerModulation() self._sendControllerStatus(proj) elif data[0] == 0x02 and data[1] == 0x00: # Get server configuration logger.info("MIDI-BLE REQ Get server configuration") config = serverconfig.getFullConfiguration() if self._sendMidiCallback is not None: self._sendMidiCallback(self._createGetServerConfigMsg(config)) elif data[0] == 0x02 and data[1] == 0x10: # Update server configuration logger.info("MIDI-BLE REQ Update server configuration") dec = sysex_data.decode(data[2:]) configGzip = zlib.decompress(bytes(dec)) configJson = str(configGzip, encoding='utf8') config = json.loads(configJson) try: serverconfig.setConfiguration(config) if self._sendMidiCallback is not None: self._sendMidiCallback( self._createUpdateServerConfigSuccessfulMsg()) except Exception: if self._sendMidiCallback is not None: self._sendMidiCallback( self._createUpdateServerConfigErrorMsg()) elif data[0] == 0x02 and data[1] == 0x20: logger.info("MIDI-BLE REQ Audio rms") rms = {"0": "NO_BUFFER"} if audio.GlobalAudio.buffer is not None: if isinstance(audio.GlobalAudio.buffer, Iterable): numChannels = len(audio.GlobalAudio.buffer) for i in range(numChannels): rmsFromChannel = dsp.rms(audio.GlobalAudio.buffer[i]) rms[str(i)] = rmsFromChannel else: logger.debug("audio.GlobalAudio.buffer not iterateable") if self._sendMidiCallback is not None: self._sendMidiCallback(self._createAudioRMSMsg( json.dumps(rms))) else: logger.error("MIDI-BLE Unknown sysex {} {}".format( hex(data[0]), hex(data[1]))) def _createAudioRMSMsg(self, rms): sendMsg = mido.Message('sysex') sendMsg.data = [0x02, 0x20] + sysex_data.encode("{}".format(rms)) return sendMsg def _createGetServerConfigMsg(self, config: dict): logger.debug("MIDI-BLE RESPONSE Get server config - Successful") json = jsonpickle.dumps(config) gzip = zlib.compress(bytes(json, encoding='utf8')) sendMsg = mido.Message('sysex') sendMsg.data = [0x02, 0x00] + sysex_data.encode(gzip) return sendMsg def _createUpdateServerConfigSuccessfulMsg(self): logger.debug("MIDI-BLE RESPONSE Update server config - Successful") sendMsg = mido.Message('sysex') sendMsg.data = [0x02, 0x10] return sendMsg def _createUpdateServerConfigErrorMsg(self): logger.debug("MIDI-BLE RESPONSE Update server config - Successful") sendMsg = mido.Message('sysex') sendMsg.data = [0x02, 0x1F] return sendMsg def _updateApp(self, app_update): try: logger.debug("Starting download in background") threading.current_thread().name = 'UpdateThread' app_update.download() if app_update.is_downloaded(): logger.info("Update downloaded") if not getattr(sys, 'frozen', False): logger.info("Not running from executable. Extract only") logger.debug("Extracting update") if app_update.extract(): logger.debug("Extract succeeded") else: logger.debug("Extract not successful") logger.debug("Extract done") else: logger.info("Extracting and restarting...") app_update.extract_overwrite() logger.info("Extracting done. Killing server") os.kill(os.getpid(), signal.SIGUSR1) else: if self._sendMidiCallback is not None: self._sendMidiCallback(self._createUpdateNotAvailableMsg()) logger.debug("End of update") finally: self._isUpdating = False def _createEnabledControllersMsg(self, proj): status = proj.getController() controllerEnabled = {} for controller in modulation.allController: if controller in inverseControllerMap: controllerEnabled[inverseControllerMap[controller]] = False logger.debug("Status: {}".format(status.keys())) for controller in status.keys(): if controller in inverseControllerMap: controllerEnabled[inverseControllerMap[controller]] = True logger.debug( "MIDI-BLE RESPONSE Enabled controllers for active scene: {}". format(controllerEnabled)) sendMsg = mido.Message('sysex') sendMsg.data = [0x01, 0x30] + sysex_data.encode( json.dumps(controllerEnabled)) return sendMsg def _createScenesMetadataMsg(self, metadata): logger.debug( "MIDI-BLE RESPONSE Get scenes metadata {}".format(metadata)) sendMsg = mido.Message('sysex') sendMsg.data = [0x01, 0x20] + sysex_data.encode(json.dumps(metadata)) return sendMsg def _createActiveSceneMetadataMsg(self, metadata): logger.debug( "MIDI-BLE RESPONSE Get active scene metadata {}".format(metadata)) sendMsg = mido.Message('sysex') sendMsg.data = [0x01, 0x10] + sysex_data.encode(json.dumps(metadata)) return sendMsg def _createGetActiveSceneIdMsg(self, sceneId: int): logger.debug( "MIDI-BLE RESPONSE Get active scene id {}".format(sceneId)) sendMsg = mido.Message('sysex') sendMsg.data = [0x01, 0x00] + sysex_data.encode("{}".format(sceneId)) return sendMsg def _createExportProjSuccessfulMsg(self, proj): logger.debug("MIDI-BLE RESPONSE Export project - Successful") projJson = jsonpickle.dumps(proj) projGzip = zlib.compress(bytes(projJson, encoding='utf8')) sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x60] + sysex_data.encode(projGzip) return sendMsg def _createExportProjNotFoundMsg(self): logger.debug("MIDI-BLE RESPONSE Export project - Not found") sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x6F] return sendMsg def _deleteProjSuccessfulMsg(self): logger.debug("MIDI-BLE RESPONSE Delete project - Successful") sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x70] return sendMsg def _deleteProjNotFoundMsg(self): logger.debug("MIDI-BLE RESPONSE Delete project - Not found") sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x7F] return sendMsg def _createImportProjSuccessfulMsg(self): logger.debug("MIDI-BLE RESPONSE Import project - Successful") sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x50] return sendMsg def _createImportProjErrorMsg(self): logger.debug("MIDI-BLE RESPONSE Import project - Error") sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x5F] return sendMsg def _createActivateProjSuccessfulMsg(self): logger.debug("MIDI-BLE RESPONSE Activate project - Successful") sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x40] return sendMsg def _createActivateProjNotFoundMsg(self): logger.debug("MIDI-BLE RESPONSE Activate project - Project not found") sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x4F] return sendMsg def _createUpdateVersionAvailableMsg(self, version): logger.debug( "MIDI-BLE RESPONSE Update to version {} available".format(version)) sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x11] + sysex_data.encode(version) return sendMsg def _createUpdateNotAvailableMsg(self): logger.debug("MIDI-BLE RESPONSE No update available") sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x1F] return sendMsg def _createUpdateBusyMsg(self): logger.debug( "MIDI-BLE RESPONSE Update or update check running. I'm busy.") sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x1E] return sendMsg def _createActiveProjectMsg(self, metadata): data = json.dumps(metadata) logger.debug("MIDI-BLE RESPONSE Active project {}".format(data)) sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x20] + sysex_data.encode(data) return sendMsg def _createProjectsMsg(self, metadata): data = json.dumps(metadata) logger.debug("MIDI-BLE RESPONSE project metadata: {}".format(data)) sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x30] + sysex_data.encode(data) return sendMsg def _createVersionMsg(self): v = version.get_version() logger.debug("MIDI-BLE RESPONSE Current Version {}".format(v)) sendMsg = mido.Message('sysex') sendMsg.data = [0x00, 0x00] + sysex_data.encode(v) return sendMsg def _sendControllerStatus(self, proj): # Send current midi controller status status = proj.getControllerModulations() for controller, v in status.items(): logger.debug( "Sending modulation controller value {} for controller {}". format(v, controller)) sendMsg = mido.Message('control_change') if controller in inverseControllerMap: sendMsg.channel = 1 sendMsg.control = inverseControllerMap[controller] if sendMsg.control >= 30: # scale color data sendMsg.value = int(v / 255 * 127) else: sendMsg.value = int(v * 127) if self._sendMidiCallback is not None: self._sendMidiCallback(sendMsg) # Send current brightness value brightness = proj.getBrightnessActiveScene() if brightness is not None: sendMsg = mido.Message('control_change') sendMsg.channel = 1 sendMsg.control = 7 sendMsg.value = int(brightness * 127) if self._sendMidiCallback is not None: self._sendMidiCallback(sendMsg) def _getUpdatePaths(self, paths: str): if ',' in paths: paths = paths.split(',') else: paths = [paths] return paths def _handleProgramChange(self, program, proj): proj.activateScene(program) self._sendControllerStatus(proj) # Send enabled controllers via sysex if self._sendMidiCallback is not None: self._sendMidiCallback(self._createEnabledControllersMsg(proj)) def _handleControlChange(self, ctrl, value, proj): if ctrl in controllerMap: controlMsg = controllerMap[ctrl] controlVal = _ctrlToValue(controlMsg, value / 127) logger.debug("Propagating control change message") if controlMsg == modulation.CTRL_BRIGHTNESS: # Handle brightness globally proj.setBrightnessForActiveScene(value / 127) else: proj.updateModulationSourceValue(0xFFF, controlMsg, controlVal) else: logger.warn("Unknown controller {}".format(ctrl)) def _updatePathsAndUrls(self, serverconfig): updatePath = serverconfig.getConfiguration( serverconfiguration.CONFIG_UPDATER_AUTOCHECK_PATH) logger.info("Scanning configuration for local directories {}".format( updatePath)) updateUrls = serverconfig.getConfiguration( serverconfiguration.CONFIG_UPDATER_URL) self._update_paths = self._getUpdatePaths(updatePath) if updateUrls is not None and isinstance(updateUrls, str): self._update_urls = updateUrls.split(",") logger.info("Scanning local directories {} and urls {}".format( self._update_paths, self._update_urls))
def __init__(self, version): self.version = version self.cfg = ClientConfig() self.cfg.UPDATE_URLS = UPDATE_URLS self.cli = Client(self.cfg, headers=AUTHORIZE_HEADER, refresh=True, data_dir=DOWNLOAD_FOLDER) self.app_update = None