def __init__(self, window: Window, database_file: Gio.File) -> None:
        super().__init__()

        filepath = database_file.get_path()

        self.window = window

        # Reset headerbar to initial state if it already exists.
        self.headerbar.title.props.title = database_file.get_basename()

        self.install_action("clear-keyfile", None, self.on_clear_keyfile)

        database = self.window.unlocked_db
        if database:
            is_current = database.database_manager.path == filepath
            if is_current:
                self.database_manager = database.database_manager

        if not self.database_manager:
            self.database_manager = DatabaseManager(filepath)

        if gsecrets.config_manager.get_remember_composite_key():
            self._set_last_used_keyfile()

        if gsecrets.const.IS_DEVEL:
            self.status_page.props.icon_name = gsecrets.const.APP_ID
Example #2
0
    def open_local_file(self, file: Gio.File):
        self._notifications.clear()
        if file is None:
            return

        def error(e):
            self._notifications.add_simple(
                _('Cannot load project: ') + str(e), Gtk.MessageType.ERROR)
            self.buffer.load_from_file(None)

        ext = os.path.splitext(file.get_basename())[1]
        if ext != '.tcp':
            error(Exception(_('Wrong file extenstion')))
            return
        if not file.query_exists():
            error(
                Exception(
                    _('File "{}" is not available').format(file.get_uri())))
            return

        try:
            self.buffer.load_from_file(file)
        except lib.CorruptedFileException as e:
            error(e)
        except lib.MissingPluginException as e:
            error(e)
        except lib.UnavailableCommandException as e:
            error(e)
        except GLib.Error as e:
            error(e)
Example #3
0
 def on_file_changed(self, monitor: Gio.FileMonitor, file: Gio.File,
                     _other_file: Gio.File,
                     event_type: Gio.FileMonitorEvent, port: int) -> None:
     if event_type == Gio.FileMonitorEvent.DELETED:
         logging.info('%s got deleted' % file.get_path())
         monitor.disconnect_by_func(self.on_file_changed, port)
     elif event_type == Gio.FileMonitorEvent.ATTRIBUTE_CHANGED:
         self.try_replace_root_watcher(monitor, file.get_path(), port)
    def set_keyfile(self, keyfile: Gio.File) -> None:
        self.keyfile_path = keyfile.get_path()
        keyfile.load_bytes_async(None, self.load_keyfile_callback)

        self.keyfile_stack.props.visible_child_name = "spinner"
        self.keyfile_spinner.start()
        self.keyfile_button.props.sensitive = False

        logging.debug("Keyfile selected: %s", keyfile.get_path())
Example #5
0
 def on_file_changed(self, monitor: Gio.FileMonitor, file: Gio.File,
                     _other_file: Gio.File,
                     event_type: Gio.FileMonitorEvent, port: int) -> None:
     if event_type == Gio.FileMonitorEvent.DELETED:
         logging.info('%s got deleted' % file.get_path())
         if port in self._handlerids:
             handler_id = self._handlerids.pop(port)
             monitor.disconnect(handler_id)
         else:
             logging.warning(f"No handler id for {port}")
     elif event_type == Gio.FileMonitorEvent.ATTRIBUTE_CHANGED:
         self.try_replace_root_watcher(monitor, file.get_path(), port)
Example #6
0
 def process_passed_image_file(self, chosen_file: Gio.File, content_type: Optional[str] = None):
     self.reset_result()
     # The file can be remote, so we should read asynchronously
     chosen_file.read_async(GLib.PRIORITY_DEFAULT, None, self.cb_file_read, content_type)
     # If this file is remote, reading it will take time, so we display progress bar.
     if not chosen_file.is_native():
         self.progress_bar.set_visible(True)
         sid = GLib.timeout_add(100, ui.update_progress, self.progress_bar)
         # Properly handle GLib event source
         if self.g_event_sources.get('update_progress'):
             GLib.Source.remove(self.g_event_sources['update_progress'])
         self.g_event_sources['update_progress'] = sid
Example #7
0
    def save_as(self, file: Gio.File) -> bool:
        """Saves buffer to the file. The file will become current project_file.

        Args:
            file (Gio.File): The file

        Returns:
            bool: True if the project was saved successfully
        """
        output = []

        # Meta-info
        output.append(('fver', str(FILE_VERSION_FORMAT)))
        for p in self.enabled_plugins:
            # Base is enabled by default
            if p == 'base':
                continue
            output.append(('plugin', p))
        output.append((('fconsole'), str(self.props.run_in_console)))

        # Commands
        output.extend(self.code.save())

        self.props.project_file = file
        outs = file.replace(None, False, Gio.FileCreateFlags.NONE)
        content = lib.save_tcp(output)
        ok, bytes_written = outs.write_all(content.encode('utf-8'))
        if ok:
            self.props.changed = False

        return ok
