Commit 25bf2d4e authored by Philipp Hörist's avatar Philipp Hörist

[preview] Rewrite plugin

parent ba0b1129
......@@ -21,7 +21,6 @@ from gi.repository import GObject
from gi.repository import Gtk
from gajim.gtk.settings import SettingsDialog
from gajim.gtk.settings import GenericSetting
from gajim.gtk.settings import SpinSetting
from gajim.gtk.const import Setting
from gajim.gtk.const import SettingKind
......@@ -40,16 +39,11 @@ class UrlImagePreviewConfigDialog(SettingsDialog):
('10485760', '10 MiB')]
actions = [
('open_menuitem', _('Open')),
('save_as_menuitem', _('Save as')),
('open_folder_menuitem', _('Open Folder')),
('copy_link_location_menuitem', _('Copy Link Location')),
('open_link_in_browser_menuitem', _('Open Link in Browser'))]
geo_providers = [
('no_preview', _('No map preview')),
('Google', _('Google Maps')),
('OSM', _('OpenStreetMap'))]
('open', _('Open')),
('save_as', _('Save as')),
('open_folder', _('Open Folder')),
('copy_link_location', _('Copy Link Location')),
('open_link_in_browser', _('Open Link in Browser'))]
self.plugin = plugin
settings = [
......@@ -77,12 +71,6 @@ class UrlImagePreviewConfigDialog(SettingsDialog):
desc=_('Action when left clicking a preview'),
props={'combo_items': actions}),
Setting(SettingKind.COMBO, _('Map Service'),
SettingType.VALUE, self.plugin.config['GEO_PREVIEW_PROVIDER'],
callback=self.on_setting, data='GEO_PREVIEW_PROVIDER',
desc=_('Service used for map previews'),
props={'combo_items': geo_providers}),
Setting(SettingKind.SWITCH, _('HTTPS Verification'),
SettingType.VALUE, self.plugin.config['VERIFY'],
desc=_('Whether to check for a valid certificate'),
......@@ -91,8 +79,8 @@ class UrlImagePreviewConfigDialog(SettingsDialog):
SettingsDialog.__init__(self, parent, _('UrlImagePreview Configuration'),
Gtk.DialogFlags.MODAL, settings, None,
extend=[
('PreviewSizeSpinSetting', SizeSpinSetting)])
extend=[('PreviewSizeSpinSetting',
SizeSpinSetting)])
def on_setting(self, value, data):
self.plugin.config[data] = value
......
......@@ -5,7 +5,7 @@
<object class="GtkMenu" id="context_menu">
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="open_menuitem">
<object class="GtkMenuItem" id="open">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Open</property>
......@@ -13,7 +13,7 @@
</object>
</child>
<child>
<object class="GtkMenuItem" id="save_as_menuitem">
<object class="GtkMenuItem" id="save_as">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Save as</property>
......@@ -21,7 +21,7 @@
</object>
</child>
<child>
<object class="GtkMenuItem" id="open_folder_menuitem">
<object class="GtkMenuItem" id="open_folder">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Open _Folder</property>
......@@ -35,7 +35,7 @@
</object>
</child>
<child>
<object class="GtkMenuItem" id="copy_link_location_menuitem">
<object class="GtkMenuItem" id="copy_link_location">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Copy Link</property>
......@@ -43,7 +43,7 @@
</object>
</child>
<child>
<object class="GtkMenuItem" id="open_link_in_browser_menuitem">
<object class="GtkMenuItem" id="open_link_in_browser">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Open Link in _Browser</property>
......
# -*- coding: utf-8 -*-
##
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
##
## Gajim is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
import urllib.request as urllib2
import socket
import ssl
import logging
import os
from gajim.common import app
from gajim.plugins.plugins_i18n import _
if os.name == 'nt':
import certifi
log = logging.getLogger('gajim.p.preview.http_functions')
def get_http_head(account, url, verify):
return _get_http_head_direct(url, verify)
def get_http_file(account, attrs):
return _get_http_direct(attrs)
def _get_http_head_direct(url, verify):
log.info('Head request direct for URL: %s', url)
try:
req = urllib2.Request(url)
req.get_method = lambda: 'HEAD'
req.add_header('User-Agent', 'Gajim %s' % app.version)
if not verify:
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
log.warning('CERT Verification disabled')
file_ = urllib2.urlopen(req, timeout=30, context=context)
else:
if os.name == 'nt':
file_ = urllib2.urlopen(req, cafile=certifi.where())
else:
file_ = urllib2.urlopen(req)
except Exception as ex:
log.debug('Error', exc_info=True)
return ('', 0)
ctype = file_.headers['Content-Type']
clen = file_.headers['Content-Length']
try:
clen = int(clen)
except (TypeError, ValueError):
pass
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:
max_size = attrs['max_size']
try:
req = urllib2.Request(attrs['src'])
req.add_header('User-Agent', 'Gajim ' + app.version)
if not attrs['verify']:
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
log.warning('CERT Verification disabled')
file_ = urllib2.urlopen(req, timeout=30, context=context)
else:
if os.name == 'nt':
file_ = urllib2.urlopen(req, cafile=certifi.where())
else:
file_ = urllib2.urlopen(req)
except Exception as ex:
log.debug('Error', exc_info=True)
pixbuf = None
alt = attrs.get('alt', 'Broken image')
else:
while True:
try:
temp = file_.read(100)
except socket.timeout as ex:
log.debug('Timeout loading image %s', attrs['src'] + str(ex))
alt = attrs.get('alt', '')
if alt:
alt += '\n'
alt += _('Timeout loading image')
break
if temp:
mem += temp
else:
break
if len(mem) > max_size:
alt = attrs.get('alt', '')
if alt:
alt += '\n'
alt += _('Image is too big')
break
return (mem, alt)
from io import BytesIO
from PIL import Image
def resize_gif(mem, path, resize_to):
frames, result = extract_and_resize_frames(mem, resize_to)
if len(frames) == 1:
frames[0].save(path, optimize=True)
else:
frames[0].save(path,
optimize=True,
save_all=True,
append_images=frames[1:],
duration=result['duration'],
loop=1000)
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,
'mode': 'full',
'duration': image.info.get('duration', 0)
}
try:
while True:
if image.tile:
tile = image.tile[0]
update_region = tile[1]
update_region_dimensions = update_region[2:]
if update_region_dimensions != image.size:
results['mode'] = 'partial'
break
image.seek(image.tell() + 1)
except EOFError:
pass
return results
def extract_and_resize_frames(mem, resize_to):
result = analyse_image(mem)
image = Image.open(BytesIO(mem))
i = 0
palette = image.getpalette()
last_frame = image.convert('RGBA')
frames = []
try:
while True:
'''
If the GIF uses local colour tables,
each frame will have its own palette.
If not, we need to apply the global palette to the new frame.
'''
if not image.getpalette():
image.putpalette(palette)
new_frame = Image.new('RGBA', image.size)
'''
Is this file a "partial"-mode GIF where frames update a region
of a different size to the entire image?
If so, we need to construct the new frame by
pasting it on top of the preceding frames.
'''
if result['mode'] == 'partial':
new_frame.paste(last_frame)
new_frame.paste(image, (0, 0), image.convert('RGBA'))
# This method preservs aspect ratio
new_frame.thumbnail(resize_to, Image.ANTIALIAS)
frames.append(new_frame)
i += 1
last_frame = new_frame
image.seek(image.tell() + 1)
except EOFError:
pass
return frames, result
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment