dev #1

Merged
kkenny merged 8 commits from dev into main 2024-10-07 14:51:01 -04:00
13 changed files with 481 additions and 419 deletions
Showing only changes of commit 410a0e3e87 - Show all commits

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import sys import sys
import time
from PyQt6.QtCore import QThreadPool from PyQt6.QtCore import QThreadPool
from PyQt6.QtGui import QIcon, QPixmap from PyQt6.QtGui import QIcon, QPixmap
@ -11,15 +12,20 @@ from _configure import CONFIG_FILE, Configure
from _find_files import FindFiles from _find_files import FindFiles
from _find_files_dialog import FindProgress from _find_files_dialog import FindProgress
from _import_dialog import DialogImport from _import_dialog import DialogImport
from _media_import import MediaImporter # from _media_import import MediaImporter
from _preview import MediaPreview from _preview import MediaPreview
from _thread_my_stuff import Worker from _thread_my_stuff import Worker
from _media_file import MediaFile
from _file_stuff import (path_exists,
is_dir,
is_file)
from _hashing import hash_path
basedir = os.path.dirname(__file__) basedir = os.path.dirname(__file__)
# TODO: verify source dir actually exists # TODO: verify source dir actually exists
# Subclass QMainWindow to customize your application's main window
class MainWindow(QMainWindow, Ui_MainWindow): class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MainWindow,self).__init__(*args,**kwargs) super(MainWindow,self).__init__(*args,**kwargs)
@ -27,22 +33,49 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.setWindowTitle("BitMover") self.setWindowTitle("BitMover")
self.setWindowIcon(QIcon(os.path.join(basedir,'assets', 'forklift.ico'))) self.setWindowIcon(QIcon(os.path.join(basedir,'assets', 'forklift.ico')))
self.threadpool = QThreadPool() self.threadpool = QThreadPool()
c = Configure(CONFIG_FILE) self.config = None
self.config = c.load_config() self.src_dir = None
self.src_dir = self.config['folders']['source']['base'] self.dst_dir = None
self.dst_dir = self.config['folders']['destination']['base'] self.verify_checksum = None
self.verify_checksum = self.config['verify_checksum'] self.cleanup_files = None
self.cleanup_files = self.config['cleanup_sd'] self.store_originals = None
self.store_originals = self.config['store_originals'] self.file_types = None
self.file_types = self.config['file_types'] self.source_path_hash = None
self.search_types = None
self.chunk_size = (16 * 1024) * 1
self.load_config()
self.destination_original_path = None
self.path_file_source = None
self.path_file_destination = None
self.path_file_destination_original = None
# File Stuff # File Stuff
self.total_files = 0 self.total_files = 0
self.file_total = 0
self.files = {} self.files = {}
self.event_name = None
self.imp_dialog = DialogImport() self.imp_dialog = DialogImport()
self.find_files_dialog = FindProgress() self.find_files_dialog = FindProgress()
self.widgets_config() self.widgets_config()
def load_config(self):
try:
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.get('verify_checksum', False)
self.cleanup_files = self.config.get('cleanup_sd', False)
self.store_originals = self.config.get('store_originals', False)
self.file_types = self.config.get('file_types', {})
except KeyError as e:
log.error(f"Missing configuration key: {e}")
sys.exit(1) # or provide a fallback
def widgets_config(self): def widgets_config(self):
# Button Setup # Button Setup
self.pushButton_src_browse.clicked.connect(self.select_src_directory) self.pushButton_src_browse.clicked.connect(self.select_src_directory)
@ -98,6 +131,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.dst_dir = directory self.dst_dir = directory
self.lineEdit_dst_dir.setText(self.dst_dir) self.lineEdit_dst_dir.setText(self.dst_dir)
def get_source_path_hash(self,f):
self.source_path_hash = hash_path(f)
return self.source_path_hash
def verify_checksum_changed(self): def verify_checksum_changed(self):
if self.checkBox_verify_checksum.isChecked(): if self.checkBox_verify_checksum.isChecked():
self.config['verify_checksum'] = True self.config['verify_checksum'] = True
@ -130,20 +167,20 @@ class MainWindow(QMainWindow, Ui_MainWindow):
'assets', 'assets',
'preview_placeholder.jpg')) 'preview_placeholder.jpg'))
def get_preview(self,i): def get_preview(self,path_file_name):
preview = MediaPreview(path_file_name = i.text(), preview = MediaPreview(path_file_name=path_file_name,
media_files = self.files) media_files=self.files)
return preview return preview
def update_preview(self,preview): def update_preview(self,preview):
self.set_thumbnail(preview.thumbnail,ratio=preview.thumbnail_ratio) self.set_thumbnail(preview.thumbnail,
ratio=preview.thumbnail_ratio)
def update_metadata(self,preview): def update_metadata(self,preview):
self.clear_metadata() self.clear_metadata()
path_hash = preview.source_path_hash path_hash = preview.source_path_hash
f = self.files[path_hash] f = self.files[path_hash]
f_date = f['date']['capture_date'] f_date = f['date']['capture_date']
f_video = f['video']
self.l_data_file_source_path.setText( self.l_data_file_source_path.setText(
self.files[path_hash]['folders']['source_path']) self.files[path_hash]['folders']['source_path'])
self.l_data_file_dest_path.setText( self.l_data_file_dest_path.setText(
@ -151,7 +188,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.l_meta_content_date_time_c.setText( self.l_meta_content_date_time_c.setText(
f"{f_date['y']}/{f_date['m']}/{f_date['d']}" f"{f_date['y']}/{f_date['m']}/{f_date['d']}"
) )
if preview.file_type == 'image': if f['file_type'] == 'image':
f_photo = f['image_meta']['photo']
self.l_meta_01.setText('Size') self.l_meta_01.setText('Size')
self.l_meta_02.setText('dpi') self.l_meta_02.setText('dpi')
self.l_meta_03.setText('ISO') self.l_meta_03.setText('ISO')
@ -160,16 +199,18 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.l_meta_06.setText('Camera') self.l_meta_06.setText('Camera')
self.l_meta_07.setText('Aperture') self.l_meta_07.setText('Aperture')
self.l_meta_08.setText('Megapixels') self.l_meta_08.setText('Megapixels')
self.l_meta_content_01.setText(str(f['photo']['size']['width_height'])) self.l_meta_content_01.setText(str(f_photo['size']['width_height']))
self.l_meta_content_02.setText(str(f['photo']['dpi'])) self.l_meta_content_02.setText(str(f_photo['dpi']))
self.l_meta_content_03.setText(str(f['photo']['iso'])) self.l_meta_content_03.setText(str(f_photo['iso']))
self.l_meta_content_04.setText(str(f['photo']['lens_model'])) self.l_meta_content_04.setText(str(f_photo['lens_model']))
self.l_meta_content_05.setText(str(f['photo']['focal_length'])) self.l_meta_content_05.setText(str(f_photo['lens_focal_length']))
self.l_meta_content_06.setText(str(f"{f['photo']['camera_brand']} {f['photo']['camera_model']}")) self.l_meta_content_06.setText(str("f_photo['camera_brand']} {f_photo['camera_model']}"))
self.l_meta_content_07.setText(str(f['photo']['aperture'])) self.l_meta_content_07.setText(str(f_photo['aperture']))
self.l_meta_content_08.setText(str(f['photo']['megapixels'])) self.l_meta_content_08.setText(str(f_photo['size']['megapixels']))
elif f['file_type'] == 'video':
f_video = f['video_meta']['video']
elif preview.file_type == 'video':
self.l_meta_01.setText('Size') self.l_meta_01.setText('Size')
self.l_meta_02.setText('Frames / Second') self.l_meta_02.setText('Frames / Second')
self.l_meta_03.setText('Bit Depth') self.l_meta_03.setText('Bit Depth')
@ -211,13 +252,43 @@ class MainWindow(QMainWindow, Ui_MainWindow):
if i is None: if i is None:
self.set_default_thumbnail() self.set_default_thumbnail()
else: else:
preview = self.get_preview(i) print(f'index changed to: {i.text()}')
preview = self.get_preview(i.text())
self.update_preview(preview) self.update_preview(preview)
self.update_metadata(preview) self.update_metadata(preview)
def get_event(self): def get_event(self):
event_name = self.eventName.text() self.event_name = self.eventName.text()
return event_name return self.event_name
def get_search_types(self):
self.search_types = []
if self.checkBox_search_for_images.isChecked():
self.search_types.append('image')
if self.checkBox_search_for_video.isChecked():
self.search_types.append('video')
if self.checkBox_search_for_audio.isChecked():
self.search_types.append('audio')
return self.search_types
def get_t_files(self):
file_total = 0
self.file_list.clear()
self.img_preview.setPixmap(QPixmap(os.path.join(basedir,
'assets',
'preview_placeholder.jpg')))
for folder, subfolders, filename in os.walk(self.src_dir):
for f_type in self.search_types:
for ext in self.file_types[f_type]:
for file in filename:
if file.lower().endswith(ext):
current_file = os.path.join(folder, file)
if is_file(current_file):
file_total += int(1)
else:
print(f"Skipping {current_file} as it does not look like a real file.")
return file_total
def set_total_files(self,t): def set_total_files(self,t):
total_file_count = t total_file_count = t
@ -228,7 +299,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
@staticmethod @staticmethod
def print_output(s): def print_output(s):
print(s) print(f'output: {s}')
def worker_thread_started(self): def worker_thread_started(self):
print('scan thread started') print('scan thread started')
@ -253,13 +324,30 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def gen_file_dict(self,d): def gen_file_dict(self,d):
self.files = d self.files = d
def process_file(self,p):
""" gather information and add to dictionary """
media_file = MediaFile(path_file_name=p,
config=self.config,
event_name=self.event_name)
i = self.get_source_path_hash(p)
print(f'processing: {p}\nhash: {i}')
self.files[i] = media_file.media
def find_files(self): def find_files(self):
""" find files to build a dictionary out of """ """ find files to build a dictionary out of """
self.files = {} self.files = {}
self.set_total_files(0) self.set_total_files(0)
self.get_event()
self.get_search_types()
self.file_total = self.get_t_files()
self.set_total_files(self.file_total)
file_finder = FindFiles() time.sleep(3)
file_finder = FindFiles(search_types=self.search_types,
src_dir=self.src_dir,
file_types=self.file_types,
file_total=self.file_total)
worker = Worker(file_finder.t_find_files) worker = Worker(file_finder.t_find_files)
worker.signals.started.connect( worker.signals.started.connect(
@ -270,10 +358,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.find_files_dialog.set_progress_finding_files) self.find_files_dialog.set_progress_finding_files)
worker.signals.found_file.connect( worker.signals.found_file.connect(
self.add_found_file_to_list) self.add_found_file_to_list)
worker.signals.found_file.connect(
self.process_file)
worker.signals.total_file_count.connect( worker.signals.total_file_count.connect(
self.set_total_files) self.set_total_files)
worker.signals.result.connect( # worker.signals.result.connect(
self.gen_file_dict) # self.gen_file_dict)
worker.signals.finished.connect( worker.signals.finished.connect(
self.thread_complete) self.thread_complete)
worker.signals.finished.connect( worker.signals.finished.connect(
@ -284,6 +374,108 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Execute. # Execute.
self.threadpool.start(worker) self.threadpool.start(worker)
# From _media_import.py
def is_video(self,f):
if self.files[f]['type'] == 'video':
r = True
else:
r = False
return r
def is_image(self,f):
if self.files[f]['type'] == 'image':
r = True
else:
r = False
return r
def t_copy_files(self,
import_progress_callback,
current_file_progress_callback,
imported_file_count_callback
):
""" Copy Files. """
count = int(0)
for file in self.files:
self.src_dir = self.files[file]['folders']['source_path']
self.dst_dir = self.files[file]['folders']['destination']
self.path_file_source = path.join(self.src_dir,
self.files[file]['name'])
self.path_file_destination = path.join(self.dst_dir,
self.files[file]['name'])
self.destination_original_path = path.join(self.dst_dir,
self.files[file]['folders']['destination_original'])
self.path_file_destination_original = path.join(self.dst_dir,
self.files[file]['folders']['destination_original'],
self.files[file]['name'])
self.imp_dialog.set_importing_file(self.path_file_source)
self.copy_a_file(
file,
current_file_progress_callback)
if self.check_store_original(file) is True:
self.copy_a_file(file,
current_file_progress_callback)
count += 1
imported_file_count_callback.emit(count)
import_progress_callback.emit(round((count / self.file_total) * 100, 1))
self.imp_dialog.add_to_imported_list(self.path_file_source)
def copy_a_file(self,
_f,
current_file_progress_callback):
size = path.getsize(self.path_file_source)
create_folder(self.dst_dir)
self.check_duplicate(_f)
if self.is_video(_f):
self.chunk_size = (1024 * 1024) * 5
else:
self.chunk_size = (16 * 1024) * 1
with open(self.path_file_source, 'rb') as fs:
with open(self.path_file_destination, 'wb') as fd:
while True:
chunk = fs.read(self.chunk_size)
if not chunk:
break
fd.write(chunk)
dst_size = path.getsize(self.path_file_destination)
current_file_progress_callback.emit(round((dst_size / size) * 100, 1))
def check_store_original(self,_f):
if self.config['store_originals'] is True:
if self.is_image(_f):
self.dst_dir = self.destination_original_path
self.path_file_destination = self.path_file_destination_original
r = True
else:
r = False
else:
r = False
return r
def check_duplicate(self,_f):
if path_exists(self.path_file_destination):
check_match = cmp_files(self.path_file_source, self.path_file_destination)
if check_match is False:
print(f'\nFound duplicate for {self.path_file_source}, renaming destination with hash appended.')
base, extension = path.splitext(self.files[_f]['name'])
f_xxhash = xx_hash(self.path_file_destination)
file_name_hash = base + '_' + f_xxhash + extension
rename(self.path_file_destination, path.join(self.dst_dir, file_name_hash))
# END from _media_import.py
def import_files(self): def import_files(self):
""" """
Import found files Import found files
@ -294,23 +486,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.imp_dialog.set_progress_importing(0) self.imp_dialog.set_progress_importing(0)
self.imp_dialog.set_progress_current_file(0) self.imp_dialog.set_progress_current_file(0)
worker = Worker(self.t_copy_files)
importer = MediaImporter()
worker = Worker(importer.t_copy_files)
worker.signals.started.connect( worker.signals.started.connect(
self.worker_thread_started) self.worker_thread_started)
worker.signals.started.connect( worker.signals.started.connect(
self.imp_dialog.open_import_dialog) self.imp_dialog.open_import_dialog)
worker.signals.import_progress.connect( worker.signals.import_progress.connect(
self.imp_dialog.set_progress_importing) self.imp_dialog.set_progress_importing)
worker.signals.current_file_progress.connect( worker.signals.current_file_progress.connect(
self.imp_dialog.set_progress_current_file) self.imp_dialog.set_progress_current_file)
worker.signals.imported_file_count.connect( worker.signals.imported_file_count.connect(
self.set_imported_files) self.set_imported_files)
worker.signals.result.connect( worker.signals.result.connect(
self.print_output) self.print_output)
worker.signals.finished.connect( worker.signals.finished.connect(
@ -323,8 +509,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def verify_checksum(self): def verify_checksum(self):
# fh_match = FileHash() # fh_match = FileHash()
print(self.config) print(f'verify_checksum,self.config: {self.config}')
app = QApplication(sys.argv) app = QApplication(sys.argv)

View File

@ -0,0 +1,47 @@
m = {
'f5c7c1a4e55d6288': {
'date': {
'capture_date': {
'y': '2024',
'm': '09',
'd': '08'
}
},
'event': {
'name': ''
},
'extension': 'JPG',
'folders': {
'destination': '/Users/kkenny/import/testing_dst/2024/2024-09/2024-09-08/PHOTO/JPG',
'destination_original': '',
'source_path': '/Users/kkenny/import/testing_src'
},
'name': 'DSC_6481.JPG',
'source_path_hash': 'f5c7c1a4e55d6288',
'type': 'image',
'image_meta': {
'photo': {
'aperture': (0x829D) Ratio = 11 @ 736,
'camera_brand': (0x010F)ASCII = NIKON CORPORATION @ 148,
'camera_model': (0x0110)ASCII = NIKON D5100 @ 168,
'dpi': < bound method PhotoFile.get_dpi of < _photo.PhotoFile object at 0x130f05fd0 >>,
'is_jpg': True,
'is_raw': False,
'iso': (0x8827) Short = 3200 @ 274,
'lens_make': None,
'lens_model': None,
'lens_focal_length': (0x920A) Ratio = 135 @ 808,
'size': {
'height': 3264,
'width': 4928,
'width_height': '4928x3264',
'megapixels': 16084992,
'ratio': 1.5098039215686274
}
}
},
'video_meta': None,
'audio_meta': None
}
}

View File

@ -1,76 +1,61 @@
#!/usr/bin/env python #!/usr/bin/env python
import os.path import os.path
import ffmpeg import ffmpeg
import time
from datetime import datetime from datetime import datetime
from _time_and_date_utils import convert_from_seconds
from _media_file import MediaFile class AudioFile:
def __init__(self,path_file_name,*args,**kwargs):
class AudioFile(MediaFile): # super(AudioFile, self).__init__(*args, **kwargs)
def __init__(self,*args,**kwargs): self.path_file_name = path_file_name
super(AudioFile, self).__init__(*args, **kwargs)
self.args = args
self.kwargs = kwargs
self.file = kwargs['file']
self.probe = ffmpeg.probe(self.path_file_name) self.probe = ffmpeg.probe(self.path_file_name)
self.audio_capture_date = self.get_audio_capture_date() self.audio_capture_date = self.get_audio_capture_date()
self.video_stream = None
self.audio_stream = None
self.format_stream = None
if 'video' == self.probe['streams'][0]['codec_type'].lower(): for i in self.probe['streams']:
self.video_stream = self.probe['streams'][0] if self.video_stream is None:
elif 'video' == self.probe['streams'][1]['codec_type'].lower(): if 'video' == i['codec_type'].lower():
self.video_stream = self.probe['streams'][1] self.video_stream = i
elif 'video' == self.probe['streams'][2]['codec_type'].lower(): if self.audio_stream is None:
self.video_stream = self.probe['streams'][2] if 'audio' == i['codec_type'].lower():
self.audio_stream = i
if 'audio' == self.probe['streams'][0]['codec_type'].lower(): try:
self.audio_stream = self.probe['streams'][0] self.format_stream = self.probe['format']
elif 'audio' == self.probe['streams'][1]['codec_type'].lower(): except AttributeError as e:
self.audio_stream = self.probe['streams'][1] print(f"{e}: Audio file has no format string")
elif 'audio' == self.probe['streams'][2]['codec_type'].lower(): self.format_stream = None
self.audio_stream = self.probe['streams'][2]
self.format_stream = self.probe['format']
self.stream = {} self.stream = {}
@staticmethod
def convert_from_seconds(seconds):
return time.strftime("%H:%M:%S", time.gmtime(seconds))
def get_audio_capture_date(self): def get_audio_capture_date(self):
#TODO: refactor this try/except logic. #TODO: refactor this try/except logic.
try: stamp = None
stamp = datetime.strptime( if self.format_stream is not None:
self.format_stream['tags']['date'],
'%Y-%m-%d'
)
except KeyError:
try: try:
stamp = datetime.fromtimestamp(os.path.getctime(self.path_file_name))
except:
stamp = datetime.strptime( stamp = datetime.strptime(
str('1900:01:01 00:00:00'), self.format_stream['tags']['date'],'%Y-%m-%d')
'%Y:%m:%d %H:%M:%S' except KeyError:
) try:
stamp = datetime.fromtimestamp(os.path.getctime(self.path_file_name))
except:
stamp = datetime.strptime(
str('1900:01:01 00:00:00'),
'%Y:%m:%d %H:%M:%S'
)
return stamp return stamp
def get_video_meta(self): def get_audio_meta(self):
self.stream = { self.stream = {
'video': { 'audio': {
'bits_per_raw_sample': self.video_stream['bits_per_raw_sample'], 'audio_channels': self.audio_stream['channels'],
'codec_long_name': self.video_stream['codec_long_name'], 'bits_per_sample': self.audio_stream['bits_per_raw_sample'],
'duration': self.convert_from_seconds(float(self.video_stream['duration'])), 'codec_long_name': self.audio_stream['codec_long_name'],
'encoding_brand': self.format_stream['tags']['major_brand'], 'duration': convert_from_seconds(float(self.audio_stream['duration'])),
'pix_fmt': self.video_stream['pix_fmt'], 'encoding_brand': self.format_stream['tags']['encoded_by'],
'profile': self.video_stream['profile'], 'sample_rate': self.audio_stream['sample_rate']
'r_frame_rate': self.video_stream['r_frame_rate'],
'size': {
'width_height': self.size,
'height': self.video_stream['height'],
'width': self.video_stream['width']
}
}, },
'audio': {}, 'video': {},
'format': {} 'format': {}
} }

View File

@ -96,3 +96,11 @@ def validate_config_dir_access(config):
accessible = True accessible = True
return accessible return accessible
def get_file_name(path_file_name):
return os.path.basename(path_file_name)
def get_dotted_file_ext(file_name):
return os.path.splitext(file_name)[1]
def get_file_ext(file_name):
return get_dotted_file_ext(file_name).split('.')[1]

View File

@ -1,84 +1,51 @@
import os import os
from PyQt6.QtGui import QPixmap
from _file_stuff import is_file from _file_stuff import is_file
from BitMover_ui import MainWindow
from _media_file import MediaFile
basedir = os.path.dirname(__file__) basedir = os.path.dirname(__file__)
class FindFiles(MainWindow): class FindFiles:
def __init__(self): def __init__(self,
super(FindFiles,self).__init__() search_types,
self.search_types = None src_dir,
self.event = self.get_event() file_total,
self.src_dir = self.config['folders']['source']['base'] file_types,
self.dst_dir = self.config['folders']['destination']['base'] *args,
self.file_types = self.config['file_types'] **kwargs):
super(FindFiles,self).__init__(*args,**kwargs)
self.file_total = 0 self.file_total = file_total
self.file_list = {} self.src_dir = src_dir
self.search_types = search_types
self.file_types = file_types
def get_search_types(self): self.file_name = None
self.search_types = [] self.path_file_source = None
if self.checkBox_search_for_images.isChecked(): self.file_type = None
self.search_types.append('image') self.file_ext = None
if self.checkBox_search_for_video.isChecked():
self.search_types.append('video')
if self.checkBox_search_for_audio.isChecked():
self.search_types.append('audio')
return self.search_types
def get_t_files(self):
self.file_list.clear()
self.img_preview.setPixmap(QPixmap(os.path.join(basedir,
'assets',
'preview_placeholder.jpg')))
for folder, subfolders, filename in os.walk(self.src_dir):
for f_type in self.search_types:
for ext in self.file_types[f_type]:
for file in filename:
if file.lower().endswith(ext):
current_file = os.path.join(folder, file)
if is_file(current_file):
self.file_total += int(1)
else:
print(f"Skipping {current_file} as it does not look like a real file.")
def t_find_files(self, def t_find_files(self,
progress_callback, progress_callback,
found_file, current_file_progress_callback,
total_file_count): imported_file_count_callback,
found_file_callback,
total_file_count_callback):
file_count = int(0) file_count = int(0)
self.search_types = self.get_search_types()
self.get_t_files()
if len(self.search_types) > 0: if len(self.search_types) > 0:
for folder, subfolders, filename in os.walk(self.src_dir): for folder, subfolders, filename in os.walk(self.src_dir):
for f_type in self.search_types: for self.file_type in self.search_types:
for ext in self.file_types[f_type]: for self.file_ext in self.file_types[self.file_type]:
for file in filename: for self.file_name in filename:
if file.lower().endswith(ext): if self.file_name.lower().endswith(self.file_ext):
current_file = os.path.join(folder, file) self.path_file_source = os.path.join(folder, self.file_name)
if is_file(current_file): if is_file(self.path_file_source):
file_count += int(1) file_count += int(1)
self.process_file(current_file) # process_file(current_file)
found_file.emit(current_file) found_file_callback.emit(self.path_file_source)
else: else:
print(f"Skipping {current_file} as it does not look like a real file.") print(f"Skipping {self.path_file_source} as it does not look like a real file.")
total_file_count.emit(file_count) total_file_count_callback.emit(file_count)
progress_callback.emit(round((file_count / self.file_total) * 100, 0)) progress_callback.emit(round((file_count / self.file_total) * 100, 0))
else: else:
print("Nothing to search for.") print("Nothing to search for.")
return self.file_list
def process_file(self, p):
""" gather information and add to dictionary """
media_file = MediaFile(p)
i = media_file.source_path_hash
self.file_list[i] = media_file.media_meta()

View File

@ -58,5 +58,5 @@ def validate_xx_checksums(f):
print(f'FATAL: Checksum validation failed for: \ print(f'FATAL: Checksum validation failed for: \
{f[file]["name"]} \n{c[i]}\n is not equal to \n{c[p]}\n') {f[file]["name"]} \n{c[i]}\n is not equal to \n{c[p]}\n')
print('\n File Meta:\n') print('\n File Meta:\n')
print(f[file]) print(f'f[file]: {f[file]}')
i = i + 1 i = i + 1

View File

@ -1,14 +1,12 @@
import exifread import exifread
from datetime import datetime from _time_and_date_utils import get_img_date
from _media_file import MediaFile
class ImageTag(MediaFile): class ImageTag:
def __init__(self,path_file_name=None,*args,**kwargs): def __init__(self,path_file_name,*args,**kwargs):
super(ImageTag,self).__init__(*args,**kwargs) super(ImageTag,self).__init__(*args,**kwargs)
if path_file_name is not None: self.path_file_name = path_file_name
self.path_file_name = path_file_name
self.date_time_original = self.get_img_date()
self.image_tags = self.get_image_tags() self.image_tags = self.get_image_tags()
self.date_time_original = get_img_date(self.image_tags)
def get_image_tag(self,t): def get_image_tag(self,t):
tag_data = None tag_data = None
@ -18,44 +16,8 @@ class ImageTag(MediaFile):
break break
return tag_data return tag_data
@staticmethod
def set_generic_date_time(date='1900:01:01',
time='00:00:00',
f='%Y:%m:%d %H:%M:%S'):
t = datetime.strptime(str(f'{date} {time}'), f)
return t
def get_image_tags(self): def get_image_tags(self):
print(f'get_image_tags, self.path_file_name: {self.path_file_name}')
with open(self.path_file_name,'rb') as f: with open(self.path_file_name,'rb') as f:
tags = exifread.process_file(f) tags = exifread.process_file(f)
f.close() return tags
return tags
def process_img_date_tag(self, tag, date_time_format):
t = None
try:
t = datetime.strptime(str(self.image_tags[tag]), date_time_format)
except:
pass
return t
def get_img_date(self):
t = None
dt_tag = None
for tag in self.image_tags:
if 'DateTime' in tag:
t = tag
break
if t is None:
dt_tag = self.set_generic_date_time()
else:
for f in ['%Y:%m:%d %H:%M:%S',
'%Y/%m/%d %H:%M:%S',
'%Y-%m-%d-%H-%M-%S']:
dt_tag = self.process_img_date_tag(t,f)
if dt_tag is not None:
break
if dt_tag is None:
dt_tag = self.set_generic_date_time()
return dt_tag

View File

@ -2,56 +2,50 @@ import os.path
import sys import sys
from _audio import AudioFile from _audio import AudioFile
from BitMover_ui import MainWindow
from _hashing import hash_path from _hashing import hash_path
from _video import VideoFile from _video import VideoFile
from _photo import PhotoFile from _photo import PhotoFile
from _file_stuff import (get_file_name,
get_file_ext,
get_dotted_file_ext)
from datetime import datetime from datetime import datetime
class MediaFile(MainWindow): class MediaFile:
def __init__(self,path_file_name,*args,**kwargs): def __init__(self,
super(MediaFile,self).__init__(*args,**kwargs) path_file_name,
self.event_name = self.get_event() config,
self.src_dir = self.config['folders']['source']['base'] event_name,
self.base_dst_dir = self.config['folders']['destination']['base'] *args,**kwargs):
self.file_types = self.config['file_types'] # super(MediaFile,self).__init__(*args,**kwargs)
self.path_file_name = path_file_name self.path_file_name = path_file_name
self.config = config
self.event_name = str(event_name)
self.store_originals = self.config['store_originals']
self.src_dir = self.config['folders']['source']['base']
self.base_dst_dir = self.config['folders']['destination']['base']
self.source_path_hash = hash_path(self.path_file_name) self.source_path_hash = hash_path(self.path_file_name)
self.file_name = self.get_file_name() self.file_name = get_file_name(self.path_file_name)
self.dotted_file_ext = self.get_dotted_file_ext() self.dotted_file_ext = get_dotted_file_ext(self.file_name)
self.file_ext = self.get_file_ext() self.file_ext = get_file_ext(self.file_name)
self.file_type = self.get_file_type() self.file_type = self.get_file_type()
self.capture_date = self.get_capture_date() self.capture_date = self.get_capture_date()
self.capture_date_year = self.capture_date[0] self.capture_date_year = self.capture_date[0]
self.capture_date_month = self.capture_date[1] self.capture_date_month = self.capture_date[1]
self.capture_Date_day = self.capture_date[2] self.capture_date_day = self.capture_date[2]
self.dst_dir = self.set_destination_path() self.dst_dir = self.set_destination_path()
self.destination_originals_path = '' self.destination_originals_path = ''
self.media = {} self.media = {}
self.video_media = VideoFile() self.media_meta = self.get_media_meta()
self.audio_media = AudioFile()
self.photo_media = PhotoFile()
def get_file_name(self): # video_media = VideoFile(path_file_name=self.path_file_name)
return os.path.basename(self.path_file_name) # audio_media = AudioFile(path_file_name=self.path_file_name)
# photo_media = PhotoFile(path_file_name=self.path_file_name)
def get_dotted_file_ext(self): def get_file_type(self):
return os.path.splitext(self.file_name)[1].lower()
def get_file_ext(self):
return self.dotted_file_ext.split('.')[1]
def get_file_type(self,file=None):
""" determine if the extension is in the list of file types """ """ determine if the extension is in the list of file types """
if file is not None:
self.file_name = file
if not self.file_ext:
self.file_ext = self.get_file_ext()
for t in self.config['file_types']: for t in self.config['file_types']:
for e in self.config['file_types'][t]: for e in self.config['file_types'][t]:
if self.file_ext.lower().endswith(e): if self.file_ext.lower().endswith(e.lower()):
return t return t
def set_destination_path(self): def set_destination_path(self):
@ -62,7 +56,7 @@ class MediaFile(MainWindow):
""" """
p1 = os.path.join(self.base_dst_dir,self.capture_date_year) p1 = os.path.join(self.base_dst_dir,self.capture_date_year)
p2 = f'{self.capture_date_year}-{self.capture_date_month}' p2 = f'{self.capture_date_year}-{self.capture_date_month}'
p3 = f'{self.capture_date_year}-{self.capture_date_month}-{self.capture_Date_day}' p3 = f'{self.capture_date_year}-{self.capture_date_month}-{self.capture_date_day}'
p = f'{p1}/{p2}/{p3}' # <--- Dumb. p = f'{p1}/{p2}/{p3}' # <--- Dumb.
if self.event_name: if self.event_name:
@ -95,12 +89,16 @@ class MediaFile(MainWindow):
def get_capture_date(self): def get_capture_date(self):
""" get capture date from meta """ """ get capture date from meta """
if self.file_type == 'image': if self.file_type == 'image':
stamp = self.photo_media.photo_capture_date photo_media = PhotoFile(path_file_name=self.path_file_name)
stamp = photo_media.photo_capture_date
elif self.file_type == 'video': elif self.file_type == 'video':
stamp = self.video_media.get_video_capture_date() video_media = VideoFile(path_file_name=self.path_file_name)
stamp = video_media.get_video_capture_date()
elif self.file_type == 'audio': elif self.file_type == 'audio':
stamp = self.audio_media.get_audio_capture_date() audio_media = AudioFile(path_file_name=self.path_file_name)
stamp = audio_media.get_audio_capture_date()
else: else:
try: try:
stamp = datetime.fromtimestamp( stamp = datetime.fromtimestamp(
@ -117,13 +115,13 @@ class MediaFile(MainWindow):
day = stamp.strftime("%d") day = stamp.strftime("%d")
return year, month, day return year, month, day
def media_meta(self): def get_media_meta(self):
self.media = { self.media = {
'date': { 'date': {
'capture_date': { 'capture_date': {
'y': self.capture_date_year, 'y': self.capture_date_year,
'm': self.capture_date_month, 'm': self.capture_date_month,
'd': self.capture_Date_day 'd': self.capture_date_day
} }
}, },
'event': { 'event': {
@ -137,16 +135,26 @@ class MediaFile(MainWindow):
}, },
'name': self.file_name, 'name': self.file_name,
'source_path_hash': self.source_path_hash, 'source_path_hash': self.source_path_hash,
'type': self.file_type 'file_type': self.file_type
} }
if self.file_type == 'video': if self.file_type == 'video':
self.media['video_meta'] = self.video_media.get_video_meta() video_media = VideoFile(path_file_name=self.path_file_name)
self.media['video_meta'] = video_media.video_meta()
self.media['image_meta'] = None self.media['image_meta'] = None
self.media['audio_meta'] = None self.media['audio_meta'] = None
if self.file_type == 'image': if self.file_type == 'image':
photo = PhotoFile() photo_media = PhotoFile(path_file_name=self.path_file_name)
self.media['image_meta'] = photo.get_photo_meta() self.media['image_meta'] = photo_media.get_photo_meta()
self.media['video_meta'] = None self.media['video_meta'] = None
self.media['audio_meta'] = None self.media['audio_meta'] = None
if self.file_type == 'audio':
audio_media = AudioFile(path_file_name=self.path_file_name)
self.media['audio_meta'] = audio_media.get_audio_meta()
self.media['image_meta'] = None
self.media['video_meta'] = None
print(f'MediaFile, self.media: {self.media}')
return self.media

View File

@ -14,103 +14,3 @@ class MediaImporter(MainWindow):
self.path_file_destination_original = None self.path_file_destination_original = None
self.path_file_destination = None self.path_file_destination = None
def is_video(self,f):
if self.files[f]['type'] == 'video':
r = True
else:
r = False
return r
def is_image(self,f):
if self.files[f]['type'] == 'image':
r = True
else:
r = False
return r
def t_copy_files(self,
import_progress_callback,
current_file_progress_callback,
imported_file_count_callback
):
""" Copy Files. """
count = int(0)
for file in self.files:
self.src_dir = self.files[file]['folders']['source_path']
self.dst_dir = self.files[file]['folders']['destination']
self.path_file_source = path.join(self.src_dir,
self.files[file]['name'])
self.path_file_destination = path.join(self.dst_dir,
self.files[file]['name'])
self.destination_original_path = path.join(self.dst_dir,
self.files[file]['folders']['destination_original'])
self.path_file_destination_original = path.join(self.dst_dir,
self.files[file]['folders']['destination_original'],
self.files[file]['name'])
self.imp_dialog.set_importing_file(self.path_file_source)
self.copy_a_file(
file,
current_file_progress_callback)
if self.check_store_original(file) is True:
self.copy_a_file(file,
current_file_progress_callback)
count += 1
imported_file_count_callback.emit(count)
import_progress_callback.emit(round((count / self.file_total) * 100, 1))
self.imp_dialog.add_to_imported_list(self.path_file_source)
def copy_a_file(self,
_f,
current_file_progress_callback):
size = path.getsize(self.path_file_source)
create_folder(self.dst_dir)
self.check_duplicate(_f)
if self.is_video(_f):
self.chunk_size = (1024 * 1024) * 5
else:
self.chunk_size = (16 * 1024) * 1
with open(self.path_file_source, 'rb') as fs:
with open(self.path_file_destination, 'wb') as fd:
while True:
chunk = fs.read(self.chunk_size)
if not chunk:
break
fd.write(chunk)
dst_size = path.getsize(self.path_file_destination)
current_file_progress_callback.emit(round((dst_size / size) * 100, 1))
def check_store_original(self,_f):
if self.config['store_originals'] is True:
if self.is_image(_f):
self.dst_dir = self.destination_original_path
self.path_file_destination = self.path_file_destination_original
r = True
else:
r = False
else:
r = False
return r
def check_duplicate(self,_f):
if path_exists(self.path_file_destination):
check_match = cmp_files(self.path_file_source, self.path_file_destination)
if check_match is False:
print(f'\nFound duplicate for {self.path_file_source}, renaming destination with hash appended.')
base, extension = path.splitext(self.files[_f]['name'])
f_xxhash = xx_hash(self.path_file_destination)
file_name_hash = base + '_' + f_xxhash + extension
rename(self.path_file_destination, path.join(self.dst_dir, file_name_hash))

View File

@ -1,21 +1,22 @@
#!/usr/bin/env python #!/usr/bin/env python
from PIL import Image from PIL import Image
from _raw_photo import get_raw_image_dimensions
from _media_file import MediaFile
from _image_tag import ImageTag from _image_tag import ImageTag
# https://exiftool.org/TagNames/EXIF.html # https://exiftool.org/TagNames/EXIF.html
# https://exiftool.org/TagNames/Sony.html # https://exiftool.org/TagNames/Sony.html
class PhotoFile(MediaFile): class PhotoFile:
def __init__(self,*args,**kwargs): def __init__(self,path_file_name,*args,**kwargs):
super(PhotoFile, self).__init__(*args, **kwargs) super(PhotoFile, self).__init__(*args, **kwargs)
self.path_file_name = path_file_name
self.args = args self.args = args
self.kwargs = kwargs self.kwargs = kwargs
self.image_tag = ImageTag() self.img = None
self.is_jpg = False self.image_tag = ImageTag(path_file_name=self.path_file_name)
self.is_raw = False self.is_jpg = self.is_photo_jpeg()
self.is_raw = self.is_photo_raw()
self.size_width = self.get_photo_width() self.size_width = self.get_photo_width()
self.size_height = self.get_photo_height() self.size_height = self.get_photo_height()
self.size = self.get_photo_size() self.size = self.get_photo_size()
@ -29,19 +30,20 @@ class PhotoFile(MediaFile):
self.lens_make = self.get_lens_make() self.lens_make = self.get_lens_make()
self.lens_model = self.get_lens_model() self.lens_model = self.get_lens_model()
self.focal_length = self.get_focal_length() self.focal_length = self.get_focal_length()
self.img = Image.open(self.path_file_name)
self.photo_meta = {} self.photo_meta = {}
self.photo_capture_date = self.get_photo_capture_date() self.photo_capture_date = self.get_photo_capture_date()
def get_photo_capture_date(self): def get_photo_capture_date(self):
return self.image_tag.date_time_original return self.image_tag.date_time_original
def is_photo_raw(self): def is_photo_raw(self):
# TODO: Actually inspect the mime type instead of relying on an extension. # TODO: Actually inspect the mime type instead of relying on an extension.
if self.file_name.lower().endswith('jpg') \ if self.path_file_name.lower().endswith('jpg') \
or self.file_name.lower().endswith('.jpeg'): or self.path_file_name.lower().endswith('.jpeg'):
self.is_raw = False self.is_raw = False
self.is_jpg = True self.is_jpg = True
self.img = Image.open(self.path_file_name)
else: else:
self.is_raw = True self.is_raw = True
self.is_jpg = False self.is_jpg = False
@ -54,10 +56,22 @@ class PhotoFile(MediaFile):
return self.is_jpg return self.is_jpg
def get_photo_width(self): def get_photo_width(self):
return self.img.width if self.is_jpg:
width = self.img.width
elif self.is_raw:
width = get_raw_image_dimensions(self.path_file_name)[1]
else:
width = None
return width
def get_photo_height(self): def get_photo_height(self):
return self.img.height if self.is_jpg:
height = self.img.height
elif self.is_raw:
height = get_raw_image_dimensions(self.path_file_name)[0]
else:
height = None
return height
def get_photo_size(self): def get_photo_size(self):
if self.size_width is None: if self.size_width is None:
@ -87,7 +101,7 @@ class PhotoFile(MediaFile):
return self.image_tag.get_image_tag( 'iso') return self.image_tag.get_image_tag( 'iso')
def get_aperture(self): def get_aperture(self):
return self.image_tag.get_image_tag( 'fnumber') return self.image_tag.get_image_tag('fnumber')
def get_camera_brand(self): def get_camera_brand(self):
tag = self.image_tag.get_image_tag('Make') tag = self.image_tag.get_image_tag('Make')
@ -122,8 +136,6 @@ class PhotoFile(MediaFile):
def get_photographer(self): def get_photographer(self):
pass pass
def get_orientation(self): def get_orientation(self):
pass pass
@ -134,7 +146,7 @@ class PhotoFile(MediaFile):
pass pass
def get_photo_meta(self): def get_photo_meta(self):
self.photo_meta = { photo_meta = {
'photo': { 'photo': {
'aperture': self.aperture, 'aperture': self.aperture,
'camera_brand': self.camera_brand, 'camera_brand': self.camera_brand,
@ -155,5 +167,5 @@ class PhotoFile(MediaFile):
} }
} }
} }
return self.photo_meta return photo_meta

View File

@ -1,19 +1,21 @@
#!/usr/bin/env python #!/usr/bin/env python
from PIL import Image from PIL import Image
from BitMover_ui import MainWindow
from _media_file import MediaFile from _media_file import MediaFile
from _raw_photo import extract_jpg_thumb from _raw_photo import extract_jpg_thumb
from _video import VideoFile from _video import VideoFile
from _hashing import hash_path
class MediaPreview(MainWindow): class MediaPreview:
def __init__(self,path_file_name,media_files): def __init__(self,path_file_name,media_files):
super(MediaPreview, self).__init__() # super(MediaPreview, self).__init__()
self.path_file_name = path_file_name self.path_file_name = path_file_name
self.media_files_list = media_files self.media_files_list = media_files
self.media_file = MediaFile(self.path_file_name) # self.media_file = MediaFile(self.path_file_name)
self.source_path_hash = self.media_file.source_path_hash self.source_path_hash = hash_path(self.path_file_name)
self.thumbnail_ratio = self.media_files_list[self.source_path_hash]['photo']['size']['ratio'] # print(f'_preview.py,MediaPreview:\n\tpath_file_name: {self.path_file_name}\n\thash: {self.source_path_hash}\n\tmedia_files_list: {self.media_files_list}\n')
self.thumbnail = 'thumbnail.jpg'
self.thumbnail_ratio = self.media_files_list[self.source_path_hash]['image_meta']['photo']['size']['ratio']
self.file_type = self.media_files_list[self.source_path_hash]['file_type'] self.file_type = self.media_files_list[self.source_path_hash]['file_type']
if self.media_files_list[self.source_path_hash]['file_type'] == 'image': if self.media_files_list[self.source_path_hash]['file_type'] == 'image':
@ -21,10 +23,10 @@ class MediaPreview(MainWindow):
elif self.media_files_list[self.source_path_hash]['file_type'] == 'video': elif self.media_files_list[self.source_path_hash]['file_type'] == 'video':
self._video_preview() self._video_preview()
self.mpixels = self.media_files_list[self.source_path_hash]['photo']['megapixels'] self.mpixels = self.media_files_list[self.source_path_hash]['image_meta']['photo']['size']['megapixels']
def _img_preview(self): def _img_preview(self):
if self.media_files_list[self.source_path_hash]['photo']['is_jpg'] is True: if self.media_files_list[self.source_path_hash]['image_meta']['photo']['is_jpg'] is True:
self._jpg_preview() self._jpg_preview()
else: else:
self._raw_preview() self._raw_preview()
@ -37,7 +39,7 @@ class MediaPreview(MainWindow):
thumb_width = 500 thumb_width = 500
thumb_size = ( thumb_size = (
thumb_width, thumb_width,
int(thumb_width // self.media_files_list[self.source_path_hash]['photo']['size']['ratio']) int(thumb_width // self.media_files_list[self.source_path_hash]['image_meta']['photo']['size']['ratio'])
) )
try: try:
with Image.open(self.path_file_name) as img: with Image.open(self.path_file_name) as img:
@ -53,14 +55,14 @@ class MediaPreview(MainWindow):
vid = VideoFile(file=self.path_file_name) vid = VideoFile(file=self.path_file_name)
self.thumbnail = vid.gen_video_thumbnail() self.thumbnail = vid.gen_video_thumbnail()
self.width = self.media_files_list[self.source_path_hash]['video']['size']['width'] self.width = self.media_files_list[self.source_path_hash]['video_meta']['video']['size']['width']
self.height = self.media_files_list[self.source_path_hash]['video']['size']['height'] self.height = self.media_files_list[self.source_path_hash]['video_meta']['video']['size']['height']
self.video_framerate = round( self.video_framerate = round(
int(self.media_files_list[self.source_path_hash]['video']['r_frame_rate'].split('/')[0]) / int(self.media_files_list[self.source_path_hash]['video_meta']['video']['r_frame_rate'].split('/')[0]) /
int(self.media_files_list[self.source_path_hash]['video']['r_frame_rate'].split('/')[1]),2) int(self.media_files_list[self.source_path_hash]['video_meta']['video']['r_frame_rate'].split('/')[1]),2)
self.video_bit_depth = self.media_files_list[self.source_path_hash]['video']['bits_per_raw_sample'] self.video_bit_depth = self.media_files_list[self.source_path_hash]['video_meta']['video']['bits_per_raw_sample']
self.video_duration = self.media_files_list[self.source_path_hash]['video']['duration'] self.video_duration = self.media_files_list[self.source_path_hash]['video_meta']['video']['duration']
self.video_encoding = self.media_files_list[self.source_path_hash]['video']['encoding_brand'] self.video_encoding = self.media_files_list[self.source_path_hash]['video_meta']['video']['encoding_brand']
self.video_codec = self.media_files_list[self.source_path_hash]['video']['codec_long_name'] self.video_codec = self.media_files_list[self.source_path_hash]['video_meta']['video']['codec_long_name']
self.video_profile = self.media_files_list[self.source_path_hash]['video']['profile'] self.video_profile = self.media_files_list[self.source_path_hash]['video_meta']['video']['profile']
self.video_pix_format = self.media_files_list[self.source_path_hash]['video']['pix_fmt'] self.video_pix_format = self.media_files_list[self.source_path_hash]['video_meta']['video']['pix_fmt']

View File

@ -27,12 +27,10 @@ class WorkerSignals(QObject):
error = pyqtSignal(tuple) error = pyqtSignal(tuple)
result = pyqtSignal(object) result = pyqtSignal(object)
progress = pyqtSignal(int) progress = pyqtSignal(int)
import_progress = pyqtSignal(int)
current_file_progress = pyqtSignal(float) current_file_progress = pyqtSignal(float)
imported_file_count = pyqtSignal(int) imported_file_count = pyqtSignal(int)
found_file = pyqtSignal(str) found_file = pyqtSignal(str)
total_file_count = pyqtSignal(int) total_file_count = pyqtSignal(int)
# current_import_file = pyqtSignal()
class Worker(QRunnable): class Worker(QRunnable):
""" """
@ -58,12 +56,10 @@ class Worker(QRunnable):
# Add the callback to our kwargs # Add the callback to our kwargs
self.kwargs['progress_callback'] = self.signals.progress 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_file_progress_callback'] = self.signals.current_file_progress
self.kwargs['imported_file_count_callback'] = self.signals.imported_file_count self.kwargs['imported_file_count_callback'] = self.signals.imported_file_count
self.kwargs['found_file'] = self.signals.found_file self.kwargs['found_file_callback'] = self.signals.found_file
self.kwargs['total_file_count'] = self.signals.total_file_count self.kwargs['total_file_count_callback'] = self.signals.total_file_count
# self.kwargs['current_import_file'] = self.signals.current_import_file
@pyqtSlot() @pyqtSlot()
def run(self): def run(self):

View File

@ -5,33 +5,24 @@ import ffmpeg
import time import time
from datetime import datetime from datetime import datetime
from _media_file import MediaFile class VideoFile:
def __init__(self,path_file_name,max_width=1024,*args,**kwargs):
class VideoFile(MediaFile): # super(VideoFile, self).__init__(*args, **kwargs)
def __init__(self,max_width=1024,*args,**kwargs): self.path_file_name = path_file_name
super(VideoFile, self).__init__(*args, **kwargs)
self.args = args
self.kwargs = kwargs
self.file = kwargs['file']
self.out = 'thumbnail.jpg'
self.max_width = max_width self.max_width = max_width
self.out = 'thumbnail.jpg'
self.probe = ffmpeg.probe(self.path_file_name) self.probe = ffmpeg.probe(self.path_file_name)
self.video_capture_date = self.get_video_capture_date() self.video_capture_date = self.get_video_capture_date()
self.video_stream = None
self.audio_stream = None
if 'video' == self.probe['streams'][0]['codec_type'].lower(): for i in self.probe['streams']:
self.video_stream = self.probe['streams'][0] if self.video_stream is None:
elif 'video' == self.probe['streams'][1]['codec_type'].lower(): if 'video' == i['codec_type'].lower():
self.video_stream = self.probe['streams'][1] self.video_stream = i
elif 'video' == self.probe['streams'][2]['codec_type'].lower(): if self.audio_stream is None:
self.video_stream = self.probe['streams'][2] if 'audio' == i['codec_type'].lower():
self.audio_stream = i
if 'audio' == self.probe['streams'][0]['codec_type'].lower():
self.audio_stream = self.probe['streams'][0]
elif 'audio' == self.probe['streams'][1]['codec_type'].lower():
self.audio_stream = self.probe['streams'][1]
elif 'audio' == self.probe['streams'][2]['codec_type'].lower():
self.audio_stream = self.probe['streams'][2]
self.format_stream = self.probe['format'] self.format_stream = self.probe['format']
self.size_width = self.get_video_width() self.size_width = self.get_video_width()
@ -48,7 +39,6 @@ class VideoFile(MediaFile):
""" """
Generate a thumbnail from a video Generate a thumbnail from a video
""" """
self.get_video_meta()
time_seconds = self.stream['video']['seconds'] // 5 time_seconds = self.stream['video']['seconds'] // 5
v_width = int(self.stream['video']['size']['width']) v_width = int(self.stream['video']['size']['width'])
width = self.set_thumb_width(v_width) width = self.set_thumb_width(v_width)
@ -56,7 +46,7 @@ class VideoFile(MediaFile):
try: try:
( (
ffmpeg.input( ffmpeg.input(
self.file, self.path_file_name,
ss = time_seconds ss = time_seconds
) )
.filter( .filter(
@ -122,7 +112,7 @@ class VideoFile(MediaFile):
) )
return stamp return stamp
def get_video_meta(self): def video_meta(self):
self.stream = { self.stream = {
'video': { 'video': {
'bits_per_raw_sample': self.video_stream['bits_per_raw_sample'], 'bits_per_raw_sample': self.video_stream['bits_per_raw_sample'],