Browse Source

mptk: use direct connection to mpd without mpc

master
Maxim Likhachev 5 years ago
parent
commit
baa80d2473
  1. 210
      scripts/mptk

210
scripts/mptk

@ -1,85 +1,187 @@
#!/usr/bin/env tclsh #!/usr/bin/env tclsh
# Ghetto MPD controller # Copyright (C) 2020, Maxim Lihachev, <envrm@yandex.ru>
#
# 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, version 3.
#
# 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, see <https://www.gnu.org/licenses/>.
#
# Offhand MPD controller
#
# Requirements:
#
# - mid3v2
#
package require Tk package require Tk
set music_directory "/mnt/DATA/Музыка/" set music_directory "/mnt/DATA/Музыка/"
array set buttons { namespace eval mpd {
prev 玲 array set settings {
toggle ▶ host 127.0.0.1
next 怜 port 6600
} }
set used_ratings {1 2 3 4 5 5+} proc cmd {args} {
set mpd [socket $::mpd::settings(host) $::mpd::settings(port)]
puts $mpd $args
puts $mpd close
flush $mpd
set mpd_answer [split [read $mpd] "\n"]
set mpd_status [lindex $mpd_answer 0]
set answer_code [lindex $mpd_answer end-1]
if {$answer_code == "OK"} {
if {![string match "ACK*" $answer_code]} {
return [lrange $mpd_answer 1 end-2]
} else {
return $answer_code
}
} else {
return "ERROR: $mpd_answer"
}
}
proc mpc {args} { proc status {} {
exec mpc {*}$args set currentsong [::mpd::cmd currentsong]
} set status [::mpd::cmd status]
if {![string match "ERROR:*" $currentsong] && ![string match "ERROR:*" $status]} {
array set song {}
foreach metadata [list {*}$currentsong {*}$status] {
regexp -expanded {^([^:]+):\s+(.*)$} $metadata match key value
set song([string tolower $key]) $value
}
if {$song(state) != "stop"} {
if {[info exists song(comment)]} {
::gui::hl_rating $song(comment)
}
return [format "♫ %s/%s: %s - %s\n\[%s - %s\]\n~ %s ~\n\[%s\] repeat: %s random: %s single: %s consume: %s" \
[incr song(song)] $song(playlistlength) \
$song(artist) $song(title) $song(date) \
$song(album) $song(genre) $song(state) \
[state $song(repeat)] [state $song(random)] \
[state $song(single)] [state $song(consume)]]
} else {
return "\[stopped\]"
}
} else {
return "${currentsong}\n${status}"
}
}
proc current_file {} { proc state {code} {
set filename [file join $::music_directory [lindex [split [exec mpc -f {%file%}] "\n"] 0]] expr {$code == 0 ? {✘} : {✔}}
} }
proc rate {rating} { proc value {data} {
set filename [current_file] regsub {^[^:]*:\s+} $data {}
}
proc current_file {} {
set filename [file join $::music_directory [value [lindex [cmd currentsong] 0]]]
}
exec mid3v2 --delete-frames=COMM $filename proc previous {} { cmd previous }
exec mid3v2 --COMM $rating $filename proc next {} { cmd next }
hl_rating $rating proc toggle {} {
set state [value [lindex [cmd status] 8]]
mpc update if {$state == "stop" || $state == ""} {
} cmd play
} else {
cmd pause
}
}
proc consume {} {
set consume [value [lindex [cmd status] 4]]
cmd consume [expr {$consume == 1 ? 0 : 1}]
}
proc current_rating {} { proc random {} {
set filename [file join $::music_directory [current_file]] set random [value [lindex [cmd status] 2]]
cmd random [expr {$random == 1 ? 0 : 1}]
}
proc rate {rating} {
set filename [current_file]
exec mid3v2 --delete-frames=COMM $filename
exec mid3v2 --COMM $rating $filename
if {[catch {set rating [exec -ignorestderr exiftool -Comment $filename 2>/dev/null]}]} { cmd update
return {}
} else {
return [lindex [split $rating " "] end]
} }
} }
proc hl_rating {rating} { namespace eval gui {
foreach r $::used_ratings { set used_ratings {1 2 3 4 5 5+}
if {[winfo exists .rating.$r]} {
set c [expr {$r == $rating ? {red2} : {black}}] array set buttons {
.rating.$r configure -activeforeground $c -foreground $c previous 玲
toggle ▶
next 怜
}
proc hl_rating {rating} {
foreach r $::gui::used_ratings {
if {[winfo exists .rating.$r]} {
set c [expr {$r == $rating ? {red2} : {black}}]
.rating.$r configure -activeforeground $c -foreground $c
}
} }
} }
}
proc update_status {} { proc update_status {} {
set ::mpd_status [mpc] set ::mpd_status [::mpd::status]
hl_rating [current_rating] after 1000 ::gui::update_status
}
after 1000 update_status proc init {} {
} frame .status
frame .rating -pady 10
frame .control -pady 10
frame .status pack .status -fill x -expand true -padx 20 -pady 20
frame .rating -pady 10 pack [label .status.current -font {Sans 20} -textvariable ::mpd_status]
frame .control -pady 10
pack .status -fill x -expand true -padx 20 -pady 20 pack .rating
pack [label .status.current -font {Sans 20} -textvariable ::mpd_status] foreach rating $::gui::used_ratings {
pack [button .rating.$rating -text $rating -font {Sans 60} -width 2 -command "::mpd::rate $rating"] -side left
}
pack .rating pack .control
foreach rating $::used_ratings { foreach btn {previous toggle next} {
pack [button .rating.$rating -text $rating -font {Sans 60} -width 2 -command "rate $rating"] -side left pack [button .control.$btn -text $::gui::buttons($btn) -font {Sans 80} -width 4 -command "::mpd::$btn"] -side left
} }
pack .control #----------------------------------------------------------------------------------------
foreach btn {prev toggle next} {
pack [button .control.$btn -text $buttons($btn) -font {Sans 80} -width 4 -command "mpc $btn"] -side left
}
update_status update_status
bind . <KeyPress-z> {::mpd::random}
bind . <KeyPress-R> {::mpd::consume}
bind . <KeyPress-Control_L><KeyPress-q> exit
bind . <KeyPress-Control_R><KeyPress-q> exit
}
}
bind . <KeyPress-z> {mpc random} gui::init
bind . <KeyPress-Control_L><KeyPress-q> exit
bind . <KeyPress-Control_R><KeyPress-q> exit

Loading…
Cancel
Save