#!/usr/bin/python
####################################
#**********************************#
# Rsync Backup v.1.0 #
#**********************************#
# python3 and gtk3 #
#**********************************#
# by rebellion #
#**********************************#
####################################
import gi,os,shutil,time,sys,getpass
from pathlib import Path
gi.require_version('Gtk', '3.0')
gi.require_version('Vte', '2.91')
from gi.repository import Gtk,Gdk,GLib,Vte
script = sys.argv[0]
if os.popen('ps aux | grep \"[%s]%s\"'% (script[:1], script[1:])).read().count(script) > 1: exit(0)
username = getpass.getuser()
dialog_prov = Gtk.FileChooserDialog(title="BACKUP (rsync): cartella da copiare", parent=None, action=Gtk.FileChooserAction.SELECT_FOLDER)
dialog_prov.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
dialog_prov.add_button(Gtk.STOCK_APPLY, Gtk.ResponseType.APPLY)
dialog_prov.set_default_response(Gtk.ResponseType.APPLY)
dialog_prov.set_current_folder('/home/%s' % username)
exclude = Gtk.CheckButton.new_with_label('exclude')
exclude.show()
dialog_prov.set_extra_widget(exclude)
prov = None
response_prov = None
while not prov and response_prov != -4 and response_prov != 0:
response_prov = dialog_prov.run()
prov = dialog_prov.get_filename()
dialog_prov.destroy()
if response_prov == Gtk.ResponseType.APPLY and prov:
excludes = []
if exclude.get_active():
label_exclude = Gtk.Label()
label_exclude.set_markup('')
label_exclude.show()
dialog_exclude = Gtk.FileChooserDialog(title="BACKUP (rsync): cartella/e da escludere", parent=None, action=Gtk.FileChooserAction.SELECT_FOLDER)
dialog_exclude.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
dialog_exclude.add_button(Gtk.STOCK_DELETE, Gtk.ResponseType.OK)
dialog_exclude.add_button(Gtk.STOCK_APPLY, Gtk.ResponseType.APPLY)
dialog_exclude.set_default_response(Gtk.ResponseType.APPLY)
dialog_exclude.set_current_folder('%s' % prov)
dialog_exclude.set_extra_widget(label_exclude)
initial_folder = '%s' % prov
def currentFolderChanged(_):
if not Path(initial_folder) in Path(dialog_exclude.get_current_folder()).parents and initial_folder != dialog_exclude.get_current_folder(): dialog_exclude.set_current_folder('%s' % prov)
dialog_exclude.connect('current-folder-changed', currentFolderChanged)
response_exclude = None
while not response_exclude:
response_exclude = dialog_exclude.run()
if response_exclude == Gtk.ResponseType.OK:
if os.path.basename(dialog_exclude.get_filename()) in excludes:
excludes.remove(os.path.relpath(dialog_exclude.get_filename(),prov))
response_exclude = None
else:
excludes.append(os.path.relpath(dialog_exclude.get_filename(),prov))
response_exclude = None
label_exclude.set_markup('<b>escluse:</b> <i>'+str(excludes)+'</i>')
dialog_exclude.destroy()
dialog_dest = Gtk.FileChooserDialog(title="BACKUP (rsync): cartella di destinazione", parent=None, action=Gtk.FileChooserAction.SELECT_FOLDER)
dialog_dest.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
dialog_dest.add_button(Gtk.STOCK_APPLY, Gtk.ResponseType.APPLY)
dialog_dest.set_default_response(Gtk.ResponseType.APPLY)
dialog_dest.set_current_folder('/home/%s' % username)
dest = None
response_dest = None
while not dest and response_dest != -4 and response_dest != 0:
response_dest = dialog_dest.run()
dest = dialog_dest.get_filename()
dialog_dest.destroy()
if response_dest == Gtk.ResponseType.APPLY:
if dest:
prov_size = os.popen("du -cs %s | grep totale | awk '{print$1}'" % prov).read().rstrip()
cmd_string = '","'.join(excludes)
fileSize = os.popen('rsync -av --exclude={\"%s\",} --dry-run %s %s | fgrep \'total size is\' | awk \'{print$4}\'' % (cmd_string,prov,dest)).read().rstrip()
destDisk_size = shutil.disk_usage(dest)[2]
if destDisk_size >= int(prov_size) and fileSize != '0':
folder_log = os.path.basename(os.path.normpath(prov))
if not os.path.exists('%s/log/%s' % (dest,folder_log)):
os.makedirs('%s/log/%s' % (dest,folder_log))
window = Gtk.Window()
window.set_urgency_hint(True)
window.set_decorated(False)
window.set_position(Gtk.WindowPosition.CENTER)
window.set_deletable(True)
window.set_default_size(500,250)
window.set_resizable(False)
window.set_border_width(0)
def cursorHandOn(_,widget):
_.get_window().set_cursor(hand_cursor)
def cursorHandOff(_,widget):
_.get_window().set_cursor()
def applyFunc(_):
applyButton.destroy()
provDestLabel.destroy()
vbox.remove(buttonBox)
window.set_default_size(1000,700)
vteTerminal = Vte.Terminal()
vteTerminal.spawn_sync(
Vte.PtyFlags.DEFAULT,
os.environ['HOME'],
["/bin/bash"],
[],
GLib.SpawnFlags.DO_NOT_REAP_CHILD,
None,
None,
)
textview_scrollable = Gtk.ScrolledWindow()
textview_scrollable.set_border_width(10)
textview_scrollable.add(vteTerminal)
current_time = str(time.localtime()[2])+'-'+str(time.localtime()[1])+'-'+str(time.localtime()[0])+'-'+str(time.localtime()[3])+'h'+str(time.localtime()[4])+'m'
cmd = 'printf \"\\033c\" ; tput rev ; tput bold ; echo \"::INIZIO BACKUP INCREMENTALE::\" ; tput sgr0 ; echo ; rsync -zvah --exclude={\"%s\",} --delete --stats --info=progress2 %s %s | tee %s/log/%s/Rsync_Backup_%s.log ; echo ; echo \"File di log: %s/log/%s/\" ; echo ; tput bold ; tput blink ; echo \"**BACKUP INCREMENTALE terminato.**\" ; tput sgr0 ; exit\n' % (cmd_string,prov,dest,dest,folder_log,current_time,dest,folder_log)
commands = bytes('stty echo ; '+cmd, encoding = 'utf-8')
closeButton.set_margin_bottom(10)
vbox.pack_start(textview_scrollable,True,True,10)
vbox.pack_end(buttonBox,False,False,0)
textview_scrollable.show_all()
vteTerminal.feed_child(b'stty -echo\n')
vteTerminal.feed_child(commands)
hand_cursor = Gdk.Cursor(Gdk.CursorType.HAND2)
vbox = Gtk.VBox(homogeneous=False)
headerbar = Gtk.HeaderBar()
titlebar = Gtk.Label()
titlebar.set_markup('\n<b><big>BACKUP incrementale</big></b>\n')
headerbar.set_custom_title(titlebar)
vbox.pack_start(headerbar,False,False,0)
provDestLabel = Gtk.Label()
provDestLabel.set_margin_top(30)
excludes_items = ''
if excludes:
excludes_items = '\n\n<b>CARTELLA/E DA ESCLUDERE</b>'
for item_excludes in excludes: excludes_items += '\n<i>' + item_excludes + '</i>'
provDestLabel.set_markup('<b>CARTELLA DA COPIARE</b>\n<i> %s</i>\n\n<b>CARTELLA DI DESTINAZIONE</b>\n<i> %s</i>%s' % (prov,dest,excludes_items))
buttonBox = Gtk.HBox()
buttonBox.set_name('buttonbox')
closeButton = Gtk.Button.new_with_label('CHIUDI')
closeButton.connect('clicked', Gtk.main_quit)
closeButton.connect('enter-notify-event', cursorHandOn)
closeButton.connect('leave-notify-event', cursorHandOff)
applyButton = Gtk.Button.new_with_label('APPLICA')
applyButton.connect('clicked', applyFunc)
applyButton.connect('enter-notify-event', cursorHandOn)
applyButton.connect('leave-notify-event', cursorHandOff)
buttonBox.pack_start(applyButton,True,True,10)
buttonBox.pack_end(closeButton,True,True,10)
vbox.pack_start(provDestLabel,False,False,0)
vbox.pack_end(buttonBox,False,False,10)
window.add(vbox)
if fileSize == '0':
applyButton.destroy()
applyButton = None
warningLabel = Gtk.Label()
warningLabel.set_markup("<b><span color='red'>ATTENZIONE: nessuna operazione sui files</span></b>")
vbox.pack_start(warningLabel,False,False,10)
elif destDisk_size < int(prov_size):
if applyButton: applyButton.destroy()
warningSpaceLabel = Gtk.Label()
warningSpaceLabel.set_markup("<b><span color='red'>ATTENZIONE: spazio su disco insufficiente</span></b>")
vbox.pack_start(warningSpaceLabel,False,False,10)
css = b"""
@keyframes shadow-pulse {
0% { box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.2); }
100% { box-shadow: 0 0 0 25px rgba(0, 0, 0, 0); }
}
button:hover {
border-radius: 30px;
opacity: 0.8;
animation: shadow-pulse 1s 1;
}
#buttonbox {
margin-top: 20px;
}
"""
style_provider = Gtk.CssProvider()
style_provider.load_from_data(css)
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(),
style_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
window.connect("delete-event", Gtk.main_quit)
window.show_all()
Gtk.main()