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 @@ @@ -1,85 +1,187 @@
#!/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
set music_directory "/mnt/DATA/Музыка/"
array set buttons {
prev 玲
toggle ▶
next 怜
}
namespace eval mpd {
array set settings {
host 127.0.0.1
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} {
exec mpc {*}$args
}
proc status {} {
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 {} {
set filename [file join $::music_directory [lindex [split [exec mpc -f {%file%}] "\n"] 0]]
}
proc state {code} {
expr {$code == 0 ? {✘} : {✔}}
}
proc rate {rating} {
set filename [current_file]
proc value {data} {
regsub {^[^:]*:\s+} $data {}
}
proc current_file {} {
set filename [file join $::music_directory [value [lindex [cmd currentsong] 0]]]
}
exec mid3v2 --delete-frames=COMM $filename
exec mid3v2 --COMM $rating $filename
proc previous {} { cmd previous }
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 {} {
set filename [file join $::music_directory [current_file]]
proc random {} {
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]}]} {
return {}
} else {
return [lindex [split $rating " "] end]
cmd update
}
}
proc hl_rating {rating} {
foreach r $::used_ratings {
if {[winfo exists .rating.$r]} {
set c [expr {$r == $rating ? {red2} : {black}}]
.rating.$r configure -activeforeground $c -foreground $c
namespace eval gui {
set used_ratings {1 2 3 4 5 5+}
array set buttons {
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 {} {
set ::mpd_status [mpc]
proc update_status {} {
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
frame .rating -pady 10
frame .control -pady 10
pack .status -fill x -expand true -padx 20 -pady 20
pack [label .status.current -font {Sans 20} -textvariable ::mpd_status]
pack .status -fill x -expand true -padx 20 -pady 20
pack [label .status.current -font {Sans 20} -textvariable ::mpd_status]
pack .rating
foreach rating $::gui::used_ratings {
pack [button .rating.$rating -text $rating -font {Sans 60} -width 2 -command "::mpd::rate $rating"] -side left
}
pack .rating
foreach rating $::used_ratings {
pack [button .rating.$rating -text $rating -font {Sans 60} -width 2 -command "rate $rating"] -side left
}
pack .control
foreach btn {previous toggle next} {
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}
bind . <KeyPress-Control_L><KeyPress-q> exit
bind . <KeyPress-Control_R><KeyPress-q> exit
gui::init

Loading…
Cancel
Save