def isLegalExtension(self, extension): """Check whether extension denotes a file type which is handled by Entry or its subclasses. String extension is the file name extension (without '.', upper and lower case handled) Return Boolean indicating whether extension is handled by Entry """ try: Installer.getProductTrader().getClassFor(extension.lower()) except: # no class registered for this extension return (False) return (True)
def __init__(self): self.logger = Logger.__call__().get_logger() self.version = "" self.file_name = "" self.l_installer = Installer() self.parsed_command = "" self.m_Commands = dict() self.m_Commands["install"] = self.install self.m_Commands["follow"] = self.follow self.m_Commands["ping"] = self.ping self.m_Commands["configure"] = self.configure self.m_Commands["uninstall"] = self.uninstall self.m_Commands["update"] = self.download self.m_Commands["download"] = self.download self.m_Commands["download_cfg"] = self.download_cfg
def getRawImageFromPath(cls, aMediaCollection, path): """Return a raw image to represent the media content of the given file. Is a class method to be useful during import. Credits for JPG/EXIF rotation: https://jdhao.github.io/2019/07/31/image_rotation_exif_info/ Model.MediaCollection aMediaCollection String path Return wx.Image or None """ (_, extension) = os.path.splitext(path) extension = extension[1:].lower( ) # normalize by removing leading dot and converting to lowercase imageType = None rawImage = None rotation = 'N' # N: normal, L: left, R: right, M: mirror if ((extension == 'jpg') or (extension == 'jpeg')): imageType = wx.BITMAP_TYPE_JPEG metadata = cls.getMetadataFromPath(path) rotation = cls.getRotationFromMetadata(metadata) elif (extension == 'png'): imageType = wx.BITMAP_TYPE_PNG elif (extension == 'gif'): imageType = wx.BITMAP_TYPE_GIF elif (extension == 'tif'): imageType = wx.BITMAP_TYPE_TIF if (imageType): try: rawImage = wx.Image(path, imageType) except Exception as exc: Logger.warning( 'Image.getRawImageFromPath(): Failed to load media "%s" due to\n%s' % (path, exc)) try: rawImage = wx.Image( os.path.join(Installer.getLibraryPath(), Image.PreviewImageFilename), wx.BITMAP_TYPE_JPEG) except Exception as exc: Logger.error( 'Image.getRawImageFromPath(): Failed to load default image due to\n%s' % exc) rotation = 'N' if (rotation != 'N'): Logger.debug('Image.getRawImageFromPath(): Rotating %s' % rotation) if (rotation == 'R'): rawImage = rawImage.Rotate90(True) elif (rotation == 'L'): rawImage = rawImage.Rotate90(False) else: # must be 'M' rawImage = rawImage.Rotate180() else: Logger.warning( 'Image.getRawImageFromPath(): Illegal type in "%s"!' % path) return (rawImage)
def getSubclassForPath(self, path): """Return the subclass of Entry to create an entry for the specified file. path String Returns a subclass of Entry """ if (os.path.isdir(path)): clas = Installer.getProductTrader().getClassFor( self.SpecificationGroup) else: (dummy, extension) = os.path.splitext(path) extension = extension[1:].lower() # remove leading '.' try: clas = Installer.getProductTrader().getClassFor(extension) except: Logger.error( 'Entry.getSubclassForPath(): No class registered to instantiate "%s" media "%s"!' % (extension, path)) return (None) return (clas)
def setSelectedEntry(self, entry): """Set the selected entry. """ Logger.debug('MediaCollection.setSelectedEntry(%s)' % entry) previousSelection = self.selectedEntry self.selectedEntry = entry if (self.selectedEntry): # store selected entry for next program run path = entry.getPath() path = path[len(Installer.getMediaPath()) + 1:] # remove "/" as well self.setConfiguration(GlobalConfigurationOptions.LastMedia, path) if (previousSelection != self.selectedEntry): self.changedAspect('selection')
def setNameHandler(self): """Set the class singleton name handler. Even if a name handler was defined, there are no dependencies and it can be simply discarded. Registering names of all Entrys does not hurt, as there are none when the class is initialized via setModel() """ path = Installer.getNamesFilePath() Logger.debug( 'OrganizationByName.setNameHandler(): Loading name handler from "%s"' % path) self.nameHandler = MediaNameHandler(path) Logger.debug( 'OrganizationByName.setNameHandler(): Registering names...') for entry in self.getModel(): self.nameHandler.registerNameAsUsed(entry.getOrganizer().getName()) Logger.debug('OrganizationByName.setNameHandler(): Names registered')
def remove( self): # TODO: inform MediaOrganization as well, to release names """Remove self from the image set. Move the image file into the trash directory. """ self.changedAspect('remove') self.setParentGroup(None) self.releaseCacheWithPriority(self.__class__.CachingLevelRawData) self.releaseCacheWithPriority( self.__class__.CachingLevelFullsizeBitmap) self.releaseCacheWithPriority( self.__class__.CachingLevelThumbnailBitmap) # move to trash oldName = self.getPath() newName = os.path.join( self.model.rootDirectory, Installer.getTrashPath(), (self.getFilename() + '.' + self.getExtension())) newName = makeUnique(newName) Logger.debug('Trashing "%s" (into "%s")' % (oldName, newName)) try: os.rename(oldName, newName) except Exception as e: Logger.error('Trashing "%s" failed:\n%s' % (oldName, e))
for entry in self.getSubEntries(): entry.removeNewIndicator() def renumberMedia(self, pairList): """Renumber media grouped in self according to pairList. List pairList contains pairs of numbers indicating the current and new number. """ pass # Internal - to change without notice def getGroupSize(self): """Return the number of media grouped in self. Return Number """ result = 0 for subEntry in self.getSubEntries(filtering=True): if (subEntry.__class__ == Group): result = (result + subEntry.getGroupSize()) else: result = (result + 1) return(result) # Class Initialization Installer.getProductTrader().registerClassFor(Group, Entry.SpecificationGroup) # register Group to handle directories
class Command: """ Handles for received messages """ server = None client = None fileName = "" timer = 0 def __init__(self): self.logger = Logger.__call__().get_logger() self.version = "" self.file_name = "" self.l_installer = Installer() self.parsed_command = "" self.m_Commands = dict() self.m_Commands["install"] = self.install self.m_Commands["follow"] = self.follow self.m_Commands["ping"] = self.ping self.m_Commands["configure"] = self.configure self.m_Commands["uninstall"] = self.uninstall self.m_Commands["update"] = self.download self.m_Commands["download"] = self.download self.m_Commands["download_cfg"] = self.download_cfg """ Init Websocket in the classe""" def set_websocket(self, server, clients): Command.server = server Command.client = clients """ Parsing Commands """ """{ { "command": "download_cfg", "software": { "name": "Firefox", "file": "Firefox.exe", "path": "AppData/Mozilla/Firefox", "url": "https://url/download/cfg" "extension": "tgz" } } """ def new_command(self, command): try: self.parsed_command = json.loads( command, object_hook=lambda d: namedtuple('X', d.keys())(*d.values())) except json.JSONDecodeError as e: eprintlog(format(str(e))) self.logger.debug(format(str(e))) else: self.logger.info(self.parsed_command) """ Check if the Command is valid """ def is_valid_command(self): if hasattr(self.parsed_command, 'command'): for l_command in self.m_Commands: if l_command == self.parsed_command.command: return True return False """ Execute Command """ def run(self): return self.m_Commands[self.parsed_command.command]() """ Ping """ def ping(self): return PacketError(self.parsed_command.command, PacketType.PING, Enum.PACKET_PING) """ Follow Process """ def follow(self): status = self.l_installer.follower(self.parsed_command.software.file) if status == "running": packet = PacketError(self.parsed_command.software.file, PacketType.F_RUNNING, Enum.PACKET_FOLLOW, self.parsed_command.software.name) else: packet = PacketError(self.parsed_command.software.file, PacketType.F_FINISH, Enum.PACKET_FOLLOW, self.parsed_command.software.name) packet.path = self.parsed_command.software.path return packet """ Configuration Software """ def configure(self): if self.parsed_command.software.extension == "tar.gz" or self.parsed_command.software.extension == "tgz": tar = tarfile.open( 'configuration\\' + self.parsed_command.software.file, "r:gz") tar.extractall('configuration\\' + self.parsed_command.software.name) tar.close() copytree('configuration\\' + self.parsed_command.software.name, ConfigurationFolder + self.parsed_command.software.path) packet = PacketError(self.parsed_command.software.file, PacketType.OK_CONFIGURATION, Enum.PACKET_CONFIGURATION, self.parsed_command.software.name) packet.path = self.parsed_command.software.path return packet """ Installation Software """ def install(self): self.l_installer.init(self.parsed_command.software) t = threading.Thread(target=self.l_installer.install, args=(Command.server, Command.client)) threads.append(t) t.start() return """ Uninstallation Software """ def uninstall(self): self.l_installer.init(self.parsed_command.software) t = threading.Thread(target=self.l_installer.uninstall, args=(Command.server, Command.client)) threads.append(t) t.start() return """ Download Software """ def download(self): self.fileName = self.parsed_command.software.file try: urllib.request.urlopen(self.parsed_command.software.url) except urllib.error.HTTPError as e: eprintlog(e.code) eprintlog(e.read()) self.logger.debug(format(str(e))) packet = PacketError(e.code, PacketType.FAILED_DOWNLOAD, Enum.PACKET_DOWNLOAD_STATE, self.parsed_command.software.name) packet.path = self.parsed_command.software.path Command.server.send_message(Command.client, packet.toJSON()) except URLError as urlerror: if hasattr(urlerror, 'reason'): self.logger.debug(format(str(e))) eprintlog('We failed to reach a server.') eprintlog('Reason: ', urlerror.reason) packet = PacketError(urlerror.reason, PacketType.FAILED_DOWNLOAD, Enum.PACKET_DOWNLOAD_STATE, self.parsed_command.software.name) packet.path = self.parsed_command.software.path Command.server.send_message(Command.client, packet.toJSON()) elif hasattr(urlerror, 'code'): self.logger.debug(format(str(e))) eprintlog('The server couldn\'t fulfill the request.') eprintlog('Error code: ', urlerror.code) packet = PacketError(urlerror.code, PacketType.FAILED_DOWNLOAD, Enum.PACKET_DOWNLOAD_STATE, self.parsed_command.software.name) packet.path = self.parsed_command.software.path Command.server.send_message(Command.client, packet.toJSON()) else: threading.Thread( target=urlretrieve, args=(self.parsed_command.software.url, 'install/' + self.parsed_command.software.file, self.reporthook)).start() return """ Download Configure """ def download_cfg(self): self.fileName = self.parsed_command.software.file try: urllib.request.urlopen(self.parsed_command.software.url) except urllib.error.HTTPError as e: eprintlog(e.code) eprintlog(e.read()) packet = PacketError(e.code, PacketType.FAILED_DOWNLOAD, Enum.PACKET_DOWNLOAD_STATE, self.parsed_command.software.name) packet.path = self.parsed_command.software.path Command.server.send_message(Command.client, packet.toJSON()) except URLError as urlerror: if hasattr(urlerror, 'reason'): eprintlog('We failed to reach a server.') eprintlog('Reason: ', urlerror.reason) packet = PacketError(urlerror.reason, PacketType.FAILED_DOWNLOAD, Enum.PACKET_DOWNLOAD_STATE_CFG, self.parsed_command.software.name) packet.path = self.parsed_command.software.url Command.server.send_message(Command.client, packet.toJSON()) elif hasattr(urlerror, 'code'): eprintlog('The server couldn\'t fulfill the request.') eprintlog('Error code: ', urlerror.code) packet = PacketError(urlerror.code, PacketType.FAILED_DOWNLOAD, Enum.PACKET_DOWNLOAD_STATE_CFG, self.parsed_command.software.name) packet.path = self.parsed_command.software.url Command.server.send_message(Command.client, packet.toJSON()) else: threading.Thread( target=urlretrieve, args=(self.parsed_command.software.url, 'configuration/' + self.parsed_command.software.file, self.reporthook_cfg)).start() return def reporthook(self, blocknum, blocksize, totalsize): readsofar = blocknum * blocksize if totalsize > 0: percent = readsofar * 1e2 / totalsize currenttimer = int(round(time.time() * 1000)) if currenttimer > self.timer + 500: self.timer = currenttimer packet = PacketError(percent, PacketType.F_RUNNING, Enum.PACKET_DOWNLOAD_STATE, self.parsed_command.software.name) packet.path = self.parsed_command.software.path Command.server.send_message(Command.client, packet.toJSON()) if readsofar >= totalsize: # near the end packet = PacketError(percent, PacketType.F_FINISH, Enum.PACKET_DOWNLOAD_STATE, self.parsed_command.software.name) packet.path = self.parsed_command.software.path Command.server.send_message(Command.client, packet.toJSON()) else: # total size is unknown sys.stderr.write("read %d\n" % (readsofar, )) def reporthook_cfg(self, blocknum, blocksize, totalsize): readsofar = blocknum * blocksize if totalsize > 0: percent = readsofar * 1e2 / totalsize currenttimer = int(round(time.time() * 1000)) if currenttimer > self.timer + 500: self.timer = currenttimer packet = PacketError(percent, PacketType.F_RUNNING, Enum.PACKET_DOWNLOAD_STATE_CFG, self.parsed_command.software.name) packet.path = self.parsed_command.software.path Command.server.send_message(Command.client, packet.toJSON()) if readsofar >= totalsize: # near the end packet = PacketError(percent, PacketType.F_FINISH, Enum.PACKET_DOWNLOAD_STATE_CFG, self.parsed_command.software.name) packet.path = self.parsed_command.software.path Command.server.send_message(Command.client, packet.toJSON()) else: # total size is unknown sys.stderr.write("read %d\n" % (readsofar, ))
def setRootDirectory (self, rootDir, processIndicator): """Change the root directory to process a different image set. String rootDir PhasedProgressBar progressIndicator """ processIndicator.beginPhase(3) self.rootDirectory = os.path.normpath(rootDir) # clear all data self.selectedEntry = None self.initialEntry = None CachingController.clear() # set up the configuration persistence #TODO: Must move to app to be available while mediacollection is created self.configuration = SecureConfigParser(Installer.getConfigurationFilePath()) self.configuration.read(Installer.getConfigurationFilePath()) if (not self.configuration.has_section(GUIId.AppTitle)): self.configuration.add_section(GUIId.AppTitle) # if (os.path.exists(Installer.getNamesFilePath())): self.organizedByDate = False self.organizationStrategy = OrganizationByName filterClass = FilterByName else: self.organizedByDate = True self.organizationStrategy = OrganizationByDate filterClass = FilterByDate self.organizationStrategy.setModel(self) processIndicator.beginStep(_('Reading tag definitions')) self.classHandler = MediaClassHandler(Installer.getClassFilePath()) if (self.classHandler.isLegalElement(MediaCollection.ReorderTemporaryTag)): index = 1 tag = ('%s%d' % (MediaCollection.ReorderTemporaryTag, index)) while (self.classHandler.isLegalElement(tag)): index = (index + 1) tag = ('%s%d' % (tag, index)) MediaCollection.ReorderTemporaryTag = tag Logger.warning('MediaCollection.setRootDirectory(): Temporary reordering tag changed to "%s"' % tag) # read groups and images self.root = Entry.createInstance(self, self.rootDirectory) self.loadSubentries(self.root, processIndicator) # implicit progressIndicator.beginStep() processIndicator.beginStep(_('Calculating collection properties')) self.cacheCollectionProperties() self.filter = filterClass(self) self.filter.addObserverForAspect(self, 'filterChanged') self.filteredEntries = self.getCollectionSize() # select initial entry if (os.path.exists(Installer.getInitialFilePath())): self.initialEntry = Entry.createInstance(self, Installer.getInitialFilePath()) path = self.getConfiguration(GlobalConfigurationOptions.LastMedia) if (path): if (not os.path.isabs(path)): # TODO: unconditional when relative path is stored for last media path = os.path.join(Installer.getMediaPath(), path) entry = self.getEntry(path=path) if (entry): Logger.info('MediaCollection.setRootDirectory(): selecting "%s" from last run.' % path) if (entry.isGroup() and (100 < len(entry.getSubEntries()))): entry = entry.getSubEntries()[0] Logger.info('MediaColleection.setRootDirectory(): Reselected "%s" because initial group contained more than 100 entries.' % entry) self.setSelectedEntry(entry) else: Logger.info('MediaCollection.setRootDirectory(): last viewed media "%s" does not exist.' % path) self.setSelectedEntry(self.root) else: Logger.info('MediaCollection.setRootDirectory(): last viewed media not saved') self.setSelectedEntry(self.root)
def getRawImageFromPath(cls, aMediaCollection, path): """Return a raw image to represent the media content of the given file. Model.MediaCollection aMediaCollection String path Return wx.Image or None """ rawImage = None ffmpeg = aMediaCollection.getConfiguration( Movie.ConfigurationOptionFfmpeg) if (ffmpeg): try: Logger.debug('Movie.getRawImageFromPath(): Using "%s"' % ffmpeg) duration = cls.getDurationFromPath(aMediaCollection, path) if (duration): target = max( 0, min(duration * Movie.CaptureFramePosition, duration - 0.1)) else: target = 5 logging.warning( 'Movie.getRawImageFromPath(): Cannot determine duration, using %s secs as offset for "%s"' % (target, path)) targetString = "{:.3f}".format(target) logging.debug( 'Movie.getRawImageFromPath(): Duration is %s, target frame is %s' % (duration, target)) args = [ ffmpeg, "-ss", targetString, "-i", path, "-map", "v:0", # first video stream "-frames:v", "1", # 1 frame "-f", "mjpeg", # motion jpeg (aka. jpeg since 1 frame) output "pipe:" # pipe output to stdout ] # proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # (output, _) = proc.communicate() # if (proc.returncode): # raise subprocess.CalledProcessError(proc.returncode, args) cp = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Python 3 if (cp.returncode): raise subprocess.CalledProcessError(cp.returncode, args) if (not cp.stdout): raise subprocess.CalledProcessError(-2, args) stream = BytesIO(cp.stdout) # rawImage = wx.ImageFromStream(stream, type=wx.BITMAP_TYPE_JPEG) rawImage = wx.Image(stream, type=wx.BITMAP_TYPE_JPEG) # wxPython 4 except Exception as e: Logger.error( 'Movie.getRawImageFromPath(): Cannot retrieve frame from "%s" due to error:\n%s' % (path, e)) rawImage = None else: Logger.warning( 'Movie.getRawImageFromPath(): No ffmpeg specified with option "%s"' % Movie.ConfigurationOptionFfmpeg) rawImage = None if (rawImage == None): try: rawImage = wx.Image( os.path.join(Installer.getLibraryPath(), Movie.PreviewImageFilename), wx.BITMAP_TYPE_JPEG) except Exception as exc: Logger.error( 'Movie.getRawImageFromPath(): Cannot load default movie image due to\n%s' % exc) rawImage = None return (rawImage)
# args = [ffmpeg, # '-i', # self.getPath()] # logging.debug('Movie.getDuration(): Calling "%s"' % args) # # proc = subprocess.Popen(args, stderr=subprocess.PIPE) # # (_, result) = proc.communicate() # output = subprocess.run(args, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout # Python 3 # m = re.search(r'Duration:\s*(\d+):(\d+):(\d+)\.(\d+)', output) # if (m == None): # logging.warning('Movie.getDuration(): Cannot determine duration for "%s"!' % self.getPath()) # else: # # Avoiding strptime here because it has some issues handling milliseconds. # m = [int(m.group(i)) for i in range(1, 5)] # self.duration = datetime.timedelta(hours=m[0], # minutes=m[1], # seconds=m[2], # # * 10 because truncated to 2 decimal places # milliseconds=m[3] * 10 # ).total_seconds() # except Exception as e: # logging.warning('Movie.getDuration(): Cannot determine duration due to error:\n%s' % e) # else: # logging.warning('Movie.getDuration(): No ffmpeg specified with %s' % Movie.ConfigurationOptionFfmpeg) return (self.duration) # Class Initialization for extension in Movie.LegalExtensions: Installer.getProductTrader().registerClassFor( Movie, extension) # register Movie to handle extension
Logger.debug('Image.getRawImage(%s)' % self) if (self.rawImage): return (self.rawImage) result = super(Image, self).getRawImage() self.rotation = self.__class__.getRotationFromMetadata( self.getMetadata()) if (self.rotation and (self.rotation != 'N')): self.changedAspect('name') return (result) def getMetadata(self): """Return image metadata, depending on image file type. Supported so far: - JPG/EXIF Return dict (empty if no metadata available) """ if ((self.getExtension() == 'jpg') or (self.getExtension() == 'jpeg')): if (self.metadataExif == None): self.metadataExif = self.__class__.getMetadataFromPath( self.getPath()) return (self.metadataExif) return ({}) # Internal - to change without notice # Class Initialization for extension in Image.LegalExtensions: Installer.getProductTrader().registerClassFor(Image, extension)