Example #8
0
    def update_track(self,
                     gloc: Gio.File,
                     force_update: bool = False) -> Optional[trax.Track]:
        """
        Rescan the track at a given location

        :param gloc: the location
        :type gloc: :class:`Gio.File`
        :param force_update: Force update of file (default only updates file
                             when mtime has changed)

        returns: the Track object, None if it could not be updated
        """
        uri = gloc.get_uri()
        if not uri:  # we get segfaults if this check is removed
            return None

        tr = self.collection.get_track_by_loc(uri)
        if tr:
            tr.read_tags(force=force_update)
        else:
            tr = trax.Track(uri)
            if tr._scan_valid:
                self.collection.add(tr)

            # Track already existed. This fixes trax.get_tracks_from_uri
            # on windows, unknown why fix isnt needed on linux.
            elif not tr._init:
                self.collection.add(tr)
        return tr
Example #9
0
 def cb_file_read(self,
                  remote_file: Gio.File,
                  res: Gio.AsyncResult,
                  content_type: Optional[str] = None):
     w, h = self.get_preview_size()
     gi_stream: Gio.FileInputStream = remote_file.read_finish(res)
     scaled_pix = GdkPixbuf.Pixbuf.new_from_stream_at_scale(
         gi_stream, w, h, True, None)
     # Prevent freezing GUI
     Gtk.main_iteration()
     self.insert_image_to_placeholder(scaled_pix)
     # Prevent freezing GUI
     Gtk.main_iteration()
     gi_stream.seek(0, GLib.SeekType.SET, None)
     logger.debug('Content type: {}', content_type)
     if content_type == 'image/svg+xml':
         svg: Rsvg.Handle = Rsvg.Handle.new_from_stream_sync(
             gi_stream, remote_file, Rsvg.HandleFlags.FLAGS_NONE, None)
         stream: io.BytesIO = export_svg(svg)
     else:
         stream = io.BytesIO()
         CHUNNK_SIZE = 8192
         # There is no method like read_all_bytes(), so have to do verbose way below
         while True:
             buf: GLib.Bytes = gi_stream.read_bytes(CHUNNK_SIZE, None)
             amount = buf.get_size()
             logger.debug('Read {} bytes', amount)
             stream.write(buf.get_data())
             if amount <= 0:
                 break
     if self.g_event_sources.get('update_progress'):
         GLib.Source.remove(self.g_event_sources['update_progress'])
         del self.g_event_sources['update_progress']
     ui.update_progress(self.progress_bar, 1)
     self.process_passed_rgb_image(stream)
Example #10
0
def get_gicon_for_file(uri):
    """
    Return a GIcon representing the file at
    the @uri, which can be *either* and uri or a path

    return None if not found
    """

    gfile = File.new_for_path(uri)
    if not gfile.query_exists():
        gfile = File.new_for_uri(uri)
        if not gfile.query_exists():
            return None

    finfo = gfile.query_info(FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None)
    gicon = finfo.get_attribute_object(FILE_ATTRIBUTE_STANDARD_ICON)
    return gicon
Example #11
0
 def _file_changed(self, _monitor, main_file: Gio.File,
                   other_file: Optional[Gio.File],
                   event_type: Gio.FileMonitorEvent) -> None:
     file_path = main_file.get_path()
     other_path = (Path(normalize_path(other_file.get_path(), True))
                   if other_file else None)
     print_d(f"Got event {event_type} on {file_path}->{other_path}")
     self.changed.append((event_type, file_path))
Example #12
0
File: app.py Project: bmars/sisko
 def _open(self, folder: Gio.File):
     """
     Open a folder.
     """
     children = folder.enumerate_children(
         ','.join([Gio.FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
                   Gio.FILE_ATTRIBUTE_STANDARD_IS_BACKUP,
                   Gio.FILE_ATTRIBUTE_STANDARD_NAME,
                   FileItem.FILE_ATTRIBUTES]),
         Gio.FileQueryInfoFlags.NONE, None)
     self._path_bar.location = folder
     del self._files[:]
     for info in children:
         if self._show_hidden or not (info.get_is_hidden() or
                                      info.get_is_backup()):
             self._files.append(FileItem(folder.get_child(info.get_name()),
                                         info))
     list.sort(self._files, key=attrgetter('name_key'))
