diff --git a/install.sh b/install.sh index c298a6c6c..7259f02f5 100644 --- a/install.sh +++ b/install.sh @@ -4,6 +4,35 @@ alias=r1 . /srv/http/bash/settings/addons.sh +# 20250111 +if [[ -e /boot/kernel.img ]]; then + if [[ $( pacman -Q cava ) != 'cava 0.7.4-1' ]]; then + wget https://github.com/rern/rern.github.io/raw/refs/heads/main/armv6h/cava-0.7.4-1-any.pkg.tar.xz + pacman -U --noconfirm cava-0.7.4-1-any.pkg.tar.xz + rm cava-0.7.4-1-any.pkg.tar.xz + fi +else + [[ $( pacman -Q cava ) != 'cava 0.10.3-2' ]] && pacman -Sy --noconfirm cava +fi + +if [[ $( pacman -Q python-rpi-gpio ) != 'python-rpi-gpio 0.7.1-3' ]]; then + pacman -R --noconfirm python-rpi-gpio + pacman -Sy --noconfirm python-rpi-gpio +fi + +file=/etc/systemd/system/mpd_oled.service +if [[ -e $file ]]; then + rm -f $file + pacman -R --noconfirm audio_spectrum_oled &> /dev/null + pacman -Sy --noconfirm mpd_oled +fi + +file=$dirsystem/lcdchar.conf +if [[ -e $dirsystem/lcdchar.conf ]]; then + conf2json $file | jq > ${file/conf/json} + rm -f $file +fi + # 20241208 rm -f $dirshm/playlist* @@ -22,56 +51,11 @@ if [[ -e /boot/kernel7.img ]] && ! grep -q mesa $file; then sed -i '/^IgnorePkg/ s/$/ mesa/' $file fi -sed -i '/^brightness/ d' $dirsystem/localbrowser.conf +[[ ! -e /boot/kernel.img ]] && sed -i '/^brightness/ d' $dirsystem/localbrowser.conf # 20241130 systemctl -q is-active mediamtx && touch $dirsystem/dabradio -# 20241110 -if [[ ! -e /boot/kernel.img ]]; then - revision=$( grep ^Revision /proc/cpuinfo ) - if [[ ${revision: -3:2} < 11 ]]; then - file=/etc/modprobe.d/brcmfmac.conf - [[ ! -e $file ]] && echo 'options brcmfmac feature_disable=0x82000' > $file - fi -fi - -# 20241108 -[[ $( pacman -Q cava ) < 'cava 0.10.2-2' ]] && pacman -Sy --noconfirm cava - -file=$dirsystem/lcdchar.conf -if [[ -e $file ]] && grep -q -m1 ^0= $file; then - rm $dirsystem/lcdchar* -fi - -# 20241111 -if [[ ! -e /boot/kernel.img ]]; then - revision=$( grep ^Revision /proc/cpuinfo ) - [[ ${revision: -3:2} < 11 ]] && echo 'options brcmfmac feature_disable=0x82000' > /etc/modprobe.d/brcmfmac.conf -fi - -file=/etc/systemd/system/dab.service -if [[ -e $file ]] && grep -q Requires $file; then - sed -i '/^Requires\|^After/ d' $file - rm -rf /etc/systemd/system/mediamtx.service.d - systemctl daemon-reload - systemctl try-restart mediamtx -fi - -# 20241108 -[[ $( pacman -Q cava ) < 'cava 0.10.2-2' ]] && pacman -Sy --noconfirm cava - -rm -f $dirsystem/lcdmodel - -file=$dirsystem/lcdchar.conf -if [[ -e $file && $( sed -n -E '/^charmap/,/^p0/ p' $file | wc -l ) -gt 2 ]]; then - . $file - for k in inf cols charmap p0 pin_rs p1 pin_rw p2 pin_e p3 backlight; do - conf+="$k=${!k}\n" - done - echo -e $conf > $file -fi - #------------------------------------------------------------------------------- installstart "$1" diff --git a/srv/http/assets/css/common.css b/srv/http/assets/css/common.css index 21f1b1aba..9b4710277 100644 --- a/srv/http/assets/css/common.css +++ b/srv/http/assets/css/common.css @@ -10,7 +10,7 @@ @font-face { font-family : rern; - src : url( '/assets/fonts/rern.woff2?v=1733921041' ); + src : url( '/assets/fonts/rern.woff2?v=1735997122' ); } @font-face { font-family : Lato; @@ -62,6 +62,7 @@ i { .i-bluealsa::before, .i-bluetooth::before, .i-btreceiver::before { content: '\F51A' } +.i-brightness::before { content: '\F55F' } .i-btsender::before { content: '\F51B' } .i-camilla::before, .i-camilladsp::before { content: '\F59D'; position: absolute; color: var( --cg60 ); } @@ -257,7 +258,8 @@ body { } i, .infobtn, -#debug { +#debug, +#lib-list .coverart { -webkit-user-select: none; user-select: none; } @@ -425,6 +427,7 @@ code { text-overflow: ellipsis; } #bannerTitle { + line-height: 22px; font-size: 18px; font-weight: 300; } @@ -468,6 +471,17 @@ code { font-size: 13px; } +input[ type=checkbox ], +input[ type=radio ], +input[ type=range ], +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button, +input[ type=range ]::-webkit-slider-thumb { + appearance: none; +} +input[ type=number ] { + appearance: textfield; +} input[ type=range ] { --track: linear-gradient( 90deg, transparent 10px, #000 10px, #000 calc( 100% - 10px ), transparent 10px ) !important; --trackborder : 1px solid var( --cgd ) !important; @@ -476,7 +490,6 @@ input[ type=range ] { margin: 0; max-width: 70%; background: transparent; - -webkit-appearance: none; } input[ type=range ]::-webkit-slider-thumb { height: 40px; @@ -486,7 +499,6 @@ input[ type=range ]::-webkit-slider-thumb { border-radius: 4px; background: var( --glossy-btn ); background-color: var( --cm ); - -webkit-appearance: none; box-shadow: var( --shadow-btn ); } input[ type=range ]::-moz-range-thumb { /* cannot be combined */ @@ -573,9 +585,11 @@ input[ type=checkbox ] { .infobtn-default { background-color: var( --cg ); } -.disabled { +.disabled, +.disabled i { color: var( --cg60 ) !important; pointer-events: none; + cursor: default; } .infobtn.disabled { background-color: var( --cgd ) !important; @@ -584,7 +598,6 @@ input[ type=checkbox ] { } #infoOk.disabled { pointer-events: auto; - cursor: default; } input[ type=checkbox ].disabled { pointer-events: auto; @@ -824,9 +837,16 @@ hr { color: var( --cg60 ); } .infofooter i { + width: auto !important; margin: 0 6px; vertical-align: -2px; } +.infofooter span { + margin-right: 10px; +} +.infofooter span:last-child { + margin-right: 0; +} .infomessage { padding-top: 5px; line-height: 24px; @@ -901,13 +921,6 @@ input[ type=number ]:disabled { border: 1px solid var( --cgl ); opacity: 1; } -input::-webkit-outer-spin-button, -input::-webkit-inner-spin-button { - -webkit-appearance: none; -} -input[ type=number ] { - -moz-appearance:textfield; -} #infoPasswordBox { margin-top: 0 !important; outline: none; @@ -919,7 +932,6 @@ input[ type=number ] { } input[ type=radio ], input[ type=checkbox ] { - -webkit-appearance: none; width: 24px; height: 24px; margin: 3px 6px 3px 1px; @@ -1028,7 +1040,6 @@ input[ type=radio ]:disabled { } @media ( max-width: 420px ) { #infoList { padding: 20px 5px 10px 5px } - .infoheader, .infofooter { padding: 0 5px } } @media ( max-width: 330px ) { #infoOverlay { @@ -1052,6 +1063,7 @@ input[ type=radio ]:disabled { .i-volume, .infobtn, .infofooter i, + .infofooter span, .infolabel i, .infomessage img, .infomessage .tagpath, diff --git a/srv/http/assets/css/hovercursor.css b/srv/http/assets/css/hovercursor.css index 30f94ab9a..20cb5514e 100644 --- a/srv/http/assets/css/hovercursor.css +++ b/srv/http/assets/css/hovercursor.css @@ -144,8 +144,9 @@ li:not( .licover ), #volume-text { cursor: pointer; } -#lib-title a:last-of-type:hover, -.mode.nodata * { +.disabled, +.mode.nodata *, +#lib-title a:last-of-type:hover { cursor: default; } #lib-title a:last-of-type:hover { diff --git a/srv/http/assets/css/main.css b/srv/http/assets/css/main.css index 5b2fee56c..f3297144b 100644 --- a/srv/http/assets/css/main.css +++ b/srv/http/assets/css/main.css @@ -1795,6 +1795,11 @@ li.active .li1 bl { font-size: 14px !important; color: var( --cg60 ); } +li.webradio .artist::before, +li.webradio .station::before, +li.webradio .url::before { + content: ' • '; +} #pl-savedlist .li2 { max-width: 100%; } @@ -1928,9 +1933,6 @@ li.active .li2 bl { margin-left: 10px; text-align: left; } -.tagfooter { - line-height: 40px; -} .tagfooter div { display: inline-block; } diff --git a/srv/http/assets/css/settings.css b/srv/http/assets/css/settings.css index decb1da45..b8b5ea792 100644 --- a/srv/http/assets/css/settings.css +++ b/srv/http/assets/css/settings.css @@ -200,8 +200,6 @@ heading { font-size: 24px; font-weight: 300; border-bottom: 1px solid var( --cg ); -} -heading { letter-spacing: 5px; } heading.subhead { diff --git a/srv/http/assets/fonts/rern.woff2 b/srv/http/assets/fonts/rern.woff2 index 7a0bbd181..c338d499d 100644 Binary files a/srv/http/assets/fonts/rern.woff2 and b/srv/http/assets/fonts/rern.woff2 differ diff --git a/srv/http/assets/js/camilla.js b/srv/http/assets/js/camilla.js index 829708cca..dac41a61e 100644 --- a/srv/http/assets/js/camilla.js +++ b/srv/http/assets/js/camilla.js @@ -1,4 +1,4 @@ -ps.volume = data => { +W.volume = data => { if ( V.local ) { V.local = false; return @@ -722,7 +722,7 @@ var graph = { } var render = { status : () => { // onload only - playbackButton(); + headIcon(); if ( S.volume !== false ) { $( '#divvolume' ).removeClass( 'hide' ); $( '#divvolume .control' ).text( S.control ); diff --git a/srv/http/assets/js/common.js b/srv/http/assets/js/common.js index 6efe74763..158964189 100644 --- a/srv/http/assets/js/common.js +++ b/srv/http/assets/js/common.js @@ -6,14 +6,9 @@ loader() local() selectSet() websocket bash() */ - -var page = location.search.replace( /\?p=|&.*/g, '' ); // .../settings.php/p=PAGE&x=XXX... > PAGE -var iconwarning = ico( 'warning yl' ) +' '; -var localhost = [ 'localhost', '127.0.0.1' ].includes( location.hostname ); -var orange = '#de810e'; -var red = '#bb2828'; -var ws; -var ps = { +S = {} // status +V = {} // variable +W = { // ws push bluetooth : () => { if ( page === 'networks' ) { S.listbt = data; @@ -68,7 +63,7 @@ var ps = { $( '#loader' ).css( 'opacity', 1 ); setTimeout( () => { $( '#loader svg' ).css( 'animation', 'none' ); - bannerHide(); + $( '#banner' ).addClass( 'hide' ); }, 10000 ); } else { // reconnect after reboot setTimeout( websocketReconnect, data.startup + 5000 ); // add shutdown 5s @@ -127,7 +122,12 @@ var ps = { } } } - +var page = location.search.replace( /\?p=|&.*/g, '' ); // .../settings.php/p=PAGE&x=XXX... > PAGE +var iconwarning = ico( 'warning yl' ) +' '; +var localhost = [ 'localhost', '127.0.0.1' ].includes( location.hostname ); +var orange = '#de810e'; +var red = '#bb2828'; +var ws; // ---------------------------------------------------------------------- /* $( ELEMENT ).press( DELEGATE, function( e ) { @@ -178,7 +178,7 @@ function banner( icon, title, message, delay ) { }, delay || 3000 ); } function bannerHide() { - if ( V.bannerdelay || V.reboot || V.relays || $( '#banner .i-warning' ).length ) return + if ( V.bannerdelay || V.relays || V.reboot || V.off ) return $( '#banner' ) .addClass( 'hide' ) @@ -964,6 +964,11 @@ function infoFileImageResize( ext, imgW, imgH ) { } } } +function infoFooterIcon( kv ) { + var footer = ''; + $.each( kv, ( l, i ) => footer += ''+ ico( i ) + l +'' ); + return footer +} function infoKey2array( key ) { if ( ! Array.isArray( I[ key ] ) ) I[ key ] = [ I[ key ] ]; } @@ -1188,7 +1193,6 @@ function infoPower() { } ); } function infoPowerCommand( action ) { - if( ! action ) action = ''; loader(); bash( [ 'power.sh', action ], nfs => { if ( nfs != -1 ) return @@ -1203,7 +1207,7 @@ function infoPowerCommand( action ) { +'

