From 3d2ef3fee596feea30efdf8936c5f4632d7010e7 Mon Sep 17 00:00:00 2001 From: Quentin Duchemin Date: Sat, 6 Apr 2019 19:20:32 +0200 Subject: [PATCH] Add screenshot uploading/saving --- .config/i3/config.laptop | 5 +- .config/i3/scripts/config.py.example | 9 +++ .config/i3/scripts/screenshot.py | 86 +++++++++++++++++++++++++--- README.md | 21 +++++-- 4 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 .config/i3/scripts/config.py.example diff --git a/.config/i3/config.laptop b/.config/i3/config.laptop index ee6f15e..d941eff 100644 --- a/.config/i3/config.laptop +++ b/.config/i3/config.laptop @@ -215,6 +215,7 @@ exec xrandr --dpi 160 exec thunderbird # Screenshot -# Selection, highlight, jpeg 9/10 quality, 5px selection border, hide cursor # Save to file -bindsym Print exec ~/.config/i3/scripts/screenshot.sh \ No newline at end of file +bindsym $mod+Print exec ~/.config/i3/scripts/screenshot.py -s +# Upload to Lychee +bindsym Print exec ~/.config/i3/scripts/screenshot.py -u diff --git a/.config/i3/scripts/config.py.example b/.config/i3/scripts/config.py.example new file mode 100644 index 0000000..871ce4c --- /dev/null +++ b/.config/i3/scripts/config.py.example @@ -0,0 +1,9 @@ +SCREENSHOT_CONFIG = { + 'base_url': '', + 'album_name': 'Screenshots', + 'user': '', + 'password': '', + 'default_path': '~/img/screenshots', + 'save_format': 'png', + 'upload_format': 'jpg' +} \ No newline at end of file diff --git a/.config/i3/scripts/screenshot.py b/.config/i3/scripts/screenshot.py index 90ce27d..7deac2e 100755 --- a/.config/i3/scripts/screenshot.py +++ b/.config/i3/scripts/screenshot.py @@ -6,6 +6,11 @@ import subprocess import pathlib import time import os +import requests +import uuid +import json +import pyperclip +from config import SCREENSHOT_CONFIG def usage(path): print('A tiny wrapper around maim to take screenshots and save or upload them (+ copy link in clipboard).\n') @@ -21,7 +26,7 @@ def usage(path): print('\t-p --path\tPath where to save locally [default: ' + path +'].') def read_args(argv): - path = '~/img/screenshots' + path = SCREENSHOT_CONFIG['default_path'] try: opts, _ = getopt.getopt(argv, "husp:", ["help", "upload", "save", "path="]) @@ -45,6 +50,7 @@ def read_args(argv): path = os.path.expanduser(path) return (upload, save, path) + def screenshot(img_format): proc = subprocess.run(['maim', '-sl', '--color=0.6,0.4,0.2,0.2', '-f', img_format, '-u', '-m', '9', '-b', '5'], capture_output=True, check=True) return proc.stdout @@ -59,28 +65,92 @@ def save_img(path, img, img_format): # Write image to appropriate file. # We get binary from maim, so open in binary mode filepath = os.path.join(path, date + '.' + img_format) - print(filepath) with open(filepath, 'wb') as f: f.write(img) - -#def upload(binary_img): + return filepath + +def login(): + auth = {'user': SCREENSHOT_CONFIG['user'], 'password': SCREENSHOT_CONFIG['password']} + + # Get initial cookies + s = requests.Session() + r = s.get(SCREENSHOT_CONFIG['base_url']) + + # Set the CSRF token for the whole session + # Also replace the ending %3D (=), they stop header from being valid (why ?) + s.headers.update({'X-XSRF-TOKEN': r.cookies['XSRF-TOKEN'].replace('%3D', '')}) + + r = s.post(SCREENSHOT_CONFIG['base_url'] + '/api/Session::login', data=auth) + + if r.status_code != 200: + raise RuntimeError("Cannot login to Lychee!") + + return s + +def get_album_id(session, name): + r = session.post(SCREENSHOT_CONFIG['base_url'] + '/api/Albums::get') + if r.headers['content-type'] != 'application/json': + raise RuntimeError("API call didn't return a JSON object!") + + albums = r.json() + for album in albums['albums']: + if album['title'] == name: + return album['id'] + raise RuntimeError("No album " + name + " found!") + +def upload_image(session, album_id, binary_img, img_format): + img_mime = 'image/' + if img_format == 'jpg': + img_mime += 'jpeg' + elif img_format == 'png': + img_mime += 'png' + else: + raise ValueError("Invalid image format {0}!".format(img_format)) + data = {'albumID': '15510292135273'} + files = {'0': (str(uuid.uuid4()) + "." + img_format, binary_img, img_mime)} + r = requests.Request('POST', SCREENSHOT_CONFIG['base_url'] + '/api/Photo::add', data=data, files=files) + dump = session.prepare_request(r) + r = session.post(SCREENSHOT_CONFIG['base_url'] + '/api/Photo::add', data=data, files=files) + + if "Error" in r.text: + raise RuntimeError("Unknown error while uploading picture") + + return r.text + +def get_image_direct_link(session, album_id, image_id): + data = {'albumID': album_id, 'photoID': image_id} + r = session.post(SCREENSHOT_CONFIG['base_url'] + "/api/Photo::get", data=data) + if r.text == "false": + raise ValueError("Image does not exist") + if r.headers['content-type'] != 'application/json': + raise RuntimeError("API call didn't return a JSON object") + + image_info = r.json() + return SCREENSHOT_CONFIG['base_url'] + '/' + image_info['url'] def main(argv): upload, save, path = read_args(argv) # If we upload the file, set format to jpg, even if we save the screenshot after - img_format = 'jpg' if upload else 'png' - + img_format = SCREENSHOT_CONFIG['upload_format'] if upload else SCREENSHOT_CONFIG['save_format'] if upload or save: try: img = screenshot(img_format) if save: - save_img(path, img, img_format) + final_path = save_img(path, img, img_format) + pyperclip.copy(final_path) + subprocess.run(['notify-send', '-u', 'low', '-i', 'Info', '-a', 'Screenchost', 'Screenshot taken', 'Path copied to clipboard!']) + if upload: + s = login() + album_id = get_album_id(s, SCREENSHOT_CONFIG['album_name']) + image_id = upload_image(s, album_id, img, img_format) + image_link = get_image_direct_link(s, album_id, image_id) + pyperclip.copy(image_link) + subprocess.run(['notify-send', '-u', 'low', '-i', 'Info', '-a', 'Screenchost', 'Screenshot uploaded', 'Link copied to clipboard!']) except subprocess.CalledProcessError as e: print("Screenshot taking has failed {0}".format(e)) if __name__ == "__main__": main(sys.argv[1:]) - \ No newline at end of file diff --git a/README.md b/README.md index fb5f905..f94d846 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,11 @@ - [Contents](#contents) - [Requirements](#requirements) - [Usage](#usage) - - [Bare repository trick](#bare-repository-trick) - - [Divergence betwteen laptop and desktop](#divergence-betwteen-laptop-and-desktop) - - [Configuration outside XDG_CONFIG_HOME, e.g. /etc](#configuration-outside-xdg_config_home-eg-etc) - - [Git Hook](#git-hook) + - [Bare repository trick](#bare-repository-trick) + - [Divergence betwteen laptop and desktop](#divergence-betwteen-laptop-and-desktop) + - [Configuration outside XDG_CONFIG_HOME, e.g. /etc](#configuration-outside-xdgconfighome-eg-etc) + - [Git Hook](#git-hook) + - [Screenshots](#screenshots) @@ -52,6 +53,7 @@ switcher * Non-Mixer, a mixer. * Ardour, a DAW. * Random things (GTK3+ theme, Redshift, taskwarrior...) +* A custom script to take screenshots and save them or upload them * Screenshots (area or windows), with saving or uploading to a Lychee server ([custom script here](.config/i3/scripts/screenshot.py)) Here is a screenshot of the rendition i3/polybar with this setup (and yeah, I use nano, sorry to disappoint) @@ -122,3 +124,14 @@ And then `chmod +x ~/.cfg/hooks/{post-merge,pre-push}`. When you pull, changes are applied automagically. Note this is a quick and quite dirty solution. + +### Screenshots + +Todo : move it on another repo + +A small script inside `~/.config/i3/scripts/` uses `maim` to take screenshots. +Then, a flag can either save locally the image or upload it (now, only on a Lychee server using the API). + +The `~/.config/i3/scripts/config.py` contains the default configuration. + +In either case, the link or path is copied to clipboard and a notification is triggered. \ No newline at end of file