#!/usr/bin/wish ;# ReWrite of tkmp3burn ;# I'm gonna take this one step at a time. ;# beltorak (c) 2003. ;# Released under the GPL. ;# -- configureGlobals --------------------------------------- ;# defines common global arrays proc configureGlobals {} { global gdir global gcmd global gext set siteConfigFile [file join / usr local etc tkburn.conf] ;# set userConfigFile [file join $env(HOME) .tkburnrc] set gdir(music) [file join / usr local music] set gdir(playlist) [file join / usr local music playlists] set gdir(temp) [file join / tmp tkmp3burn] set gdir(Cmnt) [file join / mnt C] set gcmd(player) [file join / usr bin mpg123] set gcmd(player_opts) {-o arts -v} set gcmd(decoder) [file join / usr bin madplay] set gcmd(decoder_opts) "-Q -S -o" set gcmd(recorder) [file join / usr bin cdrecord] set gcmd(recorder_opts) "speed=2 dev=0,0,0 -pad -audio" set gcmd(tagger) [file join / usr local bin id3tool] set gcmd(probe_cdrs) "$gcmd(recorder) -scanbus" set gext(all) {{All Files} {*}} set gext(mp3) {{MP3 Audio} {.mp3 .MP3}} set gext(m3u) {{MP3 Playlist} {.m3u .M3U}} return };# -- end configureGlobals ----------------------------------- ;# -- burnitUI --------------------------------------------- ;# the interface for burning. ;# options will include: burn method (audio, data), "dummy" ;# mode, re-setting the default values, no-fix, fix-only, ;# and a few others. proc burnitUI {} { ;# ---- first draw the UI ---------------- set bui [toplevel .burnItUI] set topframe [frame $bui.topframe -relief sunken] set sizelabel [label $topframe.sizelabel -relief sunken \ -padx 10 \ -text {999 Megs}] set selData [radiobutton $topframe.selData \ -relief sunken -text {Data (MP3)} \ -variable gburn(format) -value data] set selAudio [radiobutton $topframe.selAudio \ -relief sunken -text {Audio (CD)} \ -variable gburn(format) -value audio] set scsidevlabel [label $topframe.scsidevlabel \ -text {SCSI device, target, lun:}] set scsidev [entry $topframe.scsidev \ -textvariable gmain(scsidev)] pack $topframe -side top -fill x pack $sizelabel -side left -anchor w pack $selData $selAudio -side left pack $scsidev $scsidevlabel -side right -anchor e $selAudio select set cdrombar [frame $bui.cdrombar] set pdev [button $cdrombar.pdev \ -text {Probe scsi device} \ -command {scsi_probe}] pack $cdrombar -side top -fill x pack $pdev -side left -anchor w set mainframe [frame $bui.main \ -height 20 -width 50] pack $mainframe -side top -fill both -expand true };# -- end burnitUI ---------------------------------------- ;# -- scsi_probe ------------------------------------------- ;# probes the scsi controller ala cdrecord -scanbus proc scsi_probe {} { global gcmd set cmd [concat exec $gcmd(probe_cdrs)] statusWindow playSong write command [subst $cmd] statusWindow playSong write status [eval $cmd] } ;# statusWindow window {create | write | dissolve} --------- ;# win: specifies the window toplevel to perform actions in ;# create: creates the window. Returns writable widget name. ;# write command {command}: changes the command displayed at the top. ;# write status {list}: writes each item in list to the window. ;# dissolve: destroys the window. returns "OK. proc statusWindow { window command args } { set win . append win $window append win _status set f $win.f set topbar $f.topbar ;# topbar: "Command: ", command, [Kill] set cmd_label $topbar.cmd_label set cmd $topbar.cmd set cmd_kill $topbar.kill set viewindow $f.view ;# the main point: the viewing window! if { $command == "create" } { toplevel $win wm title $win "Status Display $window" frame $f -relief raised -height 40 -width 60 pack $f \ -side top -fill both -expand true frame $topbar -relief flat -height 1 pack $topbar \ -side top -fill both -expand true label $cmd_label -anchor w -padx 10 -text Command: pack $cmd_label \ -side left label $cmd -padx 10 pack $cmd \ -side left -fill x -expand true button $cmd_kill -text Kill pack $cmd_kill -side right listbox $viewindow -relief sunken pack $viewindow \ -side top -fill both -expand true return $viewindow } elseif { $command == "dissolve" } { destroy .$win return "OK" } elseif { $command == "write" } { if { ! [winfo exists $win] } { statusWindow $window create } set where [lindex $args 0] set what [lindex $args 1] if { $where == "command" } { $cmd_kill configure -command [subst {exec "killall" [lindex [split $what] 0]}] $cmd config -text $what } elseif { $where == "status" } { foreach item [concat $what] { $viewindow insert end $item } } } } ;# -- end statusWindow ----------------------------------- ;# -- newPlaylist ---------------------------------------------- ;# deletes $gmain(masterlist), $gmain(listview), ;# and $gmain(fileentry). proc newPlaylist {} { global gmain if {[ info exists gmain(masterlist) ]} { unset gmain(masterlist) updateListview } };# -- end newPlaylist ----------------------------------------- ;# -- openPlaylist ----------------------------------------- ;# Browses for and opens a playlist. ;# Appends results to gmain(masterlist). ;# Updates display in gmain(listview) ;# Sets gmain(filename) to file opened if unset; ;# or to Untitled if set. proc openPlaylist {} { global gext gdir gmain set ext [list $gext(m3u) $gext(all)] set dir $gdir(playlist) set FILE [ tk_getOpenFile -title "Open Playlist" -filetypes $ext -initialdir $dir ] if { $FILE == "" } { ;# cancel return } catch [set inFileID [open $FILE RDONLY]] openErr ;# open playlist if { $openErr < 0 } { ;# error opening statusWindow readList write status \ [list "Could not open $FILE: $openErr"] return } if {[ info exists gmain(masterlist) ]} { ;# set the filename to display set filename Untitled } else { set filename $FILE } foreach line [split [read $inFileID] \n] { ;# read the whole thing, split by newlines regsub {^\s*(.*?)$} $line {\1} line ;# drop leading whitespace regsub {(^.*?)\x23.*?$} $line {\1} line ;# drop comments if {[ regexp -nocase {^c:\\.*?$} $line ]} { ;# windows path name: normalize it regsub -nocase {^c:} $line $gdir(Cmnt) line regsub -all {\\} $line {/} line } regsub {\x00} $line {} line ;# stip out bogus nulls if {[ file exists $line ]} { ;# drop non-extant files set filetype [ file type $line ] if { $filetype == "file" } { ;# parse regular files lappend gmain(masterlist) $line } else { statusWindow readLine write status \ [list "Cannot read file type $filetype." ] } } elseif { $line != {} } { ;# warn about missing files statusWindow readList write status \ [list "File: $line does not exist."] } } if {[ catch [close $inFileID] closeErr ]} { ;# close the file. statusWindow readList write status \ [list "Could not close $FILE: $closeErr"] } ;# end reading file ;# ---- now we display it ------------------------------ updateListview ;# ---- and set the filename --------------------------- $gmain(fileentry) delete 0 end $gmain(fileentry) insert end $filename };# -- end openPlaylist ------------------------------------ ;# -- savePlaylist ----------------------------------------- ;# Saves the files in gmain(masterlist); browsing included. proc savePlaylist {} { global gmain gext gdir if {![ info exists gmain(masterlist) ]} {return} set ext [string tolower [lindex [lindex $gext(m3u) 1] 0]] set FILE [file tail [$gmain(fileentry) get]] set FILE [tk_getSaveFile \ -initialdir $gdir(playlist) \ -filetypes [list $gext(m3u) $gext(all)] \ -defaultextension $ext \ -initialfile $FILE \ -title {Save Playlist As} ] if { string equals $FILE "" } { return } catch [set fileID [open $FILE {WRONLY TRUNC CREAT}]] openErr if { $openErr < 0 } { statusWindow saving write command \ "open $FILE {WRONLY CREAT}" statusWindow SAVING write status \ "Error saving file: $openErr" return } foreach line $gmain(masterlist) { puts $fileID $line } puts $fileID {} flush $fileID if {[ catch [close $fileID] closeErr ]} { ;# close the file. statusWindow saving write status \ [list "Could not close $FILE: $closeErr"] } exec chmod 0660 $FILE exec chown :users $FILE };# -- end savePlaylist ------------------------------------ ;# -- addFile ---------------------------------------------- ;# adds a specific song to the master list. updates viewindow. proc addFile {} { global gmain gext gdir set ext [list $gext(mp3) $gext(all)] set dir $gdir(music) set FILE [tk_getOpenFile -title "Add Song To Playlist" \ -filetypes $ext \ -initialdir $dir ] ;# ---- update the master list --------------- lappend gmain(masterlist) $FILE updateListview ;# ---- and set the filename ----------------- set entryWidget $gmain(fileentry) $entryWidget delete 0 end $entryWidget insert end Untitled };# -- end addFile ----------------------------------------- ;# -- removeFile ------------------------------------------- ;# removes the selected file in the viewwindow from gmain(masterlist). ;# updates display. proc removeFile {} { global gmain set lines [$gmain(listview) curselection] ;# check for songs if {[ string equal $lines {} ]} { return } set masterlist $gmain(masterlist) foreach index [lsort -integer -decreasing $lines] { set masterlist [lreplace $masterlist $index $index] } if { [llength $masterlist] == 0} { unset gmain(masterlist) } else { set gmain(masterlist) $masterlist $gmain(fileentry) delete 0 end $gmain(fileentry) insert end Untitled } updateListview };# -- end removeFile -------------------------------------- ;# -- updateListview --------------------------------------- ;# updates gmain(listview) to reflect gmain(masterlist) proc updateListview {} { global gmain gcmd if {[ info exists gmain(masterlist) ]} { set index [lindex [$gmain(listview) yview] 0] ;# get current display position $gmain(listview) delete 0 end ;# wipe the display if { $gmain(showoption) == {fullpath} } { foreach line $gmain(masterlist) { ;# -- display by fullpath -- $gmain(listview) insert end $line } } elseif { $gmain(showoption) == {filename} } { foreach line $gmain(masterlist) { ;# -- display by filename -- $gmain(listview) insert end [file tail $line] } } elseif { $gmain(showoption) == {mp3tag} } { ;# -- display by mp3tag ---- foreach song $gmain(masterlist) { set songtags [split [eval [concat exec $gcmd(tagger) [list $song]]] "\n" ] foreach tag $songtags { set name [lindex [split $tag ":"] 0] set value [lindex [split $tag ":"] 1] if {[ string match $name "Artist" ]} { set artist $value } elseif {[ string match $name "Song Title" ]} { set title $value } } ;# if neither is present, set artist to song name and title to an error if { ![info exists artist] && ![info exists title ]} { set artist [file tail $song] set title {} } elseif { ![info exists artist ]} { set artist {No Artist in Tag} } elseif { ![info exists title ]} { set title {No Title in Tag} } $gmain(listview) insert end [concat $artist - $title] unset artist unset title } } ;# -- end display by mp3tag -- $gmain(listview) yview moveto $index ;# reset display position $gmain(trackcount) config -text "[llength $gmain(masterlist)] Tracks" } else { $gmain(listview) delete 0 end $gmain(trackcount) config -text {} $gmain(fileentry) delete 0 end } };# -- end updateListview ---------------------------------- ;# -- playSong --------------------------------------------- ;# Plays the current song with the player listed in gcmd(player) proc playSong {} { global gcmd gmain set songs [$gmain(listview) curselection] foreach song [list $songs] { set song [lindex $gmain(masterlist) $song] set cmd [concat $gcmd(player) $gcmd(player_opts) [list $song]] statusWindow playSong write command $cmd set playerOutput [open "|$cmd" r] statusWindow playSong write status $playerOutput } };# -- end playSong ---------------------------------------- ;# -- drawMain --------------------------------------------- proc drawMain {} { global gmain set main [frame .main] ;# main frames set viewframe [frame $main.viewframe -bg grey] set bframe [frame $main.bframe] pack $bframe -side right -anchor e \ -fill y -padx 5 pack $viewframe -side top \ -fill both -expand true ;# buttons set ubuttons [frame $bframe.ubuttons -borderwidth 2 -relief sunken] set newbutton [button $ubuttons.newbutton \ -text New -command {newPlaylist}] set openbutton [button $ubuttons.openbutton \ -text Open -command {openPlaylist}] set savebutton [button $ubuttons.savebutton \ -text Save -command {savePlaylist}] set sbuttons [frame $bframe.sbuttons] set addbutton [button $sbuttons.addbutton \ -text {Add Song} -command {addFile}] set removebutton [button $sbuttons.removebutton \ -text {Remove Song} -command {removeFile}] set unselectbutton [button $sbuttons.unselectbutton \ -text {Clear Selection} \ -command {$gmain(listview) selection clear 0 end}] set burnitbutton [button $sbuttons.burnit \ -text {Burn It!} -command {burnitUI} \ -foreground maroon -activeforeground red] set playbutton [button $sbuttons.playbutton \ -text {PLAY} -command {playSong} \ -foreground {dark blue} -activeforeground blue] set lbuttons [frame $bframe.lbuttons -borderwidth 2 -relief sunken] set configbutton [button $lbuttons.config \ -text {Configure} -command {user_config}] set quitbutton [button $lbuttons.quitbutton \ -text Quit -command {exit}] pack $ubuttons -side top -fill x pack $sbuttons -side top -fill x -expand true pack $lbuttons -side bottom -fill x foreach name {new open save add remove unselect play burnit config quit} { append name button pack [set $name] -side top -anchor n \ -fill x } ;# topbar set topbar [frame $viewframe.topbar] set toplabel [label $topbar.toplabel -text "Playlist"] set topentry [entry $topbar.plname -textvariable playlistname -width 50] pack $topbar -side top -anchor n \ -fill x pack $toplabel -side left pack $topentry -side left \ -fill x -expand true ;# infobar set infobar [frame $viewframe.infobar] set timelabel [label $infobar.timelabel -text Time:] set listtime [label $infobar.listtime -textvariable playlisttime \ -width 10 -relief sunken] set trackcount [label $infobar.trackcount \ -width 10 -padx 10] foreach rb {fullpath filename mp3tag} { set rbname $rb append rbname _rb set $rbname [radiobutton $infobar.$rbname \ -text $rb -value $rb -variable gmain(showoption) \ -command {updateListview} \ -selectcolor blue] } pack $infobar -side top -anchor n -fill x pack $timelabel $listtime $trackcount -side left -anchor w pack $fullpath_rb $filename_rb $mp3tag_rb \ -side right $fullpath_rb select ;# main viewing window set viewWidget [listbox $viewframe.viewWidget \ -selectmode extended \ -yscrollcommand {$gmain(yscroll) set}] set yscroll [scrollbar $viewframe.yscroll \ -command {$gmain(listview) yview}] pack $yscroll -side right -fill y pack $viewWidget -side top -anchor nw \ -fill both -expand true pack $main -padx 5 -pady 5 \ -fill both -expand true set gmain(fileentry) $topentry set gmain(showoption) {fullpath} set gmain(listview) $viewWidget set gmain(yscroll) $yscroll set gmain(trackcount) $trackcount };# -- main ------------------------------------------------ configureGlobals drawMain