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'