Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to PyQt6 #9

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packaging/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PyQt5
PyQt6
PyInstaller
beautifulsoup4
html5lib
Expand Down
2 changes: 1 addition & 1 deletion src/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
DOWNLOAD_MIN_CHUNK_SIZE = 10*1024 # 10KB

# Max items
DEFAULT_MAX_ITEMS = 50_000
DEFAULT_MAX_ITEMS = 50_000
6 changes: 3 additions & 3 deletions src/dirlist.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import os
import tempfile
import subprocess
import time

from PyQt5.QtCore import QObject, pyqtSignal
from PyQt6.QtCore import QObject, pyqtSignal

from utils import show_message_box, extract_domain, decode_data
from utils import decode_data
from nodefs import FSNode, parse_dirlist_line
from consts import DEFAULT_MAX_ITEMS


class DirlistWorker(QObject):
# Signals
finished = pyqtSignal()
Expand Down
7 changes: 4 additions & 3 deletions src/nodefs.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import re
from datetime import datetime

from utils import decode_data
from consts import DIRLIST_REGEX
from utils import decode_data


def parse_dirlist_line(line):
Expand All @@ -14,6 +13,7 @@ def parse_dirlist_line(line):
else:
raise Exception("Bad dirlist data. Could not parse line: '{}'".format(line))


def parse_dirlist(dirlist_path):
# Dirlist:
# 2016-11-14 16:14:09 0 DirName/
Expand All @@ -34,6 +34,7 @@ def parse_dirlist(dirlist_path):
stats.process_node(new_node)
return root_node, stats


def print_all_nodes(node, level=0):
print("{}{}".format(level*"\t", node.basename))
for child_node in node.children.values():
Expand Down Expand Up @@ -195,4 +196,4 @@ def get_sub_node(self, full_path):
current_node = current_node.children[path_element]
else:
raise Exception("Could not find '{}'".format(full_path))
return current_node
return current_node
1 change: 1 addition & 0 deletions src/providers/aws_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from utils import show_message_box
from providers.base_provider import StorageProvider


class S3StorageProvider(StorageProvider):
NODE_BATCH_UPDATE_COUNT = 1000

Expand Down
3 changes: 2 additions & 1 deletion src/providers/azure_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

AZURE_LINE_REGEX = re.compile(r"INFO\: (.*);\s+Content Length: (\d+)")


class AzureStorageProvider(StorageProvider):
NODE_BATCH_UPDATE_COUNT = 1000

Expand Down Expand Up @@ -79,4 +80,4 @@ def hostname(self):
return self._extract_azure_blob_name()

def _extract_azure_blob_name(self):
return urlparse(self.url).netloc.split(".blob.core.windows.net")[0]
return urlparse(self.url).netloc.split(".blob.core.windows.net")[0]
2 changes: 0 additions & 2 deletions src/providers/base_provider.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

class StorageProvider():
NODE_BATCH_UPDATE_COUNT = 1

Expand Down Expand Up @@ -27,4 +26,3 @@ def get_default_error_message(self):

def stop(self):
self.should_stop = True

14 changes: 8 additions & 6 deletions src/providers/ftp_provider.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import datetime
import os
import re
import urllib
import datetime
import time
from ftplib import FTP

from consts import FTP_MAX_RECURSE_LEVEL, FTP_USER_DEFAULT, FTP_PASS_DEFAULT
from utils import show_message_box
from dateutil import parser as dateutil_parser
from providers.base_provider import StorageProvider
from utils import show_message_box

from dateutil import parser as dateutil_parser
TEMP_OUTPUT = []


TEMP_OUTPUT = []
def _ftp_dir_collector_callback(line):
global TEMP_OUTPUT
TEMP_OUTPUT.append(line)


