Plutonergy

JPEG -> WEBP

Mar 21st, 2021
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 31.00 KB | None | 0 0
  1. from PIL            import  Image
  2. from PyQt5          import  QtCore, QtWidgets, uic
  3. from PyQt5.QtGui    import  QPixmap
  4. from file_handling  import  list_compressed_archive, unpack_file
  5. from file_handling  import  windows_unrar_extractall
  6. from os             import  listdir
  7. from os.path        import  isfile, join
  8. from tricks         import  DB, Worker, sqliteconnection, sqlitecursor, tech
  9. from zipfile        import  BadZipFile, ZipFile
  10. import os
  11. import pathlib
  12. import platform
  13. import rarfile
  14. import shutil
  15. import tempfile
  16. import time
  17.  
  18.  
  19. class BulkCompresser(QtWidgets.QWidget):
  20.     def __init__(self, main):
  21.         super(BulkCompresser, self).__init__()
  22.         uic.loadUi('./gui/bulk_compressor.ui', self)
  23.         self.main = main
  24.         self.parent = main
  25.         self.threadpool = self.main.threadpool
  26.         self.summary_covers()
  27.         self.setWindowTitle(f'LongSnabel ComicReader bulk compressor!')
  28.  
  29.         if os.path.exists('/mnt/ramdisk') == True:
  30.             self.tmp_folder = '/mnt/ramdisk/longsnabel_comic_reader_tmp_folder'
  31.         else:
  32.             self.tmp_folder = tech.fpath(f'{tempfile.gettempdir()}/longsnabel_comic_reader_tmp_folder')
  33.  
  34.         if os.path.exists(self.tmp_folder) == False:
  35.             pathlib.Path(self.tmp_folder).mkdir(parents=True)
  36.         self.tmp_file = tech.fpath(f'{self.tmp_folder}/longsnabel_comic_reader_tmp_file.jpg')
  37.  
  38.         self.fixed_width_list = ['3840', '1920', '1280', '1024']
  39.         for i in self.fixed_width_list:
  40.             self.combo_fixed_width.addItem(f'{i}')
  41.  
  42.         # TRIGGERS >
  43.         self.pte_searchbar.textChanged.connect(self.search_comics)
  44.         self.lw_left_side.itemPressed.connect(self.list_clicked)
  45.         self.slider_quality.valueChanged.connect(self.slider_changed)
  46.         self.btn_start.clicked.connect(self.start_compressing)
  47.         self.btn_show_all.clicked.connect(self.show_all_comics)
  48.         self.btn_soft_stop.clicked.connect(self.soft_stop_clicked)
  49.         # TRUGGERS <
  50.         self.slider_changed()
  51.         self.summary = {'original': {}, 'new': {}}
  52.         self.show_all_comics()
  53.         self.show()
  54.  
  55.     def soft_stop_clicked(self):
  56.         self.soft_stop = True
  57.  
  58.     def make_left_right_canvas(self):
  59.         if 'pixmap_left' in dir(self):
  60.             self.pixmap_left.close()
  61.  
  62.         if 'pixmap_right' in dir(self):
  63.             self.pixmap_right.close()
  64.  
  65.         self.pixmap_left = QtWidgets.QLabel(self.canvas, lineWidth=0)
  66.         self.pixmap_right = QtWidgets.QLabel(self.canvas, lineWidth=0)
  67.         self.pixmap_left.setGeometry(0, 0, 0, 0)
  68.         self.pixmap_right.setGeometry(0, 0, 0, 0)
  69.         self.pixmap_left.show()
  70.         self.pixmap_right.show()
  71.  
  72.     def start_compressing(self):
  73.         """
  74.        when pressing startbutton, if only highlighted its the only one beeing processed
  75.        else it runs from top to bottom but halts if count exceeds spinbox, makes a list
  76.        that is being used by threadpool thus leaving this function
  77.        """
  78.         self.soft_stop = False
  79.         self.job_total = {'total_time' : time.time(), 'files_done' : 0, 'total_files' : self.spinbox_stop_after.value()}
  80.         self.summary_covers()
  81.         self.thread_work_que = {}
  82.         if self.check_highlighted.isChecked() == True:
  83.             self.start_time = time.time()
  84.             currentrow = self.lw_left_side.currentRow()
  85.             sqlitecursor.execute('select * from comics where id = (?)', (self.drawlist[currentrow][0],))
  86.             self.database = sqlitecursor.fetchone()
  87.             self.thread_work_que.update({0 : self.database})
  88.         else:
  89.             for count in range(self.spinbox_stop_after.value()):
  90.                 if count+1 <= len(self.drawlist):
  91.                     self.start_time = time.time()
  92.                     sqlitecursor.execute('select * from comics where id = (?)', (self.drawlist[count][0],))
  93.                     self.database = sqlitecursor.fetchone()
  94.                     self.thread_work_que.update({count : self.database})
  95.  
  96.         if self.thread_work_que != {}:
  97.             self.thread_processing_que()
  98.  
  99.     def process_database_as_per_que(self):
  100.         """
  101.        unpacks the entire file into temp-folder and compress one image at
  102.        the time, choices are JPEG or WEBP from radiobuttons, extensions happens.
  103.        """
  104.         self.summary = {'original':{'file_size':self.database[DB.file_size]}, 'new':{}}
  105.         self.worklist = {}
  106.         dummy = []
  107.         rv = self.decompress_archive()
  108.         if rv == False:
  109.             return False
  110.  
  111.         all_dirs = []
  112.         dirs = [x[0] for x in os.walk(self.tmp_folder)]
  113.         for eachdir in dirs:
  114.             all_dirs.append(eachdir)
  115.         full_path = []
  116.         for i in all_dirs:
  117.             files = [f for f in listdir(i) if isfile(join(i, f))]
  118.             for count in range(len(files)):
  119.                 if files[count] == 'thumbs.db':
  120.                     os.remove(f"{i}/{files[count]}")
  121.                 else:
  122.                     full_path.append(f"{i}/{files[count]}")
  123.  
  124.         extensionlist = ['png', 'jpg', 'bmp', 'gif', 'jpeg']
  125.         for count, eachfile in enumerate(full_path):
  126.             if eachfile[-1] != '/' and eachfile[eachfile.rfind('.')+1:].lower() in extensionlist:
  127.                 dummy.append(eachfile)
  128.         dummy.sort()
  129.         for count, eachfile in enumerate(dummy):
  130.             if os.path.getsize(eachfile) == 0:
  131.                 os.remove(eachfile)
  132.             else:
  133.                 self.worklist.update({count:tech.fpath(eachfile)})
  134.  
  135.         if self.worklist == {}:
  136.             del self.worklist
  137.  
  138.     def thread_processing_que(self):
  139.         """
  140.        starting here worklist should be deleted from self, each file from thread_work_que is unpacked
  141.        removed from threadlist and put to work in thread. once worklist is empty a summary function
  142.        is called removing worklist and restaring all over
  143.        :return: is called once thread_work_que is empty
  144.        """
  145.         if 'worklist' in dir(self) and self.worklist == {}:
  146.             recompress_time = time.time()
  147.             self.recompress_archive()
  148.             self.replace_old_file_with_compressed()
  149.             self.show_summary()
  150.             recompress_time = time.time() - recompress_time
  151.             text = self.summary_label.text()
  152.             text = text + f'Re-pack time: {round(recompress_time, 2)}s'
  153.             self.summary_label.setText(text)
  154.             self.start_time = time.time()
  155.             if len(self.lw_left_side) > 0:
  156.                 self.job_total['files_done'] += 1
  157.                 self.lw_left_side.takeItem(0)
  158.                 self.lw_left_side.setCurrentRow(0)
  159.             self.spinbox_stop_after.setValue(self.spinbox_stop_after.value() -1)
  160.             del self.worklist
  161.  
  162.         if 'worklist' not in dir(self):
  163.             if self.thread_work_que == {} or self.soft_stop == True:
  164.                 return
  165.  
  166.             for count, database in self.thread_work_que.items():
  167.                 self.count = count
  168.                 self.database = database
  169.                 self.set_previews()
  170.                 rv = self.process_database_as_per_que()
  171.                 self.thread_work_que.pop(count)
  172.                 if rv == False:
  173.                     self.thread_processing_que()
  174.                 break
  175.  
  176.         self.thread_processing_que_background()
  177.         thread = Worker(self.dummy)
  178.         thread.signals.finished.connect(self.thread_processing_que)
  179.         self.threadpool.start(thread)
  180.  
  181.     def dummy(self):
  182.         if 'dummy_status' not in dir(self) or self.dummy_status['current'] != self.database[0]:
  183.             self.dummy_status = {'current':self.database[0], 'unpack_time': time.time() - self.start_time}
  184.  
  185.         try: self.dummy_status['files_done'] +=1
  186.         except KeyError: self.dummy_status['files_done'] = 1
  187.         try: self.dummy_status['file_times'] += time.time() - self.file_time
  188.         except KeyError: self.dummy_status['file_times'] = time.time() - self.file_time
  189.  
  190.         que_left = len(self.worklist)
  191.         time_per_file = self.dummy_status['file_times'] / self.dummy_status['files_done']
  192.         time_left = time_per_file * que_left
  193.         unpack_time = self.dummy_status["unpack_time"]
  194.         saved_size = 0
  195.  
  196.         for i in self.summary['new']:
  197.             if type(i) == int:
  198.                 old = self.summary['original'][i]['file_size']
  199.                 new = self.summary['new'][i]['file_size']
  200.                 saved_size += old - new
  201.  
  202.         partz = f'Files done: {self.job_total["files_done"]} / {self.job_total["total_files"]}\n'
  203.         part0 = f'Current: {round(time.time() - self.start_time, 2)}s / {round((time.time() - self.job_total["total_time"]) / 60)}min\n'
  204.         part1 = f'Files left: {que_left} / {que_left + self.dummy_status["files_done"]}\n'
  205.         part2 = f'Time left: {round(time_left, 2)}s\n'
  206.         part3 = f'Average time: {round(time_per_file, 2)}s\n'
  207.         part4 = f'Space saved: {round(saved_size /1000000, 2)}mb\n'
  208.         part5 = f'Unpack time: {round(unpack_time, 2)}s\n'
  209.  
  210.         self.summary_label.setText(partz + part0 + part1 + part2 + part3 + part4 + part5)
  211.  
  212.  
  213.     def thread_processing_que_background(self):
  214.         """
  215.        if conversion has failed, old file is kept
  216.        # TODO: you need to write more stuff here
  217.        """
  218.         for count, eachfile in self.worklist.items():
  219.             self.file_time = time.time()
  220.             self.compress_image(image_path=eachfile, count=count)
  221.             if os.path.exists(self.tmp_file) == True:
  222.                 try: os.remove(eachfile)
  223.                 except PermissionError:
  224.                     os.chmod(eachfile[0:eachfile.rfind('/')], 0o777)
  225.                     os.chmod(eachfile, 0o777)
  226.                     os.remove(eachfile)
  227.  
  228.                 if self.radio_jpeg.isChecked() == True:
  229.                     os.rename(self.tmp_file, eachfile)
  230.                 else:
  231.                     eachfile = f"{eachfile[0:eachfile.rfind('.')]}.webp"
  232.                     os.rename(self.tmp_file, eachfile)
  233.  
  234.                 summary_cover = CompressSummaryCover(
  235.                     self.canvas,
  236.                     parent      = self,
  237.                     count       = count,
  238.                     summary     = self.summary,
  239.                     image_path  = eachfile,
  240.                     show_preview= self.check_show_previews.isChecked()
  241.                 )
  242.                 self.my_summary_covers.append(summary_cover)
  243.  
  244.             self.worklist.pop(count)
  245.             break
  246.  
  247.     def slider_changed(self):
  248.         self.spinbox_quality.setValue(self.slider_quality.value())
  249.  
  250.     def list_clicked(self):
  251.         """
  252.        clicking the list it draws the first image on left side and
  253.        draws same image but compressed at right side
  254.        """
  255.         self.count = 0
  256.         self.start_time = time.time()
  257.         self.summary_covers()
  258.         currentrow = self.lw_left_side.currentRow()
  259.         sqlitecursor.execute('select * from comics where id = (?)', (self.drawlist[currentrow][0],))
  260.         self.database = sqlitecursor.fetchone()
  261.         self.summary = {'original': {'file_size': self.database[DB.file_size]}, 'new': {}}
  262.         self.set_previews()
  263.         self.show_summary()
  264.  
  265.     def set_previews(self):
  266.         """
  267.        destroys current images and draws new ones
  268.        """
  269.  
  270.         self.make_left_right_canvas()
  271.  
  272.         lister = list_compressed_archive(self.database)
  273.         unpack_file(self.database, 0)
  274.         sqlitecursor.execute('select * from comics where id = (?)', (self.database[0],))
  275.         self.database = sqlitecursor.fetchone()
  276.  
  277.         cdef str orginal_image = lister.final_location[0]
  278.         cdef int width = (self.canvas.width() / 2) - 303
  279.  
  280.         self.file_time = time.time()
  281.         self.compress_image(image_path=orginal_image, count=0)
  282.  
  283.         self.org_pixmap = QPixmap(orginal_image).scaledToWidth(width, QtCore.Qt.SmoothTransformation)
  284.         if self.org_pixmap.height() > self.canvas.height():
  285.             self.org_pixmap = QPixmap(orginal_image).scaledToHeight(self.canvas.height(), QtCore.Qt.SmoothTransformation)
  286.             self.tmp_pixmap = QPixmap(self.tmp_file).scaledToHeight(self.canvas.height(), QtCore.Qt.SmoothTransformation)
  287.         else:
  288.             self.tmp_pixmap = QPixmap(self.tmp_file).scaledToWidth(width, QtCore.Qt.SmoothTransformation)
  289.  
  290.         self.pixmap_left.setGeometry(
  291.             0,
  292.             0,
  293.             self.org_pixmap.width(),
  294.             self.org_pixmap.height()
  295.         )
  296.         self.pixmap_right.setGeometry(
  297.             self.org_pixmap.width() + 6,
  298.             0,
  299.             self.tmp_pixmap.width(),
  300.             self.tmp_pixmap.height()
  301.         )
  302.         self.pixmap_left.setPixmap(self.org_pixmap)
  303.         self.pixmap_right.setPixmap(self.tmp_pixmap)
  304.  
  305.     def compress_image(self, **kwargs):
  306.         """
  307.        processes each image here, if size are larger than the one in the combobox
  308.        if checkbox is checked, the image is resized before beeing processed
  309.        :param kwargs: count (for summary purposes only)
  310.        :param kwargs: image_path
  311.        """
  312.         image_path  = kwargs['image_path']
  313.         count       = kwargs['count']
  314.         image       = Image.open(image_path)
  315.         self.summary['original'].update({count:{'file_size':os.path.getsize(image_path), 'pixmap':image.size}})
  316.  
  317.         if self.check_fixed_width.isChecked() == True and self.combo_fixed_width.currentText().isdigit() == True:
  318.             fixed_width = int(self.combo_fixed_width.currentText())
  319.             if image.size[0] > fixed_width:
  320.                 image_size = fixed_width, round(image.size[1] * (fixed_width / image.size[0]))
  321.                 image.thumbnail(image_size, Image.ANTIALIAS)
  322.  
  323.         try:
  324.             if self.radio_jpeg.isChecked() == True:
  325.                 image.save(self.tmp_file, method=self.spinbox_webp_method.value(), optimize=self.check_optimize.isChecked(), quality=int(self.spinbox_quality.value()))
  326.             else:
  327.                 image.save(self.tmp_file, 'webp', method=self.spinbox_webp_method.value(), optimize=self.check_optimize.isChecked(), quality=int(self.spinbox_quality.value()))
  328.  
  329.             self.summary['new'].update({count: {'file_size': os.path.getsize(self.tmp_file), 'pixmap': image.size, 'time':time.time() - self.file_time}})
  330.  
  331.         except OSError:
  332.             if os.path.exists(self.tmp_file) == True:
  333.                 os.remove(self.tmp_file)
  334.  
  335.     def decompress_archive(self):
  336.         archive = tech.fpath(self.database[DB.local_path])
  337.         shutil.rmtree(self.tmp_folder)
  338.         try:
  339.             comp = ZipFile(archive)
  340.         except BadZipFile:
  341.             try:
  342.                 comp = rarfile.RarFile(archive)
  343.                 if platform.system() == "Windows":
  344.                     windows_unrar_extractall(self.database, self.tmp_folder)
  345.                     return True
  346.             except rarfile.NotRarFile:
  347.                 return False
  348.  
  349.         comp.extractall(self.tmp_folder)
  350.         return True
  351.  
  352.     def recompress_archive(self):
  353.         self.make_md5_file()
  354.         self.recompressed_file = f'{tempfile.gettempdir()}/{self.database[DB.file_name][0:self.database[DB.file_name].rfind(".")]}'
  355.         if os.path.exists(self.recompressed_file) == True:
  356.             os.remove(self.recompressed_file)
  357.         shutil.make_archive(self.recompressed_file, 'zip', self.tmp_folder)
  358.         self.final_file_path = f'{self.recompressed_file}.cbz'
  359.         self.final_file_name = f'{self.final_file_path[self.final_file_path.rfind("/")+1:]}'
  360.         shutil.move(f'{self.recompressed_file}.zip', self.final_file_path)
  361.         self.summary['new']['file_size'] = os.path.getsize(self.final_file_path)
  362.  
  363.     def replace_old_file_with_compressed(self):
  364.         if os.path.exists(self.database[DB.local_path]) == True and os.path.exists(self.final_file_path) == True:
  365.             self.new_location = f"{self.database[DB.local_path][0:self.database[DB.local_path].rfind('/')]}/{self.final_file_name}"
  366.  
  367.             os.remove(self.database[DB.local_path])
  368.             shutil.copyfile(self.final_file_path, self.new_location)
  369.  
  370.             if os.path.exists(self.new_location) == True:
  371.                 os.remove(self.final_file_path)
  372.                 self.update_database()
  373.             else:
  374.                 action = input(f"YOU GOT PROBLEM: I've removed {tech.color.YELLOW2}{self.database[DB.local_path]}{tech.color.END} from your computer while holding my dick with both hands!\nClear ID:{self.database[0]} from database? ")
  375.                 if action.lower() == 'y':
  376.                     with sqliteconnection:
  377.                         sqlitecursor.execute('delete from comics where id = (?)', (self.database[0],))
  378.                 action = input(f"Update database anyway and perhaps manually look into {tech.color.YELLOW2}{self.final_file_path}{tech.color.YELLOW2} supposally moved(ing) to: {tech.color.YELLOW2}{self.new_location}{tech.color.END}")
  379.                 if action.lower() == 'y':
  380.                     self.update_database()
  381.  
  382.     def update_database(self):
  383.         with sqliteconnection:
  384.             sqlitecursor.execute('update comics set recompressed = (?) where id = (?)', (1, self.database[0],))
  385.             sqlitecursor.execute('update comics set filecontents = (?) where id = (?)', (None, self.database[0],))
  386.             sqlitecursor.execute('update comics set bad_files = (?) where id = (?)', (None, self.database[0],))
  387.             sqlitecursor.execute('update comics set contains_bad_files = (?) where id = (?)', (None, self.database[0],))
  388.             sqlitecursor.execute('update comics set local_path = (?) where id = (?)', (self.new_location, self.database[0],))
  389.             sqlitecursor.execute('update comics set file_name = (?) where id = (?)', (self.final_file_name, self.database[0],))
  390.             sqlitecursor.execute('update comics set file_size = (?) where id = (?)', (os.path.getsize(self.new_location), self.database[0],))
  391.  
  392.     def make_md5_file(self):
  393.         """
  394.        md5hash.md5 file is beeing made, containing original filename original filesize and md5 hash.
  395.        also comicvine id if one is present and md5+filesize for each original file
  396.        """
  397.  
  398.         cdef list all_dirs = [], dirs, full_path = [], files
  399.         cdef str eachdir, eachfile, file_name, current_md5 = self.database[DB.md5]
  400.         cdef int count
  401.         cdef dict md5dict = {}, fsize_dict = {}
  402.  
  403.         if os.path.exists(self.tmp_folder + '/' + current_md5 + '.md5') == True:
  404.             return
  405.  
  406.         dirs = [x[0] for x in os.walk(self.tmp_folder)]
  407.         for eachdir in dirs:
  408.             all_dirs.append(eachdir)
  409.         for i in all_dirs:
  410.             files = [f for f in listdir(i) if isfile(join(i, f))]
  411.             for count in range(len(files)):
  412.                 full_path.append(i + '/' + files[count])
  413.  
  414.         for eachfile in full_path:
  415.             file_name = eachfile[eachfile.rfind('/')+1:]
  416.             md5 = tech.md5_hash(eachfile)
  417.             md5dict[md5]    = file_name
  418.             fsize_dict[md5] = os.path.getsize(eachfile)
  419.  
  420.         md5dict = {k: v for k, v in sorted(md5dict.items(), key=lambda item: item[1])}
  421.  
  422.         if current_md5.find('[!FAKE]') != -1: # file needs to be rehashed
  423.             current_md5 = tech.md5_hash(self.database[DB.local_path])
  424.  
  425.         with open(self.tmp_folder + '/' + current_md5 + '.md5', 'w') as crc_file:
  426.             crc_file.write('; original md5: ' + current_md5 + '\n')
  427.             crc_file.write('; original filesize in bytes: ' + str(self.database[DB.file_size]) + '\n')
  428.             crc_file.write('; original filename: ' + self.database[DB.file_name] + '\n')
  429.             if self.database[DB.comic_id] != None:
  430.                 crc_file.write('; comicvine id: ' +  str(self.database[DB.comic_id]) + '\n')
  431.             for md5, file_name in md5dict.items():
  432.                 crc_file.write(md5 + ':' + str(fsize_dict[md5]) + ':' + file_name + '\n')
  433.  
  434.     def show_all_comics(self):
  435.         sqlitecursor.execute('select * from comics where recompressed is not 1')
  436.         self.all_comics = sqlitecursor.fetchall()
  437.         if self.all_comics == []:
  438.             self.main.status_bar.showMessage('NO COMICS FOUND!')
  439.             return
  440.         self.all_comics = tech.uni_sort(self.main, self.all_comics)
  441.         self.all_comics.sort(key=lambda x:x[DB.file_size], reverse=True)
  442.         self.summary_label.setText(str(len(self.all_comics)) + ' still uncompressed')
  443.         self.drawlist = self.all_comics
  444.         self.draw_left_list()
  445.  
  446.     def set_specific_comic(self, dbinput):
  447.         self.drawlist = [dbinput]
  448.         self.draw_left_list(dbinput)
  449.         self.lw_left_side.setCurrentRow(0)
  450.         self.list_clicked()
  451.  
  452.     def draw_left_list(self, dbinput=None):
  453.         self.lw_left_side.clear()
  454.         if dbinput == None:
  455.             for count in range(len(self.drawlist)-1,-1,-1):
  456.                 if self.check_linked_only.isChecked() == True and self.drawlist[count][DB.comic_id] == None:
  457.                     self.drawlist.pop(count)
  458.  
  459.         for i in self.drawlist:
  460.             file_size = i[DB.file_size] / 1000000
  461.             self.lw_left_side.addItem(f'{round(file_size)}mb {i[DB.file_name][0:-3]}')
  462.         self.spinbox_stop_after.setValue(len(self.drawlist))
  463.  
  464.     def search_comics(self):
  465.         text = self.pte_searchbar.toPlainText()
  466.         if len(text) < 3:
  467.             return
  468.         self.drawlist = tech.uni_search(self.all_comics, text, DB.file_name)
  469.         self.drawlist.sort(key=lambda x:x[DB.file_size], reverse=True) # makes sence to show largest files first
  470.         self.draw_left_list()
  471.  
  472.     def show_summary(self):
  473.         if self.count not in self.summary['new']:
  474.             return
  475.  
  476.         total_pages = self.database[DB.filecontents].split('\n')
  477.         self.total_pages = SummaryLabel(
  478.             self.pixmap_left,
  479.             text=f"Number of pages {len(total_pages)}",
  480.             stylesheet='background-color: rgba(1, 100, 162, 225) ; color: rgb(255, 255, 255) ; font: 12pt',
  481.             height_factor=5
  482.         )
  483.         self.org_file_size = SummaryLabel(
  484.             self.pixmap_left,
  485.             text=f"File size {round(self.summary['original']['file_size']/1000000, 2)}mb",
  486.             height_factor=3
  487.         )
  488.         self.org_image_size = SummaryLabel(
  489.             self.pixmap_left,
  490.             text=f"Preview image {round(self.summary['original'][self.count]['file_size']/1000000, 2)}mb",
  491.             height_factor=2
  492.         )
  493.         self.org_pixmap_dm = SummaryLabel(
  494.             self.pixmap_left,
  495.             text=f"Dimensions {self.summary['original'][self.count]['pixmap'][0]} x {self.summary['original'][self.count]['pixmap'][1]}",
  496.             height_factor=1
  497.         )
  498.  
  499.  
  500.         if 'file_size' in self.summary['new']:
  501.             file_size = f"File size {round(self.summary['new']['file_size'] / 1000000, 2)}mb"
  502.             space_saved = self.summary['original']['file_size'] - self.summary['new']['file_size']
  503.             percent_saved = self.summary['new']['file_size'] / self.summary['original']['file_size']
  504.         else:
  505.             factor      = self.summary['new'][self.count]['file_size'] / self.summary['original'][self.count]['file_size']
  506.             file_size   = f"Estemated file size {round(self.summary['original']['file_size'] * factor / 1000000, 2)}mb"
  507.             space_saved = self.summary['original']['file_size'] - (self.summary['original']['file_size'] * factor)
  508.             percent_saved = factor
  509.  
  510.         self.new_file_size = SummaryLabel(
  511.             self.pixmap_right,
  512.             text=file_size,
  513.             height_factor=3
  514.         )
  515.         self.new_image_size = SummaryLabel(
  516.             self.pixmap_right,
  517.             text=f"Preview image {round(self.summary['new'][self.count]['file_size']/1000000, 2)}mb",
  518.             height_factor=2
  519.         )
  520.         self.new_pixmap_dm = SummaryLabel(
  521.             self.pixmap_right,
  522.             text=f"Dimensions {self.summary['new'][self.count]['pixmap'][0]} x {self.summary['new'][self.count]['pixmap'][1]}",
  523.             height_factor=1
  524.         )
  525.  
  526.         self.saved_label = SummaryLabel(
  527.             self.pixmap_right,
  528.             text=f"SAVING {round(space_saved/1000000)}mb ({round((1 - percent_saved) * 100)}%)",
  529.             stylesheet='background-color: rgba(165, 0, 0, 225); color: rgb(255, 255, 255) ; font: 20pt',
  530.             height_factor=4
  531.         )
  532.  
  533.         self.time_label = SummaryLabel(
  534.             self.pixmap_right,
  535.             text=f"CPU time {round(time.time() - self.start_time, 2)}s",
  536.             stylesheet='background-color: rgba(1, 100, 162, 225) ; color: rgb(255, 255, 255) ; font: 12pt',
  537.             height_factor=5
  538.         )
  539.  
  540.         cycle = [[self.org_image_size, self.org_file_size, self.org_pixmap_dm],[self.new_image_size, self.new_file_size, self.new_pixmap_dm]]
  541.         for eachlist in cycle:
  542.             width = 0
  543.             for i in eachlist:
  544.                 if i.width() > width:
  545.                     width = i.width()
  546.             for i in eachlist:
  547.                 i.setFixedWidth(width)
  548.  
  549.     def summary_covers(self):
  550.         if 'my_summary_covers' not in dir(self):
  551.             self.my_summary_covers = []
  552.         else:
  553.             for i in self.my_summary_covers:
  554.                 i.close()
  555.             self.my_summary_covers = []
  556.  
  557. class SummaryLabel(QtWidgets.QLabel):
  558.     def __init__(self, place, **kwargs):
  559.         super(SummaryLabel, self).__init__(place)
  560.         self.setText(f" {kwargs['text']} ")
  561.         if 'stylesheet' in kwargs:
  562.             stylesheet = kwargs['stylesheet']
  563.         else:
  564.             stylesheet = 'background-color: rgba(20, 20, 20, 225); color: rgb(255, 255, 255) ; font: 20pt'
  565.         self.setStyleSheet(stylesheet)
  566.         self.move(0, int(place.height() - ((self.height() + 15) * kwargs['height_factor'])))
  567.         self.show()
  568.  
  569. class CompressSummaryCover(QtWidgets.QFrame):
  570.     def __init__(self, place, **kwargs):
  571.         super(CompressSummaryCover, self).__init__(place)
  572.         self.parent = kwargs['parent']
  573.         self.summary = kwargs['summary']
  574.         self.who_am_i = kwargs['count']
  575.         self.offset = self.parent.pixmap_left.width() + self.parent.pixmap_right.width() + (6)
  576.         self.my_width = place.width() - self.offset
  577.         self.my_height = int(tech.icon_size()['height'] * 0.6)
  578.  
  579.         self.resize(self.my_width, self.my_height)
  580.         self.move(self.offset, 0)
  581.  
  582.         if kwargs['show_preview'] == True:
  583.             self.image_path = tech.fpath(kwargs['image_path'])
  584.             self.pixmap = QPixmap(self.image_path).scaledToHeight(self.height(), QtCore.Qt.SmoothTransformation)
  585.             if self.pixmap.width() > place.width():
  586.                 self.pixmap = QPixmap(self.image_path).scaled(place.width(), self.height(), transformMode=QtCore.Qt.SmoothTransformation)
  587.             self.cover = QtWidgets.QLabel(self, styleSheet='background-color: white')
  588.             self.cover.setPixmap(self.pixmap)
  589.             self.cover.setGeometry(0, 0, self.pixmap.width(), self.pixmap.height())
  590.         else:
  591.             self.cover = QtWidgets.QLabel(self, styleSheet='background-color: black ; color: white', text='PREVIEW DISABLED')
  592.             self.cover.setGeometry(0,0,int(self.height() * 0.7), self.height())
  593.  
  594.         self.status_plate()
  595.  
  596.         for count in range(len(self.parent.my_summary_covers)-1,-1,-1):
  597.             widget = self.parent.my_summary_covers[count]
  598.             widget.move(self.offset, widget.geometry().bottom() + 3)
  599.             if widget.geometry().bottom() > place.height():
  600.                 widget.close()
  601.                 self.parent.my_summary_covers.pop(count)
  602.  
  603.         self.show()
  604.  
  605.     def status_plate(self):
  606.         pre_size = f'{round(self.summary["original"][self.who_am_i]["file_size"] / 1000)}kb'
  607.         post_size = f'{round(self.summary["new"][self.who_am_i]["file_size"] / 1000)}kb'
  608.         ratio = self.summary["new"][self.who_am_i]["file_size"] / self.summary["original"][self.who_am_i]["file_size"]
  609.         time = self.summary["new"][self.who_am_i]["time"]
  610.  
  611.         sum_time = {'max_time' : 0, 'all_time' : 0, 'count' : 0, 'low_time' : time, 'average' : 0}
  612.         for i in self.summary['new']:
  613.             thistime = self.summary['new'][i]['time']
  614.             sum_time['all_time'] += thistime
  615.             sum_time['count'] += 1
  616.             if thistime > sum_time['max_time']:
  617.                 sum_time['max_time'] = thistime
  618.             if thistime < sum_time['low_time']:
  619.                 sum_time['low_time'] = thistime
  620.  
  621.         sum_time['average'] = sum_time['all_time'] / sum_time['count']
  622.  
  623.         cdef float time_ratio = time * (0.6 / sum_time['average'])
  624.  
  625.         self.label_pre_size = QtWidgets.QLabel(self, text=f'OLD Size: {pre_size}', styleSheet='background-color: rgba(67, 132, 0, 175) ; color: rgb(0,0,0) ; font: 12pt')
  626.         self.label_pre_size.resize(self.width() - self.cover.width(), self.label_pre_size.height())
  627.  
  628.         self.label_post_size = QtWidgets.QLabel(self, text=f'{post_size}', styleSheet='background-color: rgba(68, 255, 0, 255) ; color: rgb(0,0,0) ; font: 12pt')
  629.         self.label_post_size.resize(int(self.label_pre_size.width() * ratio), self.label_post_size.height())
  630.  
  631.  
  632.         self.label_pre_size.move(self.cover.width(), self.cover.height() - self.label_pre_size.height() - 3)
  633.         self.label_post_size.move(self.cover.width(), self.cover.height() - self.label_pre_size.height() - self.label_post_size.height() - 6)
  634.  
  635.         pre_image = f'{self.summary["original"][self.who_am_i]["pixmap"][0]} x {self.summary["original"][self.who_am_i]["pixmap"][1]}'
  636.         post_image = f'{self.summary["new"][self.who_am_i]["pixmap"][0]} x {self.summary["new"][self.who_am_i]["pixmap"][1]}'
  637.         pixmap_ratio = self.summary["original"][self.who_am_i]["pixmap"][0] / self.summary["new"][self.who_am_i]["pixmap"][0]
  638.  
  639.         self.label_time = QtWidgets.QLabel(self, text=f'CPU time: {round(time, 2)}s', styleSheet='background-color: rgba(255, 128, 0, 200) ; color: rgb(255, 255, 255) ; font: 12pt')
  640.         self.label_time.resize(int(self.label_pre_size.width() * time_ratio), self.label_time.height())
  641.  
  642.         self.pre_pixmap = QtWidgets.QLabel(self, text=f'Before:\t{pre_image}', styleSheet='background-color: rgba(1, 100, 162, 200)')
  643.         self.pre_pixmap.resize(self.width() - self.cover.width(), self.pre_pixmap.height())
  644.  
  645.         self.post_pixmap = QtWidgets.QLabel(self, text=f'After:\t{post_image}', styleSheet='background-color: rgba(1, 100, 162, 200)')
  646.         self.post_pixmap.resize(int(self.pre_pixmap.width() * pixmap_ratio), self.post_pixmap.height())
  647.  
  648.         self.label_time.move(self.cover.width(), 3)
  649.         self.pre_pixmap.move(self.cover.width(), self.label_time.height() + 5)
  650.         self.post_pixmap.move(self.cover.width(), self.label_time.height() + self.pre_pixmap.height() + 9)
  651.  
  652.         from_top = self.post_pixmap.geometry().bottom()
  653.         from_bottom = self.label_post_size.geometry().top()
  654.  
  655.         self.percentlabel = QtWidgets.QLabel(self, styleSheet='color: rgb(255, 255, 255) ; font: 64pt', lineWidth=0, text=f'{round((1 - ratio) * 100)}%', alignment=QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter)
  656.         self.percentlabel.setGeometry(self.cover.width(), from_top + 3, self.label_pre_size.width(), from_bottom - from_top - 6)
Add Comment
Please, Sign In to add comment