Example #13
0
    def load_from_file(self, file: Gio.File):
        if file is not None:
            # Reads the file
            file_dis = Gio.DataInputStream.new(file.read())
            source = file_dis.read_upto('\0', 1)[0]
            file_dis.close()
            # Parses the file
            project = lib.parse_tcp(source)
        else:
            project = DEFAULT_PROJECT.copy()

        # Reset variables
        self.props.run_in_console = False
        self.props.project_file = file

        # Load project
        # project_code contains only commands (without meta-info like plugin)
        error = None
        project_code = []
        # IDs of plugins that are requested to load
        plugin_ids = set(['base'])
        file_version = 0

        for cmd in project:
            if len(cmd) != 2:
                error = CorruptedFileException()
                continue
            id = cmd[0]
            data = cmd[1]
            if id == 'plugin':
                plugin_id = lib.Plugin.get_id_from_path(data)
                plugin_ids.add(plugin_id)
            elif id == 'fver':
                file_version = int(data)
                # Skips to conversion from Turtlico 0.x projects
                if file_version <= 1:
                    break
            elif id == 'fconsole':
                self.props.run_in_console = data == 'True'
            else:
                project_code.append(cmd)

        if file_version <= 1:
            error = None
            project_code, plugins, self.props.run_in_console = (
                legacy.tcp_1_to_2(source, file_version))
            plugin_ids.clear()
            plugin_ids.update(plugins)

        if error is not None:
            raise error

        enabled_plugin_paths = lib.Plugin.resolve_paths_from_ids(plugin_ids)
        self._reload_plugins(enabled_plugin_paths)

        self.code.load(project_code)
        self.props.changed = False
Example #14
0
File: icons.py Project: guns/kupfer
def get_gicon_for_file(uri):
    """
    Return a GIcon representing the file at
    the @uri, which can be *either* and uri or a path

    return None if not found
    """

    gfile = File.new_for_path(uri)
    if not gfile.query_exists():
        gfile = File.new_for_uri(uri)
        if not gfile.query_exists():
            return None

    finfo = gfile.query_info(FILE_ATTRIBUTE_STANDARD_ICON,
                             Gio.FileQueryInfoFlags.NONE, None)
    gicon = finfo.get_attribute_object(FILE_ATTRIBUTE_STANDARD_ICON)
    return gicon
Example #15
0
    def downloadPixbufAlbumCover(url):
        if debug:
            print("downloading album cover from " + url)

        f = File.new_for_uri(url)
        stream = f.read()

        cover = Pixbuf.new_from_stream(stream)
        stream.close()
        return cover
Example #16
0
def recursive_tracks_from_file(gfile: Gio.File) -> Iterable[Track]:
    """
        Get recursive tracks from Gio.File
        If it's a directory, expands
        Gets only valid tracks
    """
    ftype = gfile.query_info('standard::type', Gio.FileQueryInfoFlags.NONE,
                             None).get_file_type()
    if ftype == Gio.FileType.DIRECTORY:
        file_infos = gfile.enumerate_children('standard::name',
                                              Gio.FileQueryInfoFlags.NONE,
                                              None)
        files = (gfile.get_child(fi.get_name()) for fi in file_infos)
        for sub_gfile in files:
            for i in recursive_tracks_from_file(sub_gfile):
                yield i
    else:
        uri = gfile.get_uri()
        if is_valid_track(uri):
            yield Track(uri)
Example #17
0
def generate_keyfile_sync(gfile: Gio.File) -> str:
    """The callback returns a GFile as its source object and the keyfile hash as
    its user_data. Returns the hash of the keyfile."""
    key = get_random_bytes(32)
    cipher = AES.new(key, AES.MODE_EAX)
    ciphertext, tag = cipher.encrypt_and_digest(
        create_random_data(96))  # type: ignore
    contents = cipher.nonce + tag + ciphertext  # type: ignore
    keyfile_hash = GLib.compute_checksum_for_data(GLib.ChecksumType.SHA1,
                                                  contents)

    gfile.replace_contents(
        contents,
        None,
        False,
        Gio.FileCreateFlags.REPLACE_DESTINATION,
        None,
    )

    return keyfile_hash
