mpi-panel 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. #!/bin/bash
  2. me="$(readlink -f "$0")"
  3. cd "${me%/*}" # this is the working directory throughout the whole script
  4. # for sourcing plugins & help text
  5. # if cd fails, the script will still work
  6. # load sleep as a builtin if available - portable enough like this?
  7. for file in /usr/lib/bash/sleep /usr/lib64/bash/sleep /usr/lib32/bash/sleep; do
  8. [ -r "$file" ] && enable -f "$file" sleep && break
  9. done
  10. unset file
  11. function help {
  12. . help.less
  13. [[ "$1" == noless ]] && echo "$text" && exit 0
  14. echo "$text" | less -FMRiLnQX -PM"?eEnd - press q to exit - more information\\: github\\.com/ohnonot/media-player-info:More"
  15. exit 0
  16. }
  17. function usage {
  18. [[ "$*" != "" ]] && echo "
  19. $*"
  20. echo "
  21. Try \"$me -h\" for a full description
  22. "
  23. exit 1
  24. }
  25. function upvote_func {
  26. [[ "$(head -1 "$history_file")" != *"$upvote_str" ]] && upvote_str="$sep$upvote_str"
  27. sed -i "1 s/$/$upvote_str/" "$history_file"
  28. notify-send -u low "$me" "Upvoted:\n$(head -1 "$history_file")"
  29. }
  30. function update_history {
  31. printf "$(date '+%F %R')$sep${info[${chosen}2]}$sep${info[${chosen}1]}\n" > "$history_file".temp
  32. cat "$history_file" >> "$history_file".temp
  33. mv "$history_file".temp "$history_file"
  34. rm "$history_file".temp
  35. }
  36. function only_me_or_exit {
  37. # argument: pidfile
  38. # make sure only 1 instance is running
  39. touch "$1"
  40. read lastPID < "$1"
  41. # if lastPID is not null and a process with that pid exists , exit
  42. [ ! -z "$lastPID" -a -d /proc/$lastPID ] && { echo "An instance of $me is already running with pid $lastPID." ; exit 1 ; }
  43. # else - save my pid in the lock file, and continue
  44. echo $$ > "$1"
  45. }
  46. function exit_only_me {
  47. # argument1: pidfile
  48. # argument2: message (opt.)
  49. [[ "$2" != "" ]] && echo "$2"
  50. rm "$1"
  51. exit 0
  52. }
  53. ##############################################################################
  54. # player plugins work like this:
  55. # - $player is the name of the player executable so that "pgrep -x $player" will tell us whether
  56. # that player is running.
  57. # - any file inside the plugins directory called exactly ${player}.mpip will be sourced
  58. # automatically (unless the user specified) by the main script
  59. # - it contains at least one function named $player_fillarray
  60. # - the global associative array info[] is defined in the main script.
  61. # - the function fills it with fields named ${player}0 - ${player}8 - example: info[audacious0]=...
  62. #
  63. # like this:
  64. # - 0 the player's state play/pause/stop/unknown as defined in the state_nice array in the main script
  65. # - 1 the title of the currently playing song, usually 'Song Name - Artist' or, failing that, file name
  66. # - 2 the file (usually full path)
  67. # - 3 the time position in the song as elapsed/total, or merely elapsed for streams
  68. # - 4 player's play/pause toggle command
  69. # - 5 player's next song command
  70. # - 6 player's previous song command
  71. # - 7 player's show window command
  72. # - 8 icon name (as defined in global "icons" array) for the current state
  73. #
  74. # this array will later be filled with 2 more fields, but this is not player specific,
  75. # hence it is not part of the function.
  76. # see help text for -d option.
  77. #
  78. # if the function is called with "dryrun" as $1, it will not query the player
  79. # but fill the array with all static values available, i.e. fields 4-7.
  80. #
  81. # these helper functions can be utilized:
  82. # title_concat (formats the title to artist - songtitle, except when we listen to a
  83. # network stream, because those usually already format the songtitle
  84. # as artist - songtitle)
  85. # filename_as_title (if no title, use filename instead)
  86. # time_concat (concatenates the fields for time elapsed/remaining and total time
  87. #
  88. # the internal player functions and helper functions use a trick: the array is first
  89. # filled with: artist,songtitle,file,currenttime,totaltime,playerstate
  90. # all in separate fields, then the title_concat and time_concat functions reformat what
  91. # is to become the final title and time field, after which the artist (field 0) is
  92. # overwritten with the player's beautified state. now the last two array fields are
  93. # useless and are overwritten with other information.
  94. ##############################################################################
  95. ############# PLAYER: MOC ###################################################
  96. function mocp_fillarray {
  97. if [[ "$1" != "dryrun" ]]; then
  98. i=0
  99. while read info[${player}$i]; do
  100. ((i++))
  101. done < <(mocp -Q '%artist\n%song\n%file\n%ct\n%tt\n%state' 2>/dev/null)
  102. title_concat
  103. # replacing mocp's status with a nicer one
  104. case "${info[${player}5]}" in
  105. PLAY)
  106. info[${player}0]="${state_nice[0]}" # Playing
  107. [[ "$icondir" != "" ]] && info[${player}8]="${icons[0]}" # icons use different names
  108. ;;
  109. PAUSE)
  110. info[${player}0]="${state_nice[1]}" # Paused
  111. [[ "$icondir" != "" ]] && info[${player}8]="${icons[1]}" # icons use different names
  112. ;;
  113. STOP)
  114. info[${player}0]="${state_nice[2]}" # Stopped
  115. [[ "$icondir" != "" ]] && info[${player}8]="${icons[2]}" # icons use different names
  116. [[ "{info[${player}1]}" == "" ]] && info[${player}1]="$player: ${state_nice[2]}"
  117. ;;
  118. *)
  119. info[${player}0]="${state_nice[3]}" # State_Unknown
  120. [[ "$icondir" != "" ]] && info[${player}8]="${icons[3]}" # icons use different names
  121. ;;
  122. esac
  123. filename_as_title
  124. time_concat
  125. fi
  126. info[${player}4]="mocp -G"
  127. info[${player}5]="mocp -f"
  128. info[${player}6]="mocp -r"
  129. info[${player}7]="$terminal -e mocp"
  130. }
  131. ############# PLAYER: AUDACIOUS ##############################################
  132. function audacious_fillarray {
  133. if [[ "$1" != "dryrun" ]]; then
  134. # get the info
  135. i=0
  136. while read info[${player}$i]; do
  137. ((i++))
  138. done < <(audtool current-song-tuple-data artist current-song-tuple-data title current-song-filename current-song-output-length current-song-length playback-status)
  139. for (( ; i < 6 ; i++ )); do
  140. info[${player}$i]=''
  141. done
  142. title_concat
  143. case "${info[${player}5]}" in
  144. playing)
  145. info[${player}0]="${state_nice[0]}" # Playing
  146. [[ "$icondir" != "" ]] && info[${player}8]="${icons[0]}" # icons use different names
  147. ;;
  148. paused)
  149. info[${player}0]="${state_nice[1]}" # Paused
  150. [[ "$icondir" != "" ]] && info[${player}8]="${icons[1]}" # icons use different names
  151. ;;
  152. stopped)
  153. info[${player}0]="${state_nice[2]}" # Stopped
  154. [[ "$icondir" != "" ]] && info[${player}8]="${icons[2]}" # icons use different names
  155. [[ "${info[${player}1]}" == "" ]] && info[${player}1]="$player: ${state_nice[2]}"
  156. ;;
  157. *)
  158. info[${player}0]="${state_nice[3]}" # State_Unknown
  159. ;;
  160. esac
  161. filename_as_title
  162. time_concat
  163. fi
  164. info[${player}4]="audacious -t"
  165. info[${player}5]="audacious -f"
  166. info[${player}6]="audacious -r"
  167. info[${player}7]="audacious -m"
  168. }
  169. ############# PLAYER: MPD ##############################################
  170. function mpd_fillarray {
  171. player_ui="$(which ncmpcpp 2>/dev/null)"
  172. [[ "$player_ui" == "" ]] && player_ui="$(which ncmpc 2>/dev/null)"
  173. if [[ "$1" != "dryrun" ]]; then
  174. # get the info
  175. i=0
  176. while read info[${player}$i]; do
  177. ((i++))
  178. done < <(mpc -f '%artist%\n%title%\n%file%\n%name%')
  179. # if player is stopped, there's no metadata, and mpc won't display anything either
  180. # - it goes straight to the status line, no matter how the metadata request is
  181. # formatted. in that case, we fill the data manually.
  182. if [[ "${info[${player}0]}" == "volume:"* ]]; then
  183. info[${player}0]="${state_nice[2]}" # Stopped
  184. info[${player}1]="$player: ${state_nice[2]}"
  185. info[${player}2]=""
  186. info[${player}3]=""
  187. [[ "$icondir" != "" ]] && info[${player}8]="${icons[2]}" # icons use different names
  188. else
  189. title_concat
  190. auxarr=( ${info[${player}4]} ) # mpc puts some needed information on a single line that needs to be parsed separately
  191. case "${auxarr[0]}" in
  192. '[playing]')
  193. info[${player}0]="${state_nice[0]}" # Playing
  194. [[ "$icondir" != "" ]] && info[${player}8]="${icons[0]}" # icons use different names
  195. ;;
  196. '[paused]')
  197. info[${player}0]="${state_nice[1]}" # Paused
  198. [[ "$icondir" != "" ]] && info[${player}8]="${icons[1]}" # icons use different names
  199. ;;
  200. *)
  201. info[${player}0]="${state_nice[2]}" # Stopped (special case with mpd - whenever this function is called , it means the daemon is running. when it's neither playing nor paused, one can assume a stopped state - State_Unknown is not needed)
  202. [[ "$icondir" != "" ]] && info[${player}8]="${icons[2]}" # icons use different names
  203. ;;
  204. esac
  205. if [[ "${info[${player}1]}" == "" ]] && [[ "${info[${player}3]}" != "" ]];then
  206. info[${player}1]="${info[${player}3]}"
  207. else
  208. filename_as_title
  209. fi
  210. [[ "$music_directory" != "" ]] && [[ "${info[${player}2]}" != http* ]] && info[${player}2]="$music_directory/${info[${player}2]}"
  211. info[${player}3]="${auxarr[2]%\/0:00}"
  212. fi
  213. fi
  214. info[${player}4]="mpc toggle"
  215. info[${player}5]="mpc next"
  216. info[${player}6]="mpc prev"
  217. [[ "$player_ui" != "" ]] && info[${player}7]="$terminal -e $player_ui" || info[${player}7]="$terminal -title No_player_ui_found -e bash -c read"
  218. }
  219. #############################################################################
  220. # LOGIC COMMON TO ALL PLAYERS
  221. ###########################################################################
  222. function filename_as_title {
  223. # if info[${player}1] - that's the title - is empty, use the filename instead
  224. if [[ "${info[${player}1]}" == "" ]]
  225. then
  226. info[${player}1]="${info[${player}2]##*/}" # basename, but...
  227. # ...if it's shorter than 10 chars, use the full path/filename
  228. (( ${#info[${player}1]} < 10 )) && info[${player}1]="${info[${player}2]}"
  229. # remove http(s):// from front of string
  230. info[${player}1]="${info[${player}1]#http\:\/\/}"
  231. info[${player}1]="${info[${player}1]#https\:\/\/}"
  232. fi
  233. }
  234. function time_concat {
  235. # if total time is zero, display only current time
  236. # at the same time, leading double zeros are replaced by one zero
  237. if [[ "${info[${player}4]}" == "" ]] || [[ "${info[${player}4]}" =~ 0*":00" ]]; then
  238. info[${player}3]="${info[${player}3]/#00/0}"
  239. else
  240. info[${player}3]="${info[${player}3]/#00/0}/${info[${player}4]/#00/0}"
  241. fi
  242. }
  243. function title_concat {
  244. # if http != network stream, concatenate %artist% and %title%
  245. # if http == network stream, %title% is very likely to already contain artist - title info:
  246. [[ "${info[${player}0]}" != "" ]] && [[ "${info[${player}1]}" != "" ]] && [[ "${info[${player}2]}" != http* ]] && info[${player}1]="${info[${player}0]}$sep${info[${player}1]}"
  247. }
  248. function get_art {
  249. # if the directory of the currently playing song exists (i.e. it is a local file)
  250. # try to find some albumart in it.
  251. if [ -d "${info[${player}2]%/*}" ]; then
  252. for string in $arts; do
  253. for ext in $extensions; do
  254. # if there's an image with the current song's exact name, that wins:
  255. [ -r "${info[${player}2]%.*}".$ext ] && \
  256. info[${player}8]="${info[${player}2]%.*}.$ext" && return
  257. for file in "${info[${player}2]%/*}"/*.$ext; do
  258. [[ "${file,,}" == */${string}*.$ext ]] && \
  259. info[${player}8]="$file" && return
  260. done
  261. done
  262. # still no artwork? take ANY file with a valid extension (i.e. any image)
  263. for ext in $extensions; do
  264. for file in "${info[${player}2]%/*}"/*.$ext; do
  265. [ -r "$file" ] && info[${player}8]="$file" && return
  266. done
  267. done
  268. # still no artwork? try to extract an image with ffmpeg - if directory is writable
  269. [ -w "${info[${player}2]%/*}" ] && ffmpeg -i "${info[${player}2]}" "${info[${player}2]%/*}"/folder.jpg >/dev/null 2>&1 && info[${player}8]=folder.jpg
  270. done
  271. fi
  272. }
  273. function join_maxlen_func {
  274. # 2 strings - $1 and $2 - not more than $maxlen chars altogether.
  275. # but the first string is to be shortened, not the 2nd.
  276. if [[ "$2" != "" ]] ; then
  277. [ ${#1} -lt $((maxlen - ${#2} - ${#dots})) ] && dots=""
  278. echo "${1:0:((maxlen - ${#dots} - ${#2}))}$dots $2"
  279. else
  280. [ ${#1} -le $(( maxlen )) ] && dots=""
  281. echo "${1:0:((maxlen - ${#dots}))}$dots"
  282. fi
  283. }
  284. function lines_func {
  285. # cuts the input string into approx. equally long lines up to $lines.
  286. # this is not exact, however the exact number of lines _must_ be returned
  287. # otherwise tint2's exec function cannot work in continuous mode
  288. string="$1"
  289. returnarray=() # each array pos. will contain 1 line
  290. local linecount=$lines
  291. for (( arraypos=0 ; linecount > 1 ; linecount-- ))
  292. do
  293. length=${#string}
  294. for (( fwd=$((length/linecount)) , bwd=$((length/linecount)) ; bwd >= 0 ; fwd++ , bwd-- ))
  295. do
  296. if [[ "${string:$fwd:1}" = " " ]]
  297. then
  298. returnarray[arraypos]="${string:0:$fwd}"
  299. arraypos=$((arraypos + 1))
  300. string="${string:$((fwd + 1))}"
  301. break
  302. fi
  303. if [[ "${string:$bwd:1}" = " " ]]
  304. then
  305. returnarray[arraypos]="${string:0:$bwd}"
  306. arraypos=$((arraypos + 1))
  307. string="${string:$((bwd + 1))}"
  308. break
  309. fi
  310. done
  311. done
  312. returnarray[arraypos]="$string"
  313. arraypos=$((arraypos + 1))
  314. if [[ "$tint2" == "1" ]]; then max=$lines ; else max=$arraypos ; fi
  315. for (( c=0 ; c < max ; c++ )); do
  316. echo "${returnarray[c]}"
  317. done
  318. }
  319. #############################################################################
  320. # ┏┳┓┏━┓╻┏┓╻
  321. # ┃┃┃┣━┫┃┃┗┫
  322. # ╹ ╹╹ ╹╹╹ ╹
  323. #############################################################################
  324. # Our main info array
  325. declare -A info=()
  326. which "${0##*/}" >/dev/null 2>&1 && me="${0##*/}" || me="$0"
  327. [ -w "/dev/shm" ] && tmp_dir="/dev/shm" || tmp_dir="/tmp"
  328. lines=1
  329. maxlen=60 # maximum length of string before dividing into lines
  330. showtime=0 # display current/total time or not
  331. continuous=0
  332. onlyone=0
  333. altcmd=''
  334. stopped=0
  335. paused=0
  336. dump=''
  337. alwaysfirst=0
  338. tint2=0
  339. # icons
  340. icondir='' # will look for 3 icons in dir: play.* pause.* stop.* unknown.*
  341. extensions="png svg gif PNG jpg jpeg GIF JPG JPEG" # these extensions seem to be supported by tint2
  342. icons=( Playing Paused Stopped StateUnknown Altcmd ) # ordering has to be the same as in state_nice array, and don't change Altcmd - it's hardcoded in MAIN
  343. state_nice=( Playing Paused Stopped StateUnknown )
  344. # nice strings to display the four player states. these are also the basenames for the icons, e.g.: Playing.png or "StateUnknown.gif" etc.
  345. dots="…"
  346. sep=" - "
  347. default_icon="audio-speakers-symbolic"
  348. showart=0
  349. icon_art=0
  350. arts="folder cover album front art"
  351. selected_players=""
  352. supported_players="mpd audacious mocp" # builtins
  353. plugindir="plugins"
  354. music_directory=""
  355. notify=0
  356. history=0
  357. history_file=""
  358. watch_song_change=0
  359. delay=0
  360. upvote_str="☻"
  361. # assume that the $TERMINAL environment variable contains the path to a usable executable
  362. # if not, use XTERMINAL, and if that's empty, use 'xterm'
  363. [[ "$TERMINAL" != "" ]] && terminal="$TERMINAL" || terminal=${XTERMINAL-xterm}
  364. while getopts "l:w:Tc:t:p:PSd:Ds:i:a:e:r:nAIM:1hH:y:Uu:X" opt; do
  365. case $opt in
  366. l) # lines
  367. lines="$OPTARG"
  368. [ "$lines" -lt 1 ] || [ "$lines" -gt 100 ] && usage "-$opt: Invalid number: $lines"
  369. ;;
  370. w) # width = maxlen per line
  371. maxlen="$OPTARG"
  372. [ "$maxlen" -lt 10 ] && usage "-$opt: Invalid number: $maxlen"
  373. ;;
  374. T) # show time elapsed/remaining?
  375. showtime=1
  376. ;;
  377. c) # run script continuously? - interval in s
  378. continuous="$OPTARG"
  379. [ "$continuous" -lt 1 ] && usage "-$opt: Invalid number: $continuous"
  380. ;;
  381. t) # run script continuously in tint2 mode - interval in s
  382. continuous="$OPTARG"
  383. tint2=1
  384. [ "$continuous" -lt 1 ] && usage "-$opt: Invalid number: $continuous"
  385. ;;
  386. p) # which media players?
  387. selected_players="$OPTARG"
  388. ;;
  389. P) # include "Paused" to the list of valid responses?
  390. paused=1
  391. ;;
  392. S) # include "Stopped" to the list of valid responses?
  393. stopped=1
  394. paused=1
  395. ;;
  396. d)
  397. [[ "${OPTARG:0:1}" != '/' ]] && usage "-$opt: use absolute path please. \"$OPTARG\" is invalid."
  398. [ -w "${OPTARG%/*}" ] && dump="$OPTARG" || usage "-$opt: Cannot create file \"$OPTARG\""
  399. ;;
  400. D) # show window command always for first player
  401. alwaysfirst=1
  402. ;;
  403. s) # nice strings for player states
  404. state_nice=( $OPTARG )
  405. (( "${#state_nice[@]}" != 4 )) && usage "-$opt: \"${state_nice[*]}\" is not sufficient. Please provide exactly 4 strings."
  406. ;;
  407. i) # icon or not? which directory?
  408. icondir="$OPTARG"
  409. [ ! -d "$icondir" ] && usage "-$opt: The directory $icondir does not exist."
  410. ;;
  411. a) # alternative command when no media player is running
  412. altcmd="$OPTARG"
  413. #~ [ ! -x ${altcmd%% *} ] && ! which ${altcmd%% *} >/dev/null 2>&1 && usage "-$opt: Command \"$altcmd\" is not executable or not in PATH"
  414. ;;
  415. e) dots="$OPTARG"
  416. ;;
  417. r) sep="$OPTARG"
  418. ;;
  419. n) notify=1 && watch_song_change=1
  420. ;;
  421. y) delay="$OPTARG"
  422. [ "$delay" -lt 1 ] && usage "-$opt: Invalid number: $delay"
  423. ;;
  424. A) showart=1
  425. ;;
  426. I) icon_art=1
  427. showart=1
  428. ;;
  429. M) # define mpd's music directory
  430. [ ! -d "$OPTARG" ] && usage "-$opt: The directory $OPTARG does not exist."
  431. music_directory="${OPTARG%/}"
  432. ;;
  433. 1) # make sure only one instance is running
  434. onlyone=1
  435. ;;
  436. h) help
  437. ;;
  438. H) touch "$OPTARG" || usage "-$opt: Unable to touch file $OPTARG"
  439. history=1 && history_file="$OPTARG" && watch_song_change=1
  440. ;;
  441. U) upvote=1
  442. ;;
  443. u) upvote_str="$OPTARG"
  444. ;;
  445. X) help noless
  446. ;;
  447. *) usage
  448. ;;
  449. esac
  450. done
  451. [[ "$upvote" == 1 ]] && upvote_func
  452. # Can't watch for changed songs when there's no dump file to check
  453. [[ "$dump" == "" ]] && watch_song_change=0
  454. # make sure only 1 instance is running
  455. if [[ "$onlyone" = 1 ]]; then
  456. [ -w "$tmp_dir" ] || usage "-1: $tmp_dir is not writable"
  457. pidfile="$tmp_dir/${me}_pid"
  458. only_me_or_exit "$pidfile"
  459. fi
  460. if [[ "$selected_players" == "" ]]; then
  461. # user made no choice, compile a list of player functions, source plugins
  462. selected_players="$supported_players" # builtins
  463. shopt -s nullglob
  464. for plugin in "$plugindir"/*.mpip; do
  465. . "$plugin" # source it!
  466. plugin="${plugin#$plugindir/}"
  467. plugin="${plugin%.mpip}" # it is now stripped to its basename minus extension
  468. [[ "$selected_players" != *$plugin* ]] && selected_players="$selected_players $plugin"
  469. done
  470. shopt -u nullglob
  471. else
  472. # user made a list, use those, load external plugins if found
  473. for player in $selected_players ; do
  474. player="$plugindir/${player}.mpip"
  475. [ -r "$player" ] && . "$player"
  476. done
  477. # if a now loaded plugin has the same name as the builtin, it will override it.
  478. fi
  479. # don't search for icons we are not going to use
  480. if [[ "$icondir" != "" ]]; then
  481. [[ "$stopped" == "0" ]] && delete=(Stopped) && icons=( ${icons[@]/$delete} )
  482. [[ "$paused" == "0" ]] && delete=(Paused) && icons=( ${icons[@]/$delete} )
  483. fi
  484. while true ; do
  485. iconstring=''
  486. chosen=""
  487. playerson=''
  488. [[ "$tint2" == "1" ]] && >&2 printf '\e[2J'
  489. for player in $selected_players ; do
  490. # collecting all the info from all players
  491. pgrep -x $player >/dev/null 2>&1 || continue
  492. playerson="$playerson $player"
  493. ######################################################################
  494. ${player}_fillarray # this should be the only player-specific function
  495. info[${player}9]="$player" #
  496. ######################################################################
  497. # different output (e.g. tooltip) to stderr:
  498. >&2 printf "%s" "${info[${player}0]}: $player"
  499. [[ "${info[${player}1]}" != "" ]] && >&2 printf "\n%s" "${info[${player}1]}"
  500. [[ "${info[${player}3]}" != "" ]] && >&2 printf "\n%s" "${info[${player}3]}"
  501. [[ "${info[${player}2]}" != "" ]] && >&2 printf "\n%s" "${info[${player}2]}"
  502. >&2 printf "\n"
  503. done
  504. # find the $chosen player, whose info we will display
  505. for player in $playerson ; do
  506. if [[ "${info[${player}0]}" == "${state_nice[0]}" ]]; then
  507. chosen="$player" # if it plays, it wins.
  508. # optionally look for artwork for local files
  509. [[ "$showart" == "1" ]] && info[${player}8]="" && [[ "${info[${player}2]}" != http* ]] && get_art
  510. # optionally send notification
  511. if [[ "$watch_song_change" == "1" ]] && [[ "$dump" != "" ]] && [[ "$(sed -n 2p "$dump")" != "${info[${chosen}1]}" ]]; then
  512. if [[ "$notify" == 1 ]]; then
  513. [ -r "${info[${chosen}8]}" ] && icon="${info[${chosen}8]}" || icon="$default_icon"
  514. (sleep "$delay" && notify-send -i "$icon" "${info[${chosen}1]}" "$chosen ${info[${chosen}0],,}") &
  515. fi
  516. fi
  517. if [[ "$history" == 1 ]] && [ -r "$history_file" ] && [ -w "$history_file" ] && [[ "$(head -1 "$history_file")" != *"${info[${chosen}1]}"* ]]; then
  518. update_history
  519. fi
  520. break
  521. fi
  522. done
  523. if [[ "$chosen" == "" ]]; then
  524. # otherwise make a choice when -P option is active...
  525. if [[ "$paused" == "1" ]]; then
  526. for player in $playerson ; do
  527. [[ "${info[${player}0]}" == "${state_nice[1]}" ]] && chosen="$player" && break
  528. done
  529. fi
  530. # ... and with -S option:
  531. if [[ "$chosen" == "" ]] && [[ "$stopped" == "1" ]]; then
  532. for player in $playerson ; do
  533. [[ "${info[${player}0]}" == "${state_nice[2]}" ]] && chosen="$player" && break
  534. done
  535. fi
  536. fi
  537. # feed the ellipsizing function with data and define icon, depending on choice:
  538. if [[ "$chosen" == "" ]] && [[ "$altcmd" != "" ]]; then
  539. if [[ "$icondir" != "" ]]; then
  540. for ext in $extensions
  541. do
  542. [[ -f "$icondir/Altcmd.$ext" ]] && iconstring="$icondir/Altcmd.$ext" && break
  543. done
  544. [[ "$iconstring" == '' ]] && iconstring="no icon"
  545. fi
  546. infostring="$(join_maxlen_func "$($altcmd)")"
  547. else
  548. [[ "$showtime" == "1" ]] && [[ "${info[${chosen}3]}" != "" ]] &&\
  549. infostring="$(join_maxlen_func "${info[${chosen}1]}" "${info[${chosen}3]}")" \
  550. || infostring="$(join_maxlen_func "${info[${chosen}1]}")"
  551. # make song art the panel icon
  552. [[ "$icon_art" == "1" ]] && [[ "${info[${chosen}8]}" != "" ]] && \
  553. [ -r "${info[${chosen}8]}" ] && iconstring="${info[${chosen}8]}"
  554. # if we don't have an icon yet, and an icondir is defined, get one from there
  555. if [[ "$icondir" != "" ]] && [[ "$iconstring" == "" ]]; then
  556. for ext in $extensions
  557. do
  558. [ -r "$icondir/${info[${player}8]}.$ext" ] && iconstring="$icondir/${info[${player}8]}.$ext" && break
  559. done
  560. [[ "$iconstring" == '' ]] && iconstring="no icon"
  561. fi
  562. fi
  563. # dump info to file. if no player was chosen, choose first player from list of players
  564. if [[ "$dump" != "" ]] ; then
  565. [[ "$chosen" == "" ]] && chosen="${selected_players%% *}"
  566. # if no player is running we still need to fill the array with data!
  567. [[ "$playerson" == "" ]] && player="$chosen" && ${player}_fillarray dryrun && info[${player}9]="$player"
  568. # -D option:
  569. [[ "$alwaysfirst" == "1" ]] && info[${chosen}7]="${info[${selected_players%% *}7]}"
  570. for (( i=0 ; i < 10 ; i++ )); do
  571. echo "${info[${chosen}$i]}"
  572. done > "$dump"
  573. fi
  574. if [[ "$infostring" == "" ]] && [[ "$tint2" == "1" ]]; then
  575. [[ "$icondir" != "" ]] && max=$((lines +1)) || max=$lines
  576. for (( c=0 ; c < max ; c++ )); do echo; done
  577. else
  578. [[ "$iconstring" != '' ]] && echo "$iconstring"
  579. [[ "$infostring" != '' ]] && lines_func "$infostring"
  580. fi
  581. if (( continuous <= 0 )); then
  582. [[ "$onlyone" == 1 ]] && exit_only_me "$pidfile"
  583. exit 0
  584. fi
  585. sleep "$continuous"
  586. done
  587. exit 0