|
|
@ -1,25 +1,122 @@ |
|
|
|
#!/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 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 state {code} { |
|
|
|
|
|
|
|
expr {$code == 0 ? {✘} : {✔}} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
proc mpc {args} { |
|
|
|
proc value {data} { |
|
|
|
exec mpc {*}$args |
|
|
|
regsub {^[^:]*:\s+} $data {} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
proc current_file {} { |
|
|
|
proc current_file {} { |
|
|
|
set filename [file join $::music_directory [lindex [split [exec mpc -f {%file%}] "\n"] 0]] |
|
|
|
set filename [file join $::music_directory [value [lindex [cmd currentsong] 0]]] |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc previous {} { cmd previous } |
|
|
|
|
|
|
|
proc next {} { cmd next } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc toggle {} { |
|
|
|
|
|
|
|
set state [value [lindex [cmd status] 8]] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 random {} { |
|
|
|
|
|
|
|
set random [value [lindex [cmd status] 2]] |
|
|
|
|
|
|
|
cmd random [expr {$random == 1 ? 0 : 1}] |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
proc rate {rating} { |
|
|
|
proc rate {rating} { |
|
|
@ -28,23 +125,21 @@ proc rate {rating} { |
|
|
|
exec mid3v2 --delete-frames=COMM $filename |
|
|
|
exec mid3v2 --delete-frames=COMM $filename |
|
|
|
exec mid3v2 --COMM $rating $filename |
|
|
|
exec mid3v2 --COMM $rating $filename |
|
|
|
|
|
|
|
|
|
|
|
hl_rating $rating |
|
|
|
cmd update |
|
|
|
|
|
|
|
} |
|
|
|
mpc update |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
proc current_rating {} { |
|
|
|
namespace eval gui { |
|
|
|
set filename [file join $::music_directory [current_file]] |
|
|
|
set used_ratings {1 2 3 4 5 5+} |
|
|
|
|
|
|
|
|
|
|
|
if {[catch {set rating [exec -ignorestderr exiftool -Comment $filename 2>/dev/null]}]} { |
|
|
|
array set buttons { |
|
|
|
return {} |
|
|
|
previous 玲 |
|
|
|
} else { |
|
|
|
toggle ▶ |
|
|
|
return [lindex [split $rating " "] end] |
|
|
|
next 怜 |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
proc hl_rating {rating} { |
|
|
|
proc hl_rating {rating} { |
|
|
|
foreach r $::used_ratings { |
|
|
|
foreach r $::gui::used_ratings { |
|
|
|
if {[winfo exists .rating.$r]} { |
|
|
|
if {[winfo exists .rating.$r]} { |
|
|
|
set c [expr {$r == $rating ? {red2} : {black}}] |
|
|
|
set c [expr {$r == $rating ? {red2} : {black}}] |
|
|
|
.rating.$r configure -activeforeground $c -foreground $c |
|
|
|
.rating.$r configure -activeforeground $c -foreground $c |
|
|
@ -53,13 +148,12 @@ proc hl_rating {rating} { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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 .status |
|
|
|
frame .rating -pady 10 |
|
|
|
frame .rating -pady 10 |
|
|
|
frame .control -pady 10 |
|
|
|
frame .control -pady 10 |
|
|
@ -68,18 +162,26 @@ pack .status -fill x -expand true -padx 20 -pady 20 |
|
|
|
pack [label .status.current -font {Sans 20} -textvariable ::mpd_status] |
|
|
|
pack [label .status.current -font {Sans 20} -textvariable ::mpd_status] |
|
|
|
|
|
|
|
|
|
|
|
pack .rating |
|
|
|
pack .rating |
|
|
|
foreach rating $::used_ratings { |
|
|
|
foreach rating $::gui::used_ratings { |
|
|
|
pack [button .rating.$rating -text $rating -font {Sans 60} -width 2 -command "rate $rating"] -side left |
|
|
|
pack [button .rating.$rating -text $rating -font {Sans 60} -width 2 -command "::mpd::rate $rating"] -side left |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pack .control |
|
|
|
pack .control |
|
|
|
foreach btn {prev toggle next} { |
|
|
|
foreach btn {previous toggle next} { |
|
|
|
pack [button .control.$btn -text $buttons($btn) -font {Sans 80} -width 4 -command "mpc $btn"] -side left |
|
|
|
pack [button .control.$btn -text $::gui::buttons($btn) -font {Sans 80} -width 4 -command "::mpd::$btn"] -side left |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
update_status |
|
|
|
update_status |
|
|
|
|
|
|
|
|
|
|
|
bind . <KeyPress-z> {mpc random} |
|
|
|
bind . <KeyPress-z> {::mpd::random} |
|
|
|
|
|
|
|
bind . <KeyPress-R> {::mpd::consume} |
|
|
|
|
|
|
|
|
|
|
|
bind . <KeyPress-Control_L><KeyPress-q> exit |
|
|
|
bind . <KeyPress-Control_L><KeyPress-q> exit |
|
|
|
bind . <KeyPress-Control_R><KeyPress-q> exit |
|
|
|
bind . <KeyPress-Control_R><KeyPress-q> exit |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gui::init |
|
|
|
|
|
|
|
|
|
|
|