Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
mijorus committed Jan 4, 2024
1 parent a88e20a commit 90b1c95
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 34 deletions.
3 changes: 3 additions & 0 deletions data/it.mijorus.collector.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
<schema id="it.mijorus.collector" path="/it/mijorus/collector/">
<key name="clear-on-drag" type="b">
<default>true</default>
</key>
<key name="google-images-support" type="b">
<default>true</default>
</key>
</schema>
</schemalist>
57 changes: 49 additions & 8 deletions python3-requirements.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,55 @@
{
"name": "python3-pillow",
"name": "python3-requirements",
"buildsystem": "simple",
"build-commands": [
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"pillow==10.2.0\" --no-build-isolation"
],
"sources": [
"build-commands": [],
"modules": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/f8/3e/32cbd0129a28686621434cbf17bb64bf1458bfb838f1f668262fefce145c/pillow-10.2.0.tar.gz",
"sha256": "e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"
"name": "python3-pillow",
"buildsystem": "simple",
"build-commands": [
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"pillow==10.2.0\" --no-build-isolation"
],
"sources": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/f8/3e/32cbd0129a28686621434cbf17bb64bf1458bfb838f1f668262fefce145c/pillow-10.2.0.tar.gz",
"sha256": "e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"
}
]
},
{
"name": "python3-requests",
"buildsystem": "simple",
"build-commands": [
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"requests\" --no-build-isolation"
],
"sources": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/64/62/428ef076be88fa93716b576e4a01f919d25968913e817077a386fcbe4f42/certifi-2023.11.17-py3-none-any.whl",
"sha256": "e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz",
"sha256": "f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl",
"sha256": "c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl",
"sha256": "58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/96/94/c31f58c7a7f470d5665935262ebd7455c7e4c7782eb525658d3dbf4b9403/urllib3-2.1.0-py3-none-any.whl",
"sha256": "55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"
}
]
}
]
}
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pillow==10.2.0
pillow==10.2.0
requests
92 changes: 70 additions & 22 deletions src/lib/DroppedItem.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import os

import logging
from PIL import Image
from gi.repository import Gtk, Adw, Gio, GLib, GObject

from .utils import get_giofile_content_type, pillow_crop_center, get_file_hash
from .utils import get_giofile_content_type, \
pillow_crop_center, get_file_hash, \
link_is_image, download_image, get_safe_path

class DroppedItemNotSupportedException(Exception):
def __init__(self, item, *args: object) -> None:
super().__init__(*args)
self.item = item

class DroppedItem():

DROPS_DIR = f'{GLib.get_user_cache_dir()}/drops'

def __init__(self, item) -> None:
self.target_path = None
self.display_value = ''
self.preview_image = 'paper-symbolic'
self.gfile = None
self.size = 0
self.async_load = False

MAX_PREVIEW_SIZE_MB = 50

Expand All @@ -29,41 +35,83 @@ def __init__(self, item) -> None:

content_type = get_giofile_content_type(item)
if content_type in ['image/png', 'image/jpg', 'image/jpeg'] and self.size < (MAX_PREVIEW_SIZE_MB * (1024 * 1024)):
filehash = get_file_hash(item)
preview_path = f'{ GLib.get_user_cache_dir()}/drops/{filehash}.{content_type.split("/")[1]}'
image = Image.open(self.target_path)
image.thumbnail((200, 200))
image = pillow_crop_center(image, min(image.size))
image.save(preview_path)

self.preview_image = Gtk.Image(
file=preview_path,
overflow=Gtk.Overflow.HIDDEN,
css_classes=['dropped-item-thumb'],
height_request=70,
width_request=70,
)
self.generate_preview_for_image()
else:
self.preview_image = info.get_icon()
elif isinstance(item, str):
base_filename = 'collected_text_'
text_string = item

i = 1
while os.path.exists(GLib.get_user_cache_dir() + f'/drops/{base_filename}{i}.txt'):
i += 1
if text_string.startswith('http://') or text_string.startswith('https://'):
self.async_load = True

self.target_path = GLib.get_user_cache_dir() + f'/drops/{base_filename}{i}.txt'
self.target_path = get_safe_path(f'{self.DROPS_DIR}/{base_filename}', 'txt')
with open(self.target_path, 'w+') as f:
f.write(text_string)

