can't read "::snackAmpSettings(ebg)": no such variable
    while executing
"option add *ID3Editors*Entry.background      $::snackAmpSettings(ebg) startupFile"
    (in namespace eval "::request::id3Table" script line 2)
    invoked from within
"namespace eval id3Table {
   option add *ID3Editors*Entry.background      $::snackAmpSettings(ebg) startupFile
   option add *ID3Editors*Entry.foregro..."
    (in namespace eval "::request" script line 32)
    invoked from within
"namespace eval ::request $script"
    ("::try" body line 12)

OUTPUT BUFFER:

#------------------------------------------------------------------------------ # id3Table 1.0 # # ID3 table editor windows for snackAmp Player in Tcl/Tk # Uses id3Tag package # Copyright (C) 2003 Tom Wilkason # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA # # Please send any comments/bug reports to # tom.wilkason@cox.net (Tom Wilkason) # #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Function : id3Table # Description: Window to edit the proposed tag names based on file name data # Author : Tom Wilkason # Date : 2/8/2001 #------------------------------------------------------------------------------ namespace eval id3Table { option add *ID3Editors*Entry.background $::snackAmpSettings(ebg) startupFile option add *ID3Editors*Entry.foreground $::snackAmpSettings(efg) startupFile option add *ID3Editors*Label.font entry interactive ;# high priority } interp alias {} slen {} string length proc id3Table::Edit {fnames {smart 0}} { package require id3Editor package require BWidget set i 0 set tr 0 if {[llength $fnames] == 0} { tk_messageBox -type ok -icon warning -message "No files specified" return } foreach fname $fnames { if {![file exists $fname]} { saLog "'$fname' Does not exist" } } set here [focus] # Get a unique window, note $W is also a global variable # holding stuff for this instance of the GUI while {[winfo exists .ntIdent$i]} {incr i} set W .ntIdent$i # Shared database for this instance global $W.table upvar #0 $W.table myId3Data global $W.freedb upvar #0 $W.freedb freedbData global $W.renamer set id3Data $W.table ;# for linking ## # Create window # toplevel $W -cursor $::TA(arrow) -class ID3Editors wm title $W "Table Editor and Fle Renamer" settings::Hide $W wm protocol $W WM_DELETE_WINDOW [list id3Editor::closeWindow $W [me]] ## # Create a notebook, create a tab for the table data and other as needed # set bg [$W cget -background] set NB [NoteBook $W.nb -side top -arcradius 3 -tabbevelsize 3 \ -background $bg -borderwidth 1] # tabs $NB insert 0 table -text "Tags" $NB insert 1 discid -text "Freedb Search" $NB insert 2 renamer -text "File Renamer" set Table [$NB getframe table] set Discid [$NB getframe discid] set Renamer [$NB getframe renamer] set myId3Data(NB) $NB set myId3Data(Table) $Table set myId3Data(Discid) $Discid #pack $TableHull.lbf -side top -expand true -fill both # Query Tab, also grabs id3 data from tracks pack [id3Table::Tablebuild $Table.table $fnames $smart] -fill both -expand true # Freedb Tab pack [id3Table::freedbQueryTab $Discid.discid] -fill both -expand true # Renamer Tab package require id3Renamer pack [id3Renamer::build $Renamer.renamer $fnames] -fill both -expand true # Pack the notbook and raise the first tab pack $NB -fill both -expand true $NB raise table # restore any prior widths settings::restoreWidth $W [me] ontop::pinOnSystemMenu {} $W myIcon $W focus $W settings::Restore $W } #---------------------------------------------------------------------------- # Build the contents of the table tab #---------------------------------------------------------------------------- proc id3Table::Tablebuild {parent fnames smart} { set base [frame $parent] set W [winfo toplevel $base] upvar $W myData global $W.table upvar $W.table myId3Data set id3Data $W.table ## # Build the GUI # # get current data and line to global with the same name # as this widget (to allow multiple editors at once) # Decide when to use a scroll bar and how big to make the screen, # don't want to make any bigger than necessary. set i 0 set ls [font metrics entry -linespace] set needHeight [expr {round(([llength $fnames]) * $ls * 1.3)}] set maxHeight [winfo pixels . $::snackAmpSettings(scrolledCanvasHeight)] if {$maxHeight > $needHeight} { set maxHeight $needHeight } set bg [$base cget -background] set sw [ScrolledWindow $base.data -relief flat -borderwidth 1 -background $bg] set sf [ScrollableFrame $sw.f -constrainedwidth true -height $maxHeight -background $bg] $sw setwidget $sf set hull [$sf getframe] ## # Genre menubutton # Artist and Albmum menu buttons foreach {Type Width} [list Artist 30 Album 30 Year 4 Genre 12] { set myId3Data($Type) $Type set mb [menubutton $hull.lb$Type -indicatoron 1 -pady 0 -font listbox\ -text $Type -menu $hull.lb$Type.menu -direction flush -width $Width] balloon::define $mb "Click to select a common $Type for all files" if {$Type=="Genre"} { menu $mb.menu \ -postcommand [list id3Editor::_postGenreNames $mb.menu ::${id3Data}($Type)] } else { menu $mb.menu \ -postcommand [list id3Table::_postTypeNames $mb.menu ::${id3Data} $Type] } # When variable is updated during postcommand, call id3Table_commonType to set others to same trace variable ::${id3Data}($Type) w id3Table::_commonType } ## # Grid out the labels # grid \ [label $hull.lb[incr i] -text "Guess Tag"] \ [label $hull.lb[incr i] -text "Change"] \ $hull.lbArtist \ $hull.lbAlbum \ [label $hull.lb[incr i] -text "Title" -relief raised] \ $hull.lbYear \ [label $hull.lb[incr i] -text "Track" -relief raised] \ $hull.lbGenre -sticky nsew set i 0 ## # Build one row for each file # set tag 0 # Data set ent 1 foreach {primTagType altTagType} $::snackAmpSettings(ID3ReadOrder) {break} ## # For each track create a row # foreach fname $fnames { set toGrid [list] # Get existing tag data # Determine data from parsed name # Only updates entries where # the file name provides it foreach {field} {Artist Album Title Year Track Genre} { set fnameData($field) "" } # Build smart tag if desired if {$smart} { id3Editor::cbSmartTagName fname fnameData } # Overlay with existing ID3 data set hasTag [id3Tag::id3Label $fname fnameData $::snackAmpSettings(ID3ReadOrder)] set myId3Data($tag,oldFullName) $fname if {$hasTag} { foreach {field} {Artist Album Title Year Track Genre} { set myId3Data($tag,$field) $fnameData($field) } } # Default to true set myId3Data($tag,ok) 1 lappend toGrid [button $hull.bt[incr i] \ -image saImages.Guess -compound left \ -highlightthickness 0 -pady 0 -text "Guess" \ -command [list id3Table::_cbOneSmartTagName $fname $tag ::${id3Data}]] balloon::define $hull.bt$i "Press this to guess the tags for \n$fname" lappend toGrid [checkbutton $hull.en[incr i] \ -variable ::${id3Data}($tag,ok) -font {Helvetica 5}] balloon::define $hull.en$i "Check this to have OK/Apply modify the tag in \n$fname" ## # Labels, pop-ups and key bindings # foreach {field width} {Artist 30 Album 30 Title 40 Year 5 Track 3} { set item [entry $hull.en$field$ent \ -width $width -textvariable ::${id3Data}($tag,$field)] widgetWatch::setWatch $item ::${id3Data}($tag,$field) if {$field=="Track" || $field == "Year"} { $item configure -justify right } ## # Bind up/down keys to go up down in column # bind $item [list Focus $hull.en$field[expr {$ent-1}]] bind $item [list Focus $hull.en$field[expr {$ent+1}]] lappend toGrid $item if {$field=="Track"} { # Bind Control N to track to auto number those below bind $item [list id3Table::_NumTrack $hull $ent] balloon::define $item "Modify $field data then press OK or Apply to make the change. Ctrl-n to auto number in track field." } elseif {[lsearch -exact [list Title Artist Year Album] $field]>= 0} { bind $item [list id3Table::_NumCase $hull [list $field]] bind $item [list id3Table::_clear $hull $field $ent] balloon::define $item "Modify $field data then press OK or Apply to make the change. Ctrl-e to erase field, Ctrl-f to fix case." } } incr ent # Make a cascaded menu of genres set mb [menubutton $hull.en[incr i] -indicatoron 1 -pady 0 -font listbox\ -textvariable ::${id3Data}($tag,Genre) -menu $hull.en$i.menu -direction flush -width 10] balloon::define $mb "Click to select a genre" menu $mb.menu \ -postcommand [list id3Editor::_postGenreNames $mb.menu ::${id3Data}($tag,Genre)] widgetWatch::setWatch $mb ::${id3Data}($tag,Genre) lappend toGrid $mb eval grid $toGrid -sticky nsew incr tag } # Ensure only the data columns expand grid columnconfigure $hull 2 -weight 1 grid columnconfigure $hull 3 -weight 1 grid columnconfigure $hull 4 -weight 1 grid columnconfigure $hull 5 -weight 0 ## # Frame for OK/Cancel/Apply buttons # pack [frame $base.but] -fill x -expand false -side bottom # Line pack [horzLine $base.l[incr i]] \ -fill x -expand false -side bottom # Data pack $base.data -side top -expand true -fill both set wid 9 # Check All set checkAll [button $base.but.[incr i] -width $wid -default normal -text "Check All" \ -command [list id3Editor::cbSetOK ::$id3Data 1]] balloon::define $checkAll "Push this to check all checkboxes" # Check None set checkNone [button $base.but.[incr i] -width $wid -default normal -text "Check None" \ -command [list id3Editor::cbSetOK ::$id3Data 0]] balloon::define $checkNone "Push this to un-check all checkboxes" # Guess Tags set guessTags [button $base.but.[incr i] -default normal -text "Guess Tags" \ -command [list id3Table::_cbManySmartTagName ::$id3Data]\ -compound left -image saImages.Guess] balloon::define $checkNone "Push this to guess tags based on the file name" # Guess Tags set v2tag [button $base.but.[incr i] -width $wid -default normal -text "Alt Tag" \ -command [list id3Table::_cbManygetV2data $altTagType ::$id3Data]] balloon::define $v2tag "Push this to copy in ID3V2 data (if it exists)" set artAlb [button $base.but.[incr i] -width $wid -default normal -text "Art<->Alb" \ -command [list id3Table::_cbManygetSwap ::$id3Data Artist Album]] balloon::define $v2tag "Push this to Swap Artist and Album" set albTit [button $base.but.[incr i] -width $wid -default normal -text "Alb<->Tit" \ -command [list id3Table::_cbManygetSwap ::$id3Data Album Title]] balloon::define $v2tag "Push this to Swap Album and Title" set artTit [button $base.but.[incr i] -width $wid -default normal -text "Art<->Tit" \ -command [list id3Table::_cbManygetSwap ::$id3Data Artist Title]] balloon::define $v2tag "Push this to Swap Artist and Title" # Guess Case set guessCase [button $base.but.[incr i] -width $wid -default normal -text "Fix Case" \ -command [list id3Table::_NumCase $hull {Artist Album Title}]] balloon::define $guessCase "Push this to correct the text case in the tags" # Apply set app [button $base.but.[incr i] -default normal -text "Apply" \ -command [list id3Table::_apply $W [me] 0] \ -compound left -image saImages.Save] balloon::define $app "Push this to modify the ID3 tags for checked entries" # Apply and close set ok [button $base.but.[incr i] -default active -text "OK" -padx 4\ -compound left -image saImages.Apply \ -command [list id3Table::_apply $W [me] 1]] balloon::define $ok "Push this to modify the ID3 tags for checked entries, then close this dialog" # Cancel set can [button $base.but.[incr i] -default normal -text "Cancel" -compound left -image saImages.Close \ -command [list id3Editor::closeWindow $W [me]]] balloon::define $can "Push this to ignore changes made and restore the previous settings" grid $checkAll $checkNone $artAlb $albTit $artTit -padx [extraPad 1] -pady 2 -sticky ewns grid $v2tag $guessTags $guessCase $app $ok $can -padx [extraPad 1] -pady 2 -sticky ewns $ok configure -default active #bind [winfo toplevel $W] [list $ok invoke] bind [winfo toplevel $W] [list $can invoke] return $base } #------------------------------------------------------------------------------ # Function : id3Table::_NumTrack # Description: Auto number tracks below based on this one # # Author : Tom Wilkason #------------------------------------------------------------------------------ proc id3Table::_NumTrack {hull index} { set len [slen $hull.enTrack] set start 1 foreach {entry} [lsort -dictionary [winfo children $hull]] { if {[string match $hull.enTrack* $entry]} { set enIndex [string range $entry $len end] set gvalue [$entry cget -textvariable] if {$enIndex == $index} { set start [set $gvalue] if {![string is integer -strict $start]} { set start 1 set $gvalue $start } } elseif {$enIndex >= $index} { incr start set $gvalue $start } } } } #---------------------------------------------------------------------------- # Clear a field #---------------------------------------------------------------------------- proc id3Table::_clear {hull field index} { foreach {entry} [lsort -dictionary [winfo children $hull]] { regexp {[0-9]+$} $entry tIndex ;# grab what index this is if {[string match $hull.en${field}* $entry] && ($index==$tIndex)} { # Need $gvalue due to trace on it set gvalue [$entry cget -textvariable] set $gvalue $id3Tag::rmMatch } } } #------------------------------------------------------------------------------ # Function : id3Table::_NumCase # Description: Correct the case for each item in this column (field) # # Author : Tom Wilkason #------------------------------------------------------------------------------ proc id3Table::_NumCase {hull fields} { foreach {entry} [lsort -dictionary [winfo children $hull]] { foreach field $fields { if {[string match $hull.en${field}* $entry]} { # Need $gvalue due to trace on it set gvalue [$entry cget -textvariable] set $gvalue [stringTitle [set $gvalue]] } } } } #------------------------------------------------------------------------------ # Function : id3Table::_commonType # Description: Callback to change all genres for block editor # Author : Tom Wilkason #------------------------------------------------------------------------------ proc id3Table::_commonType {_Glob Element args} { upvar $_Glob Glob set value $Glob($Element) foreach {item} [array names Glob "*,$Element"] { # Traced, use full global name set Glob($item) $value } } #------------------------------------------------------------------------------ # Function : id3Table::_apply # Description: Retag a set of files # # Author : Tom Wilkason #------------------------------------------------------------------------------ proc id3Table::_apply {W winTag {close 0}} { global $W.table upvar $W.table id3Data global $W.freedb upvar $W.freedb freedbData # Process each file that is checked set indicies [lsort -dictionary [array names id3Data *,ok]] set i 0 set fnames [list] set totchanged 0 foreach {element} $indicies { # If checkbox checked, try to rename it incr i if {$id3Data($element)} { set tag [lindex [split $element ,] 0] set fname $id3Data($tag,oldFullName) lappend fnames $fname # Blank then Get existing data unset -nocomplain fnameData #id3Tag::id3Label $fname fnameData set changed 0 foreach {field} {Artist Album Title Year Track Genre} { # Copy in only existing non-blank data if {[hasData id3Data($tag,$field)] > 0} { set fnameData($field) $id3Data($tag,$field) incr changed } } # Don't bother to update if the data hasn't changed if {$changed} { if {[id3Tag::id3Modify $fname fnameData]} { waitWindow::percent $i [llength $indicies] "Setting Tag for\n[splitFile $fname]" incr totchanged } } } } if {$totchanged} { update # Update any data that is currently shown trackList::updateFiles $fnames LB::updateFiles $fnames } waitWindow::close # If close widget when done indicated. if {$close} { id3Editor::closeWindow $W $winTag } } #------------------------------------------------------------------------------ # Function : id3Table_cbOneSmartTagName # Description: Move data from a single smart tag into an indexed array # used by the block editor. # Author : Tom Wilkason #------------------------------------------------------------------------------ proc id3Table::_cbOneSmartTagName {fname index _Array} { upvar $_Array Data id3Editor::cbSmartTagName fname oneData foreach {item} [array names oneData] { if {$Data($index,$item) ne $oneData($item)} { # Traced, use full global name set Data($index,$item) $oneData($item) set Data($index,ok) 1 } } } #------------------------------------------------------------------------------ # Function : id3Table_cbManySmartTagName # Description: Create a number of smart tag to name mappings # # Author : Tom Wilkason #------------------------------------------------------------------------------ proc id3Table::_cbManySmartTagName {_Array} { upvar $_Array Data foreach {item} [array names Data *,oldFullName] { set fname $Data($item) set index [lindex [split $item ,] 0] Unset oneData id3Editor::cbSmartTagName fname oneData foreach {item} [array names oneData] { if {$Data($index,$item) ne $oneData($item)} { # Traced, use full global name set Data($index,$item) $oneData($item) set Data($index,ok) 1 } } } } #------------------------------------------------------------------------------ # id3Table::_cbManygetSwap ::$id3Data Artist Title #------------------------------------------------------------------------------ proc id3Table::_cbManygetSwap {_Array From To} { Trace upvar $_Array Data set from [array names Data *,$From] set to [array names Data *,$To] foreach {f} $from {t} $to { set save $Data($t) set Data($t) $Data($f) set Data($f) $save } } #------------------------------------------------------------------------------ # Function : id3Table_cbManygetV2data # Description: Read the ID3V2/ogg data and if it exists use # it instead # used by the block editor. # Author : Tom Wilkason #------------------------------------------------------------------------------ proc id3Table::_cbManygetV2data {altTagType _Array} { upvar $_Array Data foreach {item} [array names Data *,oldFullName] { set fname $Data($item) set index [lindex [split $item ,] 0] Unset oneData if {[id3Tag::isogg $fname]} { set type "OGG" } else { set type $altTagType } if {[id3Tag::id3Label $fname oneData $type]} { foreach field {Title Artist Album Track} { if {[info exists oneData($field)]} { if {$field=="Track"} { # Just want the base number set val [lindex [split $oneData($field) /] 0] } else { set val $oneData($field) } set Data($index,$field) $val } } set Data($index,ok) 1 } } } #------------------------------------------------------------------------------ # Function : id3Table_postTypeNames # Description: On pop-up, post the common types # Author : Tom Wilkason #------------------------------------------------------------------------------ proc id3Table::_postTypeNames {Menu _Array Element} { upvar $_Array Array ## # Get list of current entries # set last [$Menu index end] set added [list] if {![string match "none" $last]} { for {set i 0} {$i <= $last} {incr i} { lappend added [$Menu entrycget $i -label] } } ## # Add any unique items in the array to the menu # foreach {item} [lsort -dictionary [array names Array "*,$Element"]] { set value $Array($item) if {[slen $value] > 0 && [lsearch -exact $added $value]==-1} { # TFW: What we really want is to set te $item fields directly # not the Element which is also a label $Menu add command -label "$value" \ -command [list set ${_Array}($Element) $value] lappend added $value } } } #---------------------------------------------------------------------------- # Dialog to allow select of various freedb entries and select then # apply them to the associated tag editor window. #---------------------------------------------------------------------------- proc id3Table::freedbQueryTab {parent} { set hull [frame $parent] set W [winfo toplevel $hull] global $W.table upvar $W.table id3Data global $W.freedb upvar $W.freedb freedbData # get my hull set i 0 # frames frame $hull.list frame $hull.list.tf frame $hull.list.bf # # Modify to have a list box on type, then selecting a track fills # the bottom with the details. Then use the apply key # set sb [Scrolled_Listbox $hull.list.tf.sb \ -selectmode browse \ -exportselection no \ -activestyle dotbox \ -height 1 \ -width 45 \ -foreground $::snackAmpSettings(fg) \ -selectforeground $::snackAmpSettings(fg) \ -selectbackground $::snackAmpSettings(highlight) \ -background $::snackAmpSettings(bg)] settings::regColor $sb \ -foreground ::snackAmpSettings(fg) \ -selectforeground ::snackAmpSettings(fg) \ -selectbackground ::snackAmpSettings(highlight) \ -background ::snackAmpSettings(bg) set i 0 ## # Listbox to hold the track details # set lb [Scrolled_Listbox $hull.list.bf.lb \ -selectmode extended \ -exportselection no \ -activestyle dotbox \ -width 45 \ -height 1 \ -foreground $::snackAmpSettings(fg) \ -selectforeground $::snackAmpSettings(fg) \ -selectbackground $::snackAmpSettings(highlight) \ -background $::snackAmpSettings(bg)] settings::regColor $lb \ -foreground ::snackAmpSettings(fg) \ -selectforeground ::snackAmpSettings(fg) \ -selectbackground ::snackAmpSettings(highlight) \ -background ::snackAmpSettings(bg) ## # Bind a select on the entry to lookup the data # bindtags $lb none bind $sb <> { focus %W id3Table::cbAlbumSelected %W [%W get [%W curselection]] } set freedbData(LB) $lb set freedbData(SB) $sb ## # Frame for OK/Cancel/Apply buttons # frame $hull.but pack $hull.but -fill x -expand false -side bottom # Line pack [horzLine $hull.l[incr i]] \ -expand false -fill x -side bottom # Search fields, load defaults set freedbData(sArtist) $id3Data(0,Artist) set freedbData(sAlbum) $id3Data(0,Album) set s1 [button $hull.but.[incr i] -text "Artist" \ -command [list id3Table::searchFreedb $W "Artist"] \ -compound left -image saImages.Guess \ ] set s2 [entry $hull.but.[incr i] -textvariable $W.freedb(sArtist) \ -relief sunken -width 30] set s3 [button $hull.but.[incr i] -text "Album" \ -command [list id3Table::searchFreedb $W "Album"] \ -compound left -image saImages.Guess \ ] set s4 [entry $hull.but.[incr i] -textvariable $W.freedb(sAlbum) \ -relief sunken -width 30] # Guess freedb Tags set freedbSearchTags [button $hull.but.[incr i] -default normal -text "Search Tags" \ -command [list id3Table::searchFreedb $W]\ -compound left -image saImages.Guess] balloon::define $freedbSearchTags "Push this to search the freedb service for Album Artist" # Try to split album into artst / album for various artists set useVarious [checkbutton $hull.but.[incr i] \ -text "Various Artist Album" -variable $W.freedb(various)] balloon::define $useVarious "Check this to extract the Arist Name from the Track for albums with multiple artists" set l1 [label $hull.but.[incr i] -text "Year"] set l2 [label $hull.but.[incr i] -textvariable $W.freedb(Year) -relief sunken -width 30] set l3 [label $hull.but.[incr i] -text "Genre"] set l4 [label $hull.but.[incr i] -textvariable $W.freedb(Genre) -relief sunken -width 30] # Guess freedb Tags set freedbGuessTags [button $hull.but.[incr i] -default normal -text "Guess Tags" \ -command [list id3Table::populateAlbums $W]\ -compound left -image saImages.Guess] balloon::define $freedbGuessTags "Push this to query the freedb service to find tags based on computed discid" # Apply set app [button $hull.but.[incr i] -default normal -text "Apply" -compound left -image saImages.Save \ -command [list id3Table::processButtons $W apply]] balloon::define $app "Push this Apply the selected album tracks" # Cancel set can [button $hull.but.[incr i] -default normal -text "Close" -compound left -image saImages.Close \ -command [list id3Table::processButtons $W cancel]] balloon::define $can "Push this to cancel, then close this dialog" # Freedb Html set ht [button $hull.but.[incr i] -text "Details" -default normal -image saImages.web -compound left\ -command [list id3Table::freedbAlbum $sb]] balloon::define $ht "Push this to bring up a web page for the selected album" grid $s1 $s2 $s3 $s4 $freedbSearchTags $useVarious $ht -padx [extraPad 1] -pady 1 -sticky ew grid $l1 $l2 $l3 $l4 $freedbGuessTags $app $can -padx [extraPad 1] -pady 1 -sticky ew # Pack em in pack $hull.list.tf.sb -fill both -expand true -side top pack $hull.list.bf.lb -fill both -expand true -side top pack $hull.list.tf -fill both -expand true -side left pack $hull.list.bf -fill both -expand true -side right pack $hull.list -fill both -expand true -side top return $hull } #---------------------------------------------------------------------------- # Launch a web browser on the select track #---------------------------------------------------------------------------- proc id3Table::freedbAlbum {sb} { if {[catch {$sb get [$sb curselection]} key] } then { return } set W [winfo toplevel $sb] global $W.table upvar $W.table id3Data global $W.freedb upvar $W.freedb freedbData if {[slen $key]} { set record $freedbData(lbkey,$key) foreach {genre discid} [split $record] {break} #http://www.freedb.org/freedb_search_fmt.php?cat=rock&id=ad0d840d #package require HttpD set url $browser::cddbBase append url "freedb_search_fmt.php?cat=$genre&id=$discid" set redir {} set redir [subst -nocommand $redir] set redirFile [file join $::HOME redirect.html] if {[catch {open $redirFile w} result]} { saLog "Error:$result" return } else { puts $result $redir close $result } browser::urlOpen $redirFile } } #---------------------------------------------------------------------------- # When the user selects the freedb query tab or button, populate # the left table with the album list if possible. #---------------------------------------------------------------------------- proc id3Table::populateAlbums {W} { set win [string trimleft $W ":"] if {[winfo exists $win]} { global $W.table upvar $W.table id3Data global $W.freedb upvar $W.freedb freedbData ## # Raise this page # $id3Data(NB) raise discid package require freedb # Get list of all tracks set tracks [list] foreach {item} [lsort -dictionary [array names id3Data *,oldFullName]] { lappend tracks $id3Data($item) } # Query freedb to see if album exists set found [freedb::albumList $tracks] if {[llength $found]==0} { tk_messageBox -type ok -icon info -message "No Freedb Matches Found on\n$::snackAmpSettings(freedb)" return } # Populate with return records (lookup), verify window still exists if {[info exists freedbData(SB)] && [winfo exists $freedbData(SB)]} { $freedbData(SB) delete 0 end set rows 0 foreach {record} $found { ;# rock 7908110a The Shins / Chutes too Narrow if {[regexp -- {(.+? .+?) (.*)$} $record -> front back]} { set freedbData(lbkey,$back) $record ;# save a key $freedbData(SB) insert end $back incr rows } else { saLog $record } } $freedbData(SB) configure -height $rows $freedbData(SB) selection set 0 if {[llength $found]==1} { id3Table::populateTracks ::$W $record } } } else { saLog "$W no longer exists!" } } #---------------------------------------------------------------------------- # Based on Artist and Album in search field # Search the freedb database and populate the list # box with returned hits #---------------------------------------------------------------------------- proc id3Table::searchFreedb {W {field ""}} { set win [string trimleft $W ":"] if {[winfo exists $win]} { global $W.table upvar $W.table id3Data global $W.freedb upvar $W.freedb freedbData ## # Raise this page # $id3Data(NB) raise discid package require freedb # Get list of all tracks # Query freedb to see if album exists switch -- $field { "Album" { set found [freedb::albumListHTML "" $freedbData(sAlbum)] } "Artist" { set found [freedb::albumListHTML $freedbData(sArtist) ""] } default { set found [freedb::albumListHTML $freedbData(sArtist) $freedbData(sAlbum)] } } if {[llength $found]==0} { tk_messageBox -type ok -icon info -message "No Freedb Matches Found on\n$::snackAmpSettings(freedb)" return } # Populate with return records (lookup) if {[info exists freedbData(SB)] && [winfo exists $freedbData(SB)]} { $freedbData(SB) delete 0 end set rows 0 foreach {record} $found { if {[regexp -- {(.+? .+?) (.*)$} $record -> front back]} { set freedbData(lbkey,$back) $record ;# save a key $freedbData(SB) insert end $back incr rows } else { saLog $record } } $freedbData(SB) configure -height $rows $freedbData(SB) selection set 0 if {[llength $found]==1} { id3Table::populateTracks ::$W $record } } } else { saLog "$W no longer exists!" } } #------------------------------------------------------------------------------ # When the user selects a row, show the details #------------------------------------------------------------------------------ proc id3Table::cbAlbumSelected {W key} { Trace set win [string trimleft $W ":"] if {[winfo exists $win]} { set W [winfo toplevel $win] global $W.table upvar $W.table id3Data global $W.freedb upvar $W.freedb freedbData if {[slen $key]} { id3Table::populateTracks ::$W $freedbData(lbkey,$key) } } else { saLog "$W no longer exists!" } } #---------------------------------------------------------------------------- # Determine if we can lookup the tags from freedb # Compute the disc ID #---------------------------------------------------------------------------- proc id3Table::populateTracks {W record} { Trace set win [string trimleft $W ":"] if {[winfo exists $win]} { global $W.table upvar $W.table id3Data global $W.freedb upvar $W.freedb freedbData set lb $freedbData(LB) ;# listbox Unset freedbQuery set Album [freedb::albumDetails $record freedbQuery] $lb delete 0 end set freedbData(record) $record if {[slen $Album]} { array unset freedbData "*,Title" array unset freedbData "*,Track" set index 0 foreach {key} {Album Title Year Genre} { if {[info exists freedbQuery($key)]} { set freedbData($key) $freedbQuery($key) } } foreach {track} [lsort -dictionary [array names freedbQuery Track,*]] { foreach {key num} [split $track ,] {break} set freedbData($index,Title) $freedbQuery($track) set freedbData($index,Track) [id3Tag::cleanTrack $num] set length $freedbQuery(Length,$num) $lb insert end "([format %02d $num]) [timify $length] : $freedbQuery($track)" incr index } } } else { saLog "$W no longer exists!" } } #---------------------------------------------------------------------------- # Bottom button callback #---------------------------------------------------------------------------- proc id3Table::processButtons {W button} { global $W.table upvar $W.table id3Data global $W.freedb upvar $W.freedb freedbData # Parent is the associated array for the table switch -- $button { "apply" { # This should be redundant, use as a backup if {![info exists freedbData(record)]} { return "" } set hasArtist [regexp -- {(.+? .+?) (.*) / (.*)$} $freedbData(record) -> front artist album] # id3Data (index,Title) (index,Track) (index,Artist) (index,Album) # freedbData (index,Title) (index,Track) set totalID3 [llength [array names id3Data *,Title]] set totalfreedb [llength [array names freedbData *,Title]] if {$totalID3 != $totalfreedb} { set ans [tk_messageBox -type yesno -icon question \ -message "The number of tracks found differs from the number of tracks to tag\ndo you want to proceed anyway?\n\nfreedb=$totalfreedb, ID3=$totalID3"] if {$ans!="yes"} { return } set diff 1 } else { set diff 0 } # For each freedb return foreach {key} [lsort -dictionary [array names freedbData *,Title]] { foreach {index tag} [split $key ,] {break} # # Match up source and dest track numbers if tot tracks differ # set id3Index $index if {$diff} { # Try and find associated id3Index if tracks match up set fdbTrack [scan $freedbData($index,Track) %d] set found 0 foreach {key} [lsort [array names id3Data *,Track]] { set id3Track [scan $id3Data($key) %d] if {$id3Track==$fdbTrack} { foreach {idx xxx} [split $key ,] {break} set id3Index $idx incr found break } } if {!$found} { continue } } # Get track number # Especially if id3 num and freedb num don't match set id3Data($id3Index,Track) $freedbData($index,Track) # Album Title holds Artist / Album if {[hasData freedbData(Title)] && [regexp -- {(.+)/(.+)$} $freedbData(Title) -> Artist Album]} { set id3Data($id3Index,Artist) [string trim $Artist] set id3Data($id3Index,Album) [string trim $Album] } elseif {$hasArtist} { set id3Data($id3Index,Artist) $artist set id3Data($id3Index,Album) $album } else { set id3Data($id3Index,Artist) "Unknown" set id3Data($id3Index,Album) "Unknown" } # Track Title can hold track title or Aritst / Title or Artist - Title in the case of various artists set id3Data($id3Index,Title) $freedbData($index,Title) ;# Default foreach {re} {{.+?/(.+)-(.+)$} {(.+)/(.+)$} {(.+)-(.+)$}} { if {$freedbData(various) && [regexp -- $re $freedbData($index,Title) -> Artist Title]} { set id3Data($id3Index,Title) [string trim $Title] set id3Data($id3Index,Artist) [string trim $Artist] if {[info exists album]} { set id3Data($id3Index,Album) $album } elseif [info exists Album] { set id3Data($id3Index,Album) $Album } else { set id3Data($id3Index,Album) $freedbData(sAlbum) } break } } # Copy in Genre and Year only if dest fields are blank foreach {key} {Year Genre} { if {[hasData freedbData($key)] && ![hasData id3Data($index,$key)]} { set id3Data($id3Index,$key) $freedbData($key) } } } } "cancel" { id3Editor::closeWindow $W freedb } default {} } } package provide id3Table 1.0