summaryrefslogtreecommitdiff
path: root/onionshare_gui
diff options
context:
space:
mode:
authorMicah Lee <micah@micahflee.com>2019-09-15 14:52:57 -0700
committerGitHub <noreply@github.com>2019-09-15 14:52:57 -0700
commit51a1f92d9975f76a0e4bfc36078715baeec203ce (patch)
tree4063a36658eeed59ce48295a0ae789f87cbcdc38 /onionshare_gui
parent414e7f9a38a4762ea845de278f24a8cff35bdbd3 (diff)
parent16fedabf400a696c452c644bcd7031af8b0e2b9e (diff)
downloadonionshare-51a1f92d9975f76a0e4bfc36078715baeec203ce.tar.gz
onionshare-51a1f92d9975f76a0e4bfc36078715baeec203ce.zip
Merge pull request #1020 from micahflee/991_sharing_code
[WIP] Share code between share mode and website mode
Diffstat (limited to 'onionshare_gui')
-rw-r--r--onionshare_gui/mode/__init__.py33
-rw-r--r--onionshare_gui/mode/history.py149
-rw-r--r--onionshare_gui/mode/receive_mode/__init__.py6
-rw-r--r--onionshare_gui/mode/share_mode/__init__.py12
-rw-r--r--onionshare_gui/mode/share_mode/threads.py8
-rw-r--r--onionshare_gui/mode/website_mode/__init__.py29
-rw-r--r--onionshare_gui/onionshare_gui.py11
-rw-r--r--onionshare_gui/settings_dialog.py3
8 files changed, 182 insertions, 69 deletions
diff --git a/onionshare_gui/mode/__init__.py b/onionshare_gui/mode/__init__.py
index e92e36f8..3ef285c4 100644
--- a/onionshare_gui/mode/__init__.py
+++ b/onionshare_gui/mode/__init__.py
@@ -22,6 +22,8 @@ from PyQt5 import QtCore, QtWidgets, QtGui
from onionshare import strings
from onionshare.common import AutoStopTimer
+from .history import IndividualFileHistoryItem
+
from ..server_status import ServerStatus
from ..threads import OnionThread
from ..threads import AutoStartTimer
@@ -29,7 +31,7 @@ from ..widgets import Alert
class Mode(QtWidgets.QWidget):
"""
- The class that ShareMode and ReceiveMode inherit from.
+ The class that all modes inherit from
"""
start_server_finished = QtCore.pyqtSignal()
stop_server_finished = QtCore.pyqtSignal()
@@ -417,3 +419,32 @@ class Mode(QtWidgets.QWidget):
Handle REQUEST_UPLOAD_CANCELED event.
"""
pass
+
+ def handle_request_individual_file_started(self, event):
+ """
+ Handle REQUEST_INDVIDIDUAL_FILES_STARTED event.
+ Used in both Share and Website modes, so implemented here.
+ """
+ self.toggle_history.update_indicator(True)
+ self.history.requests_count += 1
+ self.history.update_requests()
+
+ item = IndividualFileHistoryItem(self.common, event["data"], event["path"])
+ self.history.add(event["data"]["id"], item)
+
+ def handle_request_individual_file_progress(self, event):
+ """
+ Handle REQUEST_INDVIDIDUAL_FILES_PROGRESS event.
+ Used in both Share and Website modes, so implemented here.
+ """
+ self.history.update(event["data"]["id"], event["data"]["bytes"])
+
+ if self.server_status.status == self.server_status.STATUS_STOPPED:
+ self.history.cancel(event["data"]["id"])
+
+ def handle_request_individual_file_canceled(self, event):
+ """
+ Handle REQUEST_INDVIDIDUAL_FILES_CANCELED event.
+ Used in both Share and Website modes, so implemented here.
+ """
+ self.history.cancel(event["data"]["id"])
diff --git a/onionshare_gui/mode/history.py b/onionshare_gui/mode/history.py
index 51b36f9a..b8baebd1 100644
--- a/onionshare_gui/mode/history.py
+++ b/onionshare_gui/mode/history.py
@@ -237,6 +237,7 @@ class ReceiveHistoryItemFile(QtWidgets.QWidget):
elif self.common.platform == 'Windows':
subprocess.Popen(['explorer', '/select,{}'.format(abs_filename)])
+
class ReceiveHistoryItem(HistoryItem):
def __init__(self, common, id, content_length):
super(ReceiveHistoryItem, self).__init__()
@@ -341,35 +342,108 @@ class ReceiveHistoryItem(HistoryItem):
self.label.setText(self.get_canceled_label_text(self.started))
-class VisitHistoryItem(HistoryItem):
+class IndividualFileHistoryItem(HistoryItem):
"""
- Download history item, for share mode
+ Individual file history item, for share mode viewing of individual files
"""
- def __init__(self, common, id, total_bytes):
- super(VisitHistoryItem, self).__init__()
+ def __init__(self, common, data, path):
+ super(IndividualFileHistoryItem, self).__init__()
self.status = HistoryItem.STATUS_STARTED
self.common = common
self.id = id
- self.visited = time.time()
- self.visited_dt = datetime.fromtimestamp(self.visited)
+ self.path = path
+ self.total_bytes = 0
+ self.downloaded_bytes = 0
+ self.started = time.time()
+ self.started_dt = datetime.fromtimestamp(self.started)
+ self.status = HistoryItem.STATUS_STARTED
- # Label
- self.label = QtWidgets.QLabel(strings._('gui_visit_started').format(self.visited_dt.strftime("%b %d, %I:%M%p")))
+ self.directory_listing = 'directory_listing' in data
+
+ # Labels
+ self.timestamp_label = QtWidgets.QLabel(self.started_dt.strftime("%b %d, %I:%M%p"))
+ self.timestamp_label.setStyleSheet(self.common.css['history_individual_file_timestamp_label'])
+ self.path_label = QtWidgets.QLabel("{}".format(self.path))
+ self.status_code_label = QtWidgets.QLabel()
+
+ # Progress bar
+ self.progress_bar = QtWidgets.QProgressBar()
+ self.progress_bar.setTextVisible(True)
+ self.progress_bar.setAttribute(QtCore.Qt.WA_DeleteOnClose)
+ self.progress_bar.setAlignment(QtCore.Qt.AlignHCenter)
+ self.progress_bar.setValue(0)
+ self.progress_bar.setStyleSheet(self.common.css['downloads_uploads_progress_bar'])
+
+ # Text layout
+ labels_layout = QtWidgets.QHBoxLayout()
+ labels_layout.addWidget(self.timestamp_label)
+ labels_layout.addWidget(self.path_label)
+ labels_layout.addWidget(self.status_code_label)
+ labels_layout.addStretch()
# Layout
layout = QtWidgets.QVBoxLayout()
- layout.addWidget(self.label)
+ layout.addLayout(labels_layout)
+ layout.addWidget(self.progress_bar)
self.setLayout(layout)
- def update(self):
- self.label.setText(self.get_finished_label_text(self.started_dt))
- self.status = HistoryItem.STATUS_FINISHED
+ # Is a status code already sent?
+ if 'status_code' in data:
+ self.status_code_label.setText("{}".format(data['status_code']))
+ if data['status_code'] >= 200 and data['status_code'] < 300:
+ self.status_code_label.setStyleSheet(self.common.css['history_individual_file_status_code_label_2xx'])
+ if data['status_code'] >= 400 and data['status_code'] < 500:
+ self.status_code_label.setStyleSheet(self.common.css['history_individual_file_status_code_label_4xx'])
+ self.status = HistoryItem.STATUS_FINISHED
+ self.progress_bar.hide()
+ return
+
+ else:
+ self.total_bytes = data['filesize']
+ self.progress_bar.setMinimum(0)
+ self.progress_bar.setMaximum(data['filesize'])
+ self.progress_bar.total_bytes = data['filesize']
+
+ # Start at 0
+ self.update(0)
+
+ def update(self, downloaded_bytes):
+ self.downloaded_bytes = downloaded_bytes
+
+ self.progress_bar.setValue(downloaded_bytes)
+ if downloaded_bytes == self.progress_bar.total_bytes:
+ self.status_code_label.setText("200")
+ self.status_code_label.setStyleSheet(self.common.css['history_individual_file_status_code_label_2xx'])
+ self.progress_bar.hide()
+ self.status = HistoryItem.STATUS_FINISHED
+
+ else:
+ elapsed = time.time() - self.started
+ if elapsed < 10:
+ # Wait a couple of seconds for the download rate to stabilize.
+ # This prevents a "Windows copy dialog"-esque experience at
+ # the beginning of the download.
+ pb_fmt = strings._('gui_all_modes_progress_starting').format(
+ self.common.human_readable_filesize(downloaded_bytes))
+ else:
+ pb_fmt = strings._('gui_all_modes_progress_eta').format(
+ self.common.human_readable_filesize(downloaded_bytes),
+ self.estimated_time_remaining)
+
+ self.progress_bar.setFormat(pb_fmt)
def cancel(self):
self.progress_bar.setFormat(strings._('gui_canceled'))
self.status = HistoryItem.STATUS_CANCELED
+ @property
+ def estimated_time_remaining(self):
+ return self.common.estimated_time_remaining(self.downloaded_bytes,
+ self.total_bytes,
+ self.started)
+
+
class HistoryItemList(QtWidgets.QScrollArea):
"""
List of items
@@ -452,26 +526,30 @@ class History(QtWidgets.QWidget):
# In progress and completed counters
self.in_progress_count = 0
self.completed_count = 0
+ self.requests_count = 0
- # In progress and completed labels
+ # In progress, completed, and requests labels
self.in_progress_label = QtWidgets.QLabel()
self.in_progress_label.setStyleSheet(self.common.css['mode_info_label'])
self.completed_label = QtWidgets.QLabel()
self.completed_label.setStyleSheet(self.common.css['mode_info_label'])
+ self.requests_label = QtWidgets.QLabel()
+ self.requests_label.setStyleSheet(self.common.css['mode_info_label'])
# Header
self.header_label = QtWidgets.QLabel(header_text)
self.header_label.setStyleSheet(self.common.css['downloads_uploads_label'])
- clear_button = QtWidgets.QPushButton(strings._('gui_all_modes_clear_history'))
- clear_button.setStyleSheet(self.common.css['downloads_uploads_clear'])
- clear_button.setFlat(True)
- clear_button.clicked.connect(self.reset)
+ self.clear_button = QtWidgets.QPushButton(strings._('gui_all_modes_clear_history'))
+ self.clear_button.setStyleSheet(self.common.css['downloads_uploads_clear'])
+ self.clear_button.setFlat(True)
+ self.clear_button.clicked.connect(self.reset)
header_layout = QtWidgets.QHBoxLayout()
header_layout.addWidget(self.header_label)
header_layout.addStretch()
header_layout.addWidget(self.in_progress_label)
header_layout.addWidget(self.completed_label)
- header_layout.addWidget(clear_button)
+ header_layout.addWidget(self.requests_label)
+ header_layout.addWidget(self.clear_button)
# When there are no items
self.empty_image = QtWidgets.QLabel()
@@ -549,14 +627,18 @@ class History(QtWidgets.QWidget):
self.completed_count = 0
self.update_completed()
+ # Reset web requests counter
+ self.requests_count = 0
+ self.update_requests()
+
def update_completed(self):
"""
Update the 'completed' widget.
"""
if self.completed_count == 0:
- image = self.common.get_resource_path('images/share_completed_none.png')
+ image = self.common.get_resource_path('images/history_completed_none.png')
else:
- image = self.common.get_resource_path('images/share_completed.png')
+ image = self.common.get_resource_path('images/history_completed.png')
self.completed_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.completed_count))
self.completed_label.setToolTip(strings._('history_completed_tooltip').format(self.completed_count))
@@ -564,14 +646,25 @@ class History(QtWidgets.QWidget):
"""
Update the 'in progress' widget.
"""
- if self.mode != 'website':
- if self.in_progress_count == 0:
- image = self.common.get_resource_path('images/share_in_progress_none.png')
- else:
- image = self.common.get_resource_path('images/share_in_progress.png')
+ if self.in_progress_count == 0:
+ image = self.common.get_resource_path('images/history_in_progress_none.png')
+ else:
+ image = self.common.get_resource_path('images/history_in_progress.png')
+
+ self.in_progress_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.in_progress_count))
+ self.in_progress_label.setToolTip(strings._('history_in_progress_tooltip').format(self.in_progress_count))
+
+ def update_requests(self):
+ """
+ Update the 'web requests' widget.
+ """
+ if self.requests_count == 0:
+ image = self.common.get_resource_path('images/history_requests_none.png')
+ else:
+ image = self.common.get_resource_path('images/history_requests.png')
- self.in_progress_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.in_progress_count))
- self.in_progress_label.setToolTip(strings._('history_in_progress_tooltip').format(self.in_progress_count))
+ self.requests_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.requests_count))
+ self.requests_label.setToolTip(strings._('history_requests_tooltip').format(self.requests_count))
class ToggleHistory(QtWidgets.QPushButton):
@@ -604,7 +697,7 @@ class ToggleHistory(QtWidgets.QPushButton):
def update_indicator(self, increment=False):
"""
Update the display of the indicator count. If increment is True, then
- only increment the counter if Downloads is hidden.
+ only increment the counter if History is hidden.
"""
if increment and not self.history_widget.isVisible():
self.indicator_count += 1
diff --git a/onionshare_gui/mode/receive_mode/__init__.py b/onionshare_gui/mode/receive_mode/__init__.py
index dbc0bc73..ecbfa54a 100644
--- a/onionshare_gui/mode/receive_mode/__init__.py
+++ b/onionshare_gui/mode/receive_mode/__init__.py
@@ -97,7 +97,7 @@ class ReceiveMode(Mode):
The auto-stop timer expired, should we stop the server? Returns a bool
"""
# If there were no attempts to upload files, or all uploads are done, we can stop
- if self.web.receive_mode.upload_count == 0 or not self.web.receive_mode.uploads_in_progress:
+ if self.web.receive_mode.cur_history_id == 0 or not self.web.receive_mode.uploads_in_progress:
self.server_status.stop_server()
self.server_status_label.setText(strings._('close_on_autostop_timer'))
return True
@@ -112,7 +112,7 @@ class ReceiveMode(Mode):
Starting the server.
"""
# Reset web counters
- self.web.receive_mode.upload_count = 0
+ self.web.receive_mode.cur_history_id = 0
self.web.reset_invalid_passwords()
# Hide and reset the uploads if we have previously shared
@@ -212,6 +212,8 @@ class ReceiveMode(Mode):
Set the info counters back to zero.
"""
self.history.reset()
+ self.toggle_history.indicator_count = 0
+ self.toggle_history.update_indicator()
def update_primary_action(self):
self.common.log('ReceiveMode', 'update_primary_action')
diff --git a/onionshare_gui/mode/share_mode/__init__.py b/onionshare_gui/mode/share_mode/__init__.py
index 143fd577..28b439af 100644
--- a/onionshare_gui/mode/share_mode/__init__.py
+++ b/onionshare_gui/mode/share_mode/__init__.py
@@ -132,7 +132,7 @@ class ShareMode(Mode):
The auto-stop timer expired, should we stop the server? Returns a bool
"""
# If there were no attempts to download the share, or all downloads are done, we can stop
- if self.web.share_mode.download_count == 0 or self.web.done:
+ if self.web.share_mode.cur_history_id == 0 or self.web.done:
self.server_status.stop_server()
self.server_status_label.setText(strings._('close_on_autostop_timer'))
return True
@@ -146,7 +146,7 @@ class ShareMode(Mode):
Starting the server.
"""
# Reset web counters
- self.web.share_mode.download_count = 0
+ self.web.share_mode.cur_history_id = 0
self.web.reset_invalid_passwords()
# Hide and reset the downloads if we have previously shared
@@ -225,12 +225,6 @@ class ShareMode(Mode):
"""
self.primary_action.hide()
- def handle_request_load(self, event):
- """
- Handle REQUEST_LOAD event.
- """
- self.system_tray.showMessage(strings._('systray_page_loaded_title'), strings._('systray_page_loaded_message'))
-
def handle_request_started(self, event):
"""
Handle REQUEST_STARTED event.
@@ -325,6 +319,8 @@ class ShareMode(Mode):
Set the info counters back to zero.
"""
self.history.reset()
+ self.toggle_history.indicator_count = 0
+ self.toggle_history.update_indicator()
@staticmethod
def _compute_total_size(filenames):
diff --git a/onionshare_gui/mode/share_mode/threads.py b/onionshare_gui/mode/share_mode/threads.py
index 24e2c242..fed362eb 100644
--- a/onionshare_gui/mode/share_mode/threads.py
+++ b/onionshare_gui/mode/share_mode/threads.py
@@ -41,12 +41,8 @@ class CompressThread(QtCore.QThread):
self.mode.common.log('CompressThread', 'run')
try:
- if self.mode.web.share_mode.set_file_info(self.mode.filenames, processed_size_callback=self.set_processed_size):
- self.success.emit()
- else:
- # Cancelled
- pass
-
+ self.mode.web.share_mode.set_file_info(self.mode.filenames, processed_size_callback=self.set_processed_size)
+ self.success.emit()
self.mode.app.cleanup_filenames += self.mode.web.share_mode.cleanup_filenames
except OSError as e:
self.error.emit(e.strerror)
diff --git a/onionshare_gui/mode/website_mode/__init__.py b/onionshare_gui/mode/website_mode/__init__.py
index 50af4725..b277b6c3 100644
--- a/onionshare_gui/mode/website_mode/__init__.py
+++ b/onionshare_gui/mode/website_mode/__init__.py
@@ -30,7 +30,7 @@ from onionshare.web import Web
from ..file_selection import FileSelection
from .. import Mode
-from ..history import History, ToggleHistory, VisitHistoryItem
+from ..history import History, ToggleHistory
from ...widgets import Alert
class WebsiteMode(Mode):
@@ -80,6 +80,8 @@ class WebsiteMode(Mode):
strings._('gui_all_modes_history'),
'website'
)
+ self.history.in_progress_label.hide()
+ self.history.completed_label.hide()
self.history.hide()
# Info label
@@ -165,12 +167,8 @@ class WebsiteMode(Mode):
Step 3 in starting the server. Display large filesize
warning, if applicable.
"""
-
- if self.web.website_mode.set_file_info(self.filenames):
- self.success.emit()
- else:
- # Cancelled
- pass
+ self.web.website_mode.set_file_info(self.filenames)
+ self.success.emit()
def start_server_error_custom(self):
"""
@@ -208,21 +206,6 @@ class WebsiteMode(Mode):
"""
self.system_tray.showMessage(strings._('systray_site_loaded_title'), strings._('systray_site_loaded_message'))
- def handle_request_started(self, event):
- """
- Handle REQUEST_STARTED event.
- """
- if ( (event["path"] == '') or (event["path"].find(".htm") != -1 ) ):
- item = VisitHistoryItem(self.common, event["data"]["id"], 0)
-
- self.history.add(event["data"]["id"], item)
- self.toggle_history.update_indicator(True)
- self.history.completed_count += 1
- self.history.update_completed()
-
- self.system_tray.showMessage(strings._('systray_website_started_title'), strings._('systray_website_started_message'))
-
-
def on_reload_settings(self):
"""
If there were some files listed for sharing, we should be ok to re-enable
@@ -262,6 +245,8 @@ class WebsiteMode(Mode):
Set the info counters back to zero.
"""
self.history.reset()
+ self.toggle_history.indicator_count = 0
+ self.toggle_history.update_indicator()
@staticmethod
def _compute_total_size(filenames):
diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py
index bed86895..20873bc8 100644
--- a/onionshare_gui/onionshare_gui.py
+++ b/onionshare_gui/onionshare_gui.py
@@ -383,7 +383,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.share_mode.server_status.autostart_timer_container.hide()
self.receive_mode.server_status.autostart_timer_container.hide()
self.website_mode.server_status.autostart_timer_container.hide()
-
+
d = SettingsDialog(self.common, self.onion, self.qtapp, self.config, self.local_only)
d.settings_saved.connect(reload_settings)
d.exec_()
@@ -470,6 +470,15 @@ class OnionShareGui(QtWidgets.QMainWindow):
elif event["type"] == Web.REQUEST_UPLOAD_CANCELED:
mode.handle_request_upload_canceled(event)
+ elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_STARTED:
+ mode.handle_request_individual_file_started(event)
+
+ elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_PROGRESS:
+ mode.handle_request_individual_file_progress(event)
+
+ elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_CANCELED:
+ mode.handle_request_individual_file_canceled(event)
+
if event["type"] == Web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE:
Alert(self.common, strings._('error_cannot_create_data_dir').format(event["data"]["receive_mode_dir"]))
diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py
index cb732aa2..25165688 100644
--- a/onionshare_gui/settings_dialog.py
+++ b/onionshare_gui/settings_dialog.py
@@ -212,10 +212,12 @@ class SettingsDialog(QtWidgets.QDialog):
self.close_after_first_download_checkbox = QtWidgets.QCheckBox()
self.close_after_first_download_checkbox.setCheckState(QtCore.Qt.Checked)
self.close_after_first_download_checkbox.setText(strings._("gui_settings_close_after_first_download_option"))
+ individual_downloads_label = QtWidgets.QLabel(strings._("gui_settings_individual_downloads_label"))
# Sharing options layout
sharing_group_layout = QtWidgets.QVBoxLayout()
sharing_group_layout.addWidget(self.close_after_first_download_checkbox)
+ sharing_group_layout.addWidget(individual_downloads_label)
sharing_group = QtWidgets.QGroupBox(strings._("gui_settings_sharing_label"))
sharing_group.setLayout(sharing_group_layout)
@@ -638,7 +640,6 @@ class SettingsDialog(QtWidgets.QDialog):
self.connect_to_tor_label.show()
self.onion_settings_widget.hide()
-
def connection_type_bundled_toggled(self, checked):
"""
Connection type bundled was toggled. If checked, hide authentication fields.