self.gfile = Gio.File.new_for_path(self.target_path)
self.size = os.stat(self.target_path).st_size
self.size = len(text_string)
self.preview_image = 'font-x-generic-symbolic'

self.display_value = text_string[:25]
if len(item) > 26:
self.display_value = self.display_value + '...'

else:
raise DroppedItemNotSupportedException(item)
raise DroppedItemNotSupportedException(item)

def complete_load(self):
if not self.async_load:
return

text_content = ''
with open(self.gfile.get_path(), 'r') as f:
text_content = f.read()

try:
is_image = link_is_image(text_content)
except Exception as e:
logging.warn(e)
return

if not is_image:
return

try:
data, extension, filename = download_image(text_content)
except Exception as e:
logging.warn(e)
return

self.target_path = get_safe_path(f'{self.DROPS_DIR}/{filename}', extension)

with open(self.target_path, 'wb') as f:
f.write(data)

self.gfile = Gio.File.new_for_path(self.target_path)

self.generate_preview_for_image()
self.async_load = False

def generate_preview_for_image(self):
logging.debug(f'Generating preview image for: {self.target_path}')

extension = os.path.splitext(self.target_path)[1]

filehash = get_file_hash(self.gfile)
preview_path = f'{self.DROPS_DIR}/__{filehash}.{extension}'

image = self.crop_image(self.target_path)
image.save(preview_path)

self.preview_image = Gio.File.new_for_path(preview_path)



def crop_image(self, image_path):
logging.debug(f'Cropping image: {image_path}')

image = Image.open(image_path)
image.thumbnail((200, 200))
image = pillow_crop_center(image, min(image.size))
return image
43 changes: 42 additions & 1 deletion src/lib/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import os
import hashlib
import logging
import requests
import re
from gi.repository import Gtk, Adw, Gio, Gdk, GObject, GLib


Expand All @@ -17,4 +21,41 @@ def get_file_hash(file: Gio.File, alg='md5') -> str:
if alg == 'md5':
return hashlib.md5(f.read()).hexdigest()
elif alg == 'sha1':
return hashlib.sha1(f.read()).hexdigest()
return hashlib.sha1(f.read()).hexdigest()

def link_is_image(link):
logging.info(f'Testing link headers for: {link}')

link = link.strip()
image_formats = ["image/png", "image/jpeg", "image/jpg"]
r = requests.head(link)

is_image = r.headers["content-type"] in image_formats

if is_image:
logging.info(f'Link appears to be an image')

return is_image


def download_image(link: str):
logging.log(f'Downloading image from url: {link}')
r = requests.get(link.strip())

extension = r.headers["content-type"].split('/')[1]
filename = link.split('/')[-1]

if r.headers.get('content-disposition', None):
d = r.headers['content-disposition']
filename = re.findall("filename=(.+)", d)[0]


return (r.content(), extension, filename)


def get_safe_path(p, ext):
i = 1
while os.path.exists(f'{p}{i}.{ext}'):
i += 1

return f'{p}{i}.{ext}'
13 changes: 11 additions & 2 deletions src/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,14 @@ def on_drop_event(self, widget, value, x, y):
new_image = Gtk.Image(icon_name=dropped_item.preview_image, pixel_size=70)
elif isinstance(dropped_item.preview_image, Gio.Icon):
new_image = Gtk.Image(gicon=dropped_item.preview_image, pixel_size=70)
else:
new_image = dropped_item.preview_image
elif isinstance(dropped_item.preview_image, Gio.File):
new_image = Gtk.Image(
file=dropped_item.preview_image.get_path(),
overflow=Gtk.Overflow.HIDDEN,
css_classes=['dropped-item-thumb'],
height_request=70,
width_request=70,
)

new_image.set_tooltip_text(dropped_item.display_value)

Expand All @@ -243,6 +249,9 @@ def on_drop_event(self, widget, value, x, y):

self.icon_carousel.scroll_to(new_image, True)
return True

def on_drop_event_complete(self):
pass

def on_drop_enter(self, widget, x, y):
if not self.is_dragging_away:
Expand Down

0 comments on commit 90b1c95

Please sign in to comment.