461 lines
15 KiB
Python
461 lines
15 KiB
Python
__author__ = "Lukas Mahler"
|
|
__copyright__ = "Copyright 2018-2021"
|
|
__version__ = "0.6.1"
|
|
__date__ = "23.05.2021"
|
|
__email__ = "lm@ankerlab.de"
|
|
__status__ = "Development"
|
|
|
|
|
|
try:
|
|
# Defaults
|
|
import os
|
|
import sys
|
|
import webbrowser
|
|
from io import BytesIO
|
|
from requests import head
|
|
from shutil import rmtree
|
|
from getpass import getpass
|
|
from tempfile import gettempdir
|
|
from socket import create_connection
|
|
from configparser import ConfigParser
|
|
from time import gmtime, strftime, sleep
|
|
|
|
# GUI
|
|
from PyQt5 import QtGui, QtWidgets, QtCore, Qt
|
|
from PyQt5.QtCore import QProcess, QPoint
|
|
from fillscreen import Ui_View
|
|
|
|
# Customs
|
|
import owncloud
|
|
import win32gui
|
|
import win32clipboard
|
|
import paramiko
|
|
from desktopmagic.screengrab_win32 import getRectAsImage
|
|
|
|
except ImportError as e:
|
|
print("ERROR: Missing Module |||", e)
|
|
sleep(10)
|
|
os.system("PAUSE>NUL")
|
|
sys.exit()
|
|
|
|
|
|
###################################################################################################
|
|
###################################################################################################
|
|
|
|
|
|
class Fillscreen(QtWidgets.QWidget, Ui_View):
|
|
|
|
def __init__(self, parent=None):
|
|
QtWidgets.QWidget.__init__(self, parent)
|
|
|
|
###############################################################################################
|
|
###############################################################################################
|
|
|
|
# Monkeycode for trying to block input for content under the transparent window.
|
|
|
|
# self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
|
|
# self.setBackgroundRole(QtGui.QPalette.Base)
|
|
# self.setAttribute(Qt.Qt.WA_NoSystemBackground, True)
|
|
self.setWindowFlags(
|
|
QtCore.Qt.WindowStaysOnTopHint |
|
|
QtCore.Qt.FramelessWindowHint |
|
|
QtCore.Qt.X11BypassWindowManagerHint
|
|
)
|
|
# self.istransparent = True
|
|
|
|
self.setupUi(self)
|
|
|
|
# self.setWindowOpacity(0.3)
|
|
# Qcolor = "rgba(255, 255, 255, 0)"
|
|
# self.setStyleSheet("background-color:{0}".format(Qcolor))
|
|
|
|
###############################################################################################
|
|
###############################################################################################
|
|
|
|
# Change Cursor to select tool on Startup
|
|
self.change_cursor()
|
|
self.begin = QtCore.QPoint()
|
|
self.end = QtCore.QPoint()
|
|
self.startingpoint = QtCore.QPoint()
|
|
self.endpoint = QtCore.QPoint()
|
|
|
|
def showFullscreen(self):
|
|
self.showMaximized()
|
|
|
|
# https://stackoverflow.com/a/44468898/5593051
|
|
def paintEvent(self, event):
|
|
qp = QtGui.QPainter(self)
|
|
# br = QtGui.QBrush(QtGui.QColor(100, 10, 10, 40))
|
|
qp.setPen(QtGui.QPen(QtCore.Qt.red, 3))
|
|
qp.drawRect(QtCore.QRect(self.begin, self.end))
|
|
|
|
def mousePressEvent(self, event):
|
|
if event.button() == QtCore.Qt.RightButton:
|
|
print("DEBUG: Exiting on Right Click Request")
|
|
app.quit()
|
|
else:
|
|
self.begin = event.pos()
|
|
self.end = event.pos()
|
|
# self.startingpoint = event.pos()
|
|
self.startingpoint = get_real_pos()
|
|
# print("Startpoint:",self.startingpoint)
|
|
self.update()
|
|
|
|
def mouseMoveEvent(self, event):
|
|
self.end = event.pos()
|
|
self.update()
|
|
|
|
def mouseReleaseEvent(self, event):
|
|
self.begin = event.pos()
|
|
self.end = event.pos()
|
|
self.endpoint = event.pos()
|
|
self.endpoint = get_real_pos()
|
|
# print("Endpoint:",self.endpoint)
|
|
self.update()
|
|
do_screen(self.startingpoint, self.endpoint)
|
|
app.quit()
|
|
|
|
@staticmethod
|
|
def change_cursor():
|
|
# http://doc.qt.io/archives/qt-4.8/qcursor.html
|
|
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.CrossCursor)
|
|
# QtGui.QApplication.restoreOverrideCursor()
|
|
|
|
|
|
###################################################################################################
|
|
###################################################################################################
|
|
|
|
def sslurl(url, ssl):
|
|
if ssl:
|
|
url = "https://" + url
|
|
else:
|
|
url = "http://" + url
|
|
|
|
return url
|
|
|
|
|
|
def read_ini(rootdir):
|
|
myini = {}
|
|
config = ConfigParser(inline_comment_prefixes=";")
|
|
try:
|
|
config.read_file(open(rootdir + 'settings.ini'))
|
|
except FileNotFoundError:
|
|
print("ERROR: Couldn't locate a 'settings.ini'")
|
|
print("--> Trying to create new blank one.")
|
|
# Creating new blank ini
|
|
with open(rootdir + 'settings.ini', 'w') as f:
|
|
txt = (
|
|
"# MAIN SETTINGS\n"
|
|
"[MAIN]\n"
|
|
"MODE = LOCAL ; MODE EITHER SFTP, NEXTCLOUD OR LOCAL\n"
|
|
"TIMEOUT = 5 ; TIMEOUT WHEN TESTING SFTP CONNECTION\n"
|
|
"SSL = TRUE ; USING HTTP (FALSE) OR HTTPS (TRUE) URLS\n"
|
|
"\n"
|
|
"# PROVIDE SFTP INFO BELOW\n"
|
|
"[SFTP]\n"
|
|
"URL = ; URL EITHER IS AN IP OR A FQDN\n"
|
|
"PORT = ; PORT IS THE SSH PORT\n"
|
|
"USR = ; YOUR SSH USER, THIS CAN BE BLANK TO GET ASKED INTERACTIVELY\n"
|
|
"PASS = ; YOUR SSH USERS PW, THIS CAN BE BLANK TO GET ASKED INTERACTIVELY\n"
|
|
"PTH = ; THE LOCAL SERVER PATH WHERE TO PUT THE SCREENSHOT\n"
|
|
"WHERE = ; THIS SHOULD BE THE FULL ONLINE URL\n"
|
|
"\n"
|
|
"# PROVIDE NEXTCLOUD INFO BELOW\n"
|
|
"[NEXTCLOUD]\n"
|
|
"URL = ; URL EITHER IS AN IP OR A FQDN OF A NEXTCLOUD SERVER\n"
|
|
"USR = ; YOUR NEXTCLOUD USER, THIS CAN BE BLANK TO GET ASKED INTERACTIVELY\n"
|
|
"PASS = ; YOUR NEXTCLOUD USERS PW, THIS CAN BE BLANK TO GET ASKED INTERACTIVELY\n"
|
|
"PTH = ; THE NEXTCLOUD FOLDER PATH WHERE TO PUT THE SCREENSHOT\n"
|
|
"\n"
|
|
"# IF MODE IS LOCAL YOU MAY CHANGE THE SETTINGS BELOW\n"
|
|
"[LOCAL]\n"
|
|
"WHERE = ; THIS SHOULD BE A FULL LOCAL PATH\n"
|
|
)
|
|
f.write(txt)
|
|
config.read_file(open(rootdir + 'settings.ini'))
|
|
|
|
# General Settings
|
|
timeout = int(config.get('MAIN', 'TIMEOUT'))
|
|
ssl = config.get('MAIN', 'SSL')
|
|
|
|
mode = config.get('MAIN', 'MODE')
|
|
if mode == "SFTP":
|
|
url = config.get(mode, 'URL')
|
|
port = int(config.get(mode, 'PORT'))
|
|
usr = config.get(mode, 'USR')
|
|
passw = config.get(mode, 'PASS')
|
|
pth = config.get(mode, 'PTH')
|
|
where = config.get(mode, 'WHERE')
|
|
myini.update(
|
|
MODE = mode,
|
|
URL = url,
|
|
PORT = port,
|
|
TIMEOUT = timeout,
|
|
USR = usr,
|
|
PASS = passw,
|
|
PTH = pth,
|
|
WHERE = sslurl(where, ssl)
|
|
)
|
|
elif mode == "NEXTCLOUD":
|
|
url = sslurl(config.get(mode, 'URL'), ssl)
|
|
usr = config.get(mode, 'USR')
|
|
passw = config.get(mode, 'PASS')
|
|
pth = config.get(mode, 'PTH')
|
|
myini.update(
|
|
MODE = mode,
|
|
URL = url,
|
|
USR = usr,
|
|
PASS = passw,
|
|
PTH = pth,
|
|
)
|
|
elif mode == "LOCAL":
|
|
where = config.get('LOCAL', 'WHERE')
|
|
myini.update(
|
|
MODE = mode,
|
|
WHERE = where
|
|
)
|
|
else:
|
|
print("ERROR: you seem to be having a corrupt or broken 'settings.ini' please recheck!")
|
|
|
|
return myini
|
|
|
|
|
|
def get_real_pos():
|
|
flags, hcursor, (x, y) = win32gui.GetCursorInfo()
|
|
return {"x": x, "y": y}
|
|
|
|
|
|
def calc_frame(startinspot, endspot):
|
|
# If dragging began from top
|
|
if startinspot['x'] < endspot['x']:
|
|
left = startinspot['x']
|
|
right = endspot['x']
|
|
top = startinspot['y']
|
|
bottom = endspot['y']
|
|
# If dragging began from bottom
|
|
else:
|
|
left = endspot['x']
|
|
right = startinspot['x']
|
|
top = endspot['y']
|
|
bottom = startinspot['y']
|
|
|
|
# print("DEBUG: Start:",left,"x",top)
|
|
# print("DEBUG: End :",right,"x",bottom)
|
|
|
|
if abs(left-right) <= 10 and abs(top-bottom) <= 10:
|
|
coordinates = None
|
|
else:
|
|
coordinates = (left, top, right+1, bottom+1)
|
|
|
|
return coordinates
|
|
|
|
|
|
def do_screen(start, end):
|
|
""" This Function somehow is the Brain now """
|
|
# QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId()).save('screenshot.jpg', 'jpg')
|
|
# Hide all windows to take a Screenshot of the underlying interface
|
|
for window in windows:
|
|
window.hide()
|
|
frame = calc_frame(start, end)
|
|
print("Took Snapshot on Coordinates:", frame)
|
|
# https://github.com/python-pillow/Pillow/issues/1547
|
|
# https://github.com/ludios/Desktopmagic/blob/master/desktopmagic/screengrab_win32.py
|
|
# im = ImageGrab.grab(bbox=frame)
|
|
|
|
if not frame:
|
|
print("ERROR: Screenshots below 5x5 px not allowed, exiting...")
|
|
sleep(5)
|
|
return
|
|
|
|
im = getRectAsImage(frame)
|
|
send_to_clipboard(im)
|
|
timestamp = strftime("%Y-%m-%d_%H-%M-%S")
|
|
|
|
tempdir = gettempdir()+"\\Screens"
|
|
# print("DEBUG:",tempdir)
|
|
if not os.path.exists(tempdir):
|
|
os.makedirs(tempdir)
|
|
tempsave = '{0}\\{1}.png'.format(tempdir, timestamp)
|
|
im.save(tempsave)
|
|
|
|
if settings['MODE'] == "SFTP":
|
|
upload_to_url_sftp(tempsave)
|
|
elif settings['MODE'] == "NEXTCLOUD":
|
|
upload_to_url_nextcloud(tempsave)
|
|
else:
|
|
save_to_local(tempsave)
|
|
|
|
clean_temp(tempdir)
|
|
|
|
|
|
# https://stackoverflow.com/questions/34322132/copy-image-to-clipboard-in-python3
|
|
def send_to_clipboard(im=None):
|
|
if im:
|
|
output = BytesIO()
|
|
im.convert("RGB").save(output, "BMP")
|
|
data = output.getvalue()[14:]
|
|
output.close()
|
|
win32clipboard.OpenClipboard()
|
|
win32clipboard.EmptyClipboard()
|
|
win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
|
|
win32clipboard.CloseClipboard()
|
|
print("Copied to Clipboard")
|
|
else:
|
|
win32clipboard.EmptyClipboard()
|
|
print("Cleared Clipboard")
|
|
|
|
|
|
# https://stackoverflow.com/questions/432385/sftp-in-python-platform-independent
|
|
def upload_to_url_sftp(tempsave):
|
|
|
|
# SFTP INFO
|
|
url = settings['URL']
|
|
port = settings['PORT']
|
|
timeout = settings['TIMEOUT']
|
|
usr = settings['USR']
|
|
passw = settings['PASS']
|
|
pth = settings['PTH'] + os.path.basename(tempsave)
|
|
where = settings['WHERE'] + os.path.basename(tempsave)
|
|
|
|
# Test Connection first or go into fallback
|
|
try:
|
|
s = create_connection((url, port), timeout)
|
|
s.close()
|
|
except Exception as e:
|
|
print("ERROR: Connection failed. |||", e)
|
|
print("\n --> Falling back to local mode")
|
|
save_to_local(tempsave, fallback=1)
|
|
return
|
|
|
|
# If not provided ask for Username & Password
|
|
if not usr:
|
|
usr = input("Please provide a username: ")
|
|
|
|
if not passw:
|
|
passw = getpass("Please provide the password for {0}: ".format(usr))
|
|
|
|
try:
|
|
transport = paramiko.Transport((url, port))
|
|
transport.connect(username=usr, password=passw)
|
|
except Exception as e:
|
|
print("ERROR: SSH2 negotiation or authentication failed. |||", e)
|
|
print("\n--> Falling back to local mode")
|
|
save_to_local(tempsave, fallback=1)
|
|
return
|
|
|
|
print("Starting to Upload...")
|
|
sftp = paramiko.SFTPClient.from_transport(transport)
|
|
sftp.put(tempsave, pth)
|
|
sftp.close()
|
|
transport.close()
|
|
|
|
print("Screen was Uploaded: URL: " + where)
|
|
open_url(where)
|
|
|
|
|
|
def upload_to_url_nextcloud(tempsave):
|
|
|
|
# NEXTCLOUD INFO
|
|
url = settings['URL']
|
|
usr = settings['USR']
|
|
passw = settings['PASS']
|
|
pth = settings['PTH'] + os.path.basename(tempsave)
|
|
|
|
# Test Connection first or go into fallback
|
|
r = head(url)
|
|
httpc = str(r.status_code)[0]
|
|
if httpc == "4" or httpc == "5":
|
|
print("ERROR: HTTP Statuscode for URL [{0}] is [{1}]".format(url, r.status_code))
|
|
print("\n--> Falling back to local mode")
|
|
save_to_local(tempsave, fallback=1)
|
|
return
|
|
|
|
# If not provided ask for Username & Password
|
|
if not usr:
|
|
usr = input("Please provide a username: ")
|
|
|
|
if not passw:
|
|
passw = getpass("Please provide the password for {0}: ".format(usr))
|
|
|
|
try:
|
|
nxt = owncloud.Client(url)
|
|
nxt.login(usr, passw)
|
|
except Exception as e:
|
|
print("ERROR: ", e)
|
|
print("\n--> Falling back to local mode")
|
|
save_to_local(tempsave, fallback=1)
|
|
return
|
|
|
|
# Had to monkeypatch the pyocclient libary here to stop AttributeErrors on 'share_file_with_link' calls
|
|
# (https://github.com/owncloud/pyocclient/issues/263)
|
|
|
|
print("Starting to Upload...")
|
|
nxt.put_file(pth, tempsave)
|
|
link_info = nxt.share_file_with_link(pth)
|
|
where = link_info.get_link()
|
|
|
|
print("Screen was Uploaded: URL: " + where)
|
|
open_url(where)
|
|
|
|
|
|
def save_to_local(tempsave, fallback=None):
|
|
|
|
if not fallback:
|
|
where = settings['WHERE']
|
|
else:
|
|
print(" -> Did fallback!")
|
|
config = ConfigParser()
|
|
config.read_file(open(root+'settings.ini'))
|
|
where = config.get('LOCAL', 'WHERE')
|
|
|
|
if not where:
|
|
print("No local Screens folder provided, creating one...")
|
|
where = os.path.dirname(os.path.abspath(__file__))+"\\Screens"
|
|
|
|
print("LOCAL MODE: Screenshot Folder:", where)
|
|
|
|
if not os.path.exists(where):
|
|
os.makedirs(where)
|
|
|
|
src = tempsave
|
|
dst = where + "\\" + os.path.basename(tempsave)
|
|
# print("Source:",src,"\nDestination:",dst)
|
|
os.rename(src, dst)
|
|
os.startfile(dst)
|
|
|
|
|
|
def open_url(where):
|
|
webbrowser.open(where, new=0, autoraise=True)
|
|
|
|
|
|
def clean_temp(tempdir):
|
|
if os.path.exists(tempdir):
|
|
print("Cleaning up the tempdir [", tempdir, "]...")
|
|
rmtree(tempdir, ignore_errors=True)
|
|
|
|
|
|
###################################################################################################
|
|
###################################################################################################
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
root = os.path.dirname(os.path.abspath(__file__))+"\\"
|
|
settings = read_ini(root)
|
|
if os.path.exists(root+"qt.conf"):
|
|
try:
|
|
os.remove(root+"qt.conf")
|
|
except Exception as e:
|
|
print("DEBUG:", e)
|
|
app = QtWidgets.QApplication(sys.argv)
|
|
windows = []
|
|
|
|
# https://stackoverflow.com/questions/51058236/create-qt-windows-based-on-number-of-list-items
|
|
for i in range(QtWidgets.QApplication.desktop().screenCount()):
|
|
topLeft = QtWidgets.QApplication.desktop().screenGeometry(i).topLeft()
|
|
window = Fillscreen()
|
|
window.move(topLeft)
|
|
window.showFullscreen()
|
|
windows.append(window)
|
|
app.exec_()
|