Continue?' , oklabel : action ? ico( 'reboot' ) +'Reboot' : ico( 'power' ) +'Off' , okcolor : action ? orange : red - , ok : () => bash( [ 'power.sh', action, 'confirm' ] ) + , ok : () => bash( [ 'power.sh', action || '', 'confirm' ] ) } ); } ); } @@ -1290,7 +1294,9 @@ function loader( fader ) { $( '#loader' ).removeClass( 'hide' ); } function loaderHide() { - if ( ! V.reboot ) $( '#loader' ).addClass( 'hide' ); + if ( 'off' in V || 'reboot' in V ) return + + $( '#loader' ).addClass( 'hide' ); } // ---------------------------------------------------------------------- @@ -1441,13 +1447,14 @@ function websocketConnect( ip ) { } }, 100 ); } - ws.onmessage = message => { // ps: push status + ws.onmessage = message => { var data = message.data; if ( data === 'pong' ) { // on pageActive - reload if ws not response V.timeoutreload = false; } else { - var json = JSON.parse( data ); - if ( json.channel in ps ) ps[ json.channel ]( json.data ); + var json = JSON.parse( data ); + var channel = json.channel; + if ( channel in W ) W[ channel ]( json.data ); } } } diff --git a/srv/http/assets/js/context.js b/srv/http/assets/js/context.js index 165e40401..af91591c8 100644 --- a/srv/http/assets/js/context.js +++ b/srv/http/assets/js/context.js @@ -2,7 +2,7 @@ function addSimilar() { var icon = 'lastfm'; var title = 'Add Similar'; banner( icon +' blink', title, 'Get similar tracks ...', -1 ); - bash( [ 'mpcsimilar', V.list.artist, V.list.name, V.apikeylastfm, 'CMD ARTIST TITLE APIKEY' ], error => { + bash( [ 'mpcsimilar', V.list.path, 'CMD FILE' ], error => { if ( error ) { bannerHide(); info( { @@ -374,8 +374,8 @@ function tagEditor() { var message = ''+ file +'' +'
'+ ico( 'folder' ) +' '+ dir; message += V.list.licover ? '
' : '
'+ ico( fileicon ) +' '+ file.split( '/' ).pop() +''; - var footer = ico( 'help', '', 'tabindex' ) +'Label'; - if ( V.list.licover ) footer += ' *  Various values in tracks'; + var footer = ''+ ico( 'help', '', 'tabindex' ) +'Label'; + if ( V.list.licover ) footer += '* Various values in tracks'; info( { icon : V.playlist ? 'info' : 'tag' , title : V.playlist ? 'Track Info' : 'Tag Editor' @@ -396,7 +396,7 @@ function tagEditor() { $( '#infoList td i:not( .i-track, .i-title )' ).css( 'cursor', 'pointer' ); if ( V.playlist ) $( '#infoList input' ).prop( 'disabled', 1 ); var inputW = parseInt( $( '#infoList input' ).css( 'width' ) ); - $( '.infofooter i' ).on( 'click', function( e ) { + $( '.infofooter span' ).on( 'click', function( e ) { if ( $( '.taglabel' ).hasClass( 'hide' ) ) { $( '#infoList input' ).css( 'width', ( inputW - 92 ) +'px' ); $( '.taglabel' ).removeClass( 'hide' ); diff --git a/srv/http/assets/js/features.js b/srv/http/assets/js/features.js index d46ae3cae..b949e1903 100644 --- a/srv/http/assets/js/features.js +++ b/srv/http/assets/js/features.js @@ -70,10 +70,6 @@ var config = { } } , localbrowser : data => { - if ( S.localbrowser ) { - var footer = ico( 'redo', 'reload', 'tabindex' ) +'Reload '+ ico( 'screenoff', 'screenoff', 'tabindex' ) +'On/Off'; - if ( data.brightness ) footer += ' '+ ico( 'gear', 'brightness', 'tabindex' ) +'Brightness'; - } info( { ...SW , list : [ @@ -83,7 +79,11 @@ var config = { , [ 'On while play', 'checkbox' ] , [ 'Mouse pointer', 'checkbox' ] ] - , footer : footer + , footer : infoFooterIcon( { + Reload : 'reload' + , Screenoff : 'screenoff' + , Brightness : 'brightness' + } ) , boxwidth : 110 , values : data.values , checkchanged : S.localbrowser @@ -99,25 +99,29 @@ var config = { .prop( 'checked', false ); } } ); - $( '#brightness' ).on( 'click', function() { - info( { - ...SW - , list : [ 'Brightness', 'range' ] - , values : data.brightness - , beforeshow : () => { - $( '#infoList input' ).on( 'input', function() { - bash( [ 'brightness', +this.value, 'CMD VAL' ] ) - } ); - } - , okno : true - } ); - switchCancel(); - } ); - $( '#reload' ).on( 'click', function() { - bash( [ 'localbrowserreload' ], () => banner( SW.icon, SW.title, 'Reloaded.' ) ); - } ); - $( '#screenoff' ).on( 'click', function() { - bash( [ 'screentoggle' ], onoff => banner( SW.icon, SW.title, onoff ) ); + $( '.infofooter' ).toggleClass( 'disabled', ! S.localbrowser ); + var $span = $( '.infofooter span' ); + $span.eq( 2 ).toggleClass( 'hide', ! data.brightness ); + $span.on( 'click', function() { + var i = $( this ).index(); + if ( i === 0 ) { + bash( [ 'localbrowserreload' ], () => banner( SW.icon, SW.title, 'Reloaded.' ) ); + } else if ( i === 1 ) { + bash( [ 'screentoggle' ], onoff => banner( SW.icon, SW.title, onoff ) ); + } else { + info( { + ...SW + , list : [ 'Brightness', 'range' ] + , values : data.brightness + , beforeshow : () => { + $( '#infoList input' ).on( 'input', function() { + bash( [ 'brightness', +this.value, 'CMD VAL' ] ) + } ); + } + , okno : true + } ); + switchCancel(); + } } ); } , cancel : switchCancel diff --git a/srv/http/assets/js/function.js b/srv/http/assets/js/function.js index 5db356cdf..2f209719e 100644 --- a/srv/http/assets/js/function.js +++ b/srv/http/assets/js/function.js @@ -259,7 +259,6 @@ function contextmenuLibrary( $li, $target ) { if ( V.mode.slice( -5 ) === 'radio' ) V.list.dir = $li.find( '.lidir' ).text(); if ( V.librarytrack && ! V.list.licover ) { V.list.name = $li.find( '.li1' ).html().replace( /Title includes: '+ title.replace( /^.*\(/, '(' ), 'checkbox' ] ); } - var footer = ''+ ico( 'lyrics' ) +' Lyrics' - +''+ ico( 'bio' ) +' Bio' - +''+ ico( 'lastfm' ) +' Add Similar' - +''+ ico( 'lastfm' ) +' Scrobble'; info( { icon : 'playback' , title : 'Current Track' , list : list - , footer : footer + , footer : infoFooterIcon( { + Lyrics : 'lyrics' + , Bio : 'bio' + , 'Add Similar' : 'lastfm' + , Scrobble : 'scrobble' + } ) , footeralign : 'left' , width : 460 , boxwidth : 'max' , values : paren ? [ artist, titlenoparen, album ] : [ artist, title, album ] , beforeshow : () => { $( '#infoList input' ).eq( 2 ).toggleClass( 'hide', album === '' ); - $( '.infofooter' ) - .css( 'padding-left', '40px' ) - .find( 'span' ).css( { 'margin-right': '20px', cursor: 'pointer' } ); - $( '.infofooter .lyrics' ).toggleClass( 'hide', ! S.lyrics ); - $( '.infofooter .scrobble' ).toggleClass( 'hide', ! S.scrobble ); if ( S.scrobble ) $( '.infofooter .scrobble' ).toggleClass( 'disabled', ! artist || ! title || ! S.webradio || S.scrobbleconf[ S.player ] ); if ( paren ) { $( '#infoList input:checkbox' ).on( 'input', function() { @@ -771,20 +767,25 @@ function infoTitle() { var val = infoVal(); $( '#infoList .scrobble' ).toggleClass( 'disabled', val[ 0 ] === '' || val[ 1 ] === '' ); } ); - $( '#infoList' ).on( 'click', '.infofooter span', function() { + $( '.infofooter' ).css( 'padding-left', '35px' ); + var $span = $( '.infofooter span' ); + $span.eq( 0 ).toggleClass( 'hide', ! S.lyrics ); + $span.eq( 3 ).toggleClass( 'hide', ! S.scrobble ); + $span.on( 'click', function() { var values = infoVal(); var artist = values[ 0 ] var title = values[ 1 ] var $this = $( this ); - if ( $this.hasClass( 'lyrics' ) ) { + var i = $( this ).index(); + if ( i === 0 ) { V.lyricsartist = artist || S.Artist; V.lyricstitle = title || S.Title; lyricsGet(); - } else if ( $this.hasClass( 'bio' ) ) { + } else if ( i === 1 ) { bio( artist ); - } else if ( $this.hasClass( 'similar' ) ) { + } else if ( i === 2 ) { addSimilar(); - } else if ( $this.hasClass( 'scrobble' ) ) { + } else { bash( [ 'scrobble.sh', ...values, 'CMD ARTIST TITLE' ] ); banner( 'lastfm blink', 'Scrobble', 'Send ...' ); } @@ -1761,6 +1762,37 @@ function setPlaylistInfoWidth() { var cW = document.body.clientWidth; $title.css( 'max-width', iWdW + titleW < cW ? '' : cW - iWdW ); } +function setPlaylistRadioInfo( stop ) { + var $liactive = $( '#pl-list li.active' ); + var $img = $liactive.find( 'img' ); + var $name = $liactive.find( '.name' ); + var $li2 = $liactive.find( '.li2' ); + var $station = $li2.find( '.station' ); + var $artist = $li2.find( '.artist' ); + var $url = $li2.find( '.url' ); + if ( S.state === 'stop' || stop ) { + $img.attr( 'src', $img.data( 'src' ) ); + $name.text( $station.text() ); + $station.addClass( 'hide' ); + $artist.addClass( 'hide' ); + $url.removeClass( 'hide' ); + } else { + if ( S.coverart ) $img + .removeClass( 'lazyload' ) + .attr( 'src', S.coverart ); + $name.html( S.Title || dots ); + if ( S.Artist ) { + $artist + .text( S.Artist + ( S.Album ? ' - '+ S.Album : '' ) ) + .removeClass( 'hide' ); + $url.addClass( 'hide' ); + } else { + $artist.addClass( 'hide' ); + $url.removeClass( 'hide' ); + } + $station.removeClass( 'hide' ); + } +} function setPlaylistScroll() { if ( ! V.playlist || ! V.playlisthome ) return @@ -1772,12 +1804,11 @@ function setPlaylistScroll() { return } - var litop = barVisible( 80, 40 ); + var litop = barVisible( 80, 40 ); $( '#menu-plaction' ).addClass( 'hide' ); $( '#pl-list li' ).removeClass( 'active pause play updn' ); - $liactive = $( '#pl-list li' ).eq( S.song ); + var $liactive = $( '#pl-list li' ).eq( S.song ); $liactive.addClass( 'active' ); - S.webradio = $liactive.hasClass( 'webradio' ); if ( ! $( '.pl-remove' ).length && ! I.active ) { if ( $( '#pl-list li' ).length < 5 ) { var top = 0; @@ -1787,43 +1818,19 @@ function setPlaylistScroll() { pageScroll( top ); } $( '#pl-list .elapsed' ).empty(); - var $elapsed = $liactive.find( '.elapsed' ); - var $name = $liactive.find( '.li1 .name' ); - var $stationname = $liactive.find( '.li2 .stationname' ); - $stationname.addClass( 'hide' ); - if ( S.state === 'stop' || S.player === 'snapcast' ) { - if ( S.webradio ) { - $name.text( $liactive.find( '.liname' ).text() ); - setPlaylistWebRadioCoverart(); - } + if ( S.webradio ) setPlaylistRadioInfo(); + if ( S.elapsed === false ) return + + $liactive.addClass( S.state ); + if ( S.player === 'upnp' ) $liactive.find( '.time' ).text( second2HMS( S.Time ) ); + if ( S.state === 'pause' ) { + elapsedtxt = second2HMS( S.elapsed ); + $liactive.find( '.elapsed' ).text( elapsedtxt ); + setPlaylistInfoWidth(); } else { - if ( S.elapsed === false ) return - - $liactive.addClass( S.state ); - if ( S.player === 'upnp' ) $liactive.find( '.time' ).text( second2HMS( S.Time ) ); - if ( S.state === 'pause' ) { - elapsedtxt = second2HMS( S.elapsed ); - $elapsed.text( elapsedtxt ); - setPlaylistInfoWidth(); - } else if ( S.state === 'play' ) { - if ( S.webradio ) { - $stationname.removeClass( 'hide' ); - $name.html( S.Title || dots ); - if ( S.coverart && S.coverart !== S.stationcover ) { - $liactive.find( 'img' ).on( 'lazyloaded', setPlaylistWebRadioCoverart ); // fix - lazysizes load stationcover - setPlaylistWebRadioCoverart(); // lazysizes already loaded - } - } - setProgressElapsed(); - } + setProgressElapsed(); } } -function setPlaylistWebRadioCoverart() { - var coverart = S.state === 'play' ? S.coverart + versionHash() : S.stationcover; - $( '#pl-list li.active img' ) - .data( 'src', coverart ) - .attr( 'src', coverart); -} function setPlayPauseColor() { var pause = S.state === 'pause'; $( '#title' ).toggleClass( 'gr', pause ); diff --git a/srv/http/assets/js/main.js b/srv/http/assets/js/main.js index f4cac603b..fb6bffc7c 100644 --- a/srv/http/assets/js/main.js +++ b/srv/http/assets/js/main.js @@ -2,7 +2,6 @@ C = {}; // counts D = {}; // display E = {}; // equalizer O = []; // order -S = {}; // status V = { // var global apikeyfanart : '06f56465de874e4c75a2e9f0cc284fa3' , apikeylastfm : '328f08885c2b5a4d1dbe1496cab60b15' @@ -1835,18 +1834,13 @@ $( '#pl-list' ).on( 'click', 'li', function( e ) { var $liactive = $( '#pl-list li.active' ); $( '#menu-plaction' ).addClass( 'hide' ); $liactive.find( '.song' ).empty(); - if ( $liactive.hasClass( 'webradio' ) ) { - if ( S.state == 'play' ) { - $liactive.find( '.li1 .name' ).text( $liactive.find( '.liname' ).text() ); - $liactive.find( '.li2 .stationname' ).addClass( 'hide' ); - $liactive.find( '.li2 .name' ).removeClass( 'hide' ); - } - } if ( $this.hasClass( 'active' ) ) { if ( S.state === 'play' ) { if ( S.webradio ) { + $liactive.removeClass( 'play' ); + $liactive.find( '.elapsed' ).empty(); + setPlaylistRadioInfo( 'stop' ); $( '#stop' ).trigger( 'click' ); - $this.find( '.elapsed' ).empty(); } else { $( '#pause' ).trigger( 'click' ); $this.find( '.elapsed i' ).toggleClass( 'i-play i-pause' ); @@ -1870,7 +1864,6 @@ $( '#pl-list' ).on( 'click', 'li', function( e ) { V.list = {}; V.list.li = $thisli; V.list.path = $thisli.find( '.lipath' ).text(); - V.list.artist = $thisli.find( '.artist' ).text(); V.list.name = $thisli.find( webradio ? '.liname' : '.name' ).eq( 0 ).text(); V.list.index = $thisli.index(); var $menu = $( '#menu-plaction' ); @@ -1944,7 +1937,6 @@ $( '#page-playlist' ).on( 'click', '#pl-savedlist li', function( e ) { V.list.name = $this.find( '.lipath' ).text().trim(); V.list.path = V.list.name; } else { - V.list.artist = $this.find( '.artist' ).text().trim(); V.list.name = $this.find( '.name' ).text().trim(); V.list.path = $this.find( '.lipath' ).text().trim() || V.list.name; V.list.track = $this.data( 'track' ); diff --git a/srv/http/assets/js/networks.js b/srv/http/assets/js/networks.js index 11e80d45b..dc3e67e87 100644 --- a/srv/http/assets/js/networks.js +++ b/srv/http/assets/js/networks.js @@ -1,4 +1,4 @@ -ps.wlan = () => { +W.wlan = () => { if ( data && 'reboot' in data ) { info( { icon : 'wifi' diff --git a/srv/http/assets/js/passive.js b/srv/http/assets/js/passive.js index bf86038db..25859878c 100644 --- a/srv/http/assets/js/passive.js +++ b/srv/http/assets/js/passive.js @@ -1,5 +1,5 @@ -ps = { - ...ps // from common.js +W = { + ...W // from common.js , airplay : data => { statusUpdate( data ); if ( V.playback ) renderPlayback(); @@ -71,6 +71,8 @@ ps = { eqOptionPreset(); } , mpdplayer : data => { + if ( 'off' in V || 'reboot' in V ) return + clearTimeout( V.debounce ); V.debounce = setTimeout( () => { if ( ! data.control && data.volume == -1 ) { // fix - upmpdcli missing values on stop/pause @@ -99,7 +101,7 @@ ps = { setProgress( 0 ); setBlinkDot(); } - setPlaylistScroll(); + if ( V.playlist ) setPlaylistRadioInfo(); } , mpdupdate : data => { if ( 'counts' in data ) { diff --git a/srv/http/assets/js/player.js b/srv/http/assets/js/player.js index 6a6201371..fc62a6a7f 100644 --- a/srv/http/assets/js/player.js +++ b/srv/http/assets/js/player.js @@ -1,4 +1,4 @@ -ps.mpdupdate = data => { +W.mpdupdate = data => { if ( 'done' in data ) { $.each( S.counts, ( k, v ) => { S[ k ] = data.done[ k ] } ); S.updatetime = data.updatetime @@ -6,7 +6,7 @@ ps.mpdupdate = data => { util.renderStatus(); } } -ps.volume = data => { +W.volume = data => { if ( ! ( 'db' in data ) ) return S.volume = data.val; @@ -349,7 +349,7 @@ var util = { } function renderPage() { - playbackButton(); + headIcon(); util.statusSet(); if ( S.bluetooth ) { $( '#btreceiver' ).html( '' ); diff --git a/srv/http/assets/js/settings.js b/srv/http/assets/js/settings.js index 503a504e6..f8d44bee2 100644 --- a/srv/http/assets/js/settings.js +++ b/srv/http/assets/js/settings.js @@ -4,48 +4,44 @@ Naming must be the same for: js - id = icon = NAME, #setting-NAME bash - cmd=NAME, save to NAME.conf */ -S = {} // status -V = {} -ps = { - ...ps // from common.js - , camilla : data => { - S.range = data; - $( '#volume' ).prop( { min: S.range.VOLUMEMIN, max: S.range.VOLUMEMAX } ) - $( '.tab input[type=range]' ).prop( { min: S.range.GAINMIN, max: S.range.GAINMAX } ); - } - , mpdplayer : data => playbackButton( data ) - , mpdradio : data => playbackButton( data ) - , player : data => { - if ( ! [ 'camilla', 'player' ].includes( page ) ) return - - var player_id = { - airplay : 'shairport-sync' - , bluetooth : 'bluetooth' - , snapcast : 'snapserver' - , spotify : 'spotifyd' - , upnp : 'upmpdcli' +W.refresh = data => { + if ( data.page !== page ) return + + clearTimeout( V.debounce ); + V.debounce = setTimeout( () => { + $.each( data, ( k, v ) => { S[ k ] = v } ); // need braces + if ( page === 'networks' ) { + if ( $( '#divinterface' ).hasClass( 'hide' ) ) $( '.back' ).trigger( 'click' ); + } else { + switchSet(); } - $( '#'+ player_id[ data.player ] ).toggleClass( 'disabled', data.active ); - } - , reboot : data => { - var msg = ''; - data.id.forEach( id => msg += '
• '+ $( '#div'+ id +' .label' ).text() +'
' ); - banner( 'reboot', 'Reboot required', msg, 5000 ); + renderPage(); + }, 300 ); +} +if ( $( 'heading .playback' ).length ) { // for player and camilla + W = { + ...W // from common.js + , mpdplayer : data => headIcon( data ) + , mpdradio : data => headIcon( data ) } - , refresh : data => { - if ( data.page !== page ) return - - clearTimeout( V.debounce ); - V.debounce = setTimeout( () => { - $.each( data, ( k, v ) => { S[ k ] = v } ); // need braces - if ( page === 'networks' ) { - if ( $( '#divinterface' ).hasClass( 'hide' ) ) $( '.back' ).trigger( 'click' ); - } else { - switchSet(); - } - renderPage(); - }, 300 ); + function headIcon( data ) { + if ( data ) { + if ( ( ! data.player || ! data.state ) || ( data.player === S.player && data.state === S.state ) ) return + + S.player = data.player; + S.state = data.state; + } + $( '.playback' ) + .prop( 'class', 'playback i-'+ ( S.state === 'play' ? 'pause' : 'play' ) ) + .toggleClass( 'disabled', page === 'player' && S.player !== 'mpd' ); + $( 'heading .player' ).prop( 'class', 'player i-'+ S.player ); } + $( '.playback' ).on( 'click', function() { + S.state = S.state === 'play' ? 'pause' : 'play' + headIcon(); + if ( page === 'camilla' && S.state === 'pause' ) render.statusStop(); + bash( [ 'cmd.sh', S.player === 'mpd' ? 'mpcplayback' : 'playerstop' ] ); + } ); } function bannerReset() { @@ -139,17 +135,6 @@ function notifyCommon( message ) { } banner( SW.icon +' blink', SW.title, message, -1 ); } -function playbackButton( data ) { - if ( ! [ 'camilla', 'player' ].includes( page ) ) return - - if ( data ) [ 'player', 'state' ].forEach( k => S[ k ] = data[ k ] ); - if ( S.pllength ) { - var btn = S.state === 'play' ? 'pause' : 'play'; - } else { - var btn = 'play disabled'; - } - $( '.playback' ).prop( 'class', 'playback i-'+ btn ); -} function refreshData() { if ( page === 'guide' || ( I.active && ! I.rangelabel ) ) return @@ -329,12 +314,6 @@ $( '.container' ).on( 'click', '.status .headtitle, .col-l.status', function() { $( '.page-icon' ).on( 'click', function() { $( '#debug' ).trigger( 'click' ); } ).press( () => location.reload() ); -$( '.playback' ).on( 'click', function() { // for player and camilla - S.state = S.state === 'play' ? 'pause' : 'play'; - if ( page === 'camilla' && S.state === 'pause' ) render.statusStop(); - playbackButton(); - bash( [ 'cmd.sh', 'mpcplayback' ] ); -} ); $( '.head .i-gear' ).on( 'click', function() { $( '#bar-bottom' ).toggle(); } ); diff --git a/srv/http/assets/js/system.js b/srv/http/assets/js/system.js index 32b233aea..3b7cec664 100644 --- a/srv/http/assets/js/system.js +++ b/srv/http/assets/js/system.js @@ -1,4 +1,7 @@ -ps.storage = data => { +W.reboot = data => { + banner( data.id, $( '#div'+ data.id +' .col-l .label' ).text(), 'Reboot required', 5000 ); +} +W.storage = data => { clearTimeout( V.debounce ); V.debounce = setTimeout( () => { S.liststorage = data.list; @@ -6,6 +9,7 @@ ps.storage = data => { if ( $( '#data' ).length ) $( '#data' ).html( highlightJSON( S ) ); }, 1000 ); } + var config = { _disable : { shareddata : () => { @@ -127,11 +131,9 @@ var config = { } } , lcdchar : data => { - 'address' in data ? util.lcdchar.i2s( data ) : util.lcdchar.gpio( data ); + util.lcdchar[ data.values.INF ]( data ); } - , mpdoled : data => { - var values = data.values; - var buttonlogo = S.mpdoled && ! data.reboot; + , mpdoled : values => { var chip = { 'SSD130x SP' : 1 , 'SSD130x I²C' : 3 @@ -144,8 +146,8 @@ var config = { , list : [ [ 'Controller', 'select', chip ] , [ 'Refresh (baud)', 'select', { kv: { '800,000': 800000, '1,000,000': 1000000, '1,200,000': 1200000 } } ] + , [ 'Spectrum only', 'checkbox' ] ] - , footer : ico( 'raudio' ) +'Logo' , values : values , checkchanged : S.mpdoled , boxwidth : 140 @@ -153,7 +155,6 @@ var config = { var $tr = $( '#infoList tr' ); var $baud = $tr.eq( 1 ) $baud.toggleClass( 'hide', S.mpdoled && ( values.CHIP < 3 || values.CHIP > 6 ) ); - $( '.infofooter i' ).toggleClass( 'disabled', ! S.mpdoled || data.reboot ) $tr.eq( 0 ).on( 'input', function() { var val = this.value; $baud.toggleClass( 'hide', val < 3 || val > 6 ); @@ -205,7 +206,7 @@ var config = { } ); } , timezone : () => util.server.ntp() - , tft : data => { + , tft : values => { var type = { 'Generic' : 'tft35a' , 'Waveshare (A)' : 'waveshare35a' @@ -213,23 +214,19 @@ var config = { , 'Waveshare (B) Rev 2.0' : 'waveshare35b-v2' , 'Waveshare (C)' : 'waveshare35c' } - var buttoncalibrate = S.tft && ! data.reboot; info( { ...SW , list : [ 'Type', 'select', type ] - , values : data.values + , footer : ''+ ico( 'cursor' ) +'Calibrate' + , values : values , checkchanged : S.tft , boxwidth : 190 - , buttonlabel : ! buttoncalibrate ? '' : 'Calibrate' - , button : ! buttoncalibrate ? '' : () => { - info( { - ...SW - , message : 'Calibrate touchscreen?' - +'
(Get stylus ready.)' - , ok : () => { + , beforeshow : () => { + $( '.infofooter span' ) + .toggleClass( 'disabled', ! S.tft ) + .on( 'click', function() { notify( SW.icon, 'Calibrate Touchscreen', 'Start ...' ); bash( [ 'tftcalibrate' ] ); - } } ); } , cancel : switchCancel @@ -343,7 +340,7 @@ var util = { } } , lcdchar : { - gpio : values => { + gpio : data => { var list0 = jsonClone( util.lcdchar.list ); var list = list0.slice( 0, 3 ); [ 'Pins:   D4', 'RS', 'D5', 'RW', 'D6', 'E', 'D7' ].forEach( ( k, i ) => { @@ -352,42 +349,36 @@ var util = { list.push( [ '', '' ], list0.slice( -1 )[ 0 ] ); info( { ...util.lcdchar.json - , tab : [ () => infoSetting( 'lcdchar', util.lcdchar.i2s ), '' ] + , tab : [ () => infoSetting( 'lcdchar i2c', config.lcdchar ), '' ] , message : util.gpiosvg , list : list , boxwidth : 70 - , values : values - , checkchanged : S.lcdchar + , values : data.values + , checkchanged : S.lcdchar && data.current === 'gpio' } ); } - , i2s : data => { + , i2c : data => { var list = jsonClone( util.lcdchar.list ); list[ 3 ][ 2 ].kv = data.address; info( { ...util.lcdchar.json - , tab : [ '', () => infoSetting( 'lcdchar gpio', util.lcdchar.gpio ) ] + , tab : [ '', () => infoSetting( 'lcdchar gpio', config.lcdchar ) ] , list : list , boxwidth : 180 , values : data.values - , checkchanged : S.lcdchar + , checkchanged : S.lcdchar && data.current === 'i2c' } ); } , json : { icon : 'lcdchar' , title : 'Character LCD' , tablabel : [ 'I²C', 'GPIO' ] - , footer : ico( 'raudio' ) +'Logo '+ ico( 'screenoff' ) +'Sleep' - , beforeshow : () => { - $( '#infoList label' ).parents( 'td' ).prop( 'colspan', 3 ); - $( '.infofooter i' ) - .toggleClass( 'disabled', ! S.lcdchar ) - .on( 'click', function() { - bash( [ 'lcdcharset', $( this ).index() ? 'off' : 'logo', 'CMD ACTION' ] ); - } ); + , beforeshow : () => $( '#infoList label' ).parents( 'td' ).prop( 'colspan', 3 ) + , cancel : switchCancel + , ok : () => { + jsonSave( 'lcdchar', infoVal() ); + switchEnable(); } - , cancel : switchCancel - , ok : switchEnable - , fileconf : true } , list : [ [ 'Type', 'hidden' ] diff --git a/srv/http/bash/bluealsa-dbus.py b/srv/http/bash/bluealsa-dbus.py index debb64951..c57267b56 100644 --- a/srv/http/bash/bluealsa-dbus.py +++ b/srv/http/bash/bluealsa-dbus.py @@ -14,8 +14,8 @@ from subprocess import Popen AGENT_INTERFACE = 'org.bluez.Agent1' -path = '/test/autoagent' -filesink = '/srv/http/data/shm/bluetoothsink' +path = '/test/autoagent' +filesink = '/srv/http/data/shm/bluetoothsink' def statusPush(): Popen( [ '/srv/http/bash/status-push.sh' ] ) @@ -41,11 +41,11 @@ def property_changed( interface, changed, invalidated, path ): if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop( set_as_default=True ) - bus = dbus.SystemBus() + bus = dbus.SystemBus() bus.add_signal_receiver( property_changed, bus_name='org.bluez', dbus_interface='org.freedesktop.DBus.Properties', signal_name='PropertiesChanged', path_keyword='path' ) mainloop = GLib.MainLoop() - obj = bus.get_object( 'org.bluez', '/org/bluez' ) + obj = bus.get_object( 'org.bluez', '/org/bluez' ) mainloop.run() diff --git a/srv/http/bash/cmd.sh b/srv/http/bash/cmd.sh index 4412b582f..c9f3e99f5 100644 --- a/srv/http/bash/cmd.sh +++ b/srv/http/bash/cmd.sh @@ -68,7 +68,6 @@ playerStart() { renice -n -19 -p $pid &> /dev/null done fi - pushData player '{ "player": "'$player'", "active": true }' } playerStop() { local player @@ -101,7 +100,6 @@ playerStop() { $dirbash/status-push.sh ;; esac - [[ $player != mpd ]] && pushData player '{ "player": "'$player'", "active": false }' } plClear() { mpc -q clear @@ -385,21 +383,10 @@ dirrename ) pushRadioList ;; display ) - pushData display $( < $dirsystem/display.json ) - # temp - if grep -q albumyear.*true $dirsystem/display.json && [[ ! -e $dirmpd/albumbyartist-year ]]; then - pushData mpdupdate '{ "type": "mpd" }' - $dirbash/cmd-list.sh &> /dev/null & - fi - [[ -e $dirsystem/vumeter ]] && prevvumeter=1 - grep -q -m1 vumeter.*true $dirsystem/display.json && touch $dirsystem/vumeter && vumeter=1 - [[ $prevvumeter == $vumeter ]] && exit -# -------------------------------------------------------------------- - if [[ $vumeter ]]; then - [[ ! -e $dirmpdconf/fifo.conf ]] && $dirsettings/player-conf.sh - else - rm -f $dirsystem/vumeter - fi + status=$( $dirbash/status.sh ) + pushData mpdplayer "$status" + systemctl try-restart radio + fifoToggle ;; equalizer ) freq=( 31 63 125 250 500 1 2 4 8 16 ) @@ -639,11 +626,15 @@ mpcshuffle ) pushPlaylist ;; mpcsimilar ) + readarray -t lines <<< $( mpc ls -f %artist%^%title% "$FILE" | tr ^ '\n' ) + artist=${lines[0]} + title=${lines[1]} + apikey=$( grep -E -m1 'apikeylastfm' /srv/http/assets/js/main.js | cut -d"'" -f2 ) lines=$( curl -sfG -m 5 \ - --data-urlencode "artist=$ARTIST" \ - --data-urlencode "track=$TITLE" \ + --data-urlencode "artist=$artist" \ + --data-urlencode "track=$title" \ --data "method=track.getsimilar" \ - --data "api_key=$APIKEY" \ + --data "api_key=$apikey" \ --data "format=json" \ --data "autocorrect=1" \ http://ws.audioscrobbler.com/2.0 \ diff --git a/srv/http/bash/common.sh b/srv/http/bash/common.sh index 9e1f05e9a..3e6213334 100644 --- a/srv/http/bash/common.sh +++ b/srv/http/bash/common.sh @@ -203,6 +203,37 @@ enableFlagSet() { exists() { [[ -e $1 ]] && echo true || echo false } +fifoToggle() { # mpdoled vuled vumeter + filefifo=$dirmpdconf/fifo.conf + [[ -e $dirsystem/mpdoled ]] && mpdoled=1 + [[ -e $dirsystem/vuled ]] && vuled=1 + if grep -q -m1 vumeter.*true $dirsystem/display.json; then + vumeter=1 + touch $dirsystem/vumeter + else + rm -f $dirsystem/vumeter + fi + if [[ $mpdoled || $vuled || $vumeter ]]; then + if [[ ! -e $filefifo ]]; then + ln -s $dirmpdconf/{conf/,}fifo.conf + systemctl restart mpd + [[ $vuled || $vumeter ]] && systemctl restart cava + fi + ! grep -q 'state="*play' $dirshm/status && return + + [[ $mpdoled ]] && systemctl restart mpd_oled + [[ $vuled || $vumeter ]] && systemctl restart cava + else + if [[ -e $filefifo ]]; then + [[ $mpdoled || $vuled || $vumeter ]] && return + + rm $filefifo + systemctl restart mpd + fi + [[ ! $mpdoled ]] && systemctl stop mpd_oled + [[ ! $vuled && ! $vumeter ]] && systemctl stop cava + fi +} getContent() { if [[ -e "$1" ]]; then cat "$1" @@ -216,13 +247,13 @@ getVar() { # var=value local data line var data=$( < $2 ) if [[ $( head -1 <<< $data ) == { ]]; then - var=$( sed -n -E '/'$1'/ {s/.*: "*|"*,*$//g; p}' <<< $data ) + var=$( sed -n -E '/'$1'/ {s/.*: "*|"*,*$//g; p}' <<< $data ) # var: value else line=$( grep ^$1= <<< $data ) # var= [[ ! $line ]] && line=$( grep -E "^${1// /|^}" <<< $data ) # var [[ ! $line ]] && line=$( grep -E "^\s*${1// /|^\s*}" <<< $data ) # var [[ $line != *=* ]] && line=$( sed 's/ \+/=/' <<< $line ) # var value > var=value - var=$( sed -E "s/.* *= *//; s/^[\"']|[\"'];*$//g" <<< $line ) # var=value || var = value || var="value"; > value + var=$( sed -E "s/.* *= *//; s/^[\"']|[\"'];*$//g" <<< $line ) # var=value || var = value || var="value"; > value fi [[ $var ]] && quoteEscape $var || echo $3 } @@ -264,6 +295,13 @@ lineCount() { line2array() { [[ $1 ]] && tr '\n' , <<< $1 | sed 's/^/[ "/; s/,$/" ]/; s/,/", "/g' || echo false } +logoLcdOled() { + [[ -e $dirsystem/lcdchar ]] && $dirbash/lcdchar.py logo + if [[ -e $dirsystem/mpdoled ]]; then + chip=$( cut -d' ' -f2 /etc/default/mpd_oled ) + mpd_oled -o $chip -x logo + fi +} mountpointSet() { umount -ql "$1" mkdir -p "$1" @@ -288,7 +326,11 @@ $2" fi } mpcElapsed() { - mpc status %currenttime% | awk -F: '{print ($1 * 60) + $2}' + if [[ $1 ]] && grep -q radioelapsed.*false $dirsystem/display.json; then # webradio + radioelapsed + echo false + else + mpc status %currenttime% | awk -F: '{print ($1 * 60) + $2}' + fi } mpcPlayback() { $dirbash/cmd.sh "mpcplayback @@ -373,7 +415,7 @@ quoteEscape() { } radioStatusFile() { local status - status=$( grep -vE '^Album|^Artist|^coverart|^elapsed|^state|^Title' $dirshm/status ) + status=$( grep -vE '^Album|^Artist|^coverart|^elapsed|^pllength|^state|^Title' $dirshm/status ) status+=' Artist="'$artist'" Album="'$album'" diff --git a/srv/http/bash/lcdchar.py b/srv/http/bash/lcdchar.py index ffdab4ad9..05b68ece8 100644 --- a/srv/http/bash/lcdchar.py +++ b/srv/http/bash/lcdchar.py @@ -1,34 +1,25 @@ #!/usr/bin/python import sys +import json -with open( '/srv/http/data/system/lcdchar.conf', 'r' ) as f: - conf = {} - for line in f: - kv = line.split( '=' ) - k = kv[ 0 ] - v = kv[ 1 ].rstrip() - if k == 'address' or k == 'cols': - v = int( v ) - elif k == 'backlight': - v = bool( v ) - conf[ k ] = v -locals().update( conf ) # inf, cols, charmap, address, chip, backlight - -rows = cols == 16 and 2 or 4 - -if inf == 'i2c': +with open( '/srv/http/data/system/lcdchar.json' ) as f: CONF = json.load( f ) +locals().update( CONF ) # INF, COLS, CHARMAP, BACKLIGHT, [ ADDRESS, CHIP | P* ... ] +rows = COLS == 16 and 2 or 4 +cmA00 = CHARMAP == 'A00' + +if INF == 'i2c': from RPLCD.i2c import CharLCD - lcd = CharLCD( cols=cols, rows=rows, charmap=charmap - , address=address, i2c_expander=chip ) + lcd = CharLCD( cols=COLS, rows=rows, charmap=CHARMAP + , address=ADDRESS, i2c_expander=CHIP ) else: - pins_data = [ p0, p1, p2, p3 ] from RPLCD.gpio import CharLCD from RPi import GPIO - lcd = CharLCD( cols=cols, rows=rows, charmap=charmap - , numbering_mode=GPIO.BOARD, pin_rs=pin_rs, pin_rw=pin_rw, pin_e=pin_e, pins_data=pins_data ) + GPIO.setwarnings( False ) + lcd = CharLCD( cols=COLS, rows=rows, charmap=CHARMAP + , numbering_mode=GPIO.BOARD, pin_rs=PIN_RS, pin_rw=PIN_RW, pin_e=PIN_E, pins_data=[ P0, P1, P2, P3 ] ) -pause = ( +pause = ( 0b00000, 0b11011, 0b11011, @@ -38,7 +29,7 @@ 0b00000, 0b00000, ) -play = ( +play = ( 0b10000, 0b11000, 0b11100, @@ -48,7 +39,7 @@ 0b10000, 0b00000, ) -stop = ( +stop = ( 0b00000, 0b11111, 0b11111, @@ -58,7 +49,7 @@ 0b00000, 0b00000, ) -logol = ( +logol = ( 0b11111, 0b11011, 0b11011, @@ -68,7 +59,7 @@ 0b11111, 0b11111, ) -logor = ( +logor = ( 0b01110, 0b10110, 0b10110, @@ -78,7 +69,7 @@ 0b11010, 0b11100, ) -dot = ( +dot = ( 0b00000, 0b00000, 0b00000, @@ -88,24 +79,24 @@ 0b00000, 0b00000, ) -char = [ pause, play, stop, logol, logor, dot ] +char = [ pause, play, stop, logol, logor, dot ] for i in range( 6 ): lcd.create_char( i, char[ i ] ) -ICON = { +ICON = { 'pause' : '\x00 ' , 'play' : '\x01 ' , 'stop' : '\x02 ' } -RA = '\x03\x04' -DOTS = '\x05 \x05 \x05' -RN = '\r\n' +RA = '\x03\x04' +DOTS = '\x05 \x05 \x05' +RN = '\r\n' -SPACES = ' ' * ( ( cols - 6 ) // 2 + 1 ) -LOGO = rows > 2 and RN or '' -LOGO += SPACES + RA + RN + SPACES +'rAudio' +SPACES = ' ' * ( ( COLS - 6 ) // 2 + 1 ) +LOGO = rows > 2 and RN or '' +LOGO += SPACES + RA + RN + SPACES +'rAudio' -argvL = len( sys.argv ) +argvL = len( sys.argv ) if argvL == 2: # 1 argument val = sys.argv[ 1 ] if val == 'off': # backlight off @@ -122,6 +113,12 @@ import math import time +if cmA00: + import unicodedata + def normalize( str ): + return ''.join( c for c in unicodedata.normalize( 'NFD', str ) + if unicodedata.category( c ) != 'Mn' ) + def backlightOff(): time.sleep( 60 ) lcd.backlight_enabled = False @@ -129,96 +126,73 @@ def backlightOff(): sys.exit() # -------------------------------------------------------------------- def second2hhmmss( sec ): - hh = math.floor( sec / 3600 ) - mm = math.floor( ( sec % 3600 ) / 60 ) - ss = sec % 60 - HH = hh > 0 and str( hh ) +':' or '' + hh = math.floor( sec / 3600 ) + mm = math.floor( ( sec % 3600 ) / 60 ) + ss = sec % 60 + HH = hh > 0 and str( hh ) +':' or '' mmt = str( mm ) - MM = hh > 0 and ( mm > 9 and mmt +':' or '0'+ mmt +':' ) or ( mm > 0 and mmt +':' or '' ) + MM = hh > 0 and ( mm > 9 and mmt +':' or '0'+ mmt +':' ) or ( mm > 0 and mmt +':' or '' ) sst = str( ss ) - SS = mm > 0 and ( ss > 9 and sst or '0'+ sst ) or sst + SS = mm > 0 and ( ss > 9 and sst or '0'+ sst ) or sst return HH + MM + SS -sys.path.append( '/srv/http/data/shm' ) -from lcdcharstatus import * -keys = [ 'Album', 'Artist', 'elapsed', 'file', 'station', 'Time', 'Title' ] -data = {} - -if charmap == 'A00': - import unicodedata - def normalize( str ): - return ''.join( c for c in unicodedata.normalize( 'NFD', str ) - if unicodedata.category( c ) != 'Mn' ) - for k in keys: - if k in locals(): - if k in [ 'elapsed', 'Time' ]: - data[ k ] = locals()[ k ] - else: - data[ k ] = normalize( locals()[ k ] ) - else: - data[ k ] = '' -else: - for k in keys: - data[ k ] = k in locals() and locals()[ k ] or '' - -Album = data[ 'Album' ][ :cols ] -Artist = data[ 'Artist' ][ :cols ] -file = data[ 'file' ][ :cols ] -station = data[ 'station' ][ :cols ] -Title = data[ 'Title' ][ :cols ] -elapsed = data[ 'elapsed' ] -Time = data[ 'Time' ] +with open( '/srv/http/data/shm/status.json' ) as f: STATUS = json.load( f ) +for k in [ 'Album', 'Artist', 'file', 'station', 'Title' ]: + if k in STATUS: + v = STATUS[ k ] or DOTS + if cmA00: STATUS[ k ] = normalize( v ) # character: accent with sequence code > single code + STATUS[ k ] = v[ :COLS ] # set width + else: + STATUS[ k ] = '' +locals().update( STATUS ) if webradio: if state != 'play': Artist = station - Album = file + Album = file else: if not Artist and not Title: Artist = station - if not Album: Album = station or file - + if not Album: Album = station or file + if not Artist: Artist = DOTS -if not Title: Title = DOTS -if not Album: Album = DOTS +if not Title: Title = DOTS +if not Album: Album = DOTS if rows == 2: - if state == 'play': - lines = Title - elif backlight: - backlightOff() + if state == 'play': lines = Title else: lines = Artist + RN + Title + RN + Album hhmmss = Time and second2hhmmss( round( float( Time ) ) ) or '' if state == 'stop': - progress = ( hhmmss + ' ' * cols )[ :cols - 4 ] + progress = ( hhmmss + ' ' * COLS )[ :COLS - 4 ] else: if elapsed is False: # can be 0 elapsedhhmmss = '' - slash = '' + slash = '' else: - elapsed = round( float( elapsed ) ) + elapsed = int( elapsed ) elapsedhhmmss = second2hhmmss( elapsed ) - slash = cols > 16 and ' / ' or '/' + slash = COLS > 16 and ' / ' or '/' if Time: hhmmss = slash + hhmmss - progress = ( elapsedhhmmss + hhmmss + ' ' * cols )[ :cols - 4 ] + progress = ( elapsedhhmmss + hhmmss + ' ' * COLS )[ :COLS - 4 ] lcd.write_string( lines + RN + ICON[ state ] + progress + RA ) -if backlight and state != 'play': backlightOff() +if BACKLIGHT and state != 'play': backlightOff() -if state != 'play': sys.exit() +if state != 'play' or elapsed is False: sys.exit() # -------------------------------------------------------------------- -row = rows - 1 +row = rows - 1 starttime = time.time() -elapsed += round( starttime - timestamp / 1000 ) -PLAY = ICON[ 'play' ] +elapsed += math.ceil( ( starttime * 1000 - timestamp ) / 1000000 ) +PLAY = ICON[ 'play' ] while True: - sl = 1 - ( ( time.time() - starttime ) % 1 ) + sl = 1 - ( ( time.time() - starttime ) % 1 ) lcd.cursor_pos = ( row, 0 ) - elapsedhhmmss = second2hhmmss( elapsed ) + elapsedhhmmss = second2hhmmss( elapsed ) lcd.write_string( PLAY + elapsedhhmmss + hhmmss ) - elapsed += 1 + elapsed += 1 time.sleep( sl ) \ No newline at end of file diff --git a/srv/http/bash/power.sh b/srv/http/bash/power.sh index 576573079..9377f7234 100644 --- a/srv/http/bash/power.sh +++ b/srv/http/bash/power.sh @@ -2,7 +2,6 @@ . /srv/http/bash/common.sh -[[ -e $dirshm/relayson ]] && $dirbash/relays.sh off if [[ $1 == reboot ]]; then reboot=1 audioCDplClear && $dirbash/status-push.sh @@ -12,11 +11,12 @@ else audioCDplClear pushData power '{ "type": "off" }' fi +$dirbash/cmd.sh playerstop +logoLcdOled +[[ -e $dirshm/relayson ]] && $dirbash/relays.sh off -playerActive upnp && $dirbash/cmd.sh playerstop - +ipserver=$( ipAddress ) if systemctl -q is-active nfs-server; then # server rAudio - ipserver=$( ipAddress ) ipclients=$( grep -v $ipserver $filesharedip ) if [[ $ipclients ]]; then [[ ! $2 ]] && echo -1 && exit # $2 confirm proceed @@ -26,17 +26,11 @@ if systemctl -q is-active nfs-server; then # server rAudio notify -ip $ip 'networks blink' 'Server rAudio' "$msg" done fi - sed -i "/$ipserver/ d" $filesharedip -elif [[ -e $filesharedip ]]; then - sed -i "/$( ipAddress )/ d" $filesharedip fi +[[ -e $filesharedip ]] && sed -i "/$ipserver/ d" $filesharedip [[ -e $dirshm/btreceiver ]] && cp $dirshm/btreceiver $dirsystem touch $dirshm/power -mpc -q stop -if [[ -e $dirsystem/lcdchar ]]; then - systemctl stop lcdchar - $dirbash/lcdchar.py logo -fi + snapclientIP playerstop cdda=$( mpc -f %file%^%position% playlist | grep ^cdda: | cut -d^ -f2 ) [[ $cdda ]] && mpc -q del $cdda @@ -47,7 +41,7 @@ if mount | grep -q -m1 $dirnas; then fi if [[ -d /sys/class/backlight/rpi_backlight ]]; then echo 1 > /sys/class/backlight/rpi_backlight/bl_power -else +elif [[ -e $dirsystem/localbrowser ]]; then DISPLAY=:0 xset dpms force off fi [[ -e /boot/shutdown.sh ]] && /boot/shutdown.sh diff --git a/srv/http/bash/relays-timer.sh b/srv/http/bash/relays-timer.sh index ea9c6c61a..52abca65c 100644 --- a/srv/http/bash/relays-timer.sh +++ b/srv/http/bash/relays-timer.sh @@ -8,18 +8,11 @@ echo $$ > $dirshm/pidrelaystimer timer=$( getVar timer $dirsystem/relays.conf ) i=$timer while sleep 60; do - if [[ -e $dirsystem/camilladsp ]]; then - running= - else - grep -q -m1 RUNNING /proc/asound/card*/pcm*p/sub*/status && running=1 || running= - fi - if grep -q -m1 '^state.*play' $dirshm/status || $running; then - i=$timer - else - (( i-- )) - case $i in - 1 ) pushData relays '{ "countdown": true }';; - 0 ) $dirbash/relays.sh off && break;; - esac - fi + grep -q -m1 ^state.*play $dirshm/status && i=$timer && continue + + (( i-- )) + case $i in + 1 ) pushData relays '{ "countdown": true }';; + 0 ) $dirbash/relays.sh off && break;; + esac done diff --git a/srv/http/bash/settings/camilla-data.sh b/srv/http/bash/settings/camilla-data.sh index b7546c3c2..3c361a2da 100644 --- a/srv/http/bash/settings/camilla-data.sh +++ b/srv/http/bash/settings/camilla-data.sh @@ -18,7 +18,7 @@ data=' , "devices" : '$( < $dirshm/hwparams )' , "player" : "'$( < $dirshm/player )'" , "pllength" : '$( mpc status %length% )' -, "state" : "'$( mpcState )'" +, "state" : "'$( getVar state $dirshm/status )'" , "volume" : '$( [[ $mixer ]] && volumeGet )' , "volumemax" : '$( volumeMaxGet )' , "volumemute" : '$( getContent $dirsystem/volumemute 0 ) diff --git a/srv/http/bash/settings/data-config.sh b/srv/http/bash/settings/data-config.sh index 3b3245294..579b2d9c1 100644 --- a/srv/http/bash/settings/data-config.sh +++ b/srv/http/bash/settings/data-config.sh @@ -3,10 +3,11 @@ . /srv/http/bash/common.sh toReboot() { - if [[ -e $dirshm/reboot ]]; then - grep -q $CMD <<< $dirshm/reboot && echo true || echo false + if [[ -s $dirshm/reboot ]]; then + grep -q $ID <<< $dirshm/reboot && echo true || echo false else echo false + rm -f $dirshm/reboot fi } @@ -56,23 +57,22 @@ i2slist ) cat /srv/http/assets/data/system-i2s.json ;; lcdchar ) - fileconf=$dirsystem/lcdchar.conf + fileconf=$dirsystem/lcdchar.json if [[ -e $fileconf ]]; then - grep -q ^p0 $fileconf && conf2json $fileconf && exit # gpio + values=$( < $fileconf ) + current=$( jq -r .INF $fileconf ) + [[ ! $2 && $current == gpio ]] && echo '{ "values": '$values', "current": "'$current'" }' && exit # -------------------------------------------------------------------- - values=$( conf2json $fileconf ) + fi + val='{ "INF": "gpio", "COLS": 20, "CHARMAP": "A00"' + if [[ $2 == gpio ]]; then + [[ $current != gpio ]] && values=$val', "P0": 21, "PIN_RS": 15, "P1": 22, "PIN_RW": 18, "P2": 23, "PIN_E": 16, "P3": 24' else - if [[ $2 ]]; then - echo '{ "INF": "gpio", "COLS": 20, "CHARMAP": "A00" - , "P0": 21, "PIN_RS": 15, "P1": 22, "PIN_RW": 18, "P2": 23, "PIN_E": 16, "P3": 24 - , "BACKLIGHT": false }' - exit -# -------------------------------------------------------------------- - fi - values='{ "INF": "i2c", "COLS": 20, "CHARMAP": "A00" - , "ADDRESS": 39, "CHIP": "PCF8574" - , "BACKLIGHT": false }' + [[ $current != i2c ]] && values=${val/gpio/i2c}', "ADDRESS": 39, "CHIP": "PCF8574"' fi + ! grep -q BACKLIGHT <<< $values && values+=', "BACKLIGHT": false }' + [[ $2 == gpio ]] && echo '{ "values": '$values', "current": "'$current'" }' && exit +# -------------------------------------------------------------------- dev=$( ls /dev/i2c* 2> /dev/null | cut -d- -f2 ) [[ $dev ]] && lines=$( i2cdetect -y $dev 2> /dev/null ) if [[ $lines ]]; then @@ -87,11 +87,7 @@ lcdchar ) else address=', "0x27": 39, "0x3f": 63' fi - echo '{ - "values" : '$values' -, "address" : { '${address:1}' } -, "reboot" : '$( toReboot )' -}' + echo '{ "values": '$values', "current": "'$current'", "address": { '${address:1}' } }' ;; localbrowser ) echo '{ @@ -100,17 +96,12 @@ localbrowser ) }' ;; mpdoled ) - chip=$( grep mpd_oled /etc/systemd/system/mpd_oled.service | cut -d' ' -f3 ) - baud=$( grep baudrate /boot/config.txt | cut -d= -f3 ) + opt=$( < /etc/default/mpd_oled ) + chip=$( cut -d' ' -f2 <<< $opt ) + spectrum=$( grep -q '\-X' <<< $opt && echo true || echo false ) + baud=$( sed -n '/baudrate/ {s/.*=//; p}' /boot/config.txt ) [[ ! $baud ]] && baud=800000 - echo '{ - "values" : { "CHIP": "'$chip'", "BAUD": '$baud' } -, "reboot" : '$( toReboot )' -}' - ;; -reboot ) - getContent $dirshm/reboot - rm -f $dirshm/{reboot,backup.gz} + echo '{ "CHIP": "'$chip'", "BAUD": '$baud', "SPECTRUM": '$spectrum' }' ;; packagelist ) filepackages=/tmp/packages @@ -139,6 +130,10 @@ $description fi grep -B1 -A2 --no-group-separator ^${2,} $filepackages ;; +reboot ) + getContent $dirshm/reboot + rm -f $dirshm/{reboot,backup.gz} + ;; relays ) if [[ -e $dirsystem/relays.conf ]]; then . $dirsystem/relays.conf @@ -253,10 +248,7 @@ spotifyoutput ) ;; tft ) model=$( sed -n -E '/rotate=/ {s/dtoverlay=(.*):rotate.*/\1/; p}' /boot/config.txt ) - echo '{ - "values" : { "MODEL": "'$( [[ $model ]] && echo $model || echo tft35a )'" } -, "reboot" : '$( toReboot )' -}' + echo '{ "MODEL": "'$( [[ $model ]] && echo $model || echo tft35a )'" }' ;; wlan ) echo '{ diff --git a/srv/http/bash/settings/features.sh b/srv/http/bash/settings/features.sh index 6047915da..8de699660 100644 --- a/srv/http/bash/settings/features.sh +++ b/srv/http/bash/settings/features.sh @@ -289,15 +289,13 @@ screentoggle ) xset q | grep 'Monitor is' ;; scrobblekey ) - keys=( $( grep -E -m2 'apikeylastfm|sharedsecret' /srv/http/assets/js/main.js | cut -d"'" -f2 ) ) - apikey=${keys[0]} - sharedsecret=${keys[1]} - apisig=$( echo -n "api_key${apikey}methodauth.getSessiontoken${TOKEN}${sharedsecret}" \ + . <( grep -E -m2 'apikeylastfm|sharedsecret' /srv/http/assets/js/main.js | sed 's/.*, //; s/ *: /=/' ) + apisig=$( echo -n "api_key${apikeylastfm}methodauth.getSessiontoken${TOKEN}${sharedsecret}" \ | md5sum \ | cut -c1-32 ) response=$( curl -sX POST \ --data "method=auth.getSession" \ - --data "api_key=$apikey" \ + --data "api_key=$apikeylastfm" \ --data "token=$TOKEN" \ --data "api_sig=$apisig" \ --data "format=json" \ @@ -305,7 +303,7 @@ scrobblekey ) [[ $response =~ error ]] && jq -r .message <<< $response && exit # -------------------------------------------------------------------- echo "\ -apikey=$apikey +apikey=$apikeylastfm sharedsecret=$sharedsecret sk=$( jq -r .session.key <<< $response ) " > $dirsystem/scrobblekey diff --git a/srv/http/bash/settings/system.sh b/srv/http/bash/settings/system.sh index 7387d286c..de1cec4d6 100644 --- a/srv/http/bash/settings/system.sh +++ b/srv/http/bash/settings/system.sh @@ -67,9 +67,9 @@ dtoverlay=gpio-shutdown,gpio_pin=17,active_low=0,gpio_pull=down" fi fi if [[ $reboot ]]; then + pushData reboot '{ "id": "'$CMD'" }' appendSortUnique $CMD $dirshm/reboot - pushData reboot '{ "id": '$( line2array $( < $dirshm/reboot ) )' }' - else + elif [[ -e $dirshm/reboot ]]; then sed -i "/$CMD/ d" $dirshm/reboot fi } @@ -200,11 +200,12 @@ dtparam=audio=on" configTxt ;; lcdchar ) - enableFlagSet - i2cset=1 - configTxt - ;; -lcdcharset ) + if [[ ! $ACTION ]]; then + enableFlagSet + i2cset=1 + configTxt + ACTION=logo + fi systemctl stop lcdchar $dirbash/lcdchar.py $ACTION ;; @@ -241,30 +242,25 @@ mountunmount ) fi pushRefresh ;; -mpdoledlogo ) - systemctl stop mpd_oled - type=$( grep mpd_oled /etc/systemd/system/mpd_oled.service | cut -d' ' -f3 ) - mpd_oled -o $type -L - ;; mpdoled ) enableFlagSet if [[ $ON ]]; then - if [[ $( grep mpd_oled /etc/systemd/system/mpd_oled.service | cut -d' ' -f3 ) != $CHIP ]]; then - sed -i 's/-o ./-o '$CHIP'/' /etc/systemd/system/mpd_oled.service - systemctl daemon-reload - fi - else - $dirsettings/player-conf.sh + opt=$( sed 's/ -X//' /etc/default/mpd_oled ) + chip=$( cut -d' ' -f2 <<< $opt ) + baud=$( sed -n '/baudrate/ {s/.*=//; p}' /boot/config.txt ) + [[ $chip != $CHIP ]] && opt=$( sed 's/-o ./-o '$CHIP'/' <<< $opt ) + [[ $SPECTRUM ]] && opt=$( sed 's/"$/ -X"/' <<< $opt ) + [[ $baud != $BAUD ]] && sed -i -E 's/(baudrate=).*/\1'$BAUD'/' /boot/config.txt + echo "$opt" > /etc/default/mpd_oled fi + fifoToggle i2cset=1 configTxt - [[ -e $dirsystem/mpdoled && ! -e $dirshm/reboot && ! -e $dirmpdconf/fifo.conf ]] && $dirsettings/player-conf.sh ;; ntp ) - file=/etc/systemd/timesyncd.conf echo "\ [Time] -NTP=$NTP" > $file +NTP=$NTP" > /etc/systemd/timesyncd.conf timedatectl set-ntp true pushRefresh ;; @@ -427,18 +423,7 @@ usbconnect | usbremove ) # for /etc/conf.d/devmon - devmon@http.service ;; vuled ) enableFlagSet - pins=$( cut -d= -f2 $dirsystem/vuled.conf ) - if [[ $ON ]]; then - [[ ! -e $dirmpdconf/fifo.conf ]] && $dirsettings/player-conf.sh - grep -q 'state="*play' $dirshm/status && systemctl start cava - else - if [[ -e $dirsystem/vumeter ]]; then - systemctl restart cava - else - systemctl stop cava - $dirsettings/player-conf.sh - fi - fi + fifoToggle pushRefresh ;; wlan ) diff --git a/srv/http/bash/startup.sh b/srv/http/bash/startup.sh index aefef08ae..2dae212f1 100644 --- a/srv/http/bash/startup.sh +++ b/srv/http/bash/startup.sh @@ -57,9 +57,7 @@ CMD ESSID" fi # pre-configure <<<----------------------------------------------------------- -[[ -e $dirsystem/lcdchar ]] && $dirbash/lcdchar.py logo - -[[ -e $dirsystem/mpdoled ]] && $dirsettings/system.sh mpdoledlogo +logoLcdOled [[ -e $dirsystem/soundprofile ]] && $dirsettings/system.sh soundprofileset @@ -72,7 +70,6 @@ fi mkdir -p $dirshm/{airplay,embedded,spotify,local,online,sampling,webradio} chmod -R 777 $dirshm chown -R http:http $dirshm -echo 'state="stop"' > $dirshm/status echo mpd > $dirshm/player lsmod | grep -q -m1 brcmfmac && touch $dirshm/onboardwlan # initial status diff --git a/srv/http/bash/status-coverartupnp.py b/srv/http/bash/status-coverartupnp.py index e1f50f5f8..d9954a80d 100644 --- a/srv/http/bash/status-coverartupnp.py +++ b/srv/http/bash/status-coverartupnp.py @@ -4,9 +4,9 @@ import upnpp device = socket.gethostname() +'-UPnP/AV' -srv = upnpp.findTypedService( device, 'avtransport', True ) # AVTransport service +srv = upnpp.findTypedService( device, 'avtransport', True ) # AVTransport service if srv: - retdata = upnpp.runaction( srv, 'GetMediaInfo', ['0'] ) + retdata = upnpp.runaction( srv, 'GetMediaInfo', ['0'] ) metadata = retdata[ 'CurrentURIMetaData' ] if metadata: dirc = upnpp.UPnPDirContent() diff --git a/srv/http/bash/status-push.sh b/srv/http/bash/status-push.sh index c417f3ee3..ca050b998 100644 --- a/srv/http/bash/status-push.sh +++ b/srv/http/bash/status-push.sh @@ -7,12 +7,16 @@ killProcess statuspush echo $$ > $dirshm/pidstatuspush -if [[ $1 == statusradio ]]; then # from status-radio.sh +if [[ $1 == statusradio ]]; then # from status-radio.sh radioStatusFile state=play + statusradio=1 else - status=$( $dirbash/status.sh ) - statusnew=$( sed -E -n '/^, "Artist|^, "Album|^, "Composer|^, "elapsed|^, "file| *"player|^, "station"|^, "state|^, "Time|^, "timestamp|^, "Title|^, "webradio"/ {s/^,* *"//; s/" *: */=/; p}' <<< $status ) - echo "$statusnew" > $dirshm/statusnew + status=$( $dirbash/status.sh | jq ) + for k in Artist Album Composer Conductor elapsed file player station state Time timestamp Title volume webradio; do + filter+='|^ "'$k'"' + done + statuslines=$( grep -E "${filter:1}" <<< $status ) + statusnew=$( sed -E 's/^ *"|,$//g; s/" *: */=/' <<< $statuslines | tee $dirshm/statusnew ) statusprev=$( < $dirshm/status ) compare='^Artist|^Title|^Album' [[ "$( grep -E "$compare" <<< $statusnew | sort )" != "$( grep -E "$compare" <<< $statusprev | sort )" ]] && trackchanged=1 @@ -51,7 +55,7 @@ if [[ $clientip ]]; then done fi if [[ -e $dirsystem/lcdchar ]]; then - sed -E 's/(true|false)$/\u\1/' $dirshm/status > $dirshm/lcdcharstatus.py + [[ ! $statusradio ]] && jq <<< "{ ${statuslines%,} }" > $dirshm/status.json # remove trailing , systemctl restart lcdchar fi diff --git a/srv/http/bash/status-radio.sh b/srv/http/bash/status-radio.sh index 9a2456616..89644fd7f 100644 --- a/srv/http/bash/status-radio.sh +++ b/srv/http/bash/status-radio.sh @@ -112,8 +112,6 @@ $( jq -r .albumTitle <<< $track )" title=$( quoteEscape ${metadata[1]} ) album=$( quoteEscape ${metadata[2]} ) coverurl=${metadata[3]} - jq .steps[$item] <<< $json - echo "$artist - $title - $album - $coverurl" if [[ ! $title || "$artist $title $album" == $dataprev ]]; then sleep 5 @@ -134,14 +132,12 @@ $( jq -r .albumTitle <<< $track )" fi fi [[ -e $coverfile ]] && coverart=${coverfile:9} || coverart= - elapsed=$( mpcElapsed ) - pllength=$( mpc status %length% ) data=' "player" : "mpd" , "Album" : "'$album'" , "Artist" : "'$artist'" -, "elapsed" : '$elapsed' -, "pllength" : '$pllength' +, "elapsed" : '$( mpcElapsed webradio )' +, "pllength" : '$( mpc status %length% )' , "state" : "play" , "Title" : "'$title'"' if [[ $coverart ]]; then @@ -155,6 +151,13 @@ webradio CMD ARTIST ALBUM MODE" &> /dev/null & fi pushData mpdradio "{ $data }" + if [[ -e $dirsystem/lcdchar ]]; then + data+=' +, "Time" : false +, "timestamp" : '$( date +%s%3N )' +, "webradio" : true' + echo "{ $data }" > $dirshm/status.json + fi [[ -e $dirsystem/scrobble ]] && cp -f $dirshm/status{,prev} radioStatusFile [[ $coverart ]] && $dirbash/cmd.sh coverfileslimit diff --git a/srv/http/bash/status.sh b/srv/http/bash/status.sh index a3c6f3f0f..9991b69ae 100644 --- a/srv/http/bash/status.sh +++ b/srv/http/bash/status.sh @@ -85,7 +85,7 @@ else , "btreceiver" : '$( exists $dirshm/btreceiver )' , "card" : '$card' , "control" : "'$mixer'" -, "counts" : '$( getContent $dirmpd/counts '{}' )' +, "counts" : '$( getContent $dirmpd/counts '{}' )' , "icon" : "'$icon'" , "librandom" : '$( exists $dirsystem/librandom )' , "lyrics" : '$( exists $dirsystem/lyrics )' @@ -279,6 +279,7 @@ elif [[ $stream ]]; then [[ $onlinefile ]] && coverart="${onlinefile:9}" fi else + webradio=1 ext=Radio if [[ $file == *rtsp://*$( hostname -f )* ]]; then ext=DAB @@ -366,7 +367,7 @@ elif [[ $stream ]]; then , "Title" : "'$Title'" , "webradio" : true' if [[ $radio_dab ]]; then # rp / rf / dab - elapsed=$( mpcElapsed ) + elapsed=$( mpcElapsed $webradio ) ######## status+=' , "coverart" : "'$coverart'" @@ -496,7 +497,7 @@ status+=' , "sampling" : "'$sampling'"' if [[ $coverart || ! $displaycover ]]; then # webradio $coverart exists - elapsed=$( mpcElapsed ) + elapsed=$( mpcElapsed $webradio ) # >>>>>>>>>> webradio with found coverart ######## status+=' @@ -515,7 +516,7 @@ $Album $filenoesc CMD ARTIST ALBUM FILE" ) fi -elapsed=$( mpcElapsed ) +elapsed=$( mpcElapsed $webradio ) ######## status+=' , "elapsed" : '$elapsed' diff --git a/srv/http/library.php b/srv/http/library.php index 5d1a1f945..86a11b82c 100644 --- a/srv/http/library.php +++ b/srv/http/library.php @@ -622,8 +622,7 @@ function htmlTrack() { // track list - no sort ($string: cuefile or search) } else { $datamode = ' data-mode="'.$GMODE.'"'; $icon = i( 'music', 'file' ); - $trackname = $cue ? basename( $file0 ).' : ' : ''; - $trackname.= basename( $path ); + $trackname = $cue ? $artist.' - '.$album : basename( $path ); } $track1 = ( $i || $search || $hidecover ) ? '' : ' class="track1"'; $i++; diff --git a/srv/http/playlist.php b/srv/http/playlist.php index 4f30a3726..e4f02aa05 100644 --- a/srv/http/playlist.php +++ b/srv/http/playlist.php @@ -72,6 +72,17 @@ function output() { //---------------------------------------------------------------------------------- } +function artistAlbum( $artist, $album, $file ) { + $ar_al = ''; + if ( $artist || $album ) { + if ( $artist ) $ar_al.= $artist; + if ( $artist && $album ) $ar_al.= ' - '; + if ( $album ) $ar_al.= $album; + return $ar_al; + } else { + return $file; + } +} $f = [ 'album', 'albumartist', 'artist', 'file', 'time', 'title', 'track' ]; $fL = count( $f ); $format = '%'.implode( '%^^%', $f ).'%'; @@ -93,26 +104,8 @@ function output() { $pos++; $v = explode( '^^', $list ); for ( $i = 0; $i < $fL; $i++ ) ${$f[ $i ]} = $v[ $i ]; - $header = strtolower( substr( $file, 0, 4 ) ); - if ( ! in_array( $header, [ 'http', 'rtmp', 'rtp:', 'rtsp' ] ) ) { + if ( in_array( $file[ 0 ], [ 'U', 'N', 'S' ] ) ) { // USB, NAS, SD $sec = HMS2second( $time ); - $li2 = ''; - if ( $track ) { - $track = preg_replace( '/^#*0*/', '', $track ); - $li2 .= ''.$track.' - '; - } - $artist = $artist ?: $albumartist; - $album = $album; - if ( $artist ) $li2.= ''.$artist.' - '; - if ( $album ) $li2.= ''.$album.''; - if ( ! $artist && ! $album ) $li2.= $file; - $datatrack = ''; - if ( strpos( $file, '.cue/track' ) ) { - $datatrack = 'data-track="'.$track.'"'; // for cue in edit - $file = substr_replace( $file , '.cue', strrpos( $file , '.' ) ); - } - $title = $title ?: pathinfo( $file, PATHINFO_FILENAME ); - $ext = ''; if ( substr( $file, 0, 4 ) === 'cdda' ) { $discid = file( '/srv/http/data/shm/audiocd', FILE_IGNORE_NEW_LINES )[ 0 ]; $cdfile = '/srv/http/data/audiocd/'.$discid; @@ -127,79 +120,84 @@ function output() { $album = $audiocd[ 1 ]; $title = $audiocd[ 2 ]; $time = second2HMS( $audiocd[ 3 ] ); - $track = $track; - $li2 = ''.$track.' - '.$artist.' - '.$album.''; } $class = 'audiocd'; $datatrack = 'data-discid="'.$discid.'"'; // for cd tag editor $thumbsrc = '/data/audiocd/'.$discid.'.jpg'; $icon = imgIcon( $thumbsrc, 'filesavedpl', 'audiocd' ); } else { + if ( $track ) $track = preg_replace( '/^#*0*/', '', $track ); + if ( ! $artist ) $artist = $albumartist; + $datatrack = ''; + if ( strpos( $file, '.cue/track' ) ) { + $datatrack = 'data-track="'.$track.'"'; // for cue in edit + $file = substr_replace( $file , '.cue', strrpos( $file , '.' ) ); + } + $title = $title ?: pathinfo( $file, PATHINFO_FILENAME ); $class = 'file'; $discid = ''; $path = pathinfo( $file, PATHINFO_DIRNAME ); $thumbsrc = '/mnt/MPD/'.rawurlencode( $path ).'/thumb.jpg'; // replaced with icon on load error(faster than existing check) $icon = imgIcon( $thumbsrc, 'filesavedpl', 'music' ); } + $li2 = $pos.' • '.$track.' - '.artistAlbum( $artist, $album, $file ); $html .= '
  • '. ''.$file.''. - $icon.''. - ''. + $icon. + ''. + '
    '.$li2.'
    '. '
  • '; $count->song++; $count->time += $sec; - } else if ( substr( $file, 0, 14 ) === 'http://192.168' ) { - $li2 = ''; - $artist = $artist; - $album = $album; - if ( $artist ) $li2.= ''.$artist.' - '; - if ( $album ) $li2.= ''.$album.''; - if ( ! $artist && ! $album ) $li2.= $file; - $html .= + continue; + } + + if ( substr( $file, 0, 14 ) === 'http://192.168' ) { // upnp + $li2 = $pos.' • '.artistAlbum( $artist, $album, $file ); + $html .= '
  • '. i( 'upnp', 'filesavedpl' ). - ''. - ''. + ''. + '
    '.$li2.'
    '. '
  • '; $count->upnp++; + continue; + } + // webradio / dabradio + if ( str_contains( $file, '://' ) ) { + $urlname = str_replace( '/', '|', $file ); + $radio = str_contains( $file, ':8554' ) ? 'dabradio' : 'webradio'; + $fileradio = '/srv/http/data/'.$radio.'/'.$urlname; + if ( ! file_exists( $fileradio ) ) $fileradio = exec( 'find /srv/http/data/'.$radio.'/ -name "'.$urlname.'" | head -1' ); + $station = $fileradio ? exec( 'head -1 "'.$fileradio.'"' ) : ''; } else { - if ( str_contains( $file, '://' ) ) { // webradio / dabradio - $urlname = str_replace( '/', '|', $file ); - $radio = str_contains( $file, ':8554' ) ? 'dabradio' : 'webradio'; - $fileradio = '/srv/http/data/'.$radio.'/'.$urlname; - if ( ! file_exists( $fileradio ) ) $fileradio = exec( 'find /srv/http/data/'.$radio.'/ -name "'.$urlname.'" | head -1' ); - $stationname = $fileradio ? exec( 'head -1 "'.$fileradio.'"' ) : ''; - } else { - $urlname = str_replace( '#', '%23', $urlname ); - $stationname = ''; - } - if ( $stationname !== '' ) { - $notsaved = 0; - $thumbsrc = '/data/'.$radio.'/img/'.$urlname.'-thumb.jpg'; - $icon = imgIcon( $thumbsrc, 'filesavedpl', $radio ); - } else { - $notsaved = 1; - $icon = i( 'save savewr' ).i( 'webradio', 'filesavedpl' ); - } - $classnotsaved = $notsaved ? ' notsaved' : ''; - $namenotsaved = $notsaved ? '' : $stationname.' • '; - $url = preg_replace( '/#charset=.*/', '', $file ); - $path = preg_replace( '/\?.*$/', '', $file ); - $html .= -'
  • '. - ''.$path.''. - $icon.''.$stationname.''. - ''. + $urlname = str_replace( '#', '%23', $urlname ); + $station = ''; + } + $li2 = $pos.''; + if ( $station !== '' ) { + $notsaved = ''; + $li2 .= $station; + $thumbsrc = '/data/'.$radio.'/img/'.$urlname.'-thumb.jpg'; + $icon = imgIcon( $thumbsrc, 'filesavedpl', $radio ); + } else { + $notsaved = ' notsaved'; + $icon = i( 'save savewr' ).i( 'webradio', 'filesavedpl' ); + $station = '. . .'; + } + $li2 .= ''.preg_replace( '/#charset=.*/', '', $file ).''; + $html .= +'
  • '. + ''.preg_replace( '/\?.*$/', '', $file ).''. + $icon. + ''. + '
    '.$li2.'
    '. '
  • '; - $count->radio++; - } + $count->radio++; } $counthtml = ''; if ( $name ) { diff --git a/srv/http/settings/camilla.php b/srv/http/settings/camilla.php index 9f96f4337..e527aec2d 100644 --- a/srv/http/settings/camilla.php +++ b/srv/http/settings/camilla.php @@ -114,7 +114,7 @@ $head = [ 'title' => 'Status' , 'status' => 'camilladsp' - , 'button' => [ 'mpd icon', 'play playback' ] + , 'button' => [ 'mpd player', 'play playback' ] , 'help' => <<< EOF $B->play$B->pause$B->stop Playback control diff --git a/srv/http/settings/system.php b/srv/http/settings/system.php index e6a0e828e..c1134aeab 100644 --- a/srv/http/settings/system.php +++ b/srv/http/settings/system.php @@ -10,11 +10,13 @@ commonVariables( [ 'buttons' => [ 'add', 'gear', 'microsd', 'networks', 'power', 'refresh', 'rserver', 'usbdrive' ] , 'labels' => [ - 'Bluetooth' => 'bluetooth' + 'Airplay' => 'airplay' + , 'Bluetooth' => 'bluetooth' , 'Device' => '' , 'Output' => '' , 'Server rAudio' => 'rserver' , 'Shared Data' => 'networks' + , 'Spotify' => 'spotify' , 'Storage' => '' ] , 'menus' => [ @@ -202,7 +204,11 @@ 'id' => 'mpdoled' , 'label' => 'Spectrum OLED' , 'sub' => 'mpd_oled' - , 'help' => 'OLED module - display audio level spectrum' + , 'help' => <<OLED module - display audio level spectrum + +Note: Not yet support $L->airplay $L->spotify +EOF ] , [ 'id' => 'tft'