From 96111a4f65ce7016e46d6fa2db5cfefc82cf0491 Mon Sep 17 00:00:00 2001 From: Kameron Kenny <1267885+kkenny@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:12:47 -0400 Subject: [PATCH] import dialog --- BitMover.ui | 154 ++++++++++++----- BitMover_ui.py | 358 ++++++++++++++++++++++++++++++++++----- _BitMover_MainWindow.py | 73 ++++++-- _configure.py | 2 +- _file_stuff.py | 4 +- _get_image_tag.py | 8 +- _hashing.py | 2 +- _img_preview.py | 6 +- _import_dialog.py | 45 +++++ _import_dialog_Window.py | 91 ++++++++++ _import_files.py | 85 ++++++++++ _media.py | 59 ++++--- _thread_my_stuff.py | 6 + _video.py | 97 ++++++++++- import_dialogue.ui | 156 +++++++++++++++++ scripts/gen_gui_py.sh | 1 + 16 files changed, 1010 insertions(+), 137 deletions(-) create mode 100644 _import_dialog.py create mode 100644 _import_dialog_Window.py create mode 100644 import_dialogue.ui diff --git a/BitMover.ui b/BitMover.ui index 8f0c3a4..df8b75d 100644 --- a/BitMover.ui +++ b/BitMover.ui @@ -6,7 +6,7 @@ 0 0 - 1473 + 1463 928 @@ -107,7 +107,7 @@ 20 - + true @@ -120,7 +120,7 @@ - + true @@ -133,7 +133,7 @@ - + @@ -153,7 +153,7 @@ - + true @@ -166,7 +166,7 @@ - + true @@ -179,7 +179,7 @@ - + true @@ -192,28 +192,28 @@ - + - + - + - + true @@ -226,14 +226,14 @@ - + - + true @@ -246,21 +246,21 @@ - + - + - + true @@ -273,14 +273,14 @@ - + - + @@ -313,32 +313,32 @@ 910 10 541 - 71 + 31 - - - - Event Label + Event + + + 910 - 90 - 221 - 41 + 50 + 182 + 91 - + @@ -354,18 +354,68 @@ + + + + + + + + 18 + + + + Files Imported + + + - 1140 - 90 - 311 - 46 + 1100 + 50 + 351 + 93 + + + + 24 + + + + + + + QFrame::NoFrame + + + + + + + QFrame::NoFrame + + + + + + + Processing Progress + + + + + + + Current File Progress + + + @@ -373,6 +423,13 @@ + + + + 24 + + + @@ -380,17 +437,34 @@ - - - - Processing Progress + + + + QFrame::NoFrame + + + QFrame::Plain - - - - 24 + + + + % + + + + + + + % + + + + + + + % @@ -680,7 +754,7 @@ 0 0 - 1473 + 1463 24 diff --git a/BitMover_ui.py b/BitMover_ui.py index 45ebdba..4615f43 100755 --- a/BitMover_ui.py +++ b/BitMover_ui.py @@ -2,70 +2,114 @@ import os import sys -from PyQt6.QtCore import QThreadPool -from PyQt6.QtWidgets import QMainWindow, QApplication -from PyQt6.QtGui import QIcon,QPixmap +from os import path, rename -from configure import CONFIG_FILE, Configure -from file_stuff import is_file -from BitMover_MainWindow import Ui_MainWindow -from media import Media -from lumberjack import timber -from thread_my_stuff import Worker -from img_preview import ImgPreview +from PyQt6.QtCore import QThreadPool +from PyQt6.QtGui import QIcon, QPixmap +from PyQt6.QtWidgets import QMainWindow, QApplication, QFileDialog + +from _BitMover_MainWindow import Ui_MainWindow +from _import_dialog import DialogImport +from _configure import CONFIG_FILE, Configure +from _file_stuff import is_file, create_folder, path_exists, cmp_files +from _hashing import xx_hash +from _img_preview import ImgPreview +from _lumberjack import timber +from _media import Media +from _thread_my_stuff import Worker log = timber(__name__) basedir = os.path.dirname(__file__) +# TODO: verify source dir actually exists + # Subclass QMainWindow to customize your application's main window class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, *args, **kwargs): super(MainWindow,self).__init__(*args,**kwargs) - self.setupUi(self) self.setWindowTitle("BitMover") self.setWindowIcon(QIcon(os.path.join(basedir,'assets', 'forklift.ico'))) - + self.threadpool = QThreadPool() c = Configure(CONFIG_FILE) self.config = c.load_config() self.src_dir = self.config['folders']['source']['base'] self.dst_dir = self.config['folders']['destination']['base'] + self.verify_checksum = self.config['verify_checksum'] + self.cleanup_files = self.config['cleanup_sd'] + self.store_originals = self.config['store_originals'] self.file_types = self.config['file_types'] - self.lineEdit_src_dir.setText(self.src_dir) - self.lineEdit_dst_dir.setText(self.dst_dir) + # File Stuff + self.total_files = 0 + self.file_total = 0 + self.files = {} + self.imp_dialog = DialogImport() + self.widgets_config() + + def widgets_config(self): # Button Setup self.pushButton_src_browse.clicked.connect(self.select_src_directory) self.pushButton_dst_browse.clicked.connect(self.select_dst_directory) self.pushButton_3_scan_dir.clicked.connect(self.find_files) - self.toggle_scan_button(True) + self.pushButton_import.clicked.connect(self.import_files) # Initialize widgets + self.lineEdit_src_dir.setText(self.src_dir) + self.lineEdit_dst_dir.setText(self.dst_dir) + self.toggle_scan_button(True) + self.toggle_import_button(False) self.lcd_files_found.display(int(0)) self.set_progress_processing(0) self.set_progress_importing(0) + self.set_progress_current_file(0) self.img_preview.setPixmap(QPixmap(os.path.join(basedir, 'assets', 'preview_placeholder.jpg'))) self.img_preview.setScaledContents(True) self.file_list.currentItemChanged.connect(self.index_changed) - - # File Stuff - self.total_files = 0 - self.file_total = 0 - self.files = {} + self.checkBox_verify_checksum.setChecked(self.verify_checksum) + self.checkBox_cleanup_files.setChecked(self.cleanup_files) + self.checkBox_store_originals.setChecked(self.store_originals) + self.checkBox_verify_checksum.checkStateChanged.connect(self.verify_checksum_changed) + self.checkBox_cleanup_files.checkStateChanged.connect(self.cleanup_files_changed) + self.checkBox_store_originals.checkStateChanged.connect(self.store_originals_changed) + self.clear_metadata() # Setup thread pool - self.threadpool = QThreadPool() print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) + def verify_checksum_changed(self): + if self.checkBox_verify_checksum.isChecked(): + self.config['verify_checksum'] = True + else: + self.config['verify_checksum'] = False + print(f"verify_checksums: {self.config['verify_checksums']}") + + def cleanup_files_changed(self): + if self.checkBox_cleanup_files.isChecked(): + self.config['cleanup_sd'] = True + else: + self.config['cleanup_sd'] = False + print(f"cleanup_sd: {self.config['cleanup_sd']}") + + def store_originals_changed(self): + if self.checkBox_store_originals.isChecked(): + self.config['store_originals'] = True + else: + self.config['store_originals'] = False + print(f"store_originals: {self.config['store_originals']}") + def toggle_scan_button(self,enable=True): self.pushButton_3_scan_dir.setEnabled(enable) + def toggle_import_button(self,enable=True): + self.pushButton_import.setEnabled(enable) + def update_preview(self,i): preview = ImgPreview(file=i.text(), event=self.get_event(), config=self.config) - self.label_data_date_time_created.setText(preview.dtc) + self.l_meta_content_date_time_c.setText(preview.dtc) path_hash = preview.path_hash self.l_data_file_source_path.setText( @@ -76,17 +120,67 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.img_preview.setPixmap(QPixmap(preview.thumbnail)) self.img_preview.setFixedHeight(self.img_preview.width() / preview.thumbnail_ratio) + self.update_metadata(preview) + + def update_metadata(self,preview): + self.clear_metadata() if preview.file_type == 'image': - self.label_data_width_height.setText(str(preview.size)) - self.label_data_dpi.setText(str(preview.dpi)) - self.label_data_iso.setText(str(preview.iso)) - self.label_data_lens.setText(str(preview.lens)) - self.label_data_zoom.setText(str(preview.zoom)) - self.label_data_camera.setText(str(preview.camera)) - self.label_data_aperture.setText(str(preview.aperture)) - self.label_data_megapixels.setText(str(preview.mpixels)) + self.l_meta_01.setText('Size') + self.l_meta_02.setText('dpi') + self.l_meta_03.setText('ISO') + self.l_meta_04.setText('Lens') + self.l_meta_05.setText('Focal Length') + self.l_meta_06.setText('Camera') + self.l_meta_07.setText('Aperture') + self.l_meta_08.setText('Megapixels') + self.l_meta_content_01.setText(str(preview.size)) + self.l_meta_content_02.setText(str(preview.dpi)) + self.l_meta_content_03.setText(str(preview.iso)) + self.l_meta_content_04.setText(str(preview.lens)) + self.l_meta_content_05.setText(str(preview.zoom)) + self.l_meta_content_06.setText(str(preview.camera)) + self.l_meta_content_07.setText(str(preview.aperture)) + self.l_meta_content_08.setText(str(preview.mpixels)) + + elif preview.file_type == 'video': + self.l_meta_01.setText('Size') + self.l_meta_02.setText('Frames / Second') + self.l_meta_03.setText('Bit Depth') + self.l_meta_04.setText('Duration') + self.l_meta_05.setText('Encoder') + self.l_meta_06.setText('Codec') + self.l_meta_07.setText('Profile') + self.l_meta_08.setText('Pix Format') + + self.l_meta_content_01.setText(str(preview.size)) + self.l_meta_content_02.setText(str(preview.video_framerate)) + self.l_meta_content_03.setText(str(preview.video_bit_depth)) + self.l_meta_content_04.setText(str(preview.video_duration)) + self.l_meta_content_05.setText(str(preview.video_encoding)) + self.l_meta_content_06.setText(str(preview.video_codec)) + self.l_meta_content_07.setText(str(preview.video_profile)) + self.l_meta_content_08.setText(str(preview.video_pix_format)) + + def clear_metadata(self): + self.l_meta_01.setText('') + self.l_meta_02.setText('') + self.l_meta_03.setText('') + self.l_meta_04.setText('') + self.l_meta_05.setText('') + self.l_meta_06.setText('') + self.l_meta_07.setText('') + self.l_meta_08.setText('') + self.l_meta_content_01.setText('') + self.l_meta_content_02.setText('') + self.l_meta_content_03.setText('') + self.l_meta_content_04.setText('') + self.l_meta_content_05.setText('') + self.l_meta_content_06.setText('') + self.l_meta_content_07.setText('') + self.l_meta_content_08.setText('') def index_changed(self,i): + self.clear_metadata() if i is None: self.img_preview.setPixmap(QPixmap(os.path.join(basedir, 'assets', @@ -118,13 +212,23 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.total_files = t self.lcd_files_found.display(self.total_files) + def set_imported_files(self,t): + self.lcd_files_imported.display(t) + def set_progress_processing(self, n): # print("%d%% done" % n) self.progressBar_processing.setValue(int(n)) + self.lcd_processing_progress.display(n) def set_progress_importing(self, n): # print("%d%% done" % n) self.progressBar_importing.setValue(int(n)) + self.lcd_import_progress.display(n) + + def set_progress_current_file(self, n): + # print("%d%% done" % n) + self.progressBar_importing_2.setValue(int(n)) + self.lcd_current_file_progress.display(n) def get_t_files(self,search_types): for folder, subfolders, filename in os.walk(self.src_dir): @@ -138,10 +242,11 @@ class MainWindow(QMainWindow, Ui_MainWindow): else: print(f"Skipping {current_file} as it does not look like a real file.") - def t_find_files(self,progress_callback): + def t_find_files(self, + progress_callback, + import_progress_callback, + current_file_progress_callback,): file_count = int(0) - - search_types = [] if self.checkBox_search_for_images.isChecked(): @@ -164,8 +269,6 @@ class MainWindow(QMainWindow, Ui_MainWindow): if is_file(current_file): file_count += int(1) self.process_file(current_file) - - else: print(f"Skipping {current_file} as it does not look like a real file.") @@ -175,17 +278,161 @@ class MainWindow(QMainWindow, Ui_MainWindow): return "Done." + def t_copy_files(self, + progress_callback, + import_progress_callback, + current_file_progress_callback): + """ Copy Files. """ + + # imp_dialog = DialogImport() + count = int(0) + chunk_size = 16 * 1024 + + for file in self.files: + self.imp_dialog.set_importing_file(path.join( + self.files[file]['folders']['source_path'], + self.files[file]['name'])) + + create_folder(self.files[file]['folders']['destination']) + if self.files[file]['type'] == 'video': + chunk_size = (1024 * 1024) * 5 + + file_exists = path_exists(path.join( + self.files[file]['folders']['destination'], + self.files[file]['name'])) + + if file_exists is True: + check_match = cmp_files( + path.join( + self.files[file]['folders']['source_path'], + self.files[file]['name']), + path.join( + self.files[file]['folders']['destination'], + self.files[file]['name'] + ) + ) + + if check_match is False: + print(f'\nFound duplicate for {self.files[file]["folders"]["source_path"]}/{self.files[file]["name"]}, renaming destination with hash appended.') + base, extension = path.splitext(self.files[file]['name']) + f_xxhash = xx_hash(path.join( + self.files[file]['folders']['destination'], + self.files[file]['name'])) + file_name_hash = base + '_' + f_xxhash + extension + rename(path.join( + self.files[file]['folders']['destination'], + self.files[file]['name']), + path.join( + self.files[file]['folders']['destination'], + file_name_hash)) + size = path.getsize( + path.join( + self.files[file]['folders']['source_path'], + self.files[file]['name']) + ) + + with open(path.join( + self.files[file]['folders']['source_path'], + self.files[file]['name']), 'rb') as fs: + with open( + path.join( + self.files[file]['folders']['destination'], + self.files[file]['name']), 'wb') as fd: + while True: + chunk = fs.read(chunk_size) + if not chunk: + break + fd.write(chunk) + dst_size = path.getsize( + path.join( + self.files[file]['folders']['destination'], + self.files[file]['name'] + ) + ) + current_file_progress_callback.emit(round(( dst_size / size ) * 100, 1)) + + if self.config['store_originals'] is True: + if self.files[file]['type'] == 'image': + create_folder(self.files[file]['folders']['destination_original']) + + file_exists = path_exists(path.join( + self.files[file]['folders']['destination_original'], + self.files[file]['name'])) + + if file_exists is True: + check_match = cmp_files( + path.join( + self.files[file]['folders']['source_path'], + self.files[file]['name']), + path.join( + self.files[file]['folders']['destination_original'], + self.files[file]['name'] + ) + ) + + if check_match is False: + print(f'\nFound duplicate for {self.files[file]["folders"]["source_path"]}/{self.files[file]["name"]}, renaming destination with hash appended.') + base, extension = path.splitext(self.files[file]['name']) + f_xxhash = xx_hash(path.join( + self.files[file]['folders']['destination_original'], + self.files[file]['name'])) + file_name_hash = base + '_' + f_xxhash + extension + rename(path.join( + self.files[file]['folders']['destination_original'], + self.files[file]['name']), + path.join( + self.files[file]['folders']['destination_original'], + file_name_hash)) + + size = path.getsize( + path.join( + self.files[file]['folders']['source_path'], + self.files[file]['name']) + ) + + with open(path.join( + self.files[file]['folders']['source_path'], + self.files[file]['name']), 'rb') as fs: + with open( + path.join( + self.files[file]['folders']['destination_original'], + self.files[file]['name']), 'wb') as fd: + while True: + chunk = fs.read(chunk_size) + if not chunk: + break + fd.write(chunk) + dst_size = path.getsize( + path.join( + self.files[file]['folders']['destination_original'], + self.files[file]['name'] + ) + ) + current_file_progress_callback.emit(round((dst_size / size) * 100, 1)) + count += 1 + self.set_imported_files(count) + import_progress_callback.emit(round((count / self.file_total) * 100, 1)) + self.imp_dialog.add_to_imported_list( + path.join( + self.files[file]['folders']['source_path'], + self.files[file]['name'])) + @staticmethod def print_output(s): print(s) - def scan_thread_started(self): + def worker_thread_started(self): print('scan thread started') self.toggle_scan_button(False) + self.toggle_import_button(False) - def scan_thread_done(self): + def worker_thread_done(self): print('scan thread complete.') self.toggle_scan_button(True) + if 0 < len(self.files): + self.toggle_import_button(True) + else: + self.toggle_import_button(False) @staticmethod def thread_complete(): @@ -209,15 +456,46 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.files = {} worker = Worker(self.t_find_files) - worker.signals.started.connect(self.scan_thread_started) + worker.signals.started.connect(self.worker_thread_started) worker.signals.result.connect(self.print_output) worker.signals.finished.connect(self.thread_complete) - worker.signals.finished.connect(self.scan_thread_done) + worker.signals.finished.connect(self.worker_thread_done) worker.signals.progress.connect(self.set_progress_processing) # Execute. self.threadpool.start(worker) + def import_files(self): + """ + Import found files + """ + + # Open Dialog + # imp_dialog = DialogImport() + # imp_dialog.open_import_dialog() + + # Initialize Widgets + self.lcd_files_imported.display(int(0)) + self.set_progress_importing(0) + self.set_progress_current_file(0) + + self.imp_dialog.set_progress_importing(0) + self.imp_dialog.set_progress_current_file(0) + + worker = Worker(self.t_copy_files) + worker.signals.started.connect(self.worker_thread_started) + worker.signals.started.connect(self.imp_dialog.open_import_dialog) + worker.signals.result.connect(self.print_output) + worker.signals.finished.connect(self.thread_complete) + worker.signals.finished.connect(self.worker_thread_done) + worker.signals.import_progress.connect(self.set_progress_importing) + worker.signals.import_progress.connect(self.imp_dialog.set_progress_importing) + worker.signals.current_file_progress.connect(self.set_progress_current_file) + worker.signals.current_file_progress.connect(self.imp_dialog.set_progress_current_file) + + # Execute + self.threadpool.start(worker) + def get_event(self): event_name = self.eventName.text() return event_name @@ -231,7 +509,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): event = self.get_event() c = self.config - log.debug(f'process_file({path_name}, {f_name}, {event}, {c})') + # print(f'process_file({path_name}, {f_name}, {event}, {c})') m = Media(os.path.join(path_name,f_name),event, c) i = m.source_path_hash diff --git a/_BitMover_MainWindow.py b/_BitMover_MainWindow.py index aa8b4bc..87108a0 100644 --- a/_BitMover_MainWindow.py +++ b/_BitMover_MainWindow.py @@ -12,7 +12,7 @@ from PyQt6 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(1473, 928) + MainWindow.resize(1463, 928) self.centralwidget = QtWidgets.QWidget(parent=MainWindow) self.centralwidget.setObjectName("centralwidget") self.gridLayoutWidget = QtWidgets.QWidget(parent=self.centralwidget) @@ -47,7 +47,7 @@ class Ui_MainWindow(object): self.file_list.setGeometry(QtCore.QRect(20, 160, 871, 701)) self.file_list.setObjectName("file_list") self.gridLayoutWidget_2 = QtWidgets.QWidget(parent=self.centralwidget) - self.gridLayoutWidget_2.setGeometry(QtCore.QRect(910, 610, 311, 211)) + self.gridLayoutWidget_2.setGeometry(QtCore.QRect(910, 650, 311, 211)) self.gridLayoutWidget_2.setObjectName("gridLayoutWidget_2") self.grid_metadata = QtWidgets.QGridLayout(self.gridLayoutWidget_2) self.grid_metadata.setContentsMargins(0, 0, 0, 0) @@ -154,7 +154,7 @@ class Ui_MainWindow(object): self.grid_metadata.addWidget(self.l_meta_content_02, 2, 1, 1, 1) self.grid_metadata.setColumnStretch(1, 1) self.l_exif_ffprobe_title = QtWidgets.QLabel(parent=self.centralwidget) - self.l_exif_ffprobe_title.setGeometry(QtCore.QRect(910, 590, 371, 16)) + self.l_exif_ffprobe_title.setGeometry(QtCore.QRect(910, 630, 371, 16)) font = QtGui.QFont() font.setPointSize(18) font.setBold(True) @@ -173,7 +173,7 @@ class Ui_MainWindow(object): self.eventName.setObjectName("eventName") self.gridLayout.addWidget(self.eventName, 0, 1, 1, 1) self.gridLayoutWidget_4 = QtWidgets.QWidget(parent=self.centralwidget) - self.gridLayoutWidget_4.setGeometry(QtCore.QRect(910, 50, 221, 41)) + self.gridLayoutWidget_4.setGeometry(QtCore.QRect(910, 50, 182, 91)) self.gridLayoutWidget_4.setObjectName("gridLayoutWidget_4") self.gridLayout_2 = QtWidgets.QGridLayout(self.gridLayoutWidget_4) self.gridLayout_2.setContentsMargins(0, 0, 0, 0) @@ -187,35 +187,73 @@ class Ui_MainWindow(object): self.lcd_files_found = QtWidgets.QLCDNumber(parent=self.gridLayoutWidget_4) self.lcd_files_found.setObjectName("lcd_files_found") self.gridLayout_2.addWidget(self.lcd_files_found, 0, 1, 1, 1) + self.lcd_files_imported = QtWidgets.QLCDNumber(parent=self.gridLayoutWidget_4) + self.lcd_files_imported.setObjectName("lcd_files_imported") + self.gridLayout_2.addWidget(self.lcd_files_imported, 1, 1, 1, 1) + self.label_4 = QtWidgets.QLabel(parent=self.gridLayoutWidget_4) + font = QtGui.QFont() + font.setPointSize(18) + self.label_4.setFont(font) + self.label_4.setObjectName("label_4") + self.gridLayout_2.addWidget(self.label_4, 1, 0, 1, 1) self.gridLayout_2.setColumnStretch(1, 1) self.gridLayoutWidget_5 = QtWidgets.QWidget(parent=self.centralwidget) - self.gridLayoutWidget_5.setGeometry(QtCore.QRect(1140, 50, 311, 46)) + self.gridLayoutWidget_5.setGeometry(QtCore.QRect(1100, 50, 351, 93)) self.gridLayoutWidget_5.setObjectName("gridLayoutWidget_5") self.gridLayout_3 = QtWidgets.QGridLayout(self.gridLayoutWidget_5) self.gridLayout_3.setContentsMargins(0, 0, 0, 0) self.gridLayout_3.setObjectName("gridLayout_3") + self.progressBar_importing_2 = QtWidgets.QProgressBar(parent=self.gridLayoutWidget_5) + self.progressBar_importing_2.setProperty("value", 24) + self.progressBar_importing_2.setObjectName("progressBar_importing_2") + self.gridLayout_3.addWidget(self.progressBar_importing_2, 2, 1, 1, 1) + self.lcd_import_progress = QtWidgets.QLCDNumber(parent=self.gridLayoutWidget_5) + self.lcd_import_progress.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) + self.lcd_import_progress.setObjectName("lcd_import_progress") + self.gridLayout_3.addWidget(self.lcd_import_progress, 1, 2, 1, 1) + self.lcd_current_file_progress = QtWidgets.QLCDNumber(parent=self.gridLayoutWidget_5) + self.lcd_current_file_progress.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) + self.lcd_current_file_progress.setObjectName("lcd_current_file_progress") + self.gridLayout_3.addWidget(self.lcd_current_file_progress, 2, 2, 1, 1) + self.l_proecessing_progress = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) + self.l_proecessing_progress.setObjectName("l_proecessing_progress") + self.gridLayout_3.addWidget(self.l_proecessing_progress, 0, 0, 1, 1) + self.l_current_file_progress = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) + self.l_current_file_progress.setObjectName("l_current_file_progress") + self.gridLayout_3.addWidget(self.l_current_file_progress, 2, 0, 1, 1) self.progressBar_processing = QtWidgets.QProgressBar(parent=self.gridLayoutWidget_5) self.progressBar_processing.setProperty("value", 24) self.progressBar_processing.setObjectName("progressBar_processing") self.gridLayout_3.addWidget(self.progressBar_processing, 0, 1, 1, 1) - self.l_import_progress = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) - self.l_import_progress.setObjectName("l_import_progress") - self.gridLayout_3.addWidget(self.l_import_progress, 1, 0, 1, 1) - self.l_proecessing_progress = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) - self.l_proecessing_progress.setObjectName("l_proecessing_progress") - self.gridLayout_3.addWidget(self.l_proecessing_progress, 0, 0, 1, 1) self.progressBar_importing = QtWidgets.QProgressBar(parent=self.gridLayoutWidget_5) self.progressBar_importing.setProperty("value", 24) self.progressBar_importing.setObjectName("progressBar_importing") self.gridLayout_3.addWidget(self.progressBar_importing, 1, 1, 1, 1) + self.l_import_progress = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) + self.l_import_progress.setObjectName("l_import_progress") + self.gridLayout_3.addWidget(self.l_import_progress, 1, 0, 1, 1) + self.lcd_processing_progress = QtWidgets.QLCDNumber(parent=self.gridLayoutWidget_5) + self.lcd_processing_progress.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) + self.lcd_processing_progress.setFrameShadow(QtWidgets.QFrame.Shadow.Plain) + self.lcd_processing_progress.setObjectName("lcd_processing_progress") + self.gridLayout_3.addWidget(self.lcd_processing_progress, 0, 2, 1, 1) + self.label_2 = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) + self.label_2.setObjectName("label_2") + self.gridLayout_3.addWidget(self.label_2, 0, 3, 1, 1) + self.label_5 = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) + self.label_5.setObjectName("label_5") + self.gridLayout_3.addWidget(self.label_5, 1, 3, 1, 1) + self.label_6 = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) + self.label_6.setObjectName("label_6") + self.gridLayout_3.addWidget(self.label_6, 2, 3, 1, 1) self.img_preview = QtWidgets.QLabel(parent=self.centralwidget) - self.img_preview.setGeometry(QtCore.QRect(910, 110, 541, 371)) + self.img_preview.setGeometry(QtCore.QRect(910, 150, 541, 371)) self.img_preview.setAutoFillBackground(True) self.img_preview.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) self.img_preview.setText("") self.img_preview.setObjectName("img_preview") self.gridLayoutWidget_6 = QtWidgets.QWidget(parent=self.centralwidget) - self.gridLayoutWidget_6.setGeometry(QtCore.QRect(910, 490, 541, 91)) + self.gridLayoutWidget_6.setGeometry(QtCore.QRect(910, 530, 541, 91)) self.gridLayoutWidget_6.setObjectName("gridLayoutWidget_6") self.grid_metadata_2 = QtWidgets.QGridLayout(self.gridLayoutWidget_6) self.grid_metadata_2.setContentsMargins(0, 0, 0, 0) @@ -322,7 +360,7 @@ class Ui_MainWindow(object): self.horizontalLayout_3.addWidget(self.pushButton_3_scan_dir) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(parent=MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 1473, 24)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1463, 24)) self.menubar.setObjectName("menubar") self.menuBit_Mover = QtWidgets.QMenu(parent=self.menubar) self.menuBit_Mover.setObjectName("menuBit_Mover") @@ -356,8 +394,13 @@ class Ui_MainWindow(object): self.l_exif_ffprobe_title.setText(_translate("MainWindow", "Exif / ffprobe Data")) self.labelEvent.setText(_translate("MainWindow", "Event")) self.label_3.setText(_translate("MainWindow", "Files Found")) - self.l_import_progress.setText(_translate("MainWindow", "Import Progress")) + self.label_4.setText(_translate("MainWindow", "Files Imported")) self.l_proecessing_progress.setText(_translate("MainWindow", "Processing Progress")) + self.l_current_file_progress.setText(_translate("MainWindow", "Current File Progress")) + self.l_import_progress.setText(_translate("MainWindow", "Import Progress")) + self.label_2.setText(_translate("MainWindow", "%")) + self.label_5.setText(_translate("MainWindow", "%")) + self.label_6.setText(_translate("MainWindow", "%")) self.l_file_source_path.setText(_translate("MainWindow", "Source Path")) self.l_file_dest_path.setText(_translate("MainWindow", "Destination Path")) self.checkBox_verify_checksum.setToolTip(_translate("MainWindow", "After copying, verify that the hash of the original file equals the hash of the copied file.")) diff --git a/_configure.py b/_configure.py index 6060819..a9774a6 100644 --- a/_configure.py +++ b/_configure.py @@ -6,7 +6,7 @@ Load the config file from yaml file import sys import os import yaml -from lumberjack import timber +from _lumberjack import timber basedir = os.path.dirname(__file__) files = {} diff --git a/_file_stuff.py b/_file_stuff.py index 6369e3a..87ab790 100644 --- a/_file_stuff.py +++ b/_file_stuff.py @@ -10,8 +10,8 @@ from tqdm import tqdm ### Local Imports # from configure import Configure, CONFIG_FILE -from hashing import xx_hash -from lumberjack import timber +from _hashing import xx_hash +from _lumberjack import timber def check_log_dir(d): create_folder(d) diff --git a/_get_image_tag.py b/_get_image_tag.py index 8035d13..216748b 100644 --- a/_get_image_tag.py +++ b/_get_image_tag.py @@ -7,7 +7,7 @@ from uu import Error import exifread from datetime import datetime -from lumberjack import timber +from _lumberjack import timber log = timber(__name__) @@ -49,16 +49,16 @@ def get_exif_tag(p,t): with open(p, "rb") as f: try: tags = exifread.process_file(f) - print(f'{p}: {tags}') + # print(f'{p}: {tags}') except Error as e: return f'Received Error: {e} when trying to open {p}' finally: f.close() for tag in tags: - print(tag) + # print(tag) if t in tag.lower(): - print(tags[tag]) + # print(tags[tag]) return tags[tag] else: pass diff --git a/_hashing.py b/_hashing.py index ab8012a..1b5550e 100644 --- a/_hashing.py +++ b/_hashing.py @@ -8,7 +8,7 @@ import os import xxhash from tqdm import tqdm # from configure import Configure, CONFIG_FILE -from lumberjack import timber +from _lumberjack import timber # c = Configure(CONFIG_FILE) # config = c.load_config() diff --git a/_img_preview.py b/_img_preview.py index cc5e524..18971cd 100644 --- a/_img_preview.py +++ b/_img_preview.py @@ -1,9 +1,9 @@ #!/usr/bin/env python -from media import Media -from get_image_tag import get_exif_tag +from _media import Media +from _get_image_tag import get_exif_tag from PIL import Image -from raw_photo import get_raw_image_dimensions, extract_jpg_thumb +from _raw_photo import get_raw_image_dimensions, extract_jpg_thumb from _video import Video class ImgPreview: diff --git a/_import_dialog.py b/_import_dialog.py new file mode 100644 index 0000000..ab1c794 --- /dev/null +++ b/_import_dialog.py @@ -0,0 +1,45 @@ +from PyQt6.QtWidgets import QDialog +from _import_dialog_Window import Ui_DialogImport + +class DialogImport(QDialog, Ui_DialogImport): + def __init__(self,*args,**kwargs): + super(DialogImport,self).__init__(*args,**kwargs) + self.setupUi(self) + # self.import_dialog = UI_DialogImport() + # self.ui_import_dialog = Ui_DialogImport() + print('DialogImport') + + def is_shown(self): + print(f'is_shown: {self.isVisible()}') + return self.isVisible() + + def open_import_dialog(self): + print(f'open_import_dialog: {self.is_shown()}') + if not self.is_shown(): + print('Inside if not self.is_shown') + print('showing window') + self.show() + + def close_import_dialog(self): + print('close_import_dialog') + if self.is_shown(): + print('inside self.is_shown()') + print('hiding window') + self.hide() + + def set_progress_importing(self, n): + print("%d%% done" % n) + self.progressBar_importing.setValue(int(n)) + self.lcd_import_progress.display(n) + + def set_progress_current_file(self, n): + print("%d%% done" % n) + self.progressBar_importing_2.setValue(int(n)) + self.lcd_current_file_progress.display(n) + + def set_importing_file(self,f): + self.l_importing_file_name.setText(f) + + def add_to_imported_list(self,n): + self.set_importing_file('') + self.list_imported.addItem(n) \ No newline at end of file diff --git a/_import_dialog_Window.py b/_import_dialog_Window.py new file mode 100644 index 0000000..1c8c08e --- /dev/null +++ b/_import_dialog_Window.py @@ -0,0 +1,91 @@ +# Form implementation generated from reading ui file 'import_dialogue.ui' +# +# Created by: PyQt6 UI code generator 6.4.2 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_DialogImport(object): + def setupUi(self, DialogImport): + DialogImport.setObjectName("DialogImport") + DialogImport.resize(640, 880) + self.gridLayoutWidget_5 = QtWidgets.QWidget(parent=DialogImport) + self.gridLayoutWidget_5.setGeometry(QtCore.QRect(20, 20, 601, 61)) + self.gridLayoutWidget_5.setObjectName("gridLayoutWidget_5") + self.gridLayout_3 = QtWidgets.QGridLayout(self.gridLayoutWidget_5) + self.gridLayout_3.setContentsMargins(0, 0, 0, 0) + self.gridLayout_3.setObjectName("gridLayout_3") + self.label_5 = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) + self.label_5.setObjectName("label_5") + self.gridLayout_3.addWidget(self.label_5, 0, 3, 1, 1) + self.label_6 = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) + self.label_6.setObjectName("label_6") + self.gridLayout_3.addWidget(self.label_6, 1, 3, 1, 1) + self.progressBar_importing = QtWidgets.QProgressBar(parent=self.gridLayoutWidget_5) + self.progressBar_importing.setProperty("value", 24) + self.progressBar_importing.setObjectName("progressBar_importing") + self.gridLayout_3.addWidget(self.progressBar_importing, 0, 1, 1, 1) + self.l_import_progress = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) + self.l_import_progress.setObjectName("l_import_progress") + self.gridLayout_3.addWidget(self.l_import_progress, 0, 0, 1, 1) + self.lcd_import_progress = QtWidgets.QLCDNumber(parent=self.gridLayoutWidget_5) + self.lcd_import_progress.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) + self.lcd_import_progress.setObjectName("lcd_import_progress") + self.gridLayout_3.addWidget(self.lcd_import_progress, 0, 2, 1, 1) + self.lcd_current_file_progress = QtWidgets.QLCDNumber(parent=self.gridLayoutWidget_5) + self.lcd_current_file_progress.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) + self.lcd_current_file_progress.setObjectName("lcd_current_file_progress") + self.gridLayout_3.addWidget(self.lcd_current_file_progress, 1, 2, 1, 1) + self.progressBar_importing_2 = QtWidgets.QProgressBar(parent=self.gridLayoutWidget_5) + self.progressBar_importing_2.setProperty("value", 24) + self.progressBar_importing_2.setObjectName("progressBar_importing_2") + self.gridLayout_3.addWidget(self.progressBar_importing_2, 1, 1, 1, 1) + self.l_current_file_progress = QtWidgets.QLabel(parent=self.gridLayoutWidget_5) + self.l_current_file_progress.setObjectName("l_current_file_progress") + self.gridLayout_3.addWidget(self.l_current_file_progress, 1, 0, 1, 1) + self.verticalLayoutWidget = QtWidgets.QWidget(parent=DialogImport) + self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 90, 601, 80)) + self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setObjectName("verticalLayout") + self.l_importing_header = QtWidgets.QLabel(parent=self.verticalLayoutWidget) + font = QtGui.QFont() + font.setPointSize(24) + font.setBold(True) + self.l_importing_header.setFont(font) + self.l_importing_header.setObjectName("l_importing_header") + self.verticalLayout.addWidget(self.l_importing_header) + self.l_importing_file_name = QtWidgets.QLabel(parent=self.verticalLayoutWidget) + self.l_importing_file_name.setText("") + self.l_importing_file_name.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop) + self.l_importing_file_name.setWordWrap(True) + self.l_importing_file_name.setObjectName("l_importing_file_name") + self.verticalLayout.addWidget(self.l_importing_file_name) + self.l_imported_list_header = QtWidgets.QLabel(parent=DialogImport) + self.l_imported_list_header.setGeometry(QtCore.QRect(20, 180, 181, 30)) + font = QtGui.QFont() + font.setPointSize(28) + self.l_imported_list_header.setFont(font) + self.l_imported_list_header.setObjectName("l_imported_list_header") + self.list_imported = QtWidgets.QListWidget(parent=DialogImport) + self.list_imported.setGeometry(QtCore.QRect(15, 211, 611, 651)) + self.list_imported.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.NoSelection) + self.list_imported.setObjectName("list_imported") + + self.retranslateUi(DialogImport) + QtCore.QMetaObject.connectSlotsByName(DialogImport) + + def retranslateUi(self, DialogImport): + _translate = QtCore.QCoreApplication.translate + DialogImport.setWindowTitle(_translate("DialogImport", "Dialog")) + self.label_5.setText(_translate("DialogImport", "%")) + self.label_6.setText(_translate("DialogImport", "%")) + self.l_import_progress.setText(_translate("DialogImport", "Import Progress")) + self.l_current_file_progress.setText(_translate("DialogImport", "Current File Progress")) + self.l_importing_header.setText(_translate("DialogImport", "Importing:")) + self.l_imported_list_header.setText(_translate("DialogImport", "Imported:")) diff --git a/_import_files.py b/_import_files.py index e69de29..8f2222e 100644 --- a/_import_files.py +++ b/_import_files.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python + +""" +Functions to copy bits around ... +""" + +from os import system,path,rename +from tqdm import tqdm + +from _file_stuff import create_folder, cmp_files, path_exists +from _lumberjack import timber +from _hashing import xx_hash +# from configure import Configure, CONFIG_FILE + +# c = Configure(CONFIG_FILE) +# config = c.load_config() +log = timber(__name__) + +def copy_with_progress(s,d,f): + """ Copy a file with the progress bar """ + log.debug(f'copy_with_progress({s},{d},{f})') + + size = path.getsize(s) + with open(s, 'rb') as fs: + with open(d, 'wb') as fd: + # with tqdm(total=size, unit='B', unit_scale=True, desc=f'Copying {f}') as pbar: + while True: + chunk = fs.read(4096) + if not chunk: + break + fd.write(chunk) + # pbar.update(len(chunk)) + +def copy_from_source(source_path,dest_path,file_name): + """ Copy file from source to destination """ + log.debug(f'copy_from_source({source_path},{dest_path},{file_name}') + + file_exists = path_exists(path.join(dest_path,file_name)) + + if file_exists is True: + log.debug(f'\nFound {file_name} at destination, checking if they match.') + check_match = cmp_files( + path.join(source_path,file_name), + path.join(dest_path, file_name)) + if check_match is False: + log.warn(f'\nFound duplicate for {source_path}/{file_name}, \ + renaming destination with hash appended.') + base, extension = path.splitext(file_name) + #md5 = md5_hash(os.path.join(dest_path, file_name)) + f_xxhash = xx_hash(path.join(dest_path, file_name)) + #file_name_hash = base + '_' + md5 + extension + file_name_hash = base + '_' + f_xxhash + extension + rename(path.join(dest_path, file_name), + path.join(dest_path, file_name_hash)) + else: + log.info(f'\n{file_name} hashes match') + # remove(path.join(source_path,file_name)) + # f.pop(file_name) + return + + # create_folder(dest_path) + # shutil.copy(os.path.join(source_path,file_name), dest_path) + copy_with_progress(path.join(source_path, file_name), + path.join(dest_path, file_name), + file_name) + system('clear') + +def copy_files(f,config): + """ Copy Files. """ + log.debug(f'copy_files({f})') + system('clear') + for file in tqdm(f, desc="Copying Files:"): + create_folder(f[file]['folders']['destination']) + + copy_from_source(f[file]['folders']['source_path'], + f[file]['folders']['destination'], + f[file]['name']) + + if config['store_originals'] is True: + if f[file]['type'] == 'image': + create_folder(f[file]['folders']['destination_original']) + + copy_from_source(f[file]['folders']['destination'], + f[file]['folders']['destination_original'], + f[file]['name']) \ No newline at end of file diff --git a/_media.py b/_media.py index 7cbb245..7e1c029 100644 --- a/_media.py +++ b/_media.py @@ -4,15 +4,16 @@ Create the media object """ import os +from os import path from datetime import datetime import ffmpeg import sys #Local Imports -from hashing import xx_hash, hash_path -from get_image_tag import get_image_date +from _hashing import xx_hash, hash_path +from _get_image_tag import get_image_date # from configure import files -from lumberjack import timber +from _lumberjack import timber log = timber(__name__) @@ -31,9 +32,9 @@ class Media: self.file_ext = self.dotted_file_ext.split('.')[1] self.file_type = self.get_file_type() self.capture_date = get_capture_date(source_path,self.file_type) - self.store_originals = config['store_originals'] - self.destination_path = self.set_destination_path() + self.store_originals = self.configuration['store_originals'] self.destination_originals_path = '' + self.destination_path = self.set_destination_path() self.filehash = '' # Only populate this one if it's called upon. @@ -53,14 +54,24 @@ class Media: p = p + '-' + self.event_name if self.file_type == 'image': + # print(f'store_originals: {self.store_originals}') p = p + '/PHOTO' if self.file_ext.lower() in ('jpg', 'jpeg'): + # print(f'store_originals: {self.store_originals}') if self.store_originals is True: - self.destination_originals_path = p + '/ORIGINALS/JPG' + self.destination_originals_path = path.join( + p, + self.configuration['folders']['originals'], + 'JPG') + # print(self.destination_originals_path) p = p + '/JPG' else: if self.store_originals is True: - self.destination_originals_path = p + '/ORIGINALS/RAW' + self.destination_originals_path = path.join( + p, + self.configuration['folders']['originals'], + 'RAW') + # print(self.destination_originals_path) p = p + '/RAW' elif self.file_type == 'video': p = p + '/VIDEO' @@ -89,25 +100,25 @@ class Media: def get_file_type(self): """ determine if the extension is in the list of file types """ - log.debug(f'get_file_type():') + # log.debug(f'get_file_type():') for t in self.configuration['file_types']: - log.debug(f'Checking if file is part of {t}') + # log.debug(f'Checking if file is part of {t}') for e in self.configuration['file_types'][t]: - log.debug(f'Checking if {self.file_ext.lower()} ends with {e}') + # log.debug(f'Checking if {self.file_ext.lower()} ends with {e}') if self.file_ext.lower().endswith(e): - log.debug(self.file_ext + ' ends with ' + e) + # log.debug(self.file_ext + ' ends with ' + e) return t - else: - log.debug(self.file_ext + f' does not end with {e}') + # else: + # log.debug(self.file_ext + f' does not end with {e}') -def get_capture_date(path, f_type): +def get_capture_date(p, f_type): """ get capture date from meta """ - log.debug(f'get_capture_date({path}, {f_type}') + # log.debug(f'get_capture_date({p}, {f_type}') if f_type == 'image': - stamp = get_image_date(path) + stamp = get_image_date(p) elif f_type == 'video': try: @@ -116,31 +127,31 @@ def get_capture_date(path, f_type): '%Y-%m-%dT%H:%M:%S.%f%z') except: try: - stamp = datetime.fromtimestamp(os.path.getctime(path)) + stamp = datetime.fromtimestamp(os.path.getctime(p)) except: try: stamp = datetime.strptime( str('1900:01:01 00:00:00'), '%Y:%m:%d %H:%M:%S') except: - log.critical(f'\nCould not get timestamp for {path}. Giving up.') + # log.critical(f'\nCould not get timestamp for {p}. Giving up.') sys.exit() elif f_type == 'audio': try: stamp = datetime.strptime(ffmpeg.probe( - path)['format']['tags']['date'], '%Y-%m-%d') + p)['format']['tags']['date'], '%Y-%m-%d') except KeyError as ke: - log.warning(f'\nError: {ke} for {path}. Trying getctime...') + # log.warning(f'\nError: {ke} for {p}. Trying getctime...') try: - stamp = datetime.fromtimestamp(os.path.getctime(path)) + stamp = datetime.fromtimestamp(os.path.getctime(p)) except: - log.critical(f'\nCould not get timestamp for {path}. Giving up.') + # log.critical(f'\nCould not get timestamp for {p}. Giving up.') sys.exit() else: try: - stamp = datetime.fromtimestamp(os.path.getctime(path)) + stamp = datetime.fromtimestamp(os.path.getctime(p)) except: - log.critical(f'\nCould not get timestamp for {path}. Giving up.') + # log.critical(f'\nCould not get timestamp for {p}. Giving up.') sys.exit() year = stamp.strftime("%Y") diff --git a/_thread_my_stuff.py b/_thread_my_stuff.py index 7560bfc..ff9499c 100644 --- a/_thread_my_stuff.py +++ b/_thread_my_stuff.py @@ -27,6 +27,9 @@ class WorkerSignals(QObject): error = pyqtSignal(tuple) result = pyqtSignal(object) progress = pyqtSignal(int) + import_progress = pyqtSignal(int) + current_file_progress = pyqtSignal(float) + # current_import_file = pyqtSignal() class Worker(QRunnable): """ @@ -52,6 +55,9 @@ class Worker(QRunnable): # Add the callback to our kwargs self.kwargs['progress_callback'] = self.signals.progress + self.kwargs['import_progress_callback'] = self.signals.import_progress + self.kwargs['current_file_progress_callback'] = self.signals.current_file_progress + # self.kwargs['current_import_file'] = self.signals.current_import_file @pyqtSlot() def run(self): diff --git a/_video.py b/_video.py index 9c57587..f81e0b1 100644 --- a/_video.py +++ b/_video.py @@ -2,6 +2,7 @@ import sys import ffmpeg +import time class Video: def __init__(self,max_width=1024,*args,**kwargs): @@ -12,15 +13,63 @@ class Video: self.out = 'thumbnail.jpg' self.max_width = max_width + self.stream = { + 'video': {}, + 'format': {}, + 'audio': {} + } + + self.stream['video']['duration'] = '' + self.stream['video']['creation_time'] = '' + self.stream['video']['encoding_brand'] = '' + self.stream['video']['codec_name'] = '' + self.stream['video']['codec_long_name'] = '' + self.stream['video']['profile'] = '' + self.stream['video']['size'] = {} + self.stream['video']['size']['width'] = int() + self.stream['video']['size']['height'] = int() + self.stream['video']['display_aspect_ratio'] = str() + self.stream['video']['level'] = '' + self.stream['video']['color_range'] = '' + self.stream['video']['field_order'] = '' + self.stream['video']['is_avc'] = '' + self.stream['video']['r_frame_rate'] = '' # -> expect for 120fps: 120000/1001 + self.stream['video']['avg_frame_rate'] = '' # -> expect for 120fps: 120000/1001 + self.stream['video']['time_base'] = '' # -> expect for 120fps: 1/120000 + self.stream['video']['bit_rate'] = '' + self.stream['video']['bits_per_raw_sample'] = '' + self.stream['video']['nb_frames'] = '' + self.stream['video']['language'] = '' + self.stream['video']['handler_name'] = '' + self.stream['video']['encoder'] = '' + self.stream['video']['pix_fmt'] = '' + + self.stream['format'] = {} + self.stream['format']['major_brand'] = '' + self.stream['format']['compatible_brands'] = '' + self.stream['format']['creation_time'] = '' + + self.stream['audio'] = {} + self.stream['audio']['codec_long_name'] = '' + self.stream['audio']['channels'] = '' + self.stream['audio']['sample_fmt'] = '' + self.stream['audio']['sample_rate'] = '' + self.stream['audio']['bits_per_sample'] = '' + self.stream['audio']['bit_rate'] = '' + + @staticmethod + def convert_from_seconds(s): + seconds = s + return time.strftime("%H:%M:%S", time.gmtime(seconds)) + def gen_video_thumbnail(self): """ Generate a thumbnail from a video """ - - probe = ffmpeg.probe(self.file) - time = float(probe['streams'][0]['duration']) // 5 - v_width = probe['streams'][0]['width'] - width = self.set_width(v_width) + self.get_video_meta() + time = self.stream['video']['seconds'] // 5 + v_width = int(self.stream['video']['size']['width']) + width = self.set_thumb_width(v_width) try: ( @@ -35,10 +84,44 @@ class Video: return self.out - def set_width(self,v_width): + def set_thumb_width(self, v_width): if v_width > self.max_width: width = self.max_width else: width = v_width - return width \ No newline at end of file + return width + + def get_video_meta(self): + probe = ffmpeg.probe(self.file) + + if 'video' == probe['streams'][0]['codec_type'].lower(): + video_stream = probe['streams'][0] + elif 'video' == probe['streams'][1]['codec_type'].lower(): + video_stream = probe['streams'][1] + elif 'video' == probe['streams'][2]['codec_type'].lower(): + video_stream = probe['streams'][2] + + if 'audio' == probe['streams'][0]['codec_type'].lower(): + audio_stream = probe['streams'][0] + elif 'audio' == probe['streams'][1]['codec_type'].lower(): + audio_stream = probe['streams'][1] + elif 'audio' == probe['streams'][2]['codec_type'].lower(): + audio_stream = probe['streams'][2] + + format_stream = probe['format'] + + self.stream['video']['size']['width'] = video_stream['width'] + self.stream['video']['size']['height'] = video_stream['height'] + self.stream['video']['r_frame_rate'] = video_stream['r_frame_rate'] + self.stream['video']['bits_per_raw_sample'] = video_stream['bits_per_raw_sample'] + self.stream['video']['seconds'] = float(video_stream['duration']) + self.stream['video']['duration'] = self.convert_from_seconds( + self.stream['video']['seconds'] + ) + self.stream['video']['encoding_brand'] = format_stream['tags']['major_brand'] + self.stream['video']['codec_long_name'] = video_stream['codec_long_name'] + self.stream['video']['profile'] = video_stream['profile'] + self.stream['video']['pix_fmt'] = video_stream['pix_fmt'] + + return self.stream diff --git a/import_dialogue.ui b/import_dialogue.ui new file mode 100644 index 0000000..5d24602 --- /dev/null +++ b/import_dialogue.ui @@ -0,0 +1,156 @@ + + + DialogImport + + + + 0 + 0 + 640 + 880 + + + + Dialog + + + + + 20 + 20 + 601 + 61 + + + + + + + % + + + + + + + % + + + + + + + 24 + + + + + + + Import Progress + + + + + + + QFrame::NoFrame + + + + + + + QFrame::NoFrame + + + + + + + 24 + + + + + + + Current File Progress + + + + + + + + + 20 + 90 + 601 + 80 + + + + + + + + 24 + true + + + + Importing: + + + + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + 20 + 180 + 181 + 30 + + + + + 28 + + + + Imported: + + + + + + 15 + 211 + 611 + 651 + + + + QAbstractItemView::NoSelection + + + + + + diff --git a/scripts/gen_gui_py.sh b/scripts/gen_gui_py.sh index 8be5d38..2d7b022 100755 --- a/scripts/gen_gui_py.sh +++ b/scripts/gen_gui_py.sh @@ -1 +1,2 @@ pyuic6 BitMover.ui -o _BitMover_MainWindow.py +pyuic6 import_dialogue.ui -o _import_dialog_Window.py