mirror of
https://github.com/onionshare/onionshare.git
synced 2025-08-25 06:29:54 -04:00
Merge pull request #1020 from micahflee/991_sharing_code
[WIP] Share code between share mode and website mode
This commit is contained in:
commit
51a1f92d99
35 changed files with 1063 additions and 396 deletions
|
@ -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"])
|
||||
|
|
|
@ -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))
|
||||
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.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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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"]))
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue