Add screenshot uploading/saving
parent
fb2d44ea07
commit
3d2ef3fee5
|
@ -215,6 +215,7 @@ exec xrandr --dpi 160
|
||||||
exec thunderbird
|
exec thunderbird
|
||||||
|
|
||||||
# Screenshot
|
# Screenshot
|
||||||
# Selection, highlight, jpeg 9/10 quality, 5px selection border, hide cursor
|
|
||||||
# Save to file
|
# Save to file
|
||||||
bindsym Print exec ~/.config/i3/scripts/screenshot.sh
|
bindsym $mod+Print exec ~/.config/i3/scripts/screenshot.py -s
|
||||||
|
# Upload to Lychee
|
||||||
|
bindsym Print exec ~/.config/i3/scripts/screenshot.py -u
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
SCREENSHOT_CONFIG = {
|
||||||
|
'base_url': '',
|
||||||
|
'album_name': 'Screenshots',
|
||||||
|
'user': '',
|
||||||
|
'password': '',
|
||||||
|
'default_path': '~/img/screenshots',
|
||||||
|
'save_format': 'png',
|
||||||
|
'upload_format': 'jpg'
|
||||||
|
}
|
|
@ -6,6 +6,11 @@ import subprocess
|
||||||
import pathlib
|
import pathlib
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
|
import requests
|
||||||
|
import uuid
|
||||||
|
import json
|
||||||
|
import pyperclip
|
||||||
|
from config import SCREENSHOT_CONFIG
|
||||||
|
|
||||||
def usage(path):
|
def usage(path):
|
||||||
print('A tiny wrapper around maim to take screenshots and save or upload them (+ copy link in clipboard).\n')
|
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 +'].')
|
print('\t-p --path\tPath where to save locally [default: ' + path +'].')
|
||||||
|
|
||||||
def read_args(argv):
|
def read_args(argv):
|
||||||
path = '~/img/screenshots'
|
path = SCREENSHOT_CONFIG['default_path']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
opts, _ = getopt.getopt(argv, "husp:", ["help", "upload", "save", "path="])
|
opts, _ = getopt.getopt(argv, "husp:", ["help", "upload", "save", "path="])
|
||||||
|
@ -45,6 +50,7 @@ def read_args(argv):
|
||||||
path = os.path.expanduser(path)
|
path = os.path.expanduser(path)
|
||||||
|
|
||||||
return (upload, save, path)
|
return (upload, save, path)
|
||||||
|
|
||||||
def screenshot(img_format):
|
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)
|
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
|
return proc.stdout
|
||||||
|
@ -59,28 +65,92 @@ def save_img(path, img, img_format):
|
||||||
# Write image to appropriate file.
|
# Write image to appropriate file.
|
||||||
# We get binary from maim, so open in binary mode
|
# We get binary from maim, so open in binary mode
|
||||||
filepath = os.path.join(path, date + '.' + img_format)
|
filepath = os.path.join(path, date + '.' + img_format)
|
||||||
print(filepath)
|
|
||||||
with open(filepath, 'wb') as f:
|
with open(filepath, 'wb') as f:
|
||||||
f.write(img)
|
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):
|
def main(argv):
|
||||||
upload, save, path = read_args(argv)
|
upload, save, path = read_args(argv)
|
||||||
|
|
||||||
# If we upload the file, set format to jpg, even if we save the screenshot after
|
# 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:
|
if upload or save:
|
||||||
try:
|
try:
|
||||||
img = screenshot(img_format)
|
img = screenshot(img_format)
|
||||||
if save:
|
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:
|
except subprocess.CalledProcessError as e:
|
||||||
print("Screenshot taking has failed {0}".format(e))
|
print("Screenshot taking has failed {0}".format(e))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
|
|
21
README.md
21
README.md
|
@ -6,10 +6,11 @@
|
||||||
- [Contents](#contents)
|
- [Contents](#contents)
|
||||||
- [Requirements](#requirements)
|
- [Requirements](#requirements)
|
||||||
- [Usage](#usage)
|
- [Usage](#usage)
|
||||||
- [Bare repository trick](#bare-repository-trick)
|
- [Bare repository trick](#bare-repository-trick)
|
||||||
- [Divergence betwteen laptop and desktop](#divergence-betwteen-laptop-and-desktop)
|
- [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)
|
- [Configuration outside XDG_CONFIG_HOME, e.g. /etc](#configuration-outside-xdgconfighome-eg-etc)
|
||||||
- [Git Hook](#git-hook)
|
- [Git Hook](#git-hook)
|
||||||
|
- [Screenshots](#screenshots)
|
||||||
|
|
||||||
<!-- /MarkdownTOC -->
|
<!-- /MarkdownTOC -->
|
||||||
|
|
||||||
|
@ -52,6 +53,7 @@ switcher
|
||||||
* Non-Mixer, a mixer.
|
* Non-Mixer, a mixer.
|
||||||
* Ardour, a DAW.
|
* Ardour, a DAW.
|
||||||
* Random things (GTK3+ theme, Redshift, taskwarrior...)
|
* 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))
|
* 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)
|
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.
|
When you pull, changes are applied automagically.
|
||||||
|
|
||||||
Note this is a quick and quite dirty solution.
|
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.
|
Loading…
Reference in New Issue