This commit is contained in:
e2002
2025-07-11 12:26:04 +03:00
parent f60432a605
commit 297c0c20f5
31 changed files with 240 additions and 532 deletions

View File

@@ -234,6 +234,20 @@ Work is in progress...
--- ---
## Version history ## Version history
### 0.9.511
**!!! a [full update](#update-over-web-interface) with Sketch data upload is required. After updating please press CTRL+F5 in browser !!!**
In this version, the contents of the data/www directory have changed, so that the first time you flash it, you will be greeted by WEB Board Uploader. Just upload all the files from data/www (11 pcs) to it
- fixed a bug with saving smartstart mode
- fixed a bug with no restart when initially uploading files to spiffs
- fixed a bug with hanging on unavailable hosts
- fixed a bug with attempting to connect with an empty playlist
- fixed a bug with passing strings with quotes in mqtt
- fixing some other bugs
- rewritten the web interface (almost), added bugs 👍
- added listening to links in the browser in playlistEditor-e
- added reboot format and reset buttons to the settings
- the beginnings of theming (theme.css)
### 0.9.434 ### 0.9.434
- fixed the issue with exiting Screensaver Blank Screen mode via button presses and IR commands. - fixed the issue with exiting Screensaver Blank Screen mode via button presses and IR commands.
- reduced the minimum frequency for tone control on I2S modules to 80Hz. - reduced the minimum frequency for tone control on I2S modules to 80Hz.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -1,107 +0,0 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=0.25">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<link rel="apple-touch-icon" type="image/png" href="elogo.png">
<link rel="icon" type="image/png" href="elogo.png">
<link rel="stylesheet" title="base" href="style.css?%VERSION%" type="text/css">
<title>ёRadio - Player</title>
<style> </style>
</head>
<body class="modeweb">
<div class="content">
<div class="logo"></div>
<div id="navbar">
<div class="playerbytton navbutton" id="playlistbutton" data-name="playlist"></div>
<div class="playerbytton navbutton" id="sdbutton" data-name="sdcard"></div>
<div class="playerbytton navbutton" id="settingsbutton" data-name="settings"></div>
</div>
<div class="playerwrap">
<div class="player">
<div id="nameset">&nbsp;</div>
<div id="meta">&nbsp;</div>
<div class="playerbyttonwrap">
<div class="playerbytton" id="prevbutton" data-name="prev"></div>
<div class="playerbytton stopped" id="playbutton" data-name="play"></div>
<div class="playerbytton" id="nextbutton" data-name="next"></div>
<div class="playerbytton" id="volmbutton" data-name="volm"></div>
<div class="playerbytton" id="volpbutton" data-name="volp"></div>
<div class="playerbytton" id="eqalbutton" data-name="equalizer"></div>
</div>
<div id="equalizerwrap">
<div id="equalizerbg" class="hidden">
<ul id="equalizer">
<li>
<li>
balance<span class="eqinfo" id="eqbalinfo">0</span>
<input type="range" id="eqbal" class="slider" data-slaveid="eqbalinfo" name="balance" min="-16" max="16" value="0">
</li>
<li>
<li>
treble<span class="eqinfo" id="eqtrebleinfo">0</span>
<input type="range" id="eqtreble" class="slider" data-slaveid="eqtrebleinfo" name="treble" min="-16" max="16" value="0">
</li>
<li>
middle<span class="eqinfo" id="eqmiddleinfo">0</span>
<input type="range" id="eqmiddle" class="slider" data-slaveid="eqmiddleinfo" name="middle" min="-16" max="16" value="0">
</li>
<li>
bass<span class="eqinfo" id="eqbassinfo">0</span>
<input type="range" id="eqbass" class="slider" data-slaveid="eqbassinfo" name="bass" min="-16" max="16" value="0">
</li>
<li class="formbuttons">
<div class="button" id="accept_button" data-name="equalizer">Acceptable...</div>
</li>
</ul>
</div>
<div id="volnav">
<div id="modeline"><span id="modeweb" class="modeitem" data-name="web">Web</span><span id="modesd" data-name="sd" class="modeitem">SD</span></div>
<input type="range" id="volrange" class="slider" name="volume" data-slaveid="volinfo" min="0" max="254" value="0">
</div>
<div id="sdnav" class="hidden">
<div id="sdposvals">
<div id="sdposvalscurrent">0:00</div>
<div id="snuffle" class="playerbytton active" data-name="snuffle"><span></span></div>
<div id="sdposvalsend">0:00</div>
</div>
<input type="range" id="sdpos" class="slider" name="sdpos" min="0" max="100" value="0">
</div>
<div class="infowrap">
<div class="infoitem">volume: <span id="volinfo">0</span></div>
<div class="infoitem" id="bitinfo">bitrate: 0kBit</div>
<div class="infoitem" id="rsiinfo">rssi: 0dBm</div>
</div>
<ul id="playlist">
</ul>
</div><!--equalizerwrap-->
</div><!--player-->
<div id="pleditorwrap" class="hidden">
<div id="pleditor">
<h2>Playlist Editor<span onclick="showEditor()"></span></h2>
<div id="pleheader"><span class="space"><input type="checkbox" onclick="selectAll(this)" /></span><span class="plename">Name</span><span class="pleurl">URL</span><span class="pleovol">Ovol</span></div>
<ol id="pleditorcontent">
<li class="pleitem">
<span>1.</span>
<input class="pleinput plename" type="text" value="" maxlength="140" />
<input class="pleinput pleurl" type="text" value="" maxlength="140" />
<input class="pleinput pleovol" type="number" min="-30" max="30" step="1" value="0" />
</li>
</ol><!--pleditorcontent-->
<div class="formbuttons">
<label for="file-upload" class="button" data-name="plimport">Import</label><input id="file-upload" type="file" accept=".txt, .csv" onchange="doPlUpload(this)" hidden/>
<div class="button" data-name="plexport">Export</div>
<div class="button" data-name="pladd">Add</div>
<div class="button" data-name="pldel">Remove</div>
<div class="button" data-name="plsubmit">Save</div>
</div>
</div><!--pleditor-->
</div><!--pleditorwrap-->
</div><!--playerwrap-->
<div id="copy">powered by <a target="_blank" href="https://github.com/e2002/yoradio/">ёRadio</a> | v%VERSION%</div>
</div>
<script src="script.js?%VERSION%"></script>
<script src="dragpl.js?%VERSION%"></script>
</body>
</html>

Binary file not shown.

View File

@@ -1,49 +0,0 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, minimum-scale=0.25">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<link rel="apple-touch-icon" type="image/png" href="elogo.png">
<link rel="icon" type="image/png" href="elogo.png">
<link rel="stylesheet" title="base" href="style.css?%VERSION%" type="text/css">
<link rel="stylesheet" title="base" href="ir.css?%VERSION%" type="text/css">
<title>ёRadio - IR Recorder</title>
<style> </style>
</head>
<body>
<div class="content">
<h2 class="irtitle">IR Recorder</h2>
<div class="playerwrap">
<div class="irwrap">
<div id="irremote">
<div class="irblank"></div><div class="irbutton blue">&#9650;</div><div class="irblank"></div>
<div class="irbutton blue">&#9194;</div><div class="irbutton red">&#9199;</div><div class="irbutton blue">&#9193;</div>
<div class="irblank"></div><div class="irbutton blue">&#9660;</div><div class="irblank"></div>
<div class="irbutton">1</div><div class="irbutton">2</div><div class="irbutton">3</div>
<div class="irbutton">4</div><div class="irbutton">5</div><div class="irbutton">6</div>
<div class="irbutton">7</div><div class="irbutton">8</div><div class="irbutton">9</div>
<div class="irbutton red">*</div><div class="irbutton">0</div><div class="irbutton red">#</div>
</div><!--irremote-->
<div id="irform">
<div id="irstartrecord"><h3>Welcome to IR Recorder!</h3>
Press the button on the left<br />to record the code.
<a href="/" class="button" id="done_ir">DONE</a>
</div>
<div id="irrecord" class="hidden">
<h3 id="irrecordtitle"></h3>
<form id="irrecordfields">
<div class="irrecordrow"><div class="irradio" data-id="0"><span></span></div><div class="irrecordvalue">0xAF00B5</div><div class="irclear" onclick="irClear(this)"></div></div>
<div class="irrecordrow"><div class="irradio" data-id="1"><span></span></div><div class="irrecordvalue"></div><div class="irclear" onclick="irClear(this)"></div></div>
<div class="irrecordrow"><div class="irradio" data-id="2"><span></span></div><div class="irrecordvalue"></div><div class="irclear" onclick="irClear(this)"></div></div>
</form>
<div id="protocol"></div>
<div class="button" id="back_ir" onclick="backRecord()">Back</div>
</div>
</div><!--irform-->
</div><!--irwrap-->
</div><!--playerwrap-->
<div id="copy">powered by <a target="_blank" href="https://github.com/e2002/yoradio/">ёRadio</a> | v%VERSION%</div>
</div>
<script src="ir.js?%VERSION%"></script>
</body>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,261 +0,0 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=0.25">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<link rel="apple-touch-icon" type="image/png" href="elogo.png">
<link rel="icon" type="image/png" href="elogo.png">
<link rel="stylesheet" title="base" href="style.css?%VERSION%" type="text/css">
<link rel="stylesheet" title="base" href="settings.css?%VERSION%" type="text/css">
<title>ёRadio - Settings</title>
<style> </style>
</head>
<body>
<div class="content">
<h2 class="pagetitle">SёTTINGS</h2>
<div class="navigation group group_system hidden" id="navigation">
<div class="navitem group group_system hidden" data-target="group_system">system</div>
<div class="navitem group group_display group_nextion hidden" data-target="group_display">screen</div>
<div class="navitem group group_controls hidden" data-target="group_controls">controls</div>
<div class="navitem group group_timezone hidden" data-target="group_timezone">timezone</div>
<div class="navitem group group_wifi hidden" data-target="group_wifi">wifi</div>
<div class="navitem group group_weather hidden" data-target="group_weather">weather</div>
</div>
<div class="playerwrap">
<div class="settingsirwrap" id="settingscontent"><a name="system"></a>
<section class="group group_system hidden" id="group_system">
<div class="title"><span>system</span></div><div class="reset" data-name="system"></div>
<div class="flex-row">
<div class="checkbox off nous" id="smartstart">Smart Start</div>
<div class="checkbox off nous" id="audioinfo">Audio info</div>
<div class="checkbox off nous group group_vu hidden" id="vumeter">VU Meter</div>
</div>
<div class="flex-row">
<div class="inputwrap">
<span class="inputtitle">soft ap reboot delay (min)</span>
<span class="inputinfo" id="slsoftapinfo">0</span>
<input type="range" id="slsoftap" class="slider" data-slaveid="slsoftapinfo" name="softap" min="0" max="20" value="0">
</div>
</div>
<div class="flex-row" id="mdnsnamerow">
<div class="inputwrap">
<span class="inputtitle">mDNS name</span>
<input type="text" id="mdnsname" class="textinput inputchange" name="mdnsname" value="" maxlength="24" />
</div>
<div class="button apply hlbutton" data-name="rebootmdns" style=" margin-top: 24px; height: 37px;">save</div>
</div>
<div class="row-title"><span>update</span></div>
<div class="flex-row last">
<div class="button apply" data-name="fwupdate" id="fwupdate">Firmware</div>
<div class="button apply" data-name="webboard" id="webboard">Board</div>
</div>
</section><a name="screen"></a>
<section class="group group_display group_oled group_nextion hidden" id="group_display">
<div class="title"><span>screen</span></div><div class="reset" data-name="screen"></div>
<div class="flex-row">
<div class="checkbox off nous group group_tft hidden" id="flipscreen">Flip screen</div>
<div class="checkbox off nous group group_tft hidden" id="invertdisplay">Invert screen</div>
<div class="checkbox on nous group group_brightness group_oled hidden" id="screenon">Turn on</div>
</div>
<div class="flex-row group group_tft group_oled group_nextion hidden">
<div class="checkbox off nous" id="numplaylist">numbered playlist</div>
</div>
<div class="flex-row group group_brightness hidden">
<div class="inputwrap">
<span class="inputtitle">brightness</span>
<span class="inputinfo" id="slbrightnessinfo">0</span>
<input type="range" id="slbrightness" class="slider" data-slaveid="slbrightnessinfo" name="brightness" min="0" max="100" value="100">
</div>
</div>
<div class="flex-row group group_nokia hidden">
<div class="inputwrap">
<span class="inputtitle">contrast</span>
<span class="inputinfo" id="slcontrastinfo">0</span>
<input type="range" id="slcontrast" class="slider" data-slaveid="slcontrastinfo" name="contrast" min="0" max="100" value="55">
</div>
</div>
<div class="row-title"><span>screensaver</span></div>
<div class="flex-row group group_tft group_oled group_nextion hidden" style="margin-top:20px;">
<div class="inputwrap">
<span class="inputtitle">while not playing</span>
<div class="checkbox off nous" id="screensaverenabled" style="padding-top:16px;"></div>
</div>
<div class="inputwrap">
<span class="inputtitle">blank screen</span>
<div class="checkbox off nous" id="screensaverblank"></div>
</div>
<div class="inputwrap">
<span class="inputtitle">timeout (sec)</span>
<input type="number" id="screensavertimeout" class="textinput inputchange" name="screensavertimeout" value="" maxlength="3" min="5" max="65520" />
</div>
</div>
<div class="flex-row group group_tft group_oled group_nextion hidden">
<div class="inputwrap">
<span class="inputtitle">while playing</span>
<div class="checkbox off nous" id="screensaverplayingenabled"></div>
</div>
<div class="inputwrap">
<span class="inputtitle">blank screen</span>
<div class="checkbox off nous" id="screensaverplayingblank"></div>
</div>
<div class="inputwrap">
<span class="inputtitle">timeout (min)</span>
<input type="number" id="screensaverplayingtimeout" class="textinput inputchange" name="screensaverplayingtimeout" value="" maxlength="3" min="1" max="1080" />
</div>
</div>
</section>
<section class="group group_controls hidden" id="group_controls">
<div class="title"><span>Controls</span></div><div class="reset" data-name="controls"></div>
<div class="flex-row">
<div class="inputwrap">
<span class="inputtitle">volume steps</span>
<span class="inputinfo" id="slvolstepsinfo">0</span>
<input type="range" id="slvolsteps" class="slider" data-slaveid="slvolstepsinfo" name="volsteps" min="1" max="10" value="1">
</div>
</div>
<div class="flex-row group group_touch hidden">
<div class="checkbox off nous" id="fliptouch">Flip touch</div>
<div class="checkbox off nous" id="dbgtouch">Debug touch</div>
</div>
<div class="flex-row group group_encoder hidden">
<div class="inputwrap">
<span class="inputtitle">encoder acceleration</span>
<span class="inputinfo" id="slencaccelerationinfo">0</span>
<input type="range" id="slencacceleration" class="slider" data-slaveid="slencaccelerationinfo" name="encacceleration" min="0" max="700" value="200">
</div>
</div>
<div class="flex-row">
<div class="inputwrap">
<span class="inputtitle">one-click station switching</span>
<div class="checkbox off nous" id="oneclickswitching"></div>
</div>
</div>
<div class="flex-row group group_ir hidden">
<div class="inputwrap">
<span class="inputtitle">IR tolerance [<a href="https://crankyoldgit.github.io/IRremoteESP8266/doxygen/html/" target="_blank">docs</a>]</span>
<span class="inputinfo" id="slirtlpinfo">0</span>
<input type="range" id="slirtlp" class="slider" data-slaveid="slirtlpinfo" name="irtlp" min="10" max="80" value="0">
</div>
</div>
<div class="flex-row group group_ir last hidden">
<div class="button apply" data-name="setupir">IR Recorder</div>
</div>
</section><a name="timezone"></a>
<section class="group group_timezone hidden" id="group_timezone">
<div class="title"><span>timezone</span></div><div class="reset" data-name="timezone"></div>
<div class="flex-row">
<div class="inputwrap">
<span class="inputtitle">hours</span>
<input type="number" id="tzhour" class="textinput" name="tzhour" value="" maxlength="3" min="-12" max="14" />
</div>
<div class="inputwrap">
<span class="inputtitle">minutes</span>
<input type="number" id="tzmin" class="textinput" name="tzmin" value="" maxlength="2" min="0" max="45" step="15" />
</div>
</div>
<div class="flex-row">
<div class="inputwrap">
<span class="inputtitle">ntp server #1</span>
<input type="text" id="sntp1" class="textinput" name="sntp1" value="" maxlength="34" />
</div>
<div class="inputwrap">
<span class="inputtitle">ntp server #2</span>
<input type="text" id="sntp2" class="textinput" name="sntp2" value="" maxlength="34" />
</div>
</div>
<div class="flex-row last">
<div class="button apply hlbutton" data-name="applytz" id="applytz">Apply</div>
</div>
</section><a name="wifi"></a>
<section class="group group_wifi hidden" id="group_wifi">
<div class="title"><span>Wi-Fi</span></div>
<div class="flex-row credential">
<div class="inputwrap">
<span class="inputtitle">ssid</span>
<input type="text" id="ssid0" class="textinput" name="ssid" value="" maxlength="30" autocomplete="off" />
</div>
<div class="inputwrap">
<span class="inputtitle">pass</span>
<input type="text" id="pass0" class="textinput" name="pass" value="" placeholder="**********" maxlength="40" autocomplete="off" readonly="readonly" data-pass="" onfocus="this.removeAttribute('readonly');" />
</div>
</div>
<div class="flex-row credential">
<div class="inputwrap">
<span class="inputtitle">ssid</span>
<input type="text" id="ssid1" class="textinput" name="ssid" maxlength="30" autocomplete="off" />
</div>
<div class="inputwrap">
<span class="inputtitle">pass</span>
<input type="text" id="pass1" class="textinput" name="pass" value="" placeholder="**********" maxlength="40" autocomplete="off" readonly="readonly" data-pass="" onfocus="this.removeAttribute('readonly');" />
</div>
</div>
<div class="flex-row credential">
<div class="inputwrap">
<span class="inputtitle">ssid</span>
<input type="text" id="ssid2" class="textinput" name="ssid" value="" maxlength="30" autocomplete="off" />
</div>
<div class="inputwrap">
<span class="inputtitle">pass</span>
<input type="text" id="pass2" class="textinput" name="pass" value="" placeholder="**********" maxlength="40" autocomplete="off" readonly="readonly" data-pass="" onfocus="this.removeAttribute('readonly');" />
</div>
</div>
<div class="flex-row credential">
<div class="inputwrap">
<span class="inputtitle">ssid</span>
<input type="text" id="ssid3" class="textinput" name="ssid" value="" maxlength="30" autocomplete="off" />
</div>
<div class="inputwrap">
<span class="inputtitle">pass</span>
<input type="text" id="pass3" class="textinput" name="pass" value="" placeholder="**********" maxlength="40" autocomplete="off" readonly="readonly" data-pass="" onfocus="this.removeAttribute('readonly');" />
</div>
</div>
<div class="flex-row credential">
<div class="inputwrap">
<span class="inputtitle">ssid</span>
<input type="text" id="ssid4" class="textinput" name="ssid" value="" maxlength="30" autocomplete="off" />
</div>
<div class="inputwrap">
<span class="inputtitle">pass</span>
<input type="text" id="pass4" class="textinput" name="pass" value="" placeholder="**********" maxlength="40" autocomplete="off" readonly="readonly" data-pass="" onfocus="this.removeAttribute('readonly');" />
</div>
</div>
<div class="flex-row last">
<input id="file-upload" type="file" accept=".txt, .csv" hidden/>
<div class="button group group_system hidden" data-name="wifiexport">Export</div>
<div class="button hlbutton" data-name="wifiupload">Save &amp; Reboot</div>
</div>
</section><a name="weather"></a>
<section class="group group_weather hidden" id="group_weather">
<div class="title"><span>Weather</span></div><div class="reset" data-name="weather"></div>
<div class="flex-row center">
<div class="checkbox off nous" id="showweather">show weather</div>
</div>
<div class="flex-row">
<div class="inputwrap">
<span class="inputtitle">latitude</span>
<input type="number" id="weatherlat" class="textinput" name="weatherlat" value="" min="-90" max="90" step="0.0001" />
</div>
<div class="inputwrap">
<span class="inputtitle">longitude</span>
<input type="number" id="weatherlon" class="textinput" name="weatherlon" value="" min="-90" max="90" step="0.0001" />
</div>
</div>
<div class="flex-row">
<div class="inputwrap">
<span class="inputtitle">openweathermap api key [<a href="https://openweathermap.org/appid" target="_blank">link</a>]</span>
<input type="text" id="weatherkey" class="textinput" name="weatherkey" value="" />
</div>
</div>
<div class="flex-row last">
<div class="button apply hlbutton" data-name="applyweather">Apply</div>
</div>
</section><a name="controls"></a>
<div class="hr">&nbsp;</div>
</div>
</div><!--playerwrap-->
<div class="button apply done group group_system hidden" data-name="settingsdone" id="settingsdone">done</div>
<div id="copy">powered by <a target="_blank" href="https://github.com/e2002/yoradio/">ёRadio</a> | v%VERSION%</div>
</div>
<script src="script.js?%VERSION%"></script>
</body>

Binary file not shown.

View File

@@ -0,0 +1,16 @@
:root {
--main-bg-color: #010101;
--main-text-color: #cccccc;
--main-hl-color: #ffffff;
--odd-bg-color: #272727;
--accent-color: #e3d25f;
--accent-dark: #ad9838;
--accent-text-color: #000000;
--input-border: #2d2d2d;
--logo: #e3d25f;
--logo-red: #ff0000;
--logo-shadow: #a89c46;
--playlist-hover: #323232;
--section-gradient: #111111;
--section-border: #555555;
}

View File

@@ -1,38 +0,0 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=0.25">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<link rel="apple-touch-icon" type="image/png" href="elogo.png">
<link rel="icon" type="image/png" href="elogo.png">
<link rel="stylesheet" title="base" href="style.css?%VERSION%" type="text/css">
<title>ёRadio - Update</title>
<style>
</style>
</head>
<body>
<div class="content">
<div class="logo"></div>
<div class="playerwrap">
<div class="player" id="uploaderwrap">
<div id="uploadstatus">Please choose firmware or SPIFFS *.bin file</div>
<form id="updateform" method="POST" action="/update" enctype="multipart/form-data">
<div class="row">
<label for="uploadtype1"><input type="radio" id="uploadtype1" name="updatetarget" value="fw" checked /><span class="checkmark"></span> firmware</label>
<label for="contactChoice2"><input type="radio" id="uploadtype2" name="updatetarget" value="spiffs" /><span class="checkmark"></span> SPIFFS</label>
</div>
<div class="row rowinput">
<input id="binfile" type="file" accept=".bin,.hex" name="update" />
<input type="button" class="button" value="Update" onclick="doUpdate(this)">
</div>
</form>
<progress id="updateprogress" max="100" value="70" hidden></progress>
<a href="/" class="button" id="update_cancel_button">Cancel</a>
</div><!--player-->
</div><!--playerwrap-->
<div id="copy">powered by <a target="_blank" href="https://github.com/e2002/yoradio/">ёRadio</a> | v%VERSION%</div>
</div>
<script src="script.js?%VERSION%"></script>
</body>
</html>

Binary file not shown.

View File

@@ -3604,15 +3604,27 @@ void Audio::playAudioData(){
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
bool Audio::parseHttpResponseHeader() { // this is the response to a GET / request bool Audio::parseHttpResponseHeader() { // this is the response to a GET / request
static uint32_t notavailablefor = 0;
if(getDatamode() != HTTP_RESPONSE_HEADER) return false; if(getDatamode() != HTTP_RESPONSE_HEADER) return false;
if(_client->available() == 0) return false; if(_client->available() == 0) {
if (notavailablefor == 0) notavailablefor = millis();
if (millis() - notavailablefor > HEADER_TIMEOUT) {
notavailablefor = 0;
if(audio_showstation) audio_showstation("");
if(audio_icydescription) audio_icydescription("");
if(audio_icyurl) audio_icyurl("");
AUDIO_ERROR("Host %s not available", m_lastHost);
m_lastHost[0] = '\0';
setDatamode(AUDIO_NONE);
stopSong();
}
return false;
}
notavailablefor = 0;
char rhl[512]; // responseHeaderline char rhl[512]; // responseHeaderline
bool ct_seen = false; bool ct_seen = false;
uint32_t ctime = millis(); uint32_t ctime = millis();
uint32_t timeout = 2500; // ms uint32_t timeout = 2500; // ms
while(true){ // outer while while(true){ // outer while
uint16_t pos = 0; uint16_t pos = 0;
if((millis() - ctime) > timeout) { if((millis() - ctime) > timeout) {

View File

@@ -36,6 +36,10 @@
#define AUDIOBUFFER_MULTIPLIER2 8 #define AUDIOBUFFER_MULTIPLIER2 8
#endif #endif
#ifndef HEADER_TIMEOUT
#define HEADER_TIMEOUT 5000
#endif
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
#include "hal/gpio_ll.h" #include "hal/gpio_ll.h"
#endif #endif

View File

@@ -18,14 +18,22 @@ void u8fix(char *src){
} }
bool Config::_isFSempty() { bool Config::_isFSempty() {
const char* reqiredFiles[] = {"dragpl.js.gz","elogo.png","elogo84.png","index.html", const char* reqiredFiles[] = {"dragpl.js.gz","ir.css.gz","irrecord.html.gz","ir.js.gz","logo.svg.gz","options.html.gz","player.html.gz","script.js.gz",
"ir.css.gz","ir.html","ir.js.gz","script.js.gz", "style.css.gz","updform.html.gz","theme.css"};
"settings.css.gz","settings.html","style.css.gz","update.html"}; const uint8_t reqiredFilesSize = 11;
const uint8_t reqiredFilesSize = 12;
char fullpath[28]; char fullpath[28];
if(SPIFFS.exists("/www/settings.html")) SPIFFS.remove("/www/settings.html");
if(SPIFFS.exists("/www/update.html")) SPIFFS.remove("/www/update.html");
if(SPIFFS.exists("/www/index.html")) SPIFFS.remove("/www/index.html");
if(SPIFFS.exists("/www/ir.html")) SPIFFS.remove("/www/ir.html");
if(SPIFFS.exists("/www/elogo.png")) SPIFFS.remove("/www/elogo.png");
if(SPIFFS.exists("/www/elogo84.png")) SPIFFS.remove("/www/elogo84.png");
for (uint8_t i=0; i<reqiredFilesSize; i++){ for (uint8_t i=0; i<reqiredFilesSize; i++){
sprintf(fullpath, "/www/%s", reqiredFiles[i]); sprintf(fullpath, "/www/%s", reqiredFiles[i]);
if(!SPIFFS.exists(fullpath)) return true; if(!SPIFFS.exists(fullpath)) {
Serial.println(fullpath);
return true;
}
} }
return false; return false;
} }
@@ -155,26 +163,28 @@ void Config::changeMode(int newmode){
initPlaylistMode(); initPlaylistMode();
if (pir) player.sendCommand({PR_PLAY, getMode()==PM_WEB?store.lastStation:store.lastSdStation}); if (pir) player.sendCommand({PR_PLAY, getMode()==PM_WEB?store.lastStation:store.lastSdStation});
netserver.resetQueue(); netserver.resetQueue();
netserver.requestOnChange(GETPLAYERMODE, 0); //netserver.requestOnChange(GETPLAYERMODE, 0);
netserver.requestOnChange(GETMODE, 0); netserver.requestOnChange(GETINDEX, 0);
//netserver.requestOnChange(GETMODE, 0);
// netserver.requestOnChange(CHANGEMODE, 0);
display.resetQueue(); display.resetQueue();
display.putRequest(NEWMODE, PLAYER); display.putRequest(NEWMODE, PLAYER);
display.putRequest(NEWSTATION); display.putRequest(NEWSTATION);
} }
void Config::initSDPlaylist() { void Config::initSDPlaylist() {
store.countStation = 0; //store.countStation = 0;
bool doIndex = !sdman.exists(INDEX_SD_PATH); bool doIndex = !sdman.exists(INDEX_SD_PATH);
if(doIndex) sdman.indexSDPlaylist(); if(doIndex) sdman.indexSDPlaylist();
if (SDPLFS()->exists(INDEX_SD_PATH)) { if (SDPLFS()->exists(INDEX_SD_PATH)) {
File index = SDPLFS()->open(INDEX_SD_PATH, "r"); File index = SDPLFS()->open(INDEX_SD_PATH, "r");
store.countStation = index.size() / 4; //store.countStation = index.size() / 4;
if(doIndex){ if(doIndex){
lastStation(_randomStation()); lastStation(_randomStation());
sdResumePos = 0; sdResumePos = 0;
} }
index.close(); index.close();
saveValue(&store.countStation, store.countStation, true, true); //saveValue(&store.countStation, store.countStation, true, true);
} }
} }
@@ -190,6 +200,7 @@ bool Config::spiffsCleanup(){
void Config::initPlaylistMode(){ void Config::initPlaylistMode(){
uint16_t _lastStation = 0; uint16_t _lastStation = 0;
uint16_t cs = playlistLength();
#ifdef USE_SD #ifdef USE_SD
if(getMode()==PM_SDCARD){ if(getMode()==PM_SDCARD){
if(!sdman.start()){ if(!sdman.start()){
@@ -203,7 +214,8 @@ void Config::initPlaylistMode(){
initSDPlaylist(); initSDPlaylist();
if(_bootDone) Serial.println("done"); else BOOTLOG("done"); if(_bootDone) Serial.println("done"); else BOOTLOG("done");
_lastStation = store.lastSdStation; _lastStation = store.lastSdStation;
if(_lastStation>store.countStation && store.countStation>0){
if(_lastStation>cs && cs>0){
_lastStation=1; _lastStation=1;
} }
if(_lastStation==0) { if(_lastStation==0) {
@@ -220,7 +232,7 @@ void Config::initPlaylistMode(){
#endif #endif
if(getMode()==PM_WEB && !emptyFS) initPlaylist(); if(getMode()==PM_WEB && !emptyFS) initPlaylist();
log_i("%d" ,_lastStation); log_i("%d" ,_lastStation);
if (_lastStation == 0 && store.countStation > 0) { if (_lastStation == 0 && cs > 0) {
_lastStation = getMode()==PM_WEB?1:_randomStation(); _lastStation = getMode()==PM_WEB?1:_randomStation();
} }
lastStation(_lastStation); lastStation(_lastStation);
@@ -467,28 +479,37 @@ void Config::indexPlaylist() {
} }
void Config::initPlaylist() { void Config::initPlaylist() {
store.countStation = 0; //store.countStation = 0;
if (!SPIFFS.exists(INDEX_PATH)) indexPlaylist(); if (!SPIFFS.exists(INDEX_PATH)) indexPlaylist();
if (SPIFFS.exists(INDEX_PATH)) { /*if (SPIFFS.exists(INDEX_PATH)) {
File index = SPIFFS.open(INDEX_PATH, "r"); File index = SPIFFS.open(INDEX_PATH, "r");
store.countStation = index.size() / 4; store.countStation = index.size() / 4;
index.close(); index.close();
saveValue(&store.countStation, store.countStation, true, true); saveValue(&store.countStation, store.countStation, true, true);
} }*/
} }
uint16_t Config::playlistLength(){
void Config::loadStation(uint16_t ls) { uint16_t out = 0;
if (SDPLFS()->exists(REAL_INDEX)) {
File index = SDPLFS()->open(REAL_INDEX, "r");
out = index.size() / 4;
index.close();
}
return out;
}
bool Config::loadStation(uint16_t ls) {
char sName[BUFLEN], sUrl[BUFLEN]; char sName[BUFLEN], sUrl[BUFLEN];
int sOvol; int sOvol;
if (store.countStation == 0) { uint16_t cs = playlistLength();
if (cs == 0) {
memset(station.url, 0, BUFLEN); memset(station.url, 0, BUFLEN);
memset(station.name, 0, BUFLEN); memset(station.name, 0, BUFLEN);
strncpy(station.name, "ёRadio", BUFLEN); strncpy(station.name, "ёRadio", BUFLEN);
station.ovol = 0; station.ovol = 0;
return; return false;
} }
if (ls > store.countStation) { if (ls > playlistLength()) {
ls = 1; ls = 1;
} }
File playlist = SDPLFS()->open(REAL_PLAYL, "r"); File playlist = SDPLFS()->open(REAL_PLAYL, "r");
@@ -507,6 +528,7 @@ void Config::loadStation(uint16_t ls) {
setLastStation(ls); setLastStation(ls);
} }
playlist.close(); playlist.close();
return true;
} }
char * Config::stationByNum(uint16_t num){ char * Config::stationByNum(uint16_t num){
@@ -527,7 +549,7 @@ uint8_t Config::fillPlMenu(int from, uint8_t count, bool fromNextion) {
int ls = from; int ls = from;
uint8_t c = 0; uint8_t c = 0;
bool finded = false; bool finded = false;
if (store.countStation == 0) { if (playlistLength() == 0) {
return 0; return 0;
} }
File playlist = SDPLFS()->open(REAL_PLAYL, "r"); File playlist = SDPLFS()->open(REAL_PLAYL, "r");
@@ -569,6 +591,19 @@ uint8_t Config::fillPlMenu(int from, uint8_t count, bool fromNextion) {
return c; return c;
} }
void Config::escapeQuotes(const char* input, char* output, size_t maxLen) {
size_t j = 0;
for (size_t i = 0; input[i] != '\0' && j < maxLen - 1; ++i) {
if (input[i] == '"' && j < maxLen - 2) {
output[j++] = '\\';
output[j++] = '"';
} else {
output[j++] = input[i];
}
}
output[j] = '\0';
}
bool Config::parseCSV(const char* line, char* name, char* url, int &ovol) { bool Config::parseCSV(const char* line, char* name, char* url, int &ovol) {
char *tmpe; char *tmpe;
const char* cursor = line; const char* cursor = line;

View File

@@ -204,11 +204,12 @@ class Config {
uint8_t setLastSSID(uint8_t val); uint8_t setLastSSID(uint8_t val);
void setTitle(const char* title); void setTitle(const char* title);
void setStation(const char* station); void setStation(const char* station);
void escapeQuotes(const char* input, char* output, size_t maxLen);
bool parseCSV(const char* line, char* name, char* url, int &ovol); bool parseCSV(const char* line, char* name, char* url, int &ovol);
bool parseJSON(const char* line, char* name, char* url, int &ovol); bool parseJSON(const char* line, char* name, char* url, int &ovol);
bool parseWsCommand(const char* line, char* cmd, char* val, uint8_t cSize); bool parseWsCommand(const char* line, char* cmd, char* val, uint8_t cSize);
bool parseSsid(const char* line, char* ssid, char* pass); bool parseSsid(const char* line, char* ssid, char* pass);
void loadStation(uint16_t station); bool loadStation(uint16_t station);
bool initNetwork(); bool initNetwork();
bool saveWifi(); bool saveWifi();
bool saveWifiFromNextion(const char* post); bool saveWifiFromNextion(const char* post);
@@ -220,6 +221,7 @@ class Config {
void initSDPlaylist(); void initSDPlaylist();
void changeMode(int newmode=-1); void changeMode(int newmode=-1);
#endif #endif
uint16_t playlistLength();
uint16_t lastStation(){ uint16_t lastStation(){
return getMode()==PM_WEB?store.lastStation:store.lastSdStation; return getMode()==PM_WEB?store.lastStation:store.lastSdStation;
} }

View File

@@ -223,7 +223,7 @@ void irNumber(uint8_t num) {
display.putRequest(NEWMODE, NUMBERS); display.putRequest(NEWMODE, NUMBERS);
if (display.numOfNextStation > UINT16_MAX / 10) return; if (display.numOfNextStation > UINT16_MAX / 10) return;
s = display.numOfNextStation * 10 + num; s = display.numOfNextStation * 10 + num;
if (s > config.store.countStation) return; if (s > config.playlistLength()) return;
display.numOfNextStation = s; display.numOfNextStation = s;
display.putRequest(NEXTSTATION, s); display.putRequest(NEXTSTATION, s);
} }
@@ -464,8 +464,9 @@ void controlsEvent(bool toRight, int8_t volDelta) {
if (display.mode() == STATIONS) { if (display.mode() == STATIONS) {
display.resetQueue(); display.resetQueue();
int p = toRight ? display.currentPlItem + 1 : display.currentPlItem - 1; int p = toRight ? display.currentPlItem + 1 : display.currentPlItem - 1;
if (p < 1) p = config.store.countStation; uint16_t cs = config.playlistLength();
if (p > config.store.countStation) p = 1; if (p < 1) p = cs;
if (p > cs) p = 1;
display.currentPlItem = p; display.currentPlItem = p;
display.putRequest(DRAWPLAYLIST, p); display.putRequest(DRAWPLAYLIST, p);
} }

View File

@@ -39,7 +39,11 @@ void mqttPublishStatus() {
memset(topic, 0, 140); memset(topic, 0, 140);
memset(status, 0, BUFLEN*3); memset(status, 0, BUFLEN*3);
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "status"); sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "status");
sprintf(status, "{\"status\": %d, \"station\": %d, \"name\": \"%s\", \"title\": \"%s\", \"on\": %d}", player.status()==PLAYING?1:0, config.lastStation(), config.station.name, config.station.title, config.store.dspon); char name[BUFLEN*2];
char title[BUFLEN*2];
config.escapeQuotes(config.station.name, name, sizeof(name));
config.escapeQuotes(config.station.title, title, sizeof(name));
sprintf(status, "{\"status\": %d, \"station\": %d, \"name\": \"%s\", \"title\": \"%s\", \"on\": %d}", player.status()==PLAYING?1:0, config.lastStation(), name, title, config.store.dspon);
mqttClient.publish(topic, 0, true, status); mqttClient.publish(topic, 0, true, status);
} }
} }
@@ -131,7 +135,8 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
int sb; int sb;
if (sscanf(buf, "play %d", &sb) == 1 ) { if (sscanf(buf, "play %d", &sb) == 1 ) {
if (sb < 1) sb = 1; if (sb < 1) sb = 1;
if (sb >= config.store.countStation) sb = config.store.countStation; uint16_t cs = config.playlistLength();
if (sb >= cs) sb = cs;
player.sendCommand({PR_PLAY, (uint16_t)sb}); player.sendCommand({PR_PLAY, (uint16_t)sb});
return; return;
} }

View File

@@ -21,7 +21,7 @@
#define NSQ_SEND_DELAY (TickType_t)100 //portMAX_DELAY? #define NSQ_SEND_DELAY (TickType_t)100 //portMAX_DELAY?
#endif #endif
//#define CORS_DEBUG //#define CORS_DEBUG //Enable CORS policy: 'Access-Control-Allow-Origin' (for testing)
NetServer netserver; NetServer netserver;
@@ -61,8 +61,10 @@ bool NetServer::begin(bool quiet) {
irRecordEnable = false; irRecordEnable = false;
nsQueue = xQueueCreate( 20, sizeof( nsRequestParams_t ) ); nsQueue = xQueueCreate( 20, sizeof( nsRequestParams_t ) );
while(nsQueue==NULL){;} while(nsQueue==NULL){;}
if(config.emptyFS){ if(config.emptyFS){
webserver.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { request->send_P(200, "text/html", emptyfs_html, processor); }); webserver.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { request->send_P(200, "text/html", emptyfs_html); });
webserver.on("/webboard", HTTP_POST, [](AsyncWebServerRequest *request) { request->redirect("/"); ESP.restart(); }, handleUploadWeb);
webserver.on("/", HTTP_POST, [](AsyncWebServerRequest *request) { webserver.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
if(request->arg("ssid")!="" && request->arg("pass")!=""){ if(request->arg("ssid")!="" && request->arg("pass")!=""){
char buf[BUFLEN]; char buf[BUFLEN];
@@ -73,11 +75,14 @@ bool NetServer::begin(bool quiet) {
return; return;
} }
request->redirect("/"); request->redirect("/");
ESP.restart(); ESP.restart();
}, handleUploadWeb); }, handleUploadWeb);
}else{ }else{
webserver.on("/", HTTP_ANY, handleHTTPArgs); webserver.on("/", HTTP_ANY, handleHTTPArgs);
webserver.on("/webboard", HTTP_GET, [](AsyncWebServerRequest * request) { request->send_P(200, "text/html", emptyfs_html, processor); }); webserver.on("/settings.html", HTTP_GET, handleHTTPArgs);
webserver.on("/update.html", HTTP_GET, handleHTTPArgs);
webserver.on("/ir.html", HTTP_GET, handleHTTPArgs);
webserver.on("/webboard", HTTP_GET, [](AsyncWebServerRequest * request) { request->send_P(200, "text/html", emptyfs_html); });
webserver.on("/webboard", HTTP_POST, [](AsyncWebServerRequest *request) { request->redirect("/"); }, handleUploadWeb); webserver.on("/webboard", HTTP_POST, [](AsyncWebServerRequest *request) { request->redirect("/"); }, handleUploadWeb);
} }
@@ -90,8 +95,13 @@ bool NetServer::begin(bool quiet) {
webserver.on("/upload", HTTP_POST, beginUpload, handleUpload); webserver.on("/upload", HTTP_POST, beginUpload, handleUpload);
webserver.on("/update", HTTP_GET, handleHTTPArgs); webserver.on("/update", HTTP_GET, handleHTTPArgs);
webserver.on("/update", HTTP_POST, beginUpdate, handleUpdate); webserver.on("/update", HTTP_POST, beginUpdate, handleUpdate);
webserver.on("/settings", HTTP_GET, handleHTTPArgs);
if (IR_PIN != 255) webserver.on("/ir", HTTP_GET, handleHTTPArgs); webserver.on("/variables.js", HTTP_GET, [](AsyncWebServerRequest * request) {
char varjsbuf[BUFLEN];
sprintf (varjsbuf, "var yoVersion='%s';\nvar formAction='%s';\nvar playMode='%s';\n", YOVERSION, (network.status == CONNECTED && !config.emptyFS)?"webboard":"", (network.status == CONNECTED)?"player":"ap");
request->send(200, "text/html", varjsbuf);
});
webserver.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=31536000"); webserver.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=31536000");
#ifdef CORS_DEBUG #ifdef CORS_DEBUG
DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Origin"), F("*")); DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Origin"), F("*"));
@@ -275,7 +285,7 @@ void NetServer::processQueue(){
sprintf (wsbuf, "{\"act\":[%s]}", act.c_str()); sprintf (wsbuf, "{\"act\":[%s]}", act.c_str());
break; break;
} }
case GETMODE: sprintf (wsbuf, "{\"pmode\":\"%s\"}", network.status == CONNECTED ? "player" : "ap"); break; //case STARTUP: sprintf (wsbuf, "{\"command\":\"startup\", \"payload\": {\"mode\":\"%s\", \"version\":\"%s\"}}", network.status == CONNECTED ? "player" : "ap", YOVERSION); break;
case GETINDEX: { case GETINDEX: {
requestOnChange(STATION, clientId); requestOnChange(STATION, clientId);
requestOnChange(TITLE, clientId); requestOnChange(TITLE, clientId);
@@ -334,11 +344,11 @@ void NetServer::processQueue(){
break; break;
case DSPON: sprintf (wsbuf, "{\"dspontrue\":%d}", 1); break; case DSPON: sprintf (wsbuf, "{\"dspontrue\":%d}", 1); break;
case STATION: requestOnChange(STATIONNAME, clientId); requestOnChange(ITEM, clientId); break; case STATION: requestOnChange(STATIONNAME, clientId); requestOnChange(ITEM, clientId); break;
case STATIONNAME: sprintf (wsbuf, "{\"nameset\": \"%s\"}", config.station.name); break; case STATIONNAME: sprintf (wsbuf, "{\"payload\":[{\"id\":\"nameset\", \"value\": \"%s\"}]}", config.station.name); break;
case ITEM: sprintf (wsbuf, "{\"current\": %d}", config.lastStation()); break; case ITEM: sprintf (wsbuf, "{\"current\": %d}", config.lastStation()); break;
case TITLE: sprintf (wsbuf, "{\"meta\": \"%s\"}", config.station.title); telnet.printf("##CLI.META#: %s\n> ", config.station.title); break; case TITLE: sprintf (wsbuf, "{\"payload\":[{\"id\":\"meta\", \"value\": \"%s\"}]}", config.station.title); telnet.printf("##CLI.META#: %s\n> ", config.station.title); break;
case VOLUME: sprintf (wsbuf, "{\"vol\": %d}", config.store.volume); telnet.printf("##CLI.VOL#: %d\n", config.store.volume); break; case VOLUME: sprintf (wsbuf, "{\"payload\":[{\"id\":\"volume\", \"value\": %d}]}", config.store.volume); telnet.printf("##CLI.VOL#: %d\n", config.store.volume); break;
case NRSSI: sprintf (wsbuf, "{\"rssi\": %d}", rssi); /*rssi = 255;*/ break; case NRSSI: sprintf (wsbuf, "{\"payload\":[{\"id\":\"rssi\", \"value\": %d}]}", rssi); /*rssi = 255;*/ break;
case SDPOS: sprintf (wsbuf, "{\"sdpos\": %d,\"sdend\": %d,\"sdtpos\": %d,\"sdtend\": %d}", case SDPOS: sprintf (wsbuf, "{\"sdpos\": %d,\"sdend\": %d,\"sdtpos\": %d,\"sdtend\": %d}",
player.getFilePos(), player.getFilePos(),
player.getFileSize(), player.getFileSize(),
@@ -347,10 +357,10 @@ void NetServer::processQueue(){
break; break;
case SDLEN: sprintf (wsbuf, "{\"sdmin\": %d,\"sdmax\": %d}", player.sd_min, player.sd_max); break; case SDLEN: sprintf (wsbuf, "{\"sdmin\": %d,\"sdmax\": %d}", player.sd_min, player.sd_max); break;
case SDSNUFFLE: sprintf (wsbuf, "{\"snuffle\": %d}", config.store.sdsnuffle); break; case SDSNUFFLE: sprintf (wsbuf, "{\"snuffle\": %d}", config.store.sdsnuffle); break;
case BITRATE: sprintf (wsbuf, "{\"bitrate\": %d, \"format\": \"%s\"}", config.station.bitrate, getFormat(config.configFmt)); break; case BITRATE: sprintf (wsbuf, "{\"payload\":[{\"id\":\"bitrate\", \"value\": %d}, {\"id\":\"fmt\", \"value\": \"%s\"}]}", config.station.bitrate, getFormat(config.configFmt)); break;
case MODE: sprintf (wsbuf, "{\"mode\": \"%s\"}", player.status() == PLAYING ? "playing" : "stopped"); telnet.info(); break; case MODE: sprintf (wsbuf, "{\"payload\":[{\"id\":\"playerwrap\", \"value\": \"%s\"}]}", player.status() == PLAYING ? "playing" : "stopped"); telnet.info(); break;
case EQUALIZER: sprintf (wsbuf, "{\"bass\": %d, \"middle\": %d, \"trebble\": %d}", config.store.bass, config.store.middle, config.store.trebble); break; case EQUALIZER: sprintf (wsbuf, "{\"payload\":[{\"id\":\"bass\", \"value\": %d}, {\"id\": \"middle\", \"value\": %d}, {\"id\": \"trebble\", \"value\": %d}]}", config.store.bass, config.store.middle, config.store.trebble); break;
case BALANCE: sprintf (wsbuf, "{\"balance\": %d}", config.store.balance); break; case BALANCE: sprintf (wsbuf, "{\"payload\":[{\"id\": \"balance\", \"value\": %d}]}", config.store.balance); break;
case SDINIT: sprintf (wsbuf, "{\"sdinit\": %d}", SDC_CS!=255); break; case SDINIT: sprintf (wsbuf, "{\"sdinit\": %d}", SDC_CS!=255); break;
case GETPLAYERMODE: sprintf (wsbuf, "{\"playermode\": \"%s\"}", config.getMode()==PM_SDCARD?"modesd":"modeweb"); break; case GETPLAYERMODE: sprintf (wsbuf, "{\"playermode\": \"%s\"}", config.getMode()==PM_SDCARD?"modesd":"modeweb"); break;
#ifdef USE_SD #ifdef USE_SD
@@ -405,7 +415,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
data[len] = 0; data[len] = 0;
char cmd[65], val[65]; char cmd[65], val[65];
if (config.parseWsCommand((const char*)data, cmd, val, 65)) { if (config.parseWsCommand((const char*)data, cmd, val, 65)) {
if (strcmp(cmd, "getmode") == 0 ) { requestOnChange(GETMODE, clientId); return; } //if (strcmp(cmd, "getmode") == 0 ) { requestOnChange(GETMODE, clientId); return; }
if (strcmp(cmd, "getindex") == 0 ) { requestOnChange(GETINDEX, clientId); return; } if (strcmp(cmd, "getindex") == 0 ) { requestOnChange(GETINDEX, clientId); return; }
if (strcmp(cmd, "getsystem") == 0 ) { requestOnChange(GETSYSTEM, clientId); return; } if (strcmp(cmd, "getsystem") == 0 ) { requestOnChange(GETSYSTEM, clientId); return; }
if (strcmp(cmd, "getscreen") == 0 ) { requestOnChange(GETSCREEN, clientId); return; } if (strcmp(cmd, "getscreen") == 0 ) { requestOnChange(GETSCREEN, clientId); return; }
@@ -433,6 +443,12 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
display.putRequest(SHOWVUMETER); display.putRequest(SHOWVUMETER);
return; return;
} }
if (strcmp(cmd, "prev") == 0) { player.prev(); return; }
if (strcmp(cmd, "toggle") == 0) { player.toggle(); return; }
if (strcmp(cmd, "next") == 0) { player.next(); return; }
if (strcmp(cmd, "volm") == 0) { player.stepVol(false); return; }
if (strcmp(cmd, "volp") == 0) { player.stepVol(true); return; }
if (strcmp(cmd, "play") == 0) { uint16_t valb = atoi(val); player.sendCommand({PR_PLAY, valb}); return; }
if (strcmp(cmd, "softap") == 0) { if (strcmp(cmd, "softap") == 0) {
uint8_t valb = atoi(val); uint8_t valb = atoi(val);
config.saveValue(&config.store.softapdelay, valb); config.saveValue(&config.store.softapdelay, valb);
@@ -718,7 +734,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
netserver.requestOnChange(BALANCE, 0); netserver.requestOnChange(BALANCE, 0);
return; return;
} }
if (strcmp(cmd, "treble") == 0) { if (strcmp(cmd, "trebble") == 0) {
int8_t valb = atoi(val); int8_t valb = atoi(val);
player.setTone(config.store.bass, config.store.middle, valb); player.setTone(config.store.bass, config.store.middle, valb);
config.setTone(config.store.bass, config.store.middle, valb); config.setTone(config.store.bass, config.store.middle, valb);
@@ -739,6 +755,19 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
netserver.requestOnChange(EQUALIZER, 0); netserver.requestOnChange(EQUALIZER, 0);
return; return;
} }
if (strcmp(cmd, "reboot") == 0) {
ESP.restart();
return;
}
if (strcmp(cmd, "format") == 0) {
SPIFFS.format();
ESP.restart();
return;
}
if (strcmp(cmd, "reset") == 0) {
config.reset();
return;
}
if (strcmp(cmd, "submitplaylist") == 0) { if (strcmp(cmd, "submitplaylist") == 0) {
return; return;
} }
@@ -884,7 +913,7 @@ void handleUploadWeb(AsyncWebServerRequest *request, String filename, size_t ind
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
switch (type) { switch (type) {
case WS_EVT_CONNECT: if (config.store.audioinfo) Serial.printf("[WEBSOCKET] client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str()); break; case WS_EVT_CONNECT: /*netserver.requestOnChange(STARTUP, client->id()); */if (config.store.audioinfo) Serial.printf("[WEBSOCKET] client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str()); break;
case WS_EVT_DISCONNECT: if (config.store.audioinfo) Serial.printf("[WEBSOCKET] client #%u disconnected\n", client->id()); break; case WS_EVT_DISCONNECT: if (config.store.audioinfo) Serial.printf("[WEBSOCKET] client #%u disconnected\n", client->id()); break;
case WS_EVT_DATA: netserver.onWsMessage(arg, data, len, client->id()); break; case WS_EVT_DATA: netserver.onWsMessage(arg, data, len, client->id()); break;
case WS_EVT_PONG: case WS_EVT_PONG:
@@ -912,17 +941,21 @@ void handleHTTPArgs(AsyncWebServerRequest * request) {
} }
return; return;
} }
Serial.println(request->url());
if (strcmp(request->url().c_str(), "/") == 0 && request->params() == 0) { if (strcmp(request->url().c_str(), "/") == 0 && request->params() == 0) {
netserver.chunkedHtmlPage(String(), request, network.status == CONNECTED ? "/www/index.html" : "/www/settings.html"); if(network.status == CONNECTED){
return; request->send_P(200, "text/html", index_html);
} }else{
if (strcmp(request->url().c_str(), "/update") == 0 || strcmp(request->url().c_str(), "/settings") == 0 || strcmp(request->url().c_str(), "/ir") == 0) { request->redirect("/settings.html");
char buf[40] = { 0 }; }
sprintf(buf, "/www%s.html", request->url().c_str()); //netserver.chunkedHtmlPage(String(), request, network.status == CONNECTED ? "/www/index.html" : "/www/settings.html", false);
netserver.chunkedHtmlPage(String(), request, buf);
return; return;
} }
} }
if (strcmp(request->url().c_str(), "/settings.html") == 0 || strcmp(request->url().c_str(), "/update.html") == 0 || strcmp(request->url().c_str(), "/ir.html") == 0){
request->send_P(200, "text/html", index_html);
return;
}
if (network.status == CONNECTED) { if (network.status == CONNECTED) {
bool commandFound=false; bool commandFound=false;
if (request->hasArg("start")) { player.sendCommand({PR_PLAY, config.lastStation()}); commandFound=true; } if (request->hasArg("start")) { player.sendCommand({PR_PLAY, config.lastStation()}); commandFound=true; }
@@ -969,7 +1002,8 @@ void handleHTTPArgs(AsyncWebServerRequest * request) {
AsyncWebParameter* p = request->getParam(request->hasArg("playstation") ? "playstation" : "play", request->method() == HTTP_POST); AsyncWebParameter* p = request->getParam(request->hasArg("playstation") ? "playstation" : "play", request->method() == HTTP_POST);
int id = atoi(p->value().c_str()); int id = atoi(p->value().c_str());
if (id < 1) id = 1; if (id < 1) id = 1;
if (id > config.store.countStation) id = config.store.countStation; uint16_t cs = config.playlistLength();
if (id > cs) id = cs;
//config.sdResumePos = 0; //config.sdResumePos = 0;
player.sendCommand({PR_PLAY, id}); player.sendCommand({PR_PLAY, id});
commandFound=true; commandFound=true;

View File

@@ -5,22 +5,28 @@
#include "../AsyncWebServer/ESPAsyncWebServer.h" #include "../AsyncWebServer/ESPAsyncWebServer.h"
#include "AsyncUDP.h" #include "AsyncUDP.h"
enum requestType_e : uint8_t { PLAYLIST=1, STATION=2, STATIONNAME=3, ITEM=4, TITLE=5, VOLUME=6, NRSSI=7, BITRATE=8, MODE=9, EQUALIZER=10, BALANCE=11, PLAYLISTSAVED=12, GETMODE=13, GETINDEX=14, GETACTIVE=15, GETSYSTEM=16, GETSCREEN=17, GETTIMEZONE=18, GETWEATHER=19, GETCONTROLS=20, DSPON=21, SDPOS=22, SDLEN=23, SDSNUFFLE=24, SDINIT=25, GETPLAYERMODE=26, CHANGEMODE=27 }; enum requestType_e : uint8_t { PLAYLIST=1, STATION=2, STATIONNAME=3, ITEM=4, TITLE=5, VOLUME=6, NRSSI=7, BITRATE=8, MODE=9, EQUALIZER=10, BALANCE=11, PLAYLISTSAVED=12, STARTUP=13, GETINDEX=14, GETACTIVE=15, GETSYSTEM=16, GETSCREEN=17, GETTIMEZONE=18, GETWEATHER=19, GETCONTROLS=20, DSPON=21, SDPOS=22, SDLEN=23, SDSNUFFLE=24, SDINIT=25, GETPLAYERMODE=26, CHANGEMODE=27 };
enum import_e : uint8_t { IMDONE=0, IMPL=1, IMWIFI=2 }; enum import_e : uint8_t { IMDONE=0, IMPL=1, IMWIFI=2 };
const char emptyfs_html[] PROGMEM = R"( const char emptyfs_html[] PROGMEM = R"(
<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=0.25"><meta charset="UTF-8"><link rel="icon" href="data:;base64,iVBORw0KGgo="><title>ёRadio - WEB Board Uploader</title><style>body{background-color:#000;color:#e3d25f;font-size:20px;} <!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=0.25"><meta charset="UTF-8">
hr{margin:20px 0;border:0; border-top:#555 1px solid;} p{text-align:center;margin-bottom:10px;} section{max-width:500px; text-align:center;margin:0 auto 30px auto;} <link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAYFBMVEUAAADYw1PcyVjYxFTaxlXYxFTbx1bcyVjZxVXbyFfcyFfaxlbax1bcyVjcyVjbyFfbyFfZxVXaxlbbx1fcyFjcyVjbx1fZxVXcyFjcyVjax1bbyFfcyVjbyFfax1bWwVKMlHGzAAAAH3RSTlMA+wv0zu6dBeVqSryjMRaCU97Fjz8liNk5HbFdDnWsEHoUsAAAAeFJREFUWMPtlllyrDAMRS1P2NjMQzc9RPvf5Ut1IPYjDRbJR1KVnD8Z7i1ZsgXsh1JW3usrC9Ta+2og620DiCjaaY65U4AIpqLqBb7R3B5xJucYRpI+U7jgHwsVLgjSLu74DmSvMTdhQVMMHAYeBhiQFAO5Y3CiGFzWBhDilmKQ4zsqm5uwQGvkCRfsytFkJIOhWWo+vz8uCfWMRqEVAJwsn+PsKgFA+YJR4UWe50Oc1Gt8vrFfyGC19153+afUvVMA+ADAaH5QXhvA/wB3yEICfgAqsvys8BngiPor4AaSpM8BN7lQRrrAbcBSLvMeKqmvVhtYh8mxqjCi7Tnnk4YDKYzRy9DPA2Uy9CoYDBShsCrKitxCnUUnm7qHFwyUYTlOAXYHWxP0TTzBbm1UBGIPfMkDZRcMur1bFPdAxEQPXhI1TNLSj+HxK9l9u8H41RrcKQZub5THbdxA7M3WAZL/EvRp0PDPGEgM9CxBqo9mYMcpAAPyzNZMx2aysUUWzYSi7lzSwALGGG3rvO/zurajM4BQJh0aXAGglACYg2v6uw64h2ZJfOIcp2lxh4ZgkEncRjAKF8AtYCI53M2mQc1IlNrAM7lyZ0akHKURsVaokxuLYxfD6ot8w+nOFuyP5/wDsZKME0E1GogAAAAASUVORK5CYII=">
<title>ёRadio - WEB Board Uploader</title><style>html, body { margin: 0; padding: 0; height: 100%; } body{background-color:#000;color:#e3d25f;font-size:20px;display:flex;flex-direction:column;}
hr{margin:20px 0;border:0; border-top:#555 1px solid;} p{text-align:center;margin-bottom:10px;} section{max-width:500px; text-align:center;margin:0 auto 30px auto;padding:20px;flex:1;}
.hidden{display:none;}a { color: var(--accent-color); text-decoration: none; font-weight: bold } a:hover { text-decoration: underline }
#copy { text-align: center; padding: 14px; font-size: 14px; }
input[type=file]{color:#ccc;} input[type=file]::file-selector-button, input[type=submit]{border:2px solid #e3d25f;color:#000;padding:6px 16px;border-radius:25px;background-color:#e3d25f;margin:0 6px;cursor:pointer;} input[type=file]{color:#ccc;} input[type=file]::file-selector-button, input[type=submit]{border:2px solid #e3d25f;color:#000;padding:6px 16px;border-radius:25px;background-color:#e3d25f;margin:0 6px;cursor:pointer;}
input[type=submit]{font-size:18px;text-transform:uppercase;padding:8px 26px;margin-top:10px;font-family:Times;} span{color:#ccc} .flex{display:flex;justify-content: space-around;margin-top:10px;} input[type=submit]{font-size:18px;text-transform:uppercase;padding:8px 26px;margin-top:10px;font-family:Times;} span{color:#ccc} .flex{display:flex;justify-content: space-around;margin-top:10px;}
input[type=text],input[type=password]{width:170px;background:#272727;color:#e3d25f;padding:6px 12px;font-size:20px;border:#2d2d2d 1px solid;margin:4px 0 0 4px;border-radius:4px;outline:none;} input[type=text],input[type=password]{width:170px;background:#272727;color:#e3d25f;padding:6px 12px;font-size:20px;border:#2d2d2d 1px solid;margin:4px 0 0 4px;border-radius:4px;outline:none;}
@media screen and (max-width:480px) {section{zoom:0.7;-moz-transform:scale(0.7);}} @media screen and (max-width:480px) {section{zoom:0.7;-moz-transform:scale(0.7);}}
</style></head><body> </style>
<script type="text/javascript" src="/variables.js"></script>
</head><body>
<section> <section>
<h2>ёRadio - WEB Board Uploader</h2> <h2>ёRadio - WEB Board Uploader</h2>
<hr /> <hr />
<span>Select <u>ALL</u> files from <i>yoRadio/data/www/</i><br />and upload them using the form below</span> <span>Select <u>ALL</u> files from <i>yoRadio/data/www/</i><br />and upload them using the form below</span>
<hr /> <hr />
<form action="/%ACTION%" method="post" enctype="multipart/form-data"> <form action="/webboard" method="post" enctype="multipart/form-data">
<p><label for="www">www:</label> <input type="file" name="www" id="www" multiple></p> <p><label for="www">www:</label> <input type="file" name="www" id="www" multiple></p>
<hr /> <hr />
<span>-= OPTIONAL =-<br />You can also upload <i>playlist.csv</i><br />and <i>wifi.csv files</i> from your backup</span> <span>-= OPTIONAL =-<br />You can also upload <i>playlist.csv</i><br />and <i>wifi.csv files</i> from your backup</span>
@@ -28,9 +34,9 @@ input[type=text],input[type=password]{width:170px;background:#272727;color:#e3d2
<hr /> <hr />
<p><input type="submit" name="submit" value="Upload Files"></p> <p><input type="submit" name="submit" value="Upload Files"></p>
</form> </form>
<div style="padding:10px 0 0;"%UPLOADWIFI%> <div style="padding:10px 0 0;" id="wupload">
<hr /> <hr />
<form action="/%ACTION%" method="post" enctype="multipart/form-data"> <form name="wifiform" method="post" enctype="multipart/form-data">
<span>-= OPTIONAL =-<br />If you can't connect from PC to 192.168.4.1 address<br />setup WiFi connection first</span> <span>-= OPTIONAL =-<br />If you can't connect from PC to 192.168.4.1 address<br />setup WiFi connection first</span>
<div class="flex"><div><label for="ssid">ssid:</label><input type="text" id="ssid" name="ssid" value="" maxlength="30" autocomplete="off"></div> <div class="flex"><div><label for="ssid">ssid:</label><input type="text" id="ssid" name="ssid" value="" maxlength="30" autocomplete="off"></div>
<div><label for="pass">pass:</label><input type="password" id="pass" name="pass" value="" maxlength="40" autocomplete="off"></div> <div><label for="pass">pass:</label><input type="password" id="pass" name="pass" value="" maxlength="40" autocomplete="off"></div>
@@ -39,9 +45,38 @@ input[type=text],input[type=password]{width:170px;background:#272727;color:#e3d2
</form> </form>
</div> </div>
</section> </section>
</body></html> <div id="copy">powered by <a target="_blank" href="https://github.com/e2002/yoradio/">ёRadio</a><span id="version"></span></div>
</body>
<script>
document.wifiform.action = `/${formAction}`;
if(playMode=='player') document.getElementById("wupload").classList.add("hidden");
document.getElementById("version").innerHTML=` | v${yoVersion}`;
</script>
</html>
)";
const char index_html[] PROGMEM = R"(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="theme-color" content="#e3d25f">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAYFBMVEUAAADYw1PcyVjYxFTaxlXYxFTbx1bcyVjZxVXbyFfcyFfaxlbax1bcyVjcyVjbyFfbyFfZxVXaxlbbx1fcyFjcyVjbx1fZxVXcyFjcyVjax1bbyFfcyVjbyFfax1bWwVKMlHGzAAAAH3RSTlMA+wv0zu6dBeVqSryjMRaCU97Fjz8liNk5HbFdDnWsEHoUsAAAAeFJREFUWMPtlllyrDAMRS1P2NjMQzc9RPvf5Ut1IPYjDRbJR1KVnD8Z7i1ZsgXsh1JW3usrC9Ta+2og620DiCjaaY65U4AIpqLqBb7R3B5xJucYRpI+U7jgHwsVLgjSLu74DmSvMTdhQVMMHAYeBhiQFAO5Y3CiGFzWBhDilmKQ4zsqm5uwQGvkCRfsytFkJIOhWWo+vz8uCfWMRqEVAJwsn+PsKgFA+YJR4UWe50Oc1Gt8vrFfyGC19153+afUvVMA+ADAaH5QXhvA/wB3yEICfgAqsvys8BngiPor4AaSpM8BN7lQRrrAbcBSLvMeKqmvVhtYh8mxqjCi7Tnnk4YDKYzRy9DPA2Uy9CoYDBShsCrKitxCnUUnm7qHFwyUYTlOAXYHWxP0TTzBbm1UBGIPfMkDZRcMur1bFPdAxEQPXhI1TNLSj+HxK9l9u8H41RrcKQZub5THbdxA7M3WAZL/EvRp0PDPGEgM9CxBqo9mYMcpAAPyzNZMx2aysUUWzYSi7lzSwALGGG3rvO/zurajM4BQJh0aXAGglACYg2v6uw64h2ZJfOIcp2lxh4ZgkEncRjAKF8AtYCI53M2mQc1IlNrAM7lyZ0akHKURsVaokxuLYxfD6ot8w+nOFuyP5/wDsZKME0E1GogAAAAASUVORK5CYII=">
<link rel="stylesheet" href="theme.css" type="text/css" />
<link rel="stylesheet" href="style.css" type="text/css" />
<script type="text/javascript" src="variables.js"></script>
<script type="text/javascript" src="script.js"></script>
<script type="text/javascript" src="dragpl.js"></script>
</head>
<body>
<div id="content" class="hidden progmem">
</div><!--content-->
<div id="progress"><span id="loader"></span></div>
</body>
</html>
)"; )";
struct nsRequestParams_t struct nsRequestParams_t
{ {
requestType_e type; requestType_e type;

View File

@@ -1,7 +1,7 @@
#ifndef options_h #ifndef options_h
#define options_h #define options_h
#define YOVERSION "0.9.434" #define YOVERSION "0.9.511"
/******************************************************* /*******************************************************
DO NOT EDIT THIS FILE. DO NOT EDIT THIS FILE.

View File

@@ -186,6 +186,7 @@ void Player::setOutputPins(bool isPlaying) {
void Player::_play(uint16_t stationId) { void Player::_play(uint16_t stationId) {
log_i("%s called, stationId=%d", __func__, stationId); log_i("%s called, stationId=%d", __func__, stationId);
setError(""); setError("");
setDefaults();
remoteStationName = false; remoteStationName = false;
config.setDspOn(1); config.setDspOn(1);
config.vuThreshold = 0; config.vuThreshold = 0;
@@ -193,21 +194,23 @@ void Player::_play(uint16_t stationId) {
config.screensaverTicks=SCREENSAVERSTARTUPDELAY; config.screensaverTicks=SCREENSAVERSTARTUPDELAY;
config.screensaverPlayingTicks=SCREENSAVERSTARTUPDELAY; config.screensaverPlayingTicks=SCREENSAVERSTARTUPDELAY;
if(config.getMode()!=PM_SDCARD) { if(config.getMode()!=PM_SDCARD) {
display.putRequest(PSTOP); display.putRequest(PSTOP);
} }
setOutputPins(false); setOutputPins(false);
//config.setTitle(config.getMode()==PM_WEB?const_PlConnect:""); //config.setTitle(config.getMode()==PM_WEB?const_PlConnect:"");
if(!config.loadStation(stationId)) return;
config.setTitle(config.getMode()==PM_WEB?const_PlConnect:"[next track]"); config.setTitle(config.getMode()==PM_WEB?const_PlConnect:"[next track]");
config.station.bitrate=0; config.station.bitrate=0;
config.setBitrateFormat(BF_UNCNOWN); config.setBitrateFormat(BF_UNCNOWN);
config.loadStation(stationId);
_loadVol(config.store.volume); _loadVol(config.store.volume);
display.putRequest(DBITRATE); display.putRequest(DBITRATE);
display.putRequest(NEWSTATION); display.putRequest(NEWSTATION);
netserver.requestOnChange(STATION, 0); netserver.requestOnChange(STATION, 0);
netserver.loop(); netserver.loop();
netserver.loop(); netserver.loop();
config.setSmartStart(0); if(config.store.smartstart!=2)
config.setSmartStart(0);
bool isConnected = false; bool isConnected = false;
if(config.getMode()==PM_SDCARD && SDC_CS!=255){ if(config.getMode()==PM_SDCARD && SDC_CS!=255){
isConnected=connecttoFS(sdman,config.station.url,config.sdResumePos==0?_resumeFilePos:config.sdResumePos-player.sd_min); isConnected=connecttoFS(sdman,config.station.url,config.sdResumePos==0?_resumeFilePos:config.sdResumePos-player.sd_min);
@@ -223,7 +226,8 @@ void Player::_play(uint16_t stationId) {
config.saveValue(&config.store.lastSdStation, stationId); config.saveValue(&config.store.lastSdStation, stationId);
} }
//config.setTitle(""); //config.setTitle("");
config.setSmartStart(1); if(config.store.smartstart!=2)
config.setSmartStart(1);
netserver.requestOnChange(MODE, 0); netserver.requestOnChange(MODE, 0);
setOutputPins(true); setOutputPins(true);
display.putRequest(NEWMODE, PLAYER); display.putRequest(NEWMODE, PLAYER);
@@ -268,7 +272,7 @@ void Player::prev() {
uint16_t lastStation = config.lastStation(); uint16_t lastStation = config.lastStation();
if(config.getMode()==PM_WEB || !config.store.sdsnuffle){ if(config.getMode()==PM_WEB || !config.store.sdsnuffle){
if (lastStation == 1) config.lastStation(config.store.countStation); else config.lastStation(lastStation-1); if (lastStation == 1) config.lastStation(config.playlistLength()); else config.lastStation(lastStation-1);
} }
sendCommand({PR_PLAY, config.lastStation()}); sendCommand({PR_PLAY, config.lastStation()});
} }
@@ -276,9 +280,9 @@ void Player::prev() {
void Player::next() { void Player::next() {
uint16_t lastStation = config.lastStation(); uint16_t lastStation = config.lastStation();
if(config.getMode()==PM_WEB || !config.store.sdsnuffle){ if(config.getMode()==PM_WEB || !config.store.sdsnuffle){
if (lastStation == config.store.countStation) config.lastStation(1); else config.lastStation(lastStation+1); if (lastStation == config.playlistLength()) config.lastStation(1); else config.lastStation(lastStation+1);
}else{ }else{
config.lastStation(random(1, config.store.countStation)); config.lastStation(random(1, config.playlistLength()));
} }
sendCommand({PR_PLAY, config.lastStation()}); sendCommand({PR_PLAY, config.lastStation()});
} }

View File

@@ -292,7 +292,8 @@ void Telnet::on_input(const char* str, uint8_t clientId) {
int sb; int sb;
if (sscanf(str, "play(%d)", &sb) == 1 || sscanf(str, "cli.play(\"%d\")", &sb) == 1 || sscanf(str, "play %d", &sb) == 1 ) { if (sscanf(str, "play(%d)", &sb) == 1 || sscanf(str, "cli.play(\"%d\")", &sb) == 1 || sscanf(str, "play %d", &sb) == 1 ) {
if (sb < 1) sb = 1; if (sb < 1) sb = 1;
if (sb >= config.store.countStation) sb = config.store.countStation; uint16_t cs = config.playlistLength();
if (sb >= cs) sb = cs;
player.sendCommand({PR_PLAY, (uint16_t)sb}); player.sendCommand({PR_PLAY, (uint16_t)sb});
return; return;
} }

View File

@@ -209,14 +209,14 @@ void Nextion::loop() {
if(strcmp(scanBuf, "up") == 0) { if(strcmp(scanBuf, "up") == 0) {
display.resetQueue(); display.resetQueue();
int p = display.currentPlItem - 1; int p = display.currentPlItem - 1;
if (p < 1) p = config.store.countStation; if (p < 1) p = config.playlistLength();
display.currentPlItem = p; display.currentPlItem = p;
display.putRequest(DRAWPLAYLIST, p); display.putRequest(DRAWPLAYLIST, p);
} }
if(strcmp(scanBuf, "dn") == 0) { if(strcmp(scanBuf, "dn") == 0) {
display.resetQueue(); display.resetQueue();
int p = display.currentPlItem + 1; int p = display.currentPlItem + 1;
if (p > config.store.countStation) p = 1; if (p > config.playlistLength()) p = 1;
display.currentPlItem = p; display.currentPlItem = p;
display.putRequest(DRAWPLAYLIST, p); display.putRequest(DRAWPLAYLIST, p);
} }