Example #18
0
    def _monitor_handler(
            self, monitor: Gio.FileMonitor, child: Gio.File,
            other_file: Gio.File, event_type: Gio.FileMonitorEvent) -> None:
        # The final path component contains the snapshot number.
        try:
            number = int(pathlib.Path(child.get_path()).name)
        except ValueError:
            return

        bootenv = self._bootenvs / str(number)

        if Gio.FileMonitorEvent.DELETED == event_type and bootenv.exists():
            self._clean_up(number)
Example #19
0
def trash_or_confirm(gfile: Gio.File) -> bool:
    """Trash or delete the given Gio.File

    Files and folders will be moved to the system Trash location
    without confirmation. If they can't be trashed, then the user is
    prompted for an irreversible deletion.

    :rtype: bool
    :returns: whether the file was deleted
    """

    try:
        gfile.trash(None)
        return True
    except GLib.GError as e:
        # Handle not-supported, as that's due to the trashing target
        # being a (probably network) mount-point, not an underlying
        # problem. We also have to handle the generic FAILED code
        # because that's what we get with NFS mounts.
        expected_error = (e.code == Gio.IOErrorEnum.NOT_SUPPORTED
                          or e.code == Gio.IOErrorEnum.FAILED)
        if not expected_error:
            raise RuntimeError(str(e))

    file_type = gfile.query_file_type(Gio.FileQueryInfoFlags.NONE, None)

    if file_type == Gio.FileType.DIRECTORY:
        raise RuntimeError(_("Deleting remote folders is not supported"))
    elif file_type != Gio.FileType.REGULAR:
        raise RuntimeError(_("Not a file or directory"))

    delete_permanently = modal_dialog(
        primary=_("“{}” can’t be put in the trash. Do you want to "
                  "delete it immediately?".format(
                      GLib.markup_escape_text(gfile.get_parse_name()))),
        secondary=_("This remote location does not support sending items "
                    "to the trash."),
        buttons=[
            (_("_Cancel"), Gtk.ResponseType.CANCEL),
            (_("_Delete Permanently"), Gtk.ResponseType.OK),
        ],
    )

    if delete_permanently != Gtk.ResponseType.OK:
        return False

    try:
        gfile.delete(None)
        # TODO: Deleting remote folders involves reimplementing
        # shutil.rmtree for gio, and then calling
        # self.recursively_update().
    except Exception as e:
        raise RuntimeError(str(e))

    return True
    def _replace_async_cb(self, gfile: Gio.File, result: Gio.AsyncResult,
                          *user_data):
        try:
            self._outputstream = gfile.replace_finish(result)
        except GLib.Error as e:
            self._error_message = e.message
            self._abort()
            return

        # The file is now open for writing -> start copying data from the inputstream
        self._inputstream.read_bytes_async(
            count=block_size,
            io_priority=GLib.PRIORITY_DEFAULT,
            cancellable=self._cancellable,
            callback=self._read_bytes_async_cb,
        )
Example #21
0
    def _create_flatpak_installation(self, remote, target_path):
        install_path = File.new_for_path(target_path)
        installation = Installation.new_for_path(install_path, False, None)
        installation.add_remote(remote, False, None)

        return installation
Example #22
0
def trash_or_confirm(gfile: Gio.File) -> bool:
    """Trash or delete the given Gio.File

    Files and folders will be moved to the system Trash location
    without confirmation. If they can't be trashed, then the user is
    prompted for an irreversible deletion.

    :rtype: bool
    :returns: whether the file was deleted
    """

    try:
        gfile.trash(None)
        return True
    except GLib.GError as e:
        # Handle not-supported, as that's due to the trashing target
        # being a (probably network) mount-point, not an underlying
        # problem. We also have to handle the generic FAILED code
        # because that's what we get with NFS mounts.
        expected_error = (
            e.code == Gio.IOErrorEnum.NOT_SUPPORTED or
            e.code == Gio.IOErrorEnum.FAILED
        )
        if not expected_error:
            raise RuntimeError(str(e))

    file_type = gfile.query_file_type(
        Gio.FileQueryInfoFlags.NONE, None)

    if file_type == Gio.FileType.DIRECTORY:
        raise RuntimeError(_("Deleting remote folders is not supported"))
    elif file_type != Gio.FileType.REGULAR:
        raise RuntimeError(_("Not a file or directory"))

    delete_permanently = modal_dialog(
        primary=_(
            "“{}” can’t be put in the trash. Do you want to "
            "delete it immediately?".format(
                GLib.markup_escape_text(gfile.get_parse_name()))
        ),
        secondary=_(
            "This remote location does not support sending items "
            "to the trash."
        ),
        buttons=[
            (_("_Cancel"), Gtk.ResponseType.CANCEL),
            (_("_Delete Permanently"), Gtk.ResponseType.OK),
        ],
    )

    if delete_permanently != Gtk.ResponseType.OK:
        return False

    try:
        gfile.delete(None)
        # TODO: Deleting remote folders involves reimplementing
        # shutil.rmtree for gio, and then calling
        # self.recursively_update().
    except Exception as e:
        raise RuntimeError(str(e))

    return True
