From e73114c01e22d7330261dbb170ad62327cc15702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Br=C3=B6tzmann?= Date: Sat, 1 Jun 2019 22:12:52 +0200 Subject: [PATCH] [preview] Cleanup code --- url_image_preview/config_dialog.py | 4 +- url_image_preview/context_menu.ui | 78 ++-------- url_image_preview/http_functions.py | 22 +-- url_image_preview/resize_gif.py | 4 +- url_image_preview/url_image_preview.py | 188 +++++++++++++------------ 5 files changed, 127 insertions(+), 169 deletions(-) diff --git a/url_image_preview/config_dialog.py b/url_image_preview/config_dialog.py index ade5677..0f485c1 100644 --- a/url_image_preview/config_dialog.py +++ b/url_image_preview/config_dialog.py @@ -91,8 +91,8 @@ class UrlImagePreviewConfigDialog(SettingsDialog): ] SettingsDialog.__init__(self, parent, _('UrlImagePreview Configuration'), - Gtk.DialogFlags.MODAL, settings, None, - extend=[ + Gtk.DialogFlags.MODAL, settings, None, + extend=[ ('PreviewSizeSpinSetting', SizeSpinSetting)]) def on_setting(self, value, data): diff --git a/url_image_preview/context_menu.ui b/url_image_preview/context_menu.ui index 9f022fb..e59bc48 100644 --- a/url_image_preview/context_menu.ui +++ b/url_image_preview/context_menu.ui @@ -1,70 +1,31 @@ - - - True - False - gtk-open - - - True - False - gtk-save-as - - - True - False - gtk-copy - - - True - False - emblem-symbolic-link - - - True - False - emblem-symbolic-link - - - True - False - folder-download - + False - + + True + False _Open - True - False True - image1 - False - True - + + True + False _Save as - True - False True - image2 - False - True - - Open _Folder + True False + Open _Folder True - image6 - False - True @@ -74,25 +35,19 @@ - - _Copy Link Location + True False + _Copy Link True - image3 - False - True - - Open Link in _Browser + True False + Open Link in _Browser True - image4 - False - True @@ -102,14 +57,11 @@ - - Open _Downloaded File in Browser + True False + Open _Downloaded File in Browser True - image5 - False - True diff --git a/url_image_preview/http_functions.py b/url_image_preview/http_functions.py index 97e0f70..19037a4 100644 --- a/url_image_preview/http_functions.py +++ b/url_image_preview/http_functions.py @@ -47,17 +47,17 @@ def _get_http_head_direct(url, verify): context.check_hostname = False context.verify_mode = ssl.CERT_NONE log.warning('CERT Verification disabled') - f = urllib2.urlopen(req, timeout=30, context=context) + file_ = urllib2.urlopen(req, timeout=30, context=context) else: if os.name == 'nt': - f = urllib2.urlopen(req, cafile=certifi.where()) + file_ = urllib2.urlopen(req, cafile=certifi.where()) else: - f = urllib2.urlopen(req) + file_ = urllib2.urlopen(req) except Exception as ex: log.debug('Error', exc_info=True) return ('', 0) - ctype = f.headers['Content-Type'] - clen = f.headers['Content-Length'] + ctype = file_.headers['Content-Type'] + clen = file_.headers['Content-Length'] try: clen = int(clen) except (TypeError, ValueError): @@ -65,10 +65,10 @@ def _get_http_head_direct(url, verify): return (ctype, clen) def _get_http_direct(attrs): - """ + ''' Download a file. This function should be launched in a separated thread. - """ + ''' log.info('Get request direct for URL: %s', attrs['src']) mem, alt, max_size = b'', '', 2 * 1024 * 1024 if 'max_size' in attrs: @@ -81,12 +81,12 @@ def _get_http_direct(attrs): context.check_hostname = False context.verify_mode = ssl.CERT_NONE log.warning('CERT Verification disabled') - f = urllib2.urlopen(req, timeout=30, context=context) + file_ = urllib2.urlopen(req, timeout=30, context=context) else: if os.name == 'nt': - f = urllib2.urlopen(req, cafile=certifi.where()) + file_ = urllib2.urlopen(req, cafile=certifi.where()) else: - f = urllib2.urlopen(req) + file_ = urllib2.urlopen(req) except Exception as ex: log.debug('Error', exc_info=True) pixbuf = None @@ -94,7 +94,7 @@ def _get_http_direct(attrs): else: while True: try: - temp = f.read(100) + temp = file_.read(100) except socket.timeout as ex: log.debug('Timeout loading image %s', attrs['src'] + str(ex)) alt = attrs.get('alt', '') diff --git a/url_image_preview/resize_gif.py b/url_image_preview/resize_gif.py index 6170f6e..0b38d0a 100644 --- a/url_image_preview/resize_gif.py +++ b/url_image_preview/resize_gif.py @@ -17,11 +17,11 @@ def resize_gif(mem, path, resize_to): def analyse_image(mem): - """ + ''' Pre-process pass over the image to determine the mode (full or additive). Necessary as assessing single frames isn't reliable. Need to know the mode before processing all frames. - """ + ''' image = Image.open(BytesIO(mem)) results = { 'size': image.size, diff --git a/url_image_preview/url_image_preview.py b/url_image_preview/url_image_preview.py index 56e0eef..b76d099 100644 --- a/url_image_preview/url_image_preview.py +++ b/url_image_preview/url_image_preview.py @@ -20,11 +20,12 @@ import hashlib import binascii import logging import math +import shutil + +from functools import partial from urllib.parse import urlparse from urllib.parse import unquote from io import BytesIO -import shutil -from functools import partial from gi.repository import Gtk from gi.repository import Gdk @@ -34,9 +35,10 @@ from gi.repository import GdkPixbuf from gajim.common import app from gajim.common import helpers from gajim.common import configpaths - -from gajim import dialogs -from gajim import gtkgui_helpers +from gajim.gtkgui_helpers import add_css_to_widget +from gajim.gtk.dialogs import ErrorDialog +from gajim.gtk.filechoosers import FileSaveDialog +from gajim.gtk.util import get_cursor from gajim.plugins import GajimPlugin from gajim.plugins.helpers import log_calls @@ -46,9 +48,6 @@ from url_image_preview.http_functions import get_http_head from url_image_preview.http_functions import get_http_file from url_image_preview.config_dialog import UrlImagePreviewConfigDialog -from gajim.gtk.filechoosers import FileSaveDialog -from gajim.gtk.util import get_cursor - log = logging.getLogger('gajim.p.preview') @@ -56,9 +55,9 @@ ERROR_MSG = None try: from PIL import Image from url_image_preview.resize_gif import resize_gif -except: +except ImportError: log.debug('Pillow not available') - ERROR_MSG = 'Please install python-pillow' + ERROR_MSG = _('Please install python-pillow') try: if os.name == 'nt': @@ -70,8 +69,8 @@ try: from cryptography.hazmat.primitives.ciphers.modes import GCM decryption_available = True except Exception: - DEP_MSG = 'For preview of encrypted images, ' \ - 'please install python-cryptography!' + DEP_MSG = _('To enable previews for encrypted images, ' + 'please install python-cryptography!') log.exception('Error') log.info('Decryption/Encryption disabled due to errors') decryption_available = False @@ -152,7 +151,7 @@ class UrlImagePreviewPlugin(GajimPlugin): return -class Base(object): +class Base: def __init__(self, plugin, textview): self.plugin = plugin self.textview = textview @@ -167,11 +166,11 @@ class Base(object): self._create_path(self.directory) self._create_path(self.thumbpath) except Exception: - log.error("Error creating download and/or thumbnail folder!") + log.error('Error creating download and/or thumbnail folder!') raise def deinit_handlers(self): - # remove all register handlers on wigets, created by self.xml + # Remove all register handlers on wigets, created by self.xml # to prevent circular references among objects for i in list(self.handlers.keys()): if self.handlers[i].handler_is_connected(i): @@ -182,7 +181,7 @@ class Base(object): additional_data): if len(real_text.split(' ')) > 1: - # urlparse dont recognises spaces as URL delimiter + # urlparse doesn't recognise spaces as URL delimiter log.debug('Url with text will not be displayed: %s', real_text) return @@ -200,8 +199,10 @@ class Base(object): # Show URL, until image is loaded (if ever) ttt = buffer_.get_tag_table() repl_start = buffer_.create_mark(None, iter_, True) - buffer_.insert_with_tags(iter_, real_text, - *[(ttt.lookup(t) if isinstance(t, str) else t) for t in ["url"]]) + buffer_.insert_with_tags( + iter_, + real_text, + *[(ttt.lookup(t) if isinstance(t, str) else t) for t in ['url']]) repl_end = buffer_.create_mark(None, iter_, True) # Handle geo:-URIs @@ -221,11 +222,17 @@ class Base(object): if lon == '': return - filename = 'location_' + geo_provider + '_' \ - + location.replace(',', '_').replace('.', '-') + filename = 'location_%(provider)s_%(location)s' % { + 'provider': geo_provider, + 'location': location.replace(',', '_').replace('.', '-') + } newfilename = filename + ext - thumbfilename = filename + '_thumb_' \ - + str(self.plugin.config['PREVIEW_SIZE']) + ext + thumbfilename = '%(filename)s_thumb_%(size)s%(ext)s' % { + 'filename': filename, + 'size': str(self.plugin.config['PREVIEW_SIZE']), + 'ext': ext + } + filepath = os.path.join(self.directory, newfilename) thumbpath = os.path.join(self.thumbpath, thumbfilename) filepaths = [filepath, thumbpath] @@ -264,9 +271,17 @@ class Base(object): # so the filename should not exceed 90 name = name[:90] namehash = hashlib.sha1(real_text.encode('utf-8')).hexdigest() - newfilename = name + '_' + namehash + ext - thumbfilename = name + '_' + namehash + '_thumb_' \ - + str(self.plugin.config['PREVIEW_SIZE']) + ext + newfilename = '%(name)s_%(namehash)s%(ext)s' % { + 'name': name, + 'namehash': namehash, + 'ext': ext + } + thumbfilename = '%(name)s_%(namehash)s_thumb_%(size)s%(ext)s' % { + 'name': name, + 'namehash': namehash, + 'size': str(self.plugin.config['PREVIEW_SIZE']), + 'ext': ext + } filepath = os.path.join(self.directory, newfilename) thumbpath = os.path.join(self.thumbpath, thumbfilename) @@ -287,19 +302,19 @@ class Base(object): if len(key) == 32 and len(iv) == 12: encrypted = True - # file exists but thumbnail got deleted + # File exists but thumbnail got deleted if os.path.exists(filepath) and not os.path.exists(thumbpath): if urlparts.scheme == 'geo': - real_text = weburl - with open(filepath, 'rb') as f: - mem = f.read() + real_text = weburl + with open(filepath, 'rb') as file_to_preview: + mem = file_to_preview.read() app.thread_interface( self._save_thumbnail, [thumbpath, mem], self._update_img, [real_text, repl_start, repl_end, filepath, encrypted]) - # display thumbnail if already downloadeded - # (but only if file also exists) + # Display thumbnail if already downloaded + # (but only if file exists) elif os.path.exists(filepath) and os.path.exists(thumbpath): if urlparts.scheme == 'geo': real_text = weburl @@ -308,7 +323,7 @@ class Base(object): self._update_img, [real_text, repl_start, repl_end, filepath, encrypted]) - # or download file, calculate thumbnail and finally display it + # Or download file, calculate thumbnail, and finally display it else: if encrypted and not decryption_available: log.debug('Please install Crytography to decrypt pictures') @@ -327,7 +342,7 @@ class Base(object): def _accept_uri(self, urlparts, real_text, additional_data): try: - oob_url = additional_data["gajim"]["oob_url"] + oob_url = additional_data['gajim']['oob_url'] except (KeyError, AttributeError): oob_url = None @@ -336,7 +351,7 @@ class Base(object): return False # geo - if urlparts.scheme == "geo": + if urlparts.scheme == 'geo': if self.plugin.config['GEO_PREVIEW_PROVIDER'] == 'no_preview': log.info('geo: link preview is disabled') return False @@ -353,7 +368,7 @@ class Base(object): log.info('Incorrect oob data found') return False - log.info('Not supported URI scheme found: %s', real_text) + log.info('Unsupported URI scheme found: %s', real_text) return False def _save_thumbnail(self, thumbpath, mem): @@ -372,7 +387,7 @@ class Base(object): log.debug(error) # Try Pillow - image = Image.open(BytesIO(mem)).convert("RGBA") + image = Image.open(BytesIO(mem)).convert('RGBA') array = GLib.Bytes.new(image.tobytes()) width, height = image.size pixbuf = GdkPixbuf.Pixbuf.new_from_bytes( @@ -430,30 +445,20 @@ class Base(object): @staticmethod def _write_file(path, data): - log.info("Writing '%s' of size %d...", path, len(data)) + log.info('Writing \'%s\' of size %d...', path, len(data)) try: - with open(path, "wb") as output_file: + with open(path, 'wb') as output_file: output_file.write(data) output_file.closed except Exception as e: - log.error("Failed to write file '%s'!", path) + log.error('Failed to write file \'%s\'!', path) raise def _get_at_end(self): - try: - # Gajim 1.0.0 - return self.textview.at_the_end() - except AttributeError: - # Gajim 1.0.1 - return self.textview.autoscroll + return self.textview.autoscroll def _scroll_to_end(self): - try: - # Gajim 1.0.0 - self.textview.scroll_to_end_iter() - except AttributeError: - # Gajim 1.0.1 - self.textview.scroll_to_end() + self.textview.scroll_to_end() def _update_img(self, pixbuf, url, repl_start, repl_end, filepath, encrypted): @@ -473,7 +478,7 @@ class Base(object): buffer_ = repl_start.get_buffer() iter_ = buffer_.get_iter_at_mark(repl_start) - buffer_.insert(iter_, "\n") + buffer_.insert(iter_, '\n') anchor = buffer_.create_child_anchor(iter_) anchor.plaintext = url @@ -491,9 +496,9 @@ class Base(object): if at_end: self._scroll_to_end() except Exception as ex: - log.exception("Exception while loading %s: %s", url, ex) + log.exception('Exception while loading %s: %s', url, ex) return False - # add to mainloop --> make call threadsafe + # Add to mainloop --> make call threadsafe GLib.idle_add(add_to_textview) def _create_clickable_image(self, pixbuf, url): @@ -505,7 +510,7 @@ class Base(object): css = '''#Preview { box-shadow: 0px 0px 3px 0px alpha(@theme_text_color, 0.2); margin: 5px 10px 5px 10px; }''' - gtkgui_helpers.add_css_to_widget(image, css) + add_css_to_widget(image, css) image.set_name('Preview') event_box = Gtk.EventBox() @@ -520,19 +525,19 @@ class Base(object): file_mime, file_size = tuple_arg # Check if mime type is acceptable if not file_mime or not file_size: - log.info("Failed to load HEAD Request for URL: '%s' " - "mime: %s, size: %s", url, file_mime, file_size) + log.info('Failed to load HEAD Request for URL: \'%s\' ' + 'mime: %s, size: %s', url, file_mime, file_size) # URL is already displayed return if file_mime.lower() not in ACCEPTED_MIME_TYPES: - log.info("Not accepted mime type '%s' for URL: '%s'", + log.info('Not accepted mime type \'%s\' for URL: \'%s\'', file_mime.lower(), url) # URL is already displayed return # Check if file size is acceptable max_size = int(self.plugin.config['MAX_FILE_SIZE']) if file_size > max_size or file_size == 0: - log.info("File size (%s) too big or unknown (zero) for URL: '%s'", + log.info('File size (%s) too big or unknown (zero) for URL: \'%s\'', file_size, url) # URL is already displayed return @@ -572,17 +577,17 @@ class Base(object): ' (see error log for more information)')) log.error(str(e)) - # Create thumbnail, write it to harddisk and return it + # Create thumbnail, write to disk and return it return self._save_thumbnail(thumbpath, mem) def _create_path(self, folder): if os.path.exists(folder): return - log.debug("creating folder '%s'" % folder) + log.debug('Creating folder \'%s\'' % folder) os.mkdir(folder, 0o700) def _aes_decrypt_fast(self, key, iv, payload): - # Use AES128 GCM with the given key and iv to decrypt the payload. + # Use AES128 GCM with the given key and iv to decrypt the payload if os.name == 'nt': be = backend else: @@ -612,7 +617,7 @@ class Base(object): 'open_file_in_browser_menuitem') extras_separator = xml.get_object('extras_separator') - if data["encrypted"]: + if data['encrypted']: open_link_in_browser_menuitem.hide() if app.config.get('autodetect_browser_mailer') \ or app.config.get('custombrowser') == '': @@ -641,25 +646,26 @@ class Base(object): return menu def _on_open_menuitem_activate(self, menu, data): - filepath = data["filepath"] - original_filename = data["original_filename"] - url = data["url"] + filepath = data['filepath'] + original_filename = data['original_filename'] + url = data['url'] if original_filename.startswith('location_'): helpers.open_uri(url) return helpers.launch_file_manager(filepath) def _on_save_as_menuitem_activate(self, menu, data): - filepath = data["filepath"] - original_filename = data["original_filename"] + filepath = data['filepath'] + original_filename = data['original_filename'] def on_ok(target_path): dirname = os.path.dirname(target_path) if not os.access(dirname, os.W_OK): - dialogs.ErrorDialog( - _('Directory "%s" is not writable') % dirname, - _('You do not have permission to ' - 'create files in this directory.')) + ErrorDialog( + _('Directory \'%s\' is not writable') % dirname, + _('You do not have the proper permissions to ' + 'create files in this directory.'), + transient_for=app.app.get_active_window()) return shutil.copy(filepath, target_path) @@ -672,29 +678,29 @@ class Base(object): helpers.launch_file_manager(self.directory) def _on_copy_link_location_menuitem_activate(self, menu, data): - url = data["url"] + url = data['url'] clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) clipboard.set_text(url, -1) clipboard.store() def _on_open_link_in_browser_menuitem_activate(self, menu, data): - url = data["url"] - if data["encrypted"]: + url = data['url'] + if data['encrypted']: self._on_open_menuitem_activate(self, data) else: helpers.open_uri(url) def _on_open_file_in_browser_menuitem_activate(self, menu, data): - if os.name == "nt": - filepath = "file://" + os.path.abspath(data["filepath"]) + if os.name == 'nt': + filepath = 'file://' + os.path.abspath(data['filepath']) else: - filepath = "file://" + data["filepath"] + filepath = 'file://' + data['filepath'] if app.config.get('autodetect_browser_mailer') \ or app.config.get('custombrowser') == '': - dialogs.ErrorDialog( + ErrorDialog( _('Cannot open downloaded file in browser'), _('You have to set a custom browser executable ' - 'in your gajim settings for this to work.'), + 'in Gajim\'s Preferences for this to work.'), transient_for=app.app.get_active_window()) return command = app.config.get('custombrowser') @@ -706,17 +712,17 @@ class Base(object): def _on_button_press_event(self, eb, event, filepath, original_filename, url, encrypted): - data = {"filepath": filepath, - "original_filename": original_filename, - "url": url, - "encrypted": encrypted} - # left click + data = {'filepath': filepath, + 'original_filename': original_filename, + 'url': url, + 'encrypted': encrypted} + # Left click if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 1: - method = getattr(self, "_on_" + method = getattr(self, '_on_' + self.plugin.config['LEFTCLICK_ACTION'] - + "_activate") + + '_activate') method(event, data) - # right klick + # Right klick elif event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3: menu = self.make_rightclick_menu(event, data) # menu.attach_to_widget(self.tv, None) @@ -726,9 +732,9 @@ class Base(object): @staticmethod def _raise_error_dialog(pritext, sectext): # Used by methods that run in a different thread - dialogs.ErrorDialog(pritext, - sectext, - transient_for=app.app.get_active_window()) + ErrorDialog(pritext, + sectext, + transient_for=app.app.get_active_window()) def disconnect_from_chat_control(self): pass