# Took this function from https://github.com/codebynumbers/ftpretty/blob/master/ftpretty.py
def split_file_info(fileinfo):
""" Parse sane directory output usually ls -l
Expand Down Expand Up @@ -113,9 +113,11 @@ def split_file_info(fileinfo):
})
return files


def is_directory(entry):
return entry.get("is_directory")


def is_file_or_dir_ok(entry):
name = entry.get("name")
if not name or name == "." or name == "..":
Expand Down Expand Up @@ -159,6 +161,7 @@ def yield_fetch_dir(ftp_conn, cwd="/", max_recurse_level=FTP_MAX_RECURSE_LEVEL,
if is_directory(f):
yield from yield_fetch_dir(ftp_conn, cwd=f.get("full_path"), max_recurse_level=max_recurse_level, recurse_level=recurse_level)


class FTPStorageProvider(StorageProvider):
NODE_BATCH_UPDATE_COUNT = 10

Expand Down Expand Up @@ -206,4 +209,3 @@ def url_path(self):
if not url_path:
url_path = "/"
return url_path

11 changes: 5 additions & 6 deletions src/providers/httpindex_provider.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import sys
import argparse
import collections
import os
import re
import time
import argparse
import requests
import collections
import urllib.parse
import bs4

from utils import show_message_box
import bs4
import requests
from consts import HTTP_MAX_RECURSE_LEVEL, USER_AGENT
from providers.base_provider import StorageProvider
from utils import show_message_box

##################################################################################
### Most of the code here is from https://github.com/gumblex/htmllisting-parser ##
Expand Down
15 changes: 4 additions & 11 deletions src/providers/providers.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import shutil
import subprocess
import codecs
from distutils.spawn import find_executable
from urllib.parse import urlparse

from utils import show_message_box
from providers.aws_provider import S3StorageProvider
from providers.httpindex_provider import HTTPIndexStorageProvider
from providers.ftp_provider import FTPStorageProvider
from providers.azure_provider import AzureStorageProvider
from .aws_provider import S3StorageProvider
from .azure_provider import AzureStorageProvider
from .ftp_provider import FTPStorageProvider
from .httpindex_provider import HTTPIndexStorageProvider


def find_provider_class_by_url(url):
Expand Down
48 changes: 23 additions & 25 deletions src/s3viewer.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import os
import sys
from PyQt6 import QtCore
from PyQt6.QtCore import QThread, pyqtSlot
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import QTreeWidgetItem, QApplication, QFrame

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QTreeWidgetItem, QApplication, QSpacerItem, QSizePolicy, QFrame
from PyQt5.QtGui import QIcon

from utils import *
from nodefs import *
from dirlist import *
from consts import *
from dirlist import *
from nodefs import *
from providers.providers import *
from utils import *


class Mode():
Expand Down Expand Up @@ -145,7 +142,7 @@ def setupUi(self, MainWindow):

# Separator
self.separatorLine = QFrame()
self.separatorLine.setFrameShape(QFrame.HLine)
self.separatorLine.setFrameShape(QFrame.Shape.HLine)
self.verticalLayout.addWidget(self.separatorLine)
#####################################

Expand Down Expand Up @@ -188,11 +185,11 @@ def setupUi(self, MainWindow):
self.treeWidget.headerItem().setText(1, "Size")
self.treeWidget.headerItem().setText(2, "Date Modified")
self.treeWidget.headerItem().setText(3, "Downloaded")
self.treeWidget.header().setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
self.treeWidget.header().setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
self.treeWidget.header().setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
self.treeWidget.header().setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
self.treeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.treeWidget.header().setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
self.treeWidget.header().setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
self.treeWidget.header().setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
self.treeWidget.header().setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
self.treeWidget.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
self.horizontalLayout_4.addWidget(self.treeWidget)
#####################################

Expand Down Expand Up @@ -278,7 +275,7 @@ def menu_context_tree_view_widget(self, point):
action_open_dir = menu.addAction("Open Directory")
action_open_dir.triggered.connect(self.tree_item_open_directory)
action_open_dir.setEnabled(is_dir or is_downloaded)
menu.exec_(self.treeWidget.mapToGlobal(point))
menu.exec(self.treeWidget.mapToGlobal(point))

def tree_item_download(self):
if self.selected_tree_node.is_file:
Expand Down Expand Up @@ -403,7 +400,7 @@ def download_node(self, node):
if not path_save_to:
return False
# Prepare download url
path_download = node.full_path.lstrip("/") # remove the first / if any
path_download = node.full_path.lstrip("/") # remove the first / if any
url_download = self.current_provider.get_download_url(path_download)
try:
# Download
Expand Down Expand Up @@ -451,8 +448,9 @@ def button_click_process_dirlist(self):
# Clear UI
self.init_ui()
# Get dirlist
file_dialog_options = QtWidgets.QFileDialog.Options()
file_dialog_options |= QtWidgets.QFileDialog.DontUseNativeDialog
file_dialog = QtWidgets.QFileDialog()
file_dialog_options = file_dialog.options()
file_dialog_options |= QtWidgets.QFileDialog.Option.DontUseNativeDialog
file_dialog_title = "Select dirlist file"
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(None, file_dialog_title, "", "All Files (*)", options=file_dialog_options)
if file_path:
Expand All @@ -478,16 +476,16 @@ def button_click_open_working_dir(self):
@pyqtSlot( )
def button_click_search_do(self):
# Hide all
for item in self.treeWidget.findItems("", QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive):
for item in self.treeWidget.findItems("", QtCore.Qt.MatchFlag.MatchContains | QtCore.Qt.MatchFlag.MatchRecursive):
item.setHidden(True)
# Mark as search mode only if there are items
self.mode.starting_search()
if self.mode.is_searching:
# Show only those that match the search
search_query = self.lineEditSearch.text()
if search_query:
flags = QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive
#flags |= QtCore.Qt.MatchRegExp # use regex
flags = QtCore.Qt.MatchFlag.MatchContains | QtCore.Qt.MatchFlag.MatchRecursive
# flags |= QtCore.Qt.MatchRegExp # use regex
for item in self.treeWidget.findItems(search_query, flags):
# Walk up the chain
item_temp = item
Expand All @@ -507,7 +505,7 @@ def button_click_search_clear(self):
if self.mode.is_searching:
self.mode.finished_search()
# Show all
for item in self.treeWidget.findItems("", QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive):
for item in self.treeWidget.findItems("", QtCore.Qt.MatchFlag.MatchContains | QtCore.Qt.MatchFlag.MatchRecursive):
item.setHidden(False)
self.update_ui()

Expand Down Expand Up @@ -657,4 +655,4 @@ def populate_tree_view_with_gui(self, dirlist_path=None):
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
sys.exit(app.exec())
20 changes: 12 additions & 8 deletions src/utils.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import sys
import os
import subprocess
import sys
import urllib
from urllib.parse import urlparse

import requests
from PyQt5 import QtWidgets
from PyQt6 import QtWidgets

from consts import USER_AGENT, DOWNLOAD_MIN_CHUNK_SIZE


# Get reference to running directory
RUNNING_DIR = os.path.dirname(os.path.abspath(__file__))
# PyInstaller - in case of PyInstaller the running directory is sys._MEIPASS
if hasattr(sys, '_MEIPASS'):
RUNNING_DIR = sys._MEIPASS


def get_asset_path(relative_path):
return os.path.join(RUNNING_DIR, relative_path)


def download_file(url, filename, report_hook):
scheme = urlparse(url).scheme
if "ftp" in scheme:
Expand Down Expand Up @@ -46,13 +47,14 @@ def download_file(url, filename, report_hook):
raise Exception("Download error: unknown scheme")


def show_message_box(msg, alert_type=QtWidgets.QMessageBox.Warning):
def show_message_box(msg, alert_type=QtWidgets.QMessageBox.Icon.Warning):
msg_box = QtWidgets.QMessageBox()
msg_box.setText(msg)
msg_box.setIcon(alert_type)
msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
msg_box.setDefaultButton(QtWidgets.QMessageBox.Ok)
ret = msg_box.exec_()
msg_box.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok)
msg_box.setDefaultButton(QtWidgets.QMessageBox.StandardButton.Ok)
ret = msg_box.exec()


def open_dir(path):
try:
Expand All @@ -76,15 +78,17 @@ def open_file(path):
except subprocess.CalledProcessError as e:
pass


def extract_domain(url):
domain = urlparse('url').netloc
return domain or url


def decode_data(data):
# Try brute forcing all popular encodings
for encoding in ["utf-8", "utf-16-le", "utf-16-be", "latin-1", "ascii"]:
try:
return data.decode(encoding)
except Exception as e:
pass
return None
return None