Movie metadata editor for Windows, to display and edit metadata of MP4 and MKV video files. It uses external applications to read and write metadata to/from movie files. On startup the script looks for three external applications: AtomicParsley.exe, mkvpropedit.exe, mkvinfo.exe
For MP4 video: Atomic Parsley, from sourceforge For MKV video: mkvinfo (to read) and mkvpropedit (to write), from MKVToolNix
It has issues with writing metadata containing the symbols & and " atomicparsley refuses to modify tags on large files over 2.9 GB with message "doesn't have full 64-bit support"
Not my prettiest code but it functions well enough for my uses so I thought to share it.
# not fully tested with TCL v9.0 (as of 2024-11-27) # workaround for images that wont display with error: Failed to create temporary file set env(JPEGMEM) 1000M catch {package require Img} result set dir [file dirname [info script]] array set var {} set var(overwrite) 1 ;# overwrite original mp4 movie file instead of writing a new file set var(gumball_delay) 2000 set var(display_thumb) 1 ;# display of artwork from file set var(save_thumb) 0 ;# save of artwork to new file set var(debug) 0 set var(textwidth) 50 ;# character width of data entry fields set var(on_color) green3 ;# color of on toggles set var(MP4_EDIT) [file join $dir AtomicParsley.exe] set var(MKV_EDIT) [file join $dir mkvpropedit.exe] set var(MKV_INFO) [file join $dir mkvinfo.exe] set var(last_dir) "" set var(defer_exec) 0 ;# for mp4 save batfile to run later in background, then click Apply catch {console show} # find last used directory and look there first if {[file readable [file join $dir settings.tcl]]} { if {[catch {source settings.tcl} result]} { puts $result } else { puts "loaded settings.tcl" } } # check external apps exist foreach {lbl vrb} { AtomicParsley MP4_EDIT MKVInfo MKV_INFO MKVPropEdit MKV_EDIT } { # set var($vrb) $exe if {[file exists $var($vrb)]} { puts "found $lbl" } else { set filetypes { {{Executable Files} {.exe} } {{All Files} *} } set f [tk_getOpenFile -title "Find $lbl" -filetypes $filetypes] if {[string length $f] && [file exists $f]} {set var($vrb) $f} } } catch {cd $var(last_dir)} # find movie player app, use last one found foreach f { "C:/Program Files (x86)/Windows Media Player/wmplayer.exe" "C:/Program Files/VideoLAN/VLC/vlc.exe" } { if {[file exists $f]} { set var(PLAY) $f puts "found player $f" } } # toggles to lock metadata values to apply to later files array set lock {} # metadata read from file or entered in gui to save back to file array set data {} # output message to console if debug flag is on proc debug {args} { global var if {![info exists var(debug)]} {set var(debug) 0} if {[string length $args]} { if {$var(debug)} {puts $args} } else { puts "var(debug) is $var(debug)" } } # dump helpful info to the console proc help {{what all}} { global var puts "HELP FOR THIS TOOL" switch -glob -nocase -- $what { mp4 { puts [string repeat "-" 30] puts "[file tail $var(MP4_EDIT)] is used for MP4 files.\n" catch {exec $var(MP4_EDIT) -longhelp} result puts $result } mkv { puts [string repeat "-" 30] puts "[file tail $var(MKV_EDIT)] is used for MKV files.\n" catch {exec $var(MKV_EDIT) -?} result puts $result catch {exec $var(MKV_EDIT) -l} result puts $result } default {help mp4; help mkv} } } # select a folder of movie files proc choose_dir {args} { global var filelist if {[string length $args] < 1} { set tmp [tk_chooseDirectory] if {[string length $tmp] < 1} {return} } else {set tmp $args} if {[catch {cd $tmp} result]} {return} set var(last_dir) $tmp # create file list with mp4 first, then mkv set filelist [list \ [lsort -dict [glob -types f -nocomplain *.mp4]] \ [lsort -dict [glob -types f -nocomplain *.mkv]] ] set filelist [join $filelist] save_settings } # write to console and to text widget proc log {{str ""} {tag ""}} { set w .f4.txt ;# text log widget if {![winfo exists $w]} {puts "text widget $w not found."; return} if {$str == "clear"} {$w delete 1.0 end; return} puts $str $w insert end "$str" $tag $w insert end "\n" # seek to bottom $w yview end } # update write status gumball proc status {{str "Write tags"} {fg SystemButtonText} {bg SystemButtonFace}} { if {[catch {.f1.write configure -text $str -foreground $fg -background $bg} result]} { puts $result} update } # save current settings before exit proc quit {args} { catch {save_settings} exit } proc save_settings {args} { global dir var # remember this folder for next run set f [file join $dir settings.tcl] if {[catch {set fout [open $f w]} result]} {puts "error: $result"; return} puts $fout "# saved settings for metadata editor" puts $fout "catch {cd \"[pwd]\"}" # dump contents of var array puts $fout "array set var {" foreach v [lsort [array names var]] { puts $fout "[list $v] [list [set var($v)]]" } puts $fout "}" close $fout debug "wrote settings to $f" } # RETURN NICELY-FORMATTED FILE SIZE STRING WITH KB MB GB TB PB proc nice_size {num {decimals 1} {fullname 0}} { set remainder 1 if {$decimals > 0} {set remainder [expr {pow(10,$decimals) + 0.0}]} foreach {n short long} {80 YB YottaByte 70 ZB ZettaByte 60 EB ExaByte 50 PB PetaByte 40 TB TeraByte 30 GB GigaByte 20 MB MegaByte 10 KB KiloByte} { set amount [expr {pow(2,$n)}] if {$fullname} {set long " ($long)"} else {set long ""} if {$num >= $amount} {return "[expr {int($num * $remainder / $amount) / $remainder}] $short$long"} } return $num } # RETURN A FORMATTED NUMBER WITH DESIRED DECIMAL POINTS proc decimals {n d} { global tcl_precision set decpts [expr {pow(10,$d)}] if {$d < 1} { set x [string first "." $n] # number is float, has decimal point if {$x > -1} { incr x -1 incr x $d return [string range $n 0 $x]} } # number is whole, no decimal set result [expr {wide($decpts * $n) / $decpts}] } # play movie file proc play {args} { global var data if {![info exists data(filename)] || ![file readable $data(filename)]} {log "no file given to play"; return} if {[info exists var(PLAY)]} { if {[string length $var(PLAY)]} { if {[catch {exec $var(PLAY) $data(filename) &} result]} {log $result} } } else { log "no player application found" } } # rename filename to match Title field, useful to recover mangled filenames proc rename_file {args} { global data if {![file exists $data(filename)]} {log "File not found: $data($filename)" err; return} set newname $data(@nam)[file extension $data(filename)] if {[catch {file rename $data(filename) $newname} result]} { log $result err } else { log "Renamed $data(filename) to $newname" set data(filename) $newname # catch {.f1.refresh invoke} } } # write to console full output from metadata reader on selected file # note mkvinfo refuses to allow some options: -a proc full_info {args} { global var data if {[info exists data(filename)]} {set filename $data(filename)} else {log "no filename given"; return} log clear log "See Console for all metadata in $filename" set ext [string trimleft [string tolower [file extension $filename]] .] switch -- $ext { mp4 {catch {exec $var(MP4_EDIT) $filename -t} result} mkv {catch {exec $var(MKV_INFO) $filename} result} default {set result "unknown file type $ext"} } puts $result } proc display_photo {filename} { set w .image if {![winfo exists $w.c]} { # create window toplevel $w -background black canvas $w.c -background black -width 600 -height 600 label $w.t -background black -foreground white -font {* 14} pack $w.c -fill both -expand 1 pack $w.t -fill x } if {[file readable $filename]} { # display file catch {image delete pic} image create photo pic -file $filename $w.c create image 1 1 -image pic -anchor nw -tag image $w.t configure -text "$filename" } } # read file proc read_file {args} { global var data lock lockdata filelist # get filename from list set n [.f2.f1.list curselection] if {[string length $n] < 1} {return} # clear old data first foreach tag [array names data] {set data($tag) ""} log clear set data(filename) [lindex $filelist $n] puts "----" log $data(filename) # check for invalid filename chars foreach c {&} { if {[string first $c $data(filename)] > -1} { log "Error: must rename file to remove $c symbol" err } } if {![file exists $data(filename)]} {log "File not found $data(filename)"; return} log "File size: [nice_size [file size $data(filename)]]" # status "Reading tags" set ext [string trimleft [string tolower [file extension $data(filename)]] .] # delete thumbnail catch {image delete pic} # parse filename to find tv series season and episode set str [regexp -all -inline -nocase -- {s\d\de\d\d} $data(filename)] if {[string length $str] == 4} { set data(tvsn) [string index $str 1] set data(tves) [string index $str 3] } elseif {[string length $str] == 6} { set s [string range $str 1 2] if {$s == "00"} {set data(tvsn) 0} else {set data(tvsn) [string trimleft $s 0]} set s [string range $str 4 5] if {$s == "00"} {set data(tves) 0} else {set data(tves) [string trimleft $s 0]} } # parse filename for vertical resolution (720/1080/2160/4K) if {[string first 640 $data(filename)] > 0} {set data(@grp) 640} if {[string first 720 $data(filename)] > 0} {set data(@grp) 720} if {[string first 1080 $data(filename)] > 0} {set data(@grp) 1080} if {[string first 2160 $data(filename)] > 0} {set data(@grp) 2160} if {[string first 4K $data(filename)] > 0} {set data(@grp) 2160} # parse music video fields from filename format Artist - Song - Album (Year) set s [split [file rootname $data(filename)] "-"] if {[llength $s] == 3} { set data(@ART) [string trim [lindex $s 0]] set data(@alb) [string trim [lindex $s 2]] # remove year if {([string index $data(@alb) end] == ")") && ([string index $data(@alb) end-5] == "(")} { set data(@alb) [string range $data(@alb) 0 end-7] } } # use appropriate tool for the file type mp4/mkv switch -- $ext { mp4 { log "MP4 container" if {[catch {exec $var(MP4_EDIT) $data(filename) -t} result]} { log "error: $result" warn write_file clear return } # parse tags foreach str [split $result "\n"] { # handle itunes ---- tags regsub -all {"\)} $str {"} str if {[string first Atom $str] > -1} { set atom [lindex $str 1] set cont [string range $str [string first contains: $str]+10 end] # remove junk regsub [binary format c "-62"] $atom "" atom regsub [binary format c "-87"] $atom "@" atom # sub if {$atom == "gnre"} {set atom "@gen"} set data($atom) $cont log "$atom : $cont" } } # attempt artwork extract to file if {[string length $data(covr)]} { # extract artwork to display if {![catch {exec $var(MP4_EDIT) $data(filename) --extractPix} result]} { set pix [string trim [lindex [split $result ":"] 1]] if {[info exists var(display_thumb)] && $var(display_thumb)} { if {[file exists $pix]} {display_photo $pix} } # delete artwork if not wanted if {[info exists var(save_thumb)] && !$var(save_thumb)} { catch {file delete $pix} } } } } mkv { log "Matroska container" if {[catch {exec $var(MKV_INFO) $data(filename)} result]} { log "error: $result" status "error reading" black red after $var(gumball_delay) status } # tags section, tag data on second line foreach {str tag} { "Name: DATE_RELEASED" @day "+ Title:" @nam "+ Date:" @day "+ Writing application:" @too "Multiplexing application:" @too "Pixel height:" @grp "+ Name: COMMENT" @cmt } { set idx1 [string first $str $result] if {$idx1 > -1} { set idx1 [expr $idx1 + [string length $str] + 1] set idx2 [string first "\n" $result $idx1] if {$idx2 < 0} {set idx2 end} else {incr idx2 -1} ;# remove newline set data($tag) [string range $result $idx1 $idx2] log "$str: $data($tag)" } } if {[string length $data(@day)] > 3} { # make date format YYYY-mm-ddTHH:MM:SS+zz:zz set data(@day) [clock format [clock scan $data(@day)] -format "%Y-%m-%dT%H:%M:%S+00:00"] } } default { log "unknown file type $ext" } } # if title is empty, copy filename if {[string length $data(@nam)] < 1} { log "Title field (@nam) empty." warn filename_to_title } # restore locked data over anything read from file foreach v [array names data] { if {[info exists lock($v)] && [info exists lockdata($v)]} { if {$lock($v)} {set data($v) $lockdata($v)} } } status } # copy filename into title field, minus year if present proc filename_to_title {args} { global data set f $data(filename) # find year set idx [string last ( $f] if {($idx > -1) && ([string index $f $idx+5] == ")")} { set year [string range $f $idx+1 $idx+4] if {[string is integer $year]} { set data(@day) $year set data(@nam) [string trim [string range $f 0 $idx-1]] } } else { set data(@nam) [string trim [file rootname $f]] } log "copied filename to Title field" warn } # write file tags proc write_file {args} { global var data lock lockdata if {[info exists data(filename)]} { set filename $data(filename) unset data(filename) } else {log "no filename given"; return} if {![file readable $filename]} {log "File not found: $filename"; return} if {$args == "clear"} {log "Clearing existing tags..." warn} if {$args == "clearartwork"} {log "Clearing artwork only..." warn} # check for readonly flag if {![file writable $filename]} {log "$filename is not writable" err} log "Writing tags to $filename" set time1 [clock seconds] set ext [string trimleft [string tolower [file extension $filename]] .] . configure -cursor watch log clear set err 0 status "Writing..." black yellow # use appropriate tool for the file type mp4/mkv switch -- $ext { mp4 { if {$var(overwrite)} {set params "--overWrite"} else {set params ""} foreach v [array names data] { # replace tag with parameter for parsley # omit if data is blank, to remove tag switch -- $v { @nam {append params " --title \"$data($v)\""} @ART {append params " --artist \"$data($v)\""} @alb {append params " --album \"$data($v)\""} @grp {append params " --grouping \"$data($v)\""} gnre {append params " --genre \"$data($v)\""} @gen {append params " --genre \"$data($v)\""} @day {append params " --year \"$data($v)\""} desc {append params " --description \"$data($v)\""} @cmt {append params " --comment \"$data($v)\""} @too {append params " --encodingTool \"$data($v)\""} disc {append params " --disk \"$data($v)\""} track {append params " --tracknum \"$data($v)\""} stik {append params " --stik \"$data($v)\""} tvnn {append params " --TVNetwork \"$data($v)\""} tvsh {append params " --TVShowName \"$data($v)\""} tven {append params " --TVEpisode \"$data($v)\""} tvsn {append params " --TVSeasonNum \"$data($v)\""} tves {append params " --TVEpisodeNum \"$data($v)\""} bpm {append params " --bpm \"$data($v)\""} cprt {append params " --copyright \"$data($v)\""} covr - artw { # only add artwork if filename present and not "piece of artwork" if {[string length $data($v)] && ([string first "piece of artwork" $data($v)] < 0)} { append params " --artwork \"$data($v)\"" } } filename {} default {log "unknown how to assign tag $v"} } } if {$args == "clear"} {set params "-P --overWrite"} if {$args == "clearartwork"} {set params " --artwork REMOVE_ALL --overWrite"} puts "$var(MP4_EDIT) $filename $params" # write to bat file then exec it, to support variable number of parameters # bat file name set batfile "$filename.update.bat" if {[file exists $batfile]} {log "previous edit found, overwriting"} set fout [open $batfile w] puts $fout "rem Script to update movie metadata, okay to delete" puts $fout "echo Applying changes to $filename" puts $fout "\"$var(MP4_EDIT)\" \"$filename\" $params" # delete bat file when done # puts $fout "del \"$batfile\"" close $fout debug "created bat file to edit mp4" debug "$var(MP4_EDIT) $filename $params" if {$var(defer_exec)} { # RUN LATER WHEN APPLY BUTTON CLICKED log "success writing tags" ok log "Defer toggle is on, must click Apply to update the movie file" status "Success" black green2 after $var(gumball_delay) status } else { # RUN NOW if {[catch {exec cmd /c $batfile} result]} { log "error: $result" err status "error writing" black red after $var(gumball_delay) status set err 1 } else { log "success writing tags" ok status "Success" black green2 after $var(gumball_delay) status } # delete bat file catch {file delete $batfile} } } mkv { if {$args == "clear"} { catch {exec $var(MKV_EDIT) $filename --tags all:} result return } # make date format YYYY-mm-ddTHH:MM:SS+zz:zz set datelen [string length $data(@day)] if {$datelen < 1} { set data(@day) [clock format [clock seconds] -format "%Y-%m-%dT%H:%M:%S+00:00"] debug "created date formatted for mkv (%Y-%m-%dT%H:%M:%S+00:00)" } if {$datelen == "4"} { append data(@day) [clock format [clock seconds] -format "-%m-%dT%H:%M:%S+00:00"] debug "appended month-day-time to the year for mkv" } if {![info exists data(covr)]} {set data(covr) ""} # add artwork attachment if {[string length $data(covr)]} { debug "exec $var(MKV_EDIT) $filename --edit info --set title=$data(@nam) --set writing-application=$data(@too) --set date=$data(@day) --attachment-name normal cover --add-attachment $data(covr)" if {[catch {exec $var(MKV_EDIT) $filename --edit info --set "title=$data(@nam)" --set "writing-application=$data(@too)" --set "date=$data(@day)" --attachment-name "normal cover" --add-attachment "$data(covr)"} result]} { log "error: $result" err status "error writing" black red after $var(gumball_delay) status set err 1 } else { log "success writing tags" ok status "Success writing" black green2 after $var(gumball_delay) status } } else { # normal tags without artwork debug "exec $var(MKV_EDIT) $filename --edit info --set title=$data(@nam) --set writing-application=$data(@too) --set date=$data(@day)" if {[catch {exec $var(MKV_EDIT) $filename --edit info --set "title=$data(@nam)" --set "writing-application=$data(@too)" --set "date=$data(@day)"} result]} { log "error: $result" err status "error writing" black red after $var(gumball_delay) status set err 1 } else { log "success writing tags" ok status "Success writing" black green2 after $var(gumball_delay) status } } } default { log "unknown file type $ext" } } set time2 [clock seconds] set timed [expr {$time2 - $time1}] if {$timed > 2} {log "elapsed time $timed seconds"} # confirm by re-reading file # if {!$err} {read_file} . configure -cursor {} update_que } # update que count proc update_que {args} { set n [llength [glob -nocomplain -- *update.bat]] .f1.process configure -text "Apply $n" } # tooltip balloon help proc tooltip {w h} { global val bind $w <Any-Enter> "after 500 [list tooltip:show %W [list $h]]" bind $w <Any-Leave> "destroy %W.tooltip" } proc tooltip:show {w arg} { if {[eval winfo containing [winfo pointerxy .]]!=$w} {return} set top $w.tooltip catch {destroy $top} toplevel $top -bd 1 -bg gray wm overrideredirect $top 1 set font TkDefaultFont if {$::tcl_platform(platform) == "unix"} {set font fixed} pack [message $top.txt -aspect 10000 -bg lightyellow -font $font -width 450 -text $arg] set wmx [winfo rootx $w] set wmy [expr {[winfo rooty $w] + [winfo height $w]}] wm geometry $top [winfo reqwidth $top.txt]x[winfo reqheight $top.txt]+$wmx+$wmy raise $top } # create GUI wm minsize . 150 150 wm title . "Movie Metadata Editor 1.5 (FB)" #wm protocol . WM_DELETE_WINDOW quit_program catch {wm iconbitmap . -default Metadata.ico} wm protocol . WM_DELETE_WINDOW quit set f .f1 frame $f pack $f -side top -anchor nw button $f.choosedir -text "Directory" -command choose_dir tooltip $f.choosedir "Choose a folder of files to tag" image create photo pic_folder -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9 kT1Iw0AcxV9TpaIVBzuIOESoTi2IijhKFYtgobQVWnUwufQLmjQkKS6OgmvBwY/FqoOLs64OroIg +AHi5Oik6CIl/i8ptIjx4Lgf7+497t4BQqPCVLNrAlA1y0jFY2I2tyoGXtGHIEIYRURipp5IL2bg Ob7u4ePrXZRneZ/7c/QreZMBPpF4jumGRbxBPLNp6Zz3iUOsJCnE58QRgy5I/Mh12eU3zkWHBZ4Z MjKpeeIQsVjsYLmDWclQiaeJw4qqUb6QdVnhvMVZrdRY6578hcG8tpLmOs0RxLGEBJIQIaOGMiqw EKVVI8VEivZjHv5hx58kl0yuMhg5FlCFCsnxg//B727NwtSkmxSMAd0vtv0xBgR2gWbdtr+Pbbt5 AvifgSut7a82gNlP0uttLXwEDGwDF9dtTd4DLneAoSddMiRH8tMUCgXg/Yy+KQcM3gK9a25vrX2c PgAZ6mr5Bjg4BMaLlL3u8e6ezt7+PdPq7we1+nLCDPs9JgAAADZQTFRFYQAAAAAADw8PHx8fLy8v Pz8/T09PX19fb29vf39/j4+Pn5+fr6+vv7+/z8/P39/f7+/v////7d6FnAAAAAF0Uk5TAEDm2GYA AAABYktHRACIBR1IAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AsMFCQwsBmnOwAAALFJ REFUOMvN00kShCAMQFGZhMiY+1+2EWSoDpTL7r/1FUbA4/iPsPXyeEMQvW75lcAoWUuGFfBiBvR1 MxAu9tIKzJm2yg4oTKmIHRAAYPNMHQhJY3oCVyQFdt4glH3gHkmhrhBVmSlQAMzeQ1ag6fMkWBrg osAxwAEiXcBwP8C5GFHost0VOApsHrEDRd8QOa/nVQAkUv3GB3AN3xmungPP+6FWnaFdGdy2vNb4 8g/8pA80MRt8RpVCvwAAAABJRU5ErkJggg==} $f.choosedir configure -compound top -image pic_folder button $f.refresh -text "Refresh" -command { # separate mp4 and mkv set filelist [list [lsort -dict [glob -types f -nocomplain *.mp4]] [lsort -dict [glob -types f -nocomplain *.mkv]]] set filelist [join $filelist] # set filelist [lsort -dict [glob -types f -nocomplain *.mp4 *.mkv]] } tooltip $f.refresh "Refresh file list" image create photo pic_refresh -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA B3RJTUUH6AUfEBclpPgIVwAABT5JREFUWMOtV19oU1cY/93MSNMWG1tdyWwJbk4tOLKHBmwrJb4I m6OrI7WOCSl1SB982HwQOp+kb5kDcYQWKqxDwb1oIXQvfZgaKEpFMHcPtSGSxNFebdOm2W1iTJr8 9rDcLKc3aRrdgQPnz+/7vt853znfd46EKgtJCYAZQMOmqagkSevV6pOqMNwK4GsAn5chsAIgAmAa wG+SJMXxroXkDpLtJCdYXVkh6SZ58K13gORuAD8C+ArAbgB4/fo1Hjx4gHA4jEQiIeAbGhpgs9lg t9uLhxcAjAL4SZKkVDUr/5TkLElms1kuLi7S4/GwpaWFALasR48epdfrZTQa1XYjS/JWfkHbNh7S pCcmJtjR0VHR8Oba19dHn89X7BZvRRIk95EMkqSqqhweHq7a8OZ6+/ZtvnnzRiNxi2RNOeO1JH8n yWQyyUuXLr2z8WISRe64XI6Ak2Qmk8nQ7XYXhGVZZjAYpCzLQn369CkvXLiwbRLT09MaCYXkJ5uN 12h+n5ubY3Nzc0Hwxo0bTKfTJe/a1atXdYZaW1tpsVh04ydPnuTS0pIm+stmAt9oJ97pdOqEZVmu SKC2tpYTE/+Gi3IunJyc1ESTJD8oDjbTJPno0SOd0MDAQPGVKkugo6ODiqL8F4lWVjgwMCDoOnDg AJPJpAa5DAAGAC0APgaAmZkZ3dkYHBxEU1NTxeu7traGdDpd6Dc2NmJwcFDABINB3L9/X+t+RtJo ANAEYE8ikUA4HBYEOjs70dXVJYzdu3evJIG5uTl4PB5hrKurC52dncLY/Py81mwG8L4hn1TqVVWF 3+8XwGfOnIHBYCj0p6amcO7cOTx+/LgkCbfbjTt37hT6BoMBZ8+eFVNmNKrtVD2AegOAjwBgY2MD L168EMDHjx8vtHO5HGRZRigUQn9/P8bHx0u6TJZlZDKZQt/hcAjzS0tLSKVSAGACULsj30Aul0Ms FhPA9fX1hXYikSgQDIVCOH/+fMldePXqFVKpFIxGIwDAZDJteXYMAJ4DgNFoxOHDh3XbpZW6ujpY rdaKh9FqtaKurq6kjnIE1gHQaDTCYrEIkz6fT/CnzWarSMBmswnnplgHABw6dAi7du0CgFUAqyB5 hOTyxsYGR0ZGhHtrt9uFe59Op7dMUMPDw7qoabfbBcz4+Lg25SfZCJK78x3evHlTp9Tr9QoKVVXl 2NgYu7u7C5ju7m6OjY1RVVUBOzU1pdP37NkzbfpucSi+TpLxeJwmk0kQOHXqFJeXlwXF2WyWiqIw HA4zHA5TURRms1kBE41Gefr0aUGX0+kshvQVE2gnmSbJ0dHRklsbj8e3/SCMx+M6V5nNZj558kSD hEql419JcnV1lf39/ToSQ0NDjEQiFY1HIhEODQ3p5K9cuaKdjxxJVykCB0n+RZIPHz7k/v37dUpM JhPdbjcDgQAVRWEsFmMsFqOiKAwEAnS73ToXAmBvby8XFxc1jjNln2Ykv9VQs7OzJUlo1eFw0OVy 0eVy0eFwlMWdOHGCgUCgOA0fqfQu/JlkhiR9Ph97enre+il28eLF4pX/TfKL7byKa/IkSJIvX77k tWvXSm5tubp3715OTk4W5/4kyS9JvlfNx+R7AD8A2AMACwsL8Hq98Pv98Pv9kGVZwB87dgzt7e1o a2tDT0+PFu0A4E8A30mS9Ee1PyMJQBuAEQC9+bANVVWxvr4uZDwAqKmpgdlsxs6dOwv5C4AHwHVJ khbe5X9oIOkgeZdkgGRii1u4lo+q10l++H//jiUAFgD7AFgBtG6CzANQAIQlSYptV+8/hTq9Na83 oLgAAAAASUVORK5CYII=} $f.refresh configure -compound top -image pic_refresh button $f.play -text "Play" -command play image create photo pic_play -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA B3RJTUUH6AUfEBcdjPqwyQAAAh5JREFUWMPll8FqE1EUhv+pYtwoaaDb4qIIgpIiQ0UIXRi60AmC q0CgWelCUguCi7gp5CHyAN258DFmUSQUAtUuTLoJkyhJJomRxhIyn5u5EBAhYnM3vXAWcxnm//jn 3HvOka76chZ5Cbgl6a6kRLzVchynZYUQuA18AH4AF3E0gCe2AB4B59PplHa7Tb/fJ17fgawNgG0g 6nQ6eJ5HsVik1WrNQ+SsAARBgOu6SCKTycxDdIGnVgEk4XkezWbTQPSBglUASaTTaRqNBlEUAYTA C6sAktjZ2eH09NQ4MQBeAo41AElsbGxwcnJinBgBu8CKNQCTmPV63TgxBt5cCsSiAJJYX1+nVqsx m80AfgKvgevWACSxtbXF0dGRceIceP9fEP8KIIlUKoXv+8aJCfDqb99fWcbRDcNQ+/v78n1fkm5K emcVQJKSyaRWV1fN44XVX7C5uTl/QQ2B59YAcrkcZ2dnJglDYNfaKchmswRBMF+knlm7BwqFAp1O x4h/u7QyvQhAPp+n1+sZ8QDYtlYL9vb2CMPQiDeBx9aqYalUYjAYGPEG8NBaOS6XywyHQyP+Bbhv rSGpVCqMRiMjfgzcW3ZLNut2uxwcHFCtVhmPx0b8E3Bn2U3pA6AfRRGTyYTpdGrE/aWLxwDXgLfA 5zjRvgIfgTWbo5kjaU3SjXir5zjOr6sxvQIrQCIIgoTruol4QP0jDg8PE4CJhTug35pCRdYcVPz6 AAAAAElFTkSuQmCC} $f.play configure -compound top -image pic_play button $f.prev -text "Prev" -command { set x [.f2.f1.list curselection] if {[string length $x] < 1} {set x [.f2.f1.list size]} if {$x > 0} { incr x -1 .f2.f1.list selection clear 0 end .f2.f1.list selection set $x .f2.f1.list see $x read_file } } image create photo pic_prev -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBI WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH6AcJEzEgGriahgAAAgJJREFUWMO9lzGLGlEUhc+TFbVI YMVCjP6IgH/ANDZJ2tinCVttmoiQOloEUgim00rt0tuksdhUcfsEtFkiOqzMEBgDm/3SOGE2jI5m nTlwu8e93znv8d6MdE8BaeCh4haQAF4CF8BXoAOcxjXcAK8Al7t6H9fwujfccRzm87kH4Mbq3LZt 6vU6g8HgbwSxObdtm0ajgSSGw2G0AEHOveGRA+xyfgyAkz3WPJVUl5R2HEetVkvNZvPOgkKh4Ad+ vaPXtaQvkr4ZY273cf8CuN7m3KvZbMYBugKehCYAPJP0UdLper0OdO5psVgolUrtNGOMUTabVTKZ LEh6K+mzJJltzv3De72ezs7OtjYvl8vKZDKhifb7fZVKJUn6aYx5sNW5F7vrunS73cDY/6em06m3 DZehe+66Lp1O52jDQwGAx8AyCuf7AryLyvkugISPIStJ6/Vak8kk9uddwHPgFmC5XFKtVmPfgtTm o+K3B1Gr1eID8EE0/RDHTCIUIOokggBOAq7MX5sHxZb0JpfLJdrttlarlUajUSD0eDxWsVgMPWf5 fP6gQ7l3Ej5n++gGOD8EIvRM/ANwuaMugHMgc9Qk/ABR3RE7k4gcICyJWAC2JVGpVOIDCErCsiws y4rnx2RbEj5143y80sAH4PumPgGPDu1j7vvPIMm7An8YY24O7fEHG8pO3TtfA0kAAAAASUVORK5C YII=} $f.prev configure -compound top -image pic_prev button $f.next -text "Next" -command { set x [.f2.f1.list curselection] if {[string length $x] < 1} {set x -1} if {$x < [expr {[.f2.f1.list size]-1}]} { incr x 1 .f2.f1.list selection clear 0 end .f2.f1.list selection set $x .f2.f1.list see $x read_file } } image create photo pic_next -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBI WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH6AcJEzEnhNwPJQAAAl5JREFUWMPNlr+L02Acxp8cpZgc CHXpoDek0qFwCKWr7dCMgtx4iiBCu+jUwcFZcPAf6NAKLqKLIIpwS2bD3VAHFyk09Bc1luCWppTk cWmOXJrrtZG++IUXkveF9/18n/d9n+8LJAySeyT3ITqWCz8keULyjORLktdEAtwj6fJivCeZEQXQ IknHcWjbdhjisxAIkm9Jst/vM5/Ps9friVUiDCDLMjVNi0LsVokwgKIoBEBVVcUpEQcAILESUmTy PIAjAIfLiePiLoDbg8EAhUIBjuOcD6iqCl3Xoapq0PUBwFNJkv5sklme5E9uGFEFkiohhQCeA3jt eR5s24bv+2uBR6MRKpUKZrPZytg2SqRC34cAYNs2Go0GhsPhWgDXdeG6buyYaZqo1+totVoBxDGA fZKPL92O4HBNJhOWy+UVaZO0TW7H3i49I1DCNM2g6xjAG5LXhQAAgK7r0DQtDHEE4IkwgECJWq0G y7KCg/9IKEDckRMKoKoq2u02stls0PVJGICmaVFP+AKg+d9cw1TUFSVJQjqdhqIoazPzfR/z+Rwk L808ZERB5ituGLbiVwBeLBYLWJZ1pRWPx2NUq9VYN0xUlEjeIfnrX4tRsVhkt9vduBiFt+AHgPsA HgC4cVU5jhuQZRnNZhO5XG77crzFg+RjnAKlUomGYYQzP9nJq4jk9yiALMs0DIOe5wWLn5LM7upJ dgEgJvMzkge7fJSeA2QymbjMD3Zr4uQ3kpxOp+x0OuIyDwE8I+lHbuWpkMWXABmS70iay/aV5M2k 80kJIVIAbi1/f0uS5CQF+Av6cTDVWV9omwAAAABJRU5ErkJggg==} $f.next configure -compound top -image pic_next # write all metadata back to file, overwriting button $f.write -text "Write tags" -command write_file -borderwidth 4 tooltip $f.write "Write tag fields to file" image create photo pic_save -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA B3RJTUUH6AUfEBcrQ0AlUAAAAy9JREFUWMPtl99LU2EYx9+dQSKWUwYh3pQERUFBQUQIE/SyLiL6 XZJpQ2gNhbzqIuheRaY0g0IQRPyxi4E3EnghaJY3kfU3CHPb0e1I7ezHp4vOO16XO6dVC4JeeO/O eT7f5/t83/dsQvxfVViAG7gDJPi+PgBn/qaAB0CcveszcLHaYBfwENABdF1ncXGRQqEAULBEnKsm /K60PRaL0dvbS1NTE9FolHw+L534BJythoBuaXsymaSvrw8hBEIIGhsbmZ2dLR3HhT8F1oD7QFLC Ozs7i3C5W1pamJmZkU4ULCeO2RX2AIcBzUHAHWALYGtra0/npdvr9bKwsKCO43a5ok+so/MReAUc srE9IQMXCATKwuWemJggl8tJAb2lBQ8Az4CvyrxywAvAU+6cJxIJuru7bcEej4dQKEQ6nZZ148D5 0lk+BXZlijc2NtTgvAQOWs/eBWIA8XicgYEBx87D4bAKTwLXAbd6hJ4DXwBSqRR+vx+fz8fy8rJ8 KQu8Bh7Jzre3t/H7/bbgmpoawuEwhmHIOingUjFbQI3V+Rdpp5pin8/H2tqaGhxk54FAAJfLZStg ZGSEVColX4sBV/cEG3gMGLJoMBj8oUh7ezurq6tFuK7rP2X7+Pi4anvKsl0rDV4cwDAMurq60DRt 32Ktra2srKyg6zo9PT22YK/XWxq4beDyvkfaSjlLS0uOHbW1tdHR0eH4XIntceBm2fsEeANgmibR aNSxeIVpN6yZu+1usqPAW6BgmiZTU1M0NzdXDK6vr2d4eFjtPAlccbpJpYiTlghyuRxzc3MVCwiF Quzs7BRzal1U7ko+KtIJMpkMkUiEhoYGR7CmaYyNjamdG1bn7l/5sh0H1gCy2SyTk5PU1tbaChgc HFQ7jwM3fsp2h3EURUQikbLw0dFRFZ6u2HaHcbwDCplMhunp6T3BrKurY2hoSLU9/cu224g4ZYko jkMK6O/vZ3NzU8ITwL0/CldEnJAiTNNkfn6eYDCowneBW1WBKyKOAe+BQj6fJ5vNqtfrtarCFRGn gXXlY7hj/Q78bbirAhFHhBCXhRC1QogVIcS6y+XK/f8f+M+vb2N0CS88sgyfAAAAAElFTkSuQmCC} $f.write configure -compound top -image pic_save # apply updates to mp4 files which can be slow and take minutes button $f.process -text "Apply" -command { # get list of bat Files set files [glob -nocomplain -- *.update.bat] set num [llength $files] log clear log "$num files to update in [pwd]" set count 0 foreach w [winfo children .f1] {$w configure -state disabled} update foreach f $files { set now [clock seconds] incr count # update progress in text widget log "$f" update if {[catch {exec cmd /c "$f"} result]} { log " error: $result" err } else { # elapsed time set delta [expr {[clock seconds] - $now}] log " complete in $delta seconds" } # delete bat file even if error occurred if {[file exists $f]} {catch {file delete $f}} set n [expr {$num-$count}] log "$n files to go" .f1.process configure -text "$n to go" progressbar [expr {100*$count/$num}] update } foreach w [winfo children .f1] {$w configure -state normal} progressbar 0 update_que } tooltip $f.process "Apply updates to MP4 files (can take minutes)" image create photo pic_apply -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBI WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH6AcJEzETpWj7kAAAA4tJREFUWMO1l0tIa1cUhr9zkhqj OFDx1aLUiSI+msT4QMoVRYhWB4LKvYiTCI6KIeCsDoQWHBQ66AUdVAgIEQMKEqSWgKmIqFcQtQOH egY2Uky8DkTTesHVUYI3BvN0wRnsvRdnf/tfaz8WACJiEJHvRORPyb79LSI/iciXxDMR0YvIexH5 V17XPohIZez8ioi8Af54fHzMOz8/x+VycXd3R7asu7ubvr4+cnJyAOYVRfk+VoFfRUQCgYCYTCYB sv4tLy9HVAiLSGGsAvtAu9/vp6enB6vVyuDgILm5uRmvfmdnB6/XS2trKwcHB5Fuk6Iof0UaesAI cHV1BYDFYsHhcFBQUJAxgNFoxOv1vuijj0qhKJGQICJRh4uLCw4PD6msrKSqqorS0tKkAZ7+JyFA PAuHw0xMTODz+QCw2Wz09/djs9moqanJSpKqLw0GAoHo5AA+nw+Hw0FtbS0jIyMcHx9zfX39egDV 1dV0dHRE2y6Xi6OjI4aHh1ldXcViseB0OgmFQq8DoNPpsNvtAMzMzDA6OorZbGZlZYX9/X2mpqZw u92MjY2lDaEmcujs7ASguLgYg8EQ7W9vb6e3tzcamsnJybQgEgIUFRUxNDTE9vb2s3hHti6Ax+NJ S4mEAIWFhVitVjY2Nj47ojVNY3Fx8TPfdJTQJyRUVZqamgiHw/j9frq6uri/v2dgYABN0575ezwe bm5uWFpaip4tGQFELpSSkhLGx8eTWpXP58PpdGI0GjMPAUBubi7r6+s0NzcnLa3b7WZhYSHe0Fcp AwC0tLQwNzeX1KoS2JyImFIGUFWVtrY2tra2UlICIBgMPk3Mr4HfROSLlAAyUULTtNjd0QK8SQsg XSU8Hg97e3vPckFNN5DpKBHvKk8bIBUlVFVlfn4ekymae5+AzYwAUlGisbERu93+9Jn3i6Iol1kB SEYJg8HwdPLfgZ8zDkEiJXQ6HWVlZfFcf1QU5ebZURx5vz08PHB7e5sWRF1dHV6vl7W1Nerr67m8 vGR2djbW7b/YuyAAfNPQ0ADA7u4u09PTkUIirZCoqsrp6SknJycA5Ofnv/hy/UFEJBgMyrt377Je lJSXl8vm5makMPkYrzSrB/wiUhYKhTg7O0PTtKSu0kSWl5eH2WymoqICvV4vgFNRlPfxVHgrIv+8 YmH6SURWYssyAOUJRD3wFvgWKCJ79gFYB7YURbmPHfwfVdHBbH7urkkAAAAASUVORK5CYII=} $f.process configure -compound top -image pic_apply # remove all metadata from file, used when tool throws error on read, to allow edits to succeed button $f.clear -text "Clear tags" -command {write_file clear} tooltip $f.clear "Remove all metadata tags from file" image create photo pic_clear -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA B3RJTUUH6AUfEBcDdvWNqgAAA1FJREFUWMPFlz1IG2EYx/9vE5WIBqzix1DKKc4iFexYIlUQOhSE RhxqxAwdVFBQRAQdNBgEcXERkquDZ8EM0kULLk4dOnXQFjFHoH4UT8UTokPMv0vvSEy0d/GgDxzc Pc+99/zu/7yfAhlGUgBoAfAWwEsAT2HNzgF8BbAmhPiOQoykIDlG8pyF2wnJd3byigyAZgBf0ul0 1cnJCXZ2dix/pLS0FK2traipqQGACwDvhRCf7SowS5KHh4dsbm4mAFuXz+djPB43lDi3qwRIyiS5 sbFhO7lxSZJ0F+LNv/K67zqSyaR5v7i4iOrq6odrKAQ2NzchyzJUVUUwGMTy8jIkSaoA8JHkByHE J8sKKIpi/pGqqpZ6XjKZ5Ojo6KOUeBQASV5dXWVBtLW12esTjwUgyaOjIwYCgcKUcAKAJI+Pj7Mg LCvhFMB9SiQSCSN8QPJZwQCpVIqpVOqf/rtKdHd3U9M0Izxg5H1iZ67QdR0TExNYWVnBzc2N6dc0 DZ2dndje3kY6nQYA1NbWYmZmBoFAAACgKAr29/eNJi9sK3B5ecnx8XEztrS0xOvra56entLv95v+ 9fV13t7emu12d3fN2OrqquGWbQPIspwz80UiEXZ0dGT5XC4X9/b2zHaqqpoxRVFyACyXoL293ZTT sL6+PmxtbWX55ubmUF9fb7mslgHq6uqyappvRZyfn8fIyAiKi4udBzAgxsbGUFlZmRPz+/0IBoO2 9yG2ADRNw9DQEM7OznJikUgEsVgsa3Q4CqBpGgYGBnJqfrdPRKNRWxCWAWKxGNbW1sznoqIihEKh nD4xOzuLRCJhGcBt9cWenh7E43GEw2EAQCgUwuDgoFmOaDQKAFhYWEBDQ4PzAGVlZZiamoLL5UJV VRWGh4ezZrySkhL4fD50dXXZ6gNuOy97PB5MTk7C7XbnjI5wOAyPx2N7FLjtNrgvSXl5eUHHgSf4 z+bOsyaY99PT0/B6vY9KoOt63m/fC5C5C5Zl2dG/lSTpwRL8BICmpib09vY6LnV/fz8aGxuNx2/5 jmYNALYBPL+4uICu63klK8SEEPB6vaioqACAAwCvhBC/8u0JXpP8QTJN5+2W5B7JlryH0wyI2r9H c8nhKsQBfBVC/M50/gG1d08Kc4+JFQAAAABJRU5ErkJggg==} $f.clear configure -compound top -image pic_clear # read file and send to console all discovered metadata button $f.alltags -text "File Metadata" -command full_info tooltip $f.alltags "Show all metadata of file in Console" image create photo pic_metadata -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA B3RJTUUH6AUfEBcXbC9Z1wAAA5pJREFUWMPtl81LY2cUxn/vzVVjokMiWOkiNRalYEm0EiKIaael m4ogrYKLFqQT3cymTHEziyIO9Q+o2JXYRaF1MaJ0Y3cWlCxaYz8EBbtoQr+EGU3mphkjOsnpwptw negwXhO66QMv9+M9957nvPe8zzlX8RRE5AXgFeBV89YOsKeUemDOa0AjINiDAFmllAAoi2MN+BD4 2CTgMKfywB7wKZAD7pgEuAaBB8AnSqm4lcAt4HPAmclkyGQyAHg8HhoaGopEsBC7Ln4FbirT+cvA T8CNZDLJzMwMa2trAAwMDDA5OUlraysAmUyGlZUVstmsLa8tLS0MDg7idDofAW8Wo/9MRMQwDGlr axNzmUojGAxKLpcTEZH9/X2JRCJlNs87JiYmxDAMEZFHIvKaJiJ1QBhgdXWVRCJRxnp7e5uNjQ2q AQ2oMQeGYVxqWMyJihNQSmXNrKS7u/tSw66urqoQ0M3jMvBOb28vU1NTTE9PnzOanZ2lvb39jLGm 4fP5iEQithx6vV40TStdF3eBG/gWiORyOeLxOEtLS7jdboaHhwkGg9TU1JyJQj7P4eEhhULBXsS6 TlNTE5qmpUq7wCTxkojE5GIUROR7EflBKoMnIrIgInWltVBK/Q68B3wE/GIh/SNw25zbq+DnTwAn z6+fImEReSyVw58i0q5fgXEj4EqlUiwuLnJ8fGwr7ObmZkZGRnC5XI1Xqiki8lZVlJD/GP8TuEoS KoDa2lpGR0cZGhqynYS6rpfepy5INh9wC3jd7IA2gS+AF4HvAFeFgv8LuGl17BSReyJydMGePRKR u6YaVgpfiYhubcnuAXcBPZ1OE4vFqKurIxQK4fV6iy2ZXPGzPQu/AW8Xi1EX8DPA1tYWY2Nj7Ozs ABAKhZifny+V6nQ6zdzcHAcHB7a8dnR0EI1Gqa+vN6wt2ZciIolEQvx+f5l4dHZ2SiqVKrVk/f39 toVofHy8rCVzAh0A6+vrJJPJMta7u7vEYjEshcv2uj/9rGa22TrA6enppQ+enJxUR4iUUo+BPwDC 4TAOR3nb73a76enpqaoQLQLvBgIBFhYWiEaj5PP5ktHy8jJ+v//sr8ThwOfzEQ6HbTn0eDzngrRu w/vAcKFQUJubm8TjcXRdp6+vj0AgUI3gHwJvWIXIKyL3nyEcX5ttVCVwJCK3zwmRhcj7wAdAi3nr b2BeKfWNRaqbrivDSqmDC2uBhcgN8/Sf4q90NfAvDrs9IzerBxgAAAAASUVORK5CYII=} $f.alltags configure -compound top -image pic_metadata # intended to apply all LOCKED fields to all the files in the current folder button $f.writeall -text "Tag All Files" -command { set stop 0 set max [.f2.f1.list size] if {$max < 1} {log "No files in folder."; return} log "Tagging all files in folder..." for {set n 0} {$n <= $max} {incr n} { .f2.f1.list selection clear 0 end .f2.f1.list selection set $n if {$stop} {break} log clear log "----" log "reading file list item $n" read_file log "writing $n" progressbar [expr {100*$n/$max}] write_file } after $var(gumball_delay) status progressbar 0 . configure -cursor {} log "Done." } tooltip $f.writeall "Write tags to all files in list\nUseful when some fields are Locked\nLike Movie, Genre, Comments" image create photo pic_tagall -data { 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 RU5ErkJggg==} $f.writeall configure -compound top -image pic_tagall button $f.help -text "Help" -command help tooltip $f.help "Show usage and commands in console" image create photo pic_help -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA B3RJTUUH6AUfEBcQ8kvMdAAAAVFJREFUSMfVlL1KA0EUhc+msBAiPoFgaQQLEcTOVoI+QUDExkqf QVGwsUlrK1ikshQbQaIGLRTFKoWghcRCUAzB1f0sNo7ZuJudnVTeaZa7c87cc/+kf29enBMSLntW BCH8UVXVdK17jWtK05rRUAJFDMEbZdR1JjkDkmLrIjj6Aw9PLYYiF0dxJ2lXz/rUq65UMv41tWze D5hjg19rUDAx3KaLgAfEjbkIsG4ITm0k1FXUWMSTN1/DNhKIpAp8Zk0lmnZ1iNIdGwEHtoXshD8x 0YZv4Wd/v8lyG17mIzs8YBsh8hwSuKgPO7JIPav2n4koIFZ5cYFLcI4o8Z4CzyX/akla0WDKEPcg GJE0kLoDehCMake4rbToYusdgZcGl1sOQKpqSQuqyJdTES/NCO25dcFmxzr9cusDq0wnR3BhIth3 kwAnLDJPJWWM+y5j3/YND6wkTBwWJDsAAAAASUVORK5CYII=} $f.help configure -compound top -image pic_help button $f.unlockall -text "Unlock all" -command { foreach n [array names lock] { # toggle all locks off if {$lock($n)} {set lock($n) 0} } # update symbols catch {.f2.f3.lock configure -compound top -image pic_unlocked_16} for {set x 1} {$x <= 16} {incr x} {catch {.f2.f4.lock$x configure -compound top -image pic_unlocked_16}} } tooltip $f.unlockall "Unlock all fields" image create photo pic_unlocked -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAA CwwAAAsMAT9AIsgAAAAHdElNRQfoBR8RNSfshkVsAAABHUlEQVRIx+2VoU4DQRCGP5qCI6UBBA9B 05YmSEgdCCwGbH0FkmCw1GBwNfQF6gFTR9ISEpJqcEVACKGo+xG3ucBey+7mQkAwo25m/m925m5z kNFmAmp32WGJIedchzcq0UPG3zmhGCavcp/IY++y4C9f5s6SC3HsDzgiQogRTbY4SwbZ8O0fCx5Y NZEGY4ToMesDOEWIN7aTSJ4OQryw5pYvcosQF8x9iq6YMxy6ATVeEWLPircRYugG7JuF2bZpNuO0 eOdXEzKjGJBzANYBeKSeylinksOfKU/vUnfKv5k3l/U6Zwbkvzwd0LfyES0q/oA+l6mKpx8e4R9g A6IJFQr5DlqplyaqIYDKL+3A7/d2Mz1VYOC8ygPm+aP2AfGtlRA73z9TAAAAAElFTkSuQmCC} $f.unlockall configure -compound top -image pic_unlocked # top row buttons in this order # unused widgets: $f.help grid $f.choosedir $f.refresh $f.play $f.prev $f.next $f.write $f.clear $f.alltags $f.writeall $f.unlockall $f.process -padx 3 # use top row buttons as a progress bar with background color highlight # input: number percent (0-100) proc progressbar {pct} { # toggle button backgrounds on if pct >= their position up to 100 set buttons {choosedir refresh help play prev next write clear alltags writeall unlockall process} # number of button to light up set num [expr {[llength $buttons] * $pct / 100}] foreach w [lrange $buttons 0 $num-1] {.f1.$w configure -background green3} foreach w [lrange $buttons $num end] {.f1.$w configure -background SystemButtonFace} update } image create photo pic_locked_16 -data { iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAA CwwAAAsMAT9AIsgAAAAHdElNRQfoBR8SACzbI+ALAAAA2UlEQVQoz4XQLU4DQRwF8F+725YgSBCQ 4EuCQlBHVuAJd6jiErjegIoK7kCCImiSShIMGJDgEAhoaJd2EZ0ls1sS3sh5X/+XiNFxpOfNxJ/Y culL7lGmsfqdGvl279rUg51VwoFPt7Z1DOTO6h5NA1MnYNOzO2tVQsuNFxsSiZaRid0yeYk9PXND C7Cv7di5otQnLhS19249dkjxZBw0XZl0WTONeoydhoi+Q6odoLAwh0AL5/2D2KGrH7TR1DEh+01u aJYnpiH91Udl3MKV2ZJbLtmuhc/k8APBOjlCVIIkYgAAAABJRU5ErkJggg==} image create photo pic_unlocked_16 -data { iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAA CwwAAAsMAT9AIsgAAAAHdElNRQfoBR8SABtjnkUEAAAA2klEQVQoz4XRvS4EARSG4Wf+kgnNRCPR KCcalYToRKcRjWjcgrgAqr0C10BLQS1uwAWothEJuw02YtjsjGJZM2MT3ynPe77zF/mrwKIts56U U7JCux4MvTkxMw1Y1jNwoevTobCdjp15ty+R67o33wYWPLqRInDgw17bYknmSoHKuUIeC21anwAr YhtO9dHTcUvuVdWI0lF9qFBi5NLz95LbMnEdgKFjdyC1KmuuNVZpBEaq9t3+0dghsmMNJM0GP0Ci M7GOqLeJDfTNNYpeXNdfG0hbk5SKX48vZ/A5OLH2FA8AAAAASUVORK5CYII=} # right column container set f .f2 frame $f pack $f -side top -anchor nw -expand 1 -fill both # filename listing set f .f2.f1 frame $f pack $f -side left -anchor nw -fill both -expand 1 #scrollbar $f.x -orient horizontal -command "$f.list xview" scrollbar $f.y -orient vertical -command "$f.list yview" listbox $f.list -listvariable filelist -yscrollcommand "$f.y set" -width 40 -selectmode single grid $f.list $f.y -sticky nsew # grid $f.x -sticky nsew grid rowconfigure $f $f.list -weight 1 grid columnconfigure $f $f.list -weight 1 bind $f.list <<ListboxSelect>> read_file # column container to right of listbox set f .f2.f2 frame $f pack $f -side top -anchor nw # filename label $f.filename -textvariable data(filename) -font {-size 12} pack $f.filename -side top set f .f2.f3 frame $f pack $f -side top -anchor nw -fill x label $f.lbl -text "Type: " pack $f.lbl -side left # file type tags supported by MP4 only, not MKV set num 0 foreach txt {Movie Normal Audiobook "Music Video" "Short Film" "TV Show"} { radiobutton $f.type$num -text $txt -variable data(stik) -value $txt \ -indicatoron 0 -relief raised -bd 1 -highlightthickness 0 -anchor c \ -selectcolor $var(on_color) pack $f.type$num -side left -padx 3 incr num } checkbutton $f.lock -text "" -variable lock(stik) \ -indicatoron 0 -relief raised -bd 1 -highlightthickness 0 -anchor c \ -takefocus 0 -selectcolor $var(on_color) -command "set_lockdata $f.lock stik" tooltip $f.lock "Lock this value" $f.lock configure -compound top -image pic_unlocked_16 pack $f.lock -side left # toggle for mp4 write later checkbutton $f.deferwrite -text D -variable var(defer_exec) -indicatoron 0 -relief raised \ -bd 1 -highlightthickness 0 -anchor c -takefocus 0 -selectcolor $var(on_color) tooltip $f.deferwrite "MP4 bat files run when Apply clicked" pack $f.deferwrite -side left -padx 5 # metadata table set f .f2.f4 frame $f pack $f -side top -anchor nw set count 1 # table of tags in order of display foreach {tag lbl} { @nam Title @ART Artist @alb Album @grp Grouping @gen Genre @day Year track {Track Num} disc {Disc Num} desc Description @cmt Comments cprt Copyright @too Encoder tvnn TVNetwork tvsh TVShowName tven TVEpisode tvsn TVSeasonNum tves TVEpisodeNum } { label $f.lbl$count -text $lbl entry $f.ent$count -textvariable data($tag) -width $var(textwidth) checkbutton $f.lock$count -text "" -variable lock($tag) \ -indicatoron 0 -relief raised -bd 1 -highlightthickness 0 -anchor c \ -takefocus 0 -selectcolor $var(on_color) -command "set_lockdata $f.lock$count $tag" tooltip $f.lock$count "Lock this value" $f.lock$count configure -compound top -image pic_unlocked_16 button $f.clr$count -text "X" -command "set data($tag) {}" -takefocus 0 tooltip $f.clr$count "Clear field" if {$tag == "@nam"} { # TITLE FIELD button $f.copy -text "C" -command filename_to_title -takefocus 0 tooltip $f.copy "Copy filename to Title" button $f.rename -text "P" -command rename_file -takefocus 0 tooltip $f.rename "Paste Title to filename" grid $f.lbl$count $f.ent$count $f.lock$count $f.clr$count $f.copy $f.rename -sticky w } else { # layout left-to-right text label, entry field, lock, clear grid $f.lbl$count $f.ent$count $f.lock$count $f.clr$count -sticky w } incr count } # save or clear lock data from table # pass in widget number and variable to change proc set_lockdata {w v} { global data lock lockdata if {$lock($v)} { set lockdata($v) $data($v) puts "locked $v: $data($v)" catch {$w configure -compound top -image pic_locked_16} } else { set lockdata($v) "" puts "unlocked $v" catch {$w configure -compound top -image pic_unlocked_16} } } # artwork hack set f .f2.f5 frame $f pack $f -side top -anchor nw set tag covr button $f.lbl$count -text Artwork -command { set f [tk_getOpenFile -title "Select picture file" -filetypes { {{JPEG Files} {.jpg}} {{PNG Files} {.png}} {{All Files} *} } ] if {[file exists $f]} {set data(covr) $f} # check file size of artwork, warn if too large } tooltip $f.lbl$count "Select artwork file to embed\nPrefer JPG under 300KB" image create photo pic_attach -data { iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA B3RJTUUH6AUfEBY2OV14yAAAA41JREFUWMPtV01IY1cYPS+JCdokWBdFAioB3ZSxI2iFEnRVu9ON G0WmxkWHgRkRCgm4tMaFMIJQfyhSDOhCdKmUNIXCqNUQiBSVEkEIKqPVIfqGxJeY9OXMQt9DG40x iZ1ND7zNdy/fd+6537vnXgH3gKQGQAOAlwCeANAiO8QA/ArgZ0EQDpArSH5F8h1zh4+k5a78QhYE fgPwtSRJWF5ehiiKWRGvqalBbW0t9Ho9ADwXBGEqVwUuSHJxcZEAHvTt7e0pKtxZXJcFBz0ARKNR AEB1dTVsNpuysjRIkgSPx4NwOIxUKqWEi/IhcAM2mw1DQ0MwmUy3jh8fHyMUCmFtbS2rfLoM0usA PE2TQ6+HyWSC2Wy+UwGtVpv1gjQZxp4BeJNh/BzACwA/XO15TtDdsXojgNfxePyTk5MTGI3G26b9 A8AP4EcAgiiKWF1dhcVigUajyVuBbwCUeb1eVFVVweFwZMrxJQDMzMygtbUVTqcTRUVFeRMwAsDB weUBtr29fe9fsru7e3mwCMKDtkCDj4z/CeRLgB+TQDGAxv+cQCQSgSRJSvf/BACpVEr1ikcjUFJS AgCYm5vD4OAgdnZ2AADJZBLz8/NYWFh4XALNzc1wuVwAgImJCbS3t8Pv92N2dhadnZ2IRCJoa2tD b28vZFnOs7PIb0lybGyMANjY2EiSjMfj9Hq9LC8vT/P+np4e7u/v8+joiE1NTQTAUCik3AfcBekB g8GAlpYWLC0twW63q3Gn04nx8XFUVFQUxozuQ319PUZGRlBXVwez2YyOjg4UFxcXzg3/jUQigYuL CxgMBjVWVlaGvr6+dI8+P1f+kvybUKe75BcMBjE8PIyzs7OMyba2tuByuRAIBHIyputN+AVJORgM 0m63q41mtVq5srLCRCJx494tyzJ9Ph+tVqs61+12MxaLKVNePZSAhuQvJCmKIicnJ9XEpaWlHBgY 4OHhIUkyFovR7XbfIOnz+SjLslI8QPKzXFSwkPSQTJFkIBBgV1eXWqihoYF+v5/9/f1qrLu7m5ub m0rhFMk/Mj1KsiFhIvmKZJQkT09POT09Ta1Wm3YOOBwOhsPh6zszSdJcEMsk2ULyLyXz+vq62huV lZUcHR29Xvg9yRcF922S5SRnlCqiKNLj8XBjY4PJZFIJv716Swp4LJD8juTftzxCfydZ+eg3GJIC yc9JTpH8k6Sf5PckP80l3wcl6a2QCOhG6QAAAABJRU5ErkJggg==} # $f.lbl$count configure -compound top -image pic_attach label $f.ent$count -textvariable data($tag) -width 40 button $f.clr$count -text "X" -takefocus 0 -command "write_file clearartwork" tooltip $f.clr$count "Remove all artwork from file" checkbutton $f.disp$count -text "D" -variable var(display_thumb) \ -indicatoron 0 -relief raised -bd 1 -highlightthickness 0 -anchor c \ -takefocus 0 -selectcolor $var(on_color) tooltip $f.disp$count "Display embedded artwork" checkbutton $f.save$count -text "S" -variable var(save_thumb) \ -indicatoron 0 -relief raised -bd 1 -highlightthickness 0 -anchor c \ -takefocus 0 -selectcolor $var(on_color) tooltip $f.save$count "Save embedded artwork to file" grid $f.lbl$count $f.ent$count $f.clr$count $f.disp$count $f.save$count # text widget console set f .f4 frame $f scrollbar $f.x -orient horizontal -command [list $f.txt xview] scrollbar $f.y -orient vertical -command [list $f.txt yview] # 80 columns, 10 rows text $f.txt -width 80 -height 10 -bg white -wrap word \ -undo 0 -maxundo 0 -font {Courier 8} \ -xscrollcommand [list $f.x set] -yscrollcommand [list $f.y set] pack [ttk::sizegrip $f.grip] -side right -anchor se pack $f.x -side bottom -fill x pack $f.y -side right -fill y pack $f.txt -side left -fill both -expand 1 pack $f -side bottom -fill x -anchor nw # MAIN LOG ADD ALL KNOWN COLORS AS TAGS foreach {tag fg bg} { debug black gray70 ok black white warn black yellow err red white } { $f.txt tag configure $tag -foreground $fg -background $bg } .f1.refresh invoke
2024-05-31 FB I am actively updating this script and will backport the changes here when it is tested stable again. Added artwork to MKV files, images onto the buttons, and more error handling and console support.
2024-11-27 FB replaced script with latest version, currently testing with TCL v9.0
[Category movie] [Category Matroska]