Skip to content

Commit

Permalink
more on groups, doc mod, minor fixes, more cde templates
Browse files Browse the repository at this point in the history
  • Loading branch information
piotrj committed Feb 15, 2024
1 parent 75c20e1 commit 8b308ae
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 67 deletions.
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,20 @@ Portable executable packages created with [PyInstaller](https://pyinstaller.org/

## [SOFTPEDIA review](https://www.softpedia.com/get/Others/File-CD-DVD-Catalog/Librer.shtml) ##

## [Tutorial](./info/tutorial.md) ##

## Guidelines for crafting custom data extractors
## Guidelines for crafting Custom Data Extractors
Custom data extractor is a command that can be invoked with a single parameter - the full path to a specific file from which data is extracted. The command should provide the expected data in any textual format to the standard output (stdout). CDE can be an executable file (e.g., 7z, zip, ffmpeg, md5sum etc.) or an executable shell script (extract.sh, extract.bat etc.). The conditions it should meet are reasonably short execution time and reasonably limited information output. The criteria allowing the execution of a particular **Custom data extractor** include the glob expression (on file name) and the file size range.

### [Custom Data Extractor - Tutorial](./info/tutorial.md) ##

### Examples of software suitable for creating Custom Data Extractors:

[7-zip](https://www.7-zip.org/) - lists the contents of the most popular archives, etc.

[exiftool](https://exiftool.sourceforge.net/) - get exif data from images like *.jpg, info on executables, and many more

[ffprobe](https://ffmpeg.org/ffprobe.html), [MediaInfo](https://mediaarea.net/en/MediaInfo) - both get tags and other metadata from multimedia files like mp3, mp4, mkv, etc.

system shell basic commands like: [type](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/type), [more](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/more), [cat](https://linuxize.com/post/linux-cat-command/)

## Usage tips:
- do not put any destructive actions in your Custom Data Extractor scripts
Expand Down
13 changes: 0 additions & 13 deletions info/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,3 @@ All of the above will apply to Linux as well, with consideration of obvious diff
Once we have the full set of custom data extractors set up and tested, we can start scanning the media we want to catalog. Once a new record is created, files with successfully collected custom data will be shown with a small green dot. A double click or menu action allows you to view the data and commands used to extract. The collected data is searchable

![image info](../info/tutorial_08.png)

## Examples of other Custom Data extractors

> "more" - Listing the contents of the entire file. May be applied to *.cue files or media playlists *.m3u or any text files (windows). Similarly on Linux we can use "cat"
![image info](../info/example_01.png)

> "[ffprobe](https://ffmpeg.org/ffprobe.html) -hide_banner" - get media tags and other metadata from media files like *.mp3 etc.
> "[exiftool](https://exiftool.sourceforge.net/)" get exif data from images like *.jpg and many more
> "[pdftotext](https://linux.die.net/man/1/pdftotext) - - <" - get text content of pdf file
> "strings" - get printable strings from any file
15 changes: 12 additions & 3 deletions src/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1829,7 +1829,7 @@ def import_records_wii_scan(self,import_filenames):
else:
return res

def import_records_wii_do(self,compr,postfix,label,quant_files,quant_folders,filenames_set,wii_path_tuple_to_data,wii_paths_dict,cd_set,update_callback):
def import_records_wii_do(self,compr,postfix,label,quant_files,quant_folders,filenames_set,wii_path_tuple_to_data,wii_paths_dict,cd_set,update_callback,group=None):
import_res=[]

self.wii_path_tuple_to_data = wii_path_tuple_to_data
Expand Down Expand Up @@ -1904,14 +1904,17 @@ def import_records_wii_do(self,compr,postfix,label,quant_files,quant_folders,fil

new_record.save(print,file_path=new_file_path,compression_level=compr)

if group:
self.assign_new_group(new_record,group)

update_callback(new_record)

if import_res:
return '\n'.join(import_res)
else:
return None

def import_records(self,import_filenames,update_callback):
def import_records(self,import_filenames,update_callback,group):
self.log.info(f"import {','.join(import_filenames)}")

import_res=[]
Expand Down Expand Up @@ -1944,6 +1947,7 @@ def import_records(self,import_filenames,update_callback):
zip_file.writestr('customdata',src_zip_file.read('customdata'))

new_record = self.create()

if res:=new_record.load(new_file_path) :
#self.log.warning('removing:%s',file_name)
self.records.remove(new_record)
Expand All @@ -1952,6 +1956,9 @@ def import_records(self,import_filenames,update_callback):
import_res.append(str(res))
else:
#self.records_to_show.append( (new_record,info_curr_quant,info_curr_size) )
if group:
self.assign_new_group(new_record,group)

update_callback(new_record)

except Exception as ex_in:
Expand Down Expand Up @@ -1996,7 +2003,7 @@ def export_record(self,record,new_file_path):
else:
return None

def repack_record(self,record,new_label,new_compression,keep_cd,update_callback):
def repack_record(self,record,new_label,new_compression,keep_cd,update_callback,group=None):
self.log.info(f'repack_record {record.header.label}->{new_label},{new_compression},{keep_cd}')

messages = []
Expand Down Expand Up @@ -2074,6 +2081,8 @@ def repack_record(self,record,new_label,new_compression,keep_cd,update_callback)
send2trash_delete(new_file_path)
messages.append(str(res))
else:
if group:
self.assign_new_group(new_record,group)
update_callback(new_record)

except Exception as ex_in:
Expand Down
2 changes: 2 additions & 0 deletions src/dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ def ok(self):

def show(self,title='',message='',initial=''):
self.entry_val.set(initial)
self.entry.selection_range(0, 'end')

self.res_str=''
super().show(title,message)
Expand Down Expand Up @@ -585,6 +586,7 @@ def ok(self):

def show(self,title='',message='',initial=''):
self.entry_val.set(initial)
self.combobox.selection_range(0, 'end')

self.res_str=''
super().show(title,message)
Expand Down
137 changes: 89 additions & 48 deletions src/librer.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,12 @@ def read(self):
line_list1a = ['0','*.7z,*.zip,*.bzip2,*.xz,*.z,*.gzip,*.iso,*.rar','','','C:\\Program Files\\7-Zip\\7z.exe l % | more +12','','1','5','0']
line_list2 = ['0','*.txt,*.nfo','1','256kB','more %','','1','5','0']
line_list3 = ['0','*.pls,*.m3u,*.cue','','','more %','','1','5','0']
line_list4 = ['0','*.mp3,*.mp4,*.mpeg','','','ffprobe.exe -hide_banner %','','1','5','0']
line_list5 = ['0','*.jpg','','','exif.exe','%','0','5','0']
line_list4 = ['0','*.mp3,*.mp4,*.mpeg,*.mkv','','','ffprobe.exe -hide_banner %','','1','5','0']
line_list4a = ['0','*.mp3,*.mp4,*.mpeg,*.mkv','','','MediaInfo.exe','%','0','5','0']
line_list5 = ['0','*.jpg','','','exiftool.exe','%','0','5','0']
line_list5a = ['0','*.exe','','','exiftool.exe','%','0','5','0']

cde_sklejka_list=[line_list1,line_list1a,line_list2,line_list3,line_list4,line_list5]
cde_sklejka_list=[line_list1,line_list1a,line_list2,line_list3,line_list4,line_list4a,line_list5,line_list5a]
else:
line_list1 = ['0','*.7z,*.zip,*.bzip2,*.xz,*.z,*.gzip,*.iso,*.rar','','','7z l % | tail -n+10','','1','5','0']
line_list2 = ['0','*.txt,*.nfo','1','256kB','cat','%','0','5','0']
Expand Down Expand Up @@ -1074,36 +1076,38 @@ def get_scan_dialog(self):
temp_frame = Frame(dialog.area_main,borderwidth=2,bg=self.bg_color)
temp_frame.grid(row=0,column=0,sticky='we',padx=4,pady=4)

ul_lab=Label(temp_frame,text="Internal Label:",bg=self.bg_color,anchor='w')
ul_lab.grid(row=0, column=0, sticky='news',padx=4,pady=4)\
ul_lab=Label(temp_frame,text="Label:",bg=self.bg_color,anchor='w')
ul_lab.grid(row=0, column=4, sticky='news',padx=4,pady=4)\

label_tooltip = "Internal Label of record to be created."
label_tooltip = "Internal Label of the record to be created"
self.widget_tooltip(ul_lab,label_tooltip)

self.scan_label_entry_var=StringVar(value='')
scan_label_entry = Entry(temp_frame,textvariable=self.scan_label_entry_var)
scan_label_entry.grid(row=0, column=1, sticky='news',padx=4,pady=4)
self.scan_label_entry = Entry(temp_frame,textvariable=self.scan_label_entry_var)
self.scan_label_entry.grid(row=0, column=5, sticky='news',padx=4,pady=4)

self.widget_tooltip(scan_label_entry,label_tooltip)
self.widget_tooltip(self.scan_label_entry,label_tooltip)

Label(temp_frame,text="Path to scan:",bg=self.bg_color,anchor='w').grid(row=0, column=2, sticky='news',padx=4,pady=4)
(path_to_scan_label := Label(temp_frame,text="Path:",bg=self.bg_color,anchor='w')).grid(row=0, column=0, sticky='news',padx=4,pady=4)
self.widget_tooltip(path_to_scan_label,"Path to scan")

self.path_to_scan_entry_var=StringVar(value=self.last_dir)
path_to_scan_entry = Entry(temp_frame,textvariable=self.path_to_scan_entry_var)
path_to_scan_entry.grid(row=0, column=3, sticky='news',padx=4,pady=4)
path_to_scan_entry.grid(row=0, column=1, sticky='news',padx=4,pady=4)
self.widget_tooltip(path_to_scan_entry,"Path to scan")

self.add_path_button = Button(temp_frame,width=18,image = self.ico_open, command=self.set_path_to_scan,underline=0)
self.add_path_button.grid(row=0, column=4, sticky='news',padx=4,pady=4)
self.add_path_button.grid(row=0, column=2, sticky='news',padx=4,pady=4)
self.widget_tooltip(self.add_path_button,"Set path to scan.")

self.add_dev_button = Menubutton(temp_frame,width=18,image = self.ico_drive,underline=0)
self.add_dev_button.grid(row=0, column=5, sticky='news',padx=4,pady=4)
self.add_dev_button.grid(row=0, column=3, sticky='news',padx=4,pady=4)
self.widget_tooltip(self.add_dev_button,"Select device to scan.")

self.drives_menu = Menu(self.add_dev_button, tearoff=0,postcommand=self.set_dev_to_scan_menu)
self.add_dev_button["menu"] = self.drives_menu

temp_frame.grid_columnconfigure(3, weight=1)
temp_frame.grid_columnconfigure(1, weight=1)

##############
self.scan_button = Button(dialog.area_buttons,width=12,text="Scan",compound='left',command=self.scan_wrapper,underline=0)
Expand Down Expand Up @@ -1918,6 +1922,13 @@ def alias_name(self):
@restore_status_line
@block
def record_repack(self):
group = None
if self.current_group:
group = self.current_group
elif self.current_record :
if group_temp:=librer_core.get_record_group(self.current_record):
group = group_temp

if self.current_record:
dialog = self.get_repack_dialog()

Expand All @@ -1932,7 +1943,7 @@ def record_repack(self):
dialog.show()

if self.repack_dialog_do_it:
if messages := librer_core.repack_record(self.current_record,self.repack_label_var.get(),self.repack_compr_var.get(),self.repack_cd_var.get(),self.single_record_show):
if messages := librer_core.repack_record(self.current_record,self.repack_label_var.get(),self.repack_compr_var.get(),self.repack_cd_var.get(),self.single_record_show,group):
self.info_dialog_on_main.show('Repacking failed','\n'.join(messages) )
else:
self.find_clear()
Expand All @@ -1947,7 +1958,17 @@ def wii_import_to_local(self):
def record_import_wii(self):
initialdir = self.last_dir if self.last_dir else self.cwd
self.wii_import_dialog_do_it= False
if import_filenames := askopenfilenames(initialdir=self.last_dir,parent = self.main,title='Choose "Where Is It?" Report xml files to import', defaultextension=".xml",filetypes=[("XML Files","*.xml"),("All Files","*.*")]):

group = None
if self.current_group:
group = self.current_group
elif self.current_record :
if group_temp:=librer_core.get_record_group(self.current_record):
group = group_temp

postfix = f' to group:{group}' if group else ''

if import_filenames := askopenfilenames(initialdir=self.last_dir,parent = self.main,title='Choose "Where Is It?" Report xml files to import' + postfix, defaultextension=".xml",filetypes=[("XML Files","*.xml"),("All Files","*.*")]):
self.status('Parsing WII files ... ')
self.main.update()

Expand Down Expand Up @@ -1992,7 +2013,7 @@ def record_import_wii(self):
quant_folders=3

label = disk_name
sub_res = librer_core.import_records_wii_do(compr,postfix,label,quant_files,quant_folders,filenames_set_per_disk[disk_name],wii_path_tuple_to_data_curr,wii_paths_dict_per_disk[disk_name],cd_set_per_disk[disk_name],self.single_record_show)
sub_res = librer_core.import_records_wii_do(compr,postfix,label,quant_files,quant_folders,filenames_set_per_disk[disk_name],wii_path_tuple_to_data_curr,wii_paths_dict_per_disk[disk_name],cd_set_per_disk[disk_name],self.single_record_show,group)
postfix+=1
if sub_res:
res.append(sub_res)
Expand All @@ -2008,7 +2029,7 @@ def record_import_wii(self):
label = self.wii_import_label_var.get()
self.status(f'importing {label} ... ')

res = librer_core.import_records_wii_do(compr,postfix,label,quant_files,quant_folders,filenames_set,wii_path_tuple_to_data,wii_paths_dict,cd_set,self.single_record_show)
res = librer_core.import_records_wii_do(compr,postfix,label,quant_files,quant_folders,filenames_set,wii_path_tuple_to_data,wii_paths_dict,cd_set,self.single_record_show,group)

if not res:
###########################
Expand All @@ -2024,9 +2045,18 @@ def record_import_wii(self):
def record_import(self):
initialdir = self.last_dir if self.last_dir else self.cwd

if import_filenames := askopenfilenames(initialdir=self.last_dir,parent = self.main,title='Choose record file(s) to import', defaultextension=".dat",filetypes=[("Dat Files","*.dat"),("All Files","*.*")]):
group = None
if self.current_group:
group = self.current_group
elif self.current_record :
if group_temp:=librer_core.get_record_group(self.current_record):
group = group_temp

postfix = f' to group:{group}' if group else ''

if import_filenames := askopenfilenames(initialdir=self.last_dir,parent = self.main,title='Choose record files to import' + postfix, defaultextension=".dat",filetypes=[("Dat Files","*.dat"),("All Files","*.*")]):
self.last_dir = dirname(import_filenames[0])
if import_res := librer_core.import_records(import_filenames,self.single_record_show):
if import_res := librer_core.import_records(import_filenames,self.single_record_show,group):
self.info_dialog_on_main.show('Import failed',import_res)
else:
self.info_dialog_on_main.show('Import','Successful.')
Expand Down Expand Up @@ -3349,40 +3379,46 @@ def remove_from_group(self):

@logwrapper
def assign_to_group(self):
curr_group = librer_core.get_record_group(self.current_record)
#item=self.tree.focus()

dial = self.get_assign_to_group_dialog()
#is_group = bool(self.tree.tag_has(self.GROUP,item))
#is_record = bool(self.tree.tag_has(self.RECORD_RAW,item) or self.tree.tag_has(self.RECORD,item))

values = list(librer_core.groups.keys())
dial.combobox.configure(values=values)
record = self.current_record
current = librer_core.get_record_group(record)
if self.current_record:
curr_group = librer_core.get_record_group(self.current_record)

dial = self.get_assign_to_group_dialog()

if not current:
current = values[0]
values = list(librer_core.groups.keys())
dial.combobox.configure(values=values)
record = self.current_record
current = librer_core.get_record_group(record)

dial.show('Assign to group','Assign record to group:',current)
if not current:
current = values[0]

if dial.res_bool:
group = dial.entry_val.get()
dial.show('Assign to group','Assign record to group:',current)

if group:
res2=librer_core.assign_new_group(record,group)
if res2:
self.info_dialog_on_main.show('assign_new_group Error',res2)
else:
group_item = self.group_to_item[group]
record_item = self.record_to_item[record]
self.tree.move(record_item,group_item,0)
#self.tree.open(group_item)
self.open_item(group_item)
self.tree.focus(record_item)
self.tree.see(record_item)
self.tree.update()
if dial.res_bool:
group = dial.entry_val.get()

self.find_clear()
if group:
res2=librer_core.assign_new_group(record,group)
if res2:
self.info_dialog_on_main.show('assign_new_group Error',res2)
else:
group_item = self.group_to_item[group]
record_item = self.record_to_item[record]
self.tree.move(record_item,group_item,0)
#self.tree.open(group_item)
self.open_item(group_item)
self.tree.focus(record_item)
self.tree.see(record_item)
self.tree.update()

self.column_sort(self.tree)
self.find_clear()

self.column_sort(self.tree)

@logwrapper
def column_sort_click(self, tree, colname):
Expand Down Expand Up @@ -3478,8 +3514,9 @@ def scan_wrapper(self):
return

if self.scan_label_entry_var.get()=='':
self.get_info_dialog_on_scan().show('Error. Empty label.','Set user label.')
return
self.scan_label_entry_var.set(platform_node() + ':' + str(abspath(self.path_to_scan_entry_var.get())) )
#self.get_info_dialog_on_scan().show('Error. Empty label.','Set internal label.')
#return

self.scanning_in_progress=True

Expand Down Expand Up @@ -3962,12 +3999,16 @@ def set_dev_to_scan_menu(self):
def set_dev_to_scan(self,dev,label=None):
self.path_to_scan_entry_var.set(dev)
self.scan_label_entry_var.set(label if label else dev)
self.scan_label_entry.selection_range(0, 'end')

def set_path_to_scan(self):
initialdir = self.last_dir if self.last_dir else self.cwd
if res:=askdirectory(title='Select Directory',initialdir=initialdir,parent=self.scan_dialog.area_main):
self.last_dir = res
self.path_to_scan_entry_var.set(normpath(abspath(res)))
self.scan_label_entry.focus_set()
self.scan_label_entry_var.set( platform_node() + ':' + str(abspath(self.path_to_scan_entry_var.get())) )
self.scan_label_entry.selection_range(0, 'end')

def threaded_simple_run(self,command_list,shell):
l_info(f'threaded_simple_run {command_list=}')
Expand Down

0 comments on commit 8b308ae

Please sign in to comment.