Example #23
0
    def __file_changed(self, _monitor, main_file: Gio.File,
                       other_file: Optional[Gio.File],
                       event: Gio.FileMonitorEvent) -> None:
        if event == Event.CHANGES_DONE_HINT:
            # This seems to work fine on most Linux, but not on Windows / macOS
            # Or at least, not in CI anyway.
            # So shortcut the whole thing
            return
        try:
            file_path = main_file.get_path()
            if file_path is None:
                return
            file_path = normalize_path(file_path, True)
            song = self.get(file_path)
            file_path = Path(file_path)
            other_path = (Path(normalize_path(other_file.get_path(), True))
                          if other_file else None)
            if event in (Event.CREATED, Event.MOVED_IN):
                if file_path.is_dir():
                    self.monitor_dir(file_path)
                    copool.add(self.scan, [str(file_path)])
                elif not song:
                    print_d(f"Auto-adding created file: {file_path}", self._name)
                    self.add_filename(file_path)
            elif event == Event.RENAMED:
                if not other_path:
                    print_w(f"No destination found for rename of {file_path}",
                            self._name)
                if song:
                    print_d(f"Moving {file_path} to {other_path}...", self._name)
                    if self.move_song(song, str(other_path)):  # type:ignore
                        print_w(f"Song {file_path} has gone")
                elif self.is_monitored_dir(file_path):
                    if self.librarian:
                        print_d(f"Moving tracks from {file_path} -> {other_path}...",
                                self._name)
                        copool.add(self.librarian.move_root,
                                   str(file_path), str(other_path),
                                   write_files=False,
                                   priority=GLib.PRIORITY_DEFAULT)
                    self.unmonitor_dir(file_path)
                    if other_path:
                        self.monitor_dir(other_path)
                else:
                    print_w(f"Seems {file_path} is not a track (deleted?)", self._name)
                    # On some (Windows?) systems CHANGED is called which can remove
                    # before we get here, so let's try adding the new path back
                    self.add_filename(other_path)
            elif event == Event.CHANGED:
                if song:
                    # QL created (or knew about) this one; still check if it changed
                    if not song.valid():
                        self.reload(song)
                else:
                    print_d(f"Auto-adding new file: {file_path}", self._name)
                    self.add_filename(file_path)
            elif event in (Event.MOVED_OUT, Event.DELETED):
                if song:
                    print_d(f"...so deleting {file_path}", self._name)
                    self.reload(song)
                else:
                    # either not a song, or a song that was renamed by QL
                    if self.is_monitored_dir(file_path):
                        self.unmonitor_dir(file_path)

                    # And try to remove all songs under that dir. Slowly.
                    gone = set()
                    for key, song in self.iteritems():
                        if file_path in Path(key).parents:
                            gone.add(song)
                    if gone:
                        print_d(f"Removing {len(gone)} contained songs in {file_path}",
                                self._name)
                        actually_gone = self.remove(gone)
                        if gone != actually_gone:
                            print_w(f"Couldn't remove all: {gone - actually_gone}",
                                    self._name)
            else:
                print_d(f"Unhandled event {event} on {file_path} ({other_path})",
                        self._name)
                return
        except Exception:
            print_w("Failed to run file monitor callback", self._name)
            print_exc()
        print_d(f"Finished handling {event}", self._name)
Example #24
0
def guess_content_type(file: Gio.File) -> str:
    info: Gio.FileInfo = file.query_info(
        Gio.FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE,
        Gio.FileQueryInfoFlags.NONE, None)
    return info.get_attribute_as_string(
        Gio.FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE)
Example #25
0
File: vcview.py Project: Mu-L/meld
    def on_file_selected(self, button: Gtk.Button, pane: int,
                         file: Gio.File) -> None:

        path = file.get_path()
        self.set_location(path)