@@ -1032,15 +1032,19 @@ class RadioBrowserCard extends HTMLElement {
10321032 }
10331033
10341034 async stop ( ) {
1035+ // Set _isPlaying to false IMMEDIATELY to prevent keepalive from triggering recovery
1036+ this . _isPlaying = false ;
1037+ this . _currentStationIndex = - 1 ;
1038+ this . _currentStationMetadata = null ;
1039+
1040+ // Stop visualizer immediately
1041+ this . stopVisualizer ( ) ;
1042+
10351043 // Stop direct playback if active
10361044 if ( this . _isUsingDirectPlayback && this . _audioElement ) {
10371045 this . _audioElement . pause ( ) ;
10381046 this . _audioElement . currentTime = 0 ;
10391047 this . _isUsingDirectPlayback = false ;
1040- this . _isPlaying = false ;
1041- this . _currentStationIndex = - 1 ;
1042- this . _currentStationMetadata = null ;
1043- this . stopVisualizer ( ) ;
10441048 this . updatePlaylistSelection ( ) ;
10451049 this . updateStationInfo ( ) ;
10461050 this . _saveState ( ) ;
@@ -1055,10 +1059,6 @@ class RadioBrowserCard extends HTMLElement {
10551059 await this . _hass . callService ( 'media_player' , 'media_stop' , {
10561060 entity_id : this . _selectedMediaPlayer
10571061 } ) ;
1058- this . _isPlaying = false ;
1059- this . _currentStationIndex = - 1 ;
1060- this . _currentStationMetadata = null ;
1061- this . stopVisualizer ( ) ; // Immediately stop visualizer
10621062 this . updatePlaylistSelection ( ) ;
10631063 this . updateStationInfo ( ) ;
10641064 this . _saveState ( ) ;
@@ -2062,106 +2062,218 @@ class RadioBrowserCard extends HTMLElement {
20622062 color: ${ colors . primary } ;
20632063 }
20642064
2065- /* Electric Border Effect */
2065+ /* Electric Border Effect - Advanced version with turbulence */
20662066 .electric-border-active {
20672067 position: relative;
2068+ padding: 2px;
2069+ border-radius: 8px;
20682070 overflow: visible !important;
2071+ background: linear-gradient(
2072+ -30deg,
2073+ rgba(221, 132, 72, 0.4),
2074+ transparent,
2075+ rgba(221, 132, 72, 0.4)
2076+ ),
2077+ linear-gradient(
2078+ to bottom,
2079+ ${ colors . background } ,
2080+ ${ colors . background }
2081+ );
2082+ }
2083+
2084+ .electric-border-active .main-window-inner {
2085+ position: relative;
2086+ width: 100%;
2087+ height: 100%;
2088+ }
2089+
2090+ .electric-border-active .border-outer {
2091+ border: 2px solid rgba(221, 132, 72, 0.5);
2092+ border-radius: 8px;
2093+ padding-right: 4px;
2094+ padding-bottom: 4px;
2095+ position: relative;
2096+ width: 100%;
2097+ height: 100%;
2098+ }
2099+
2100+ .electric-border-active .main-card-layer {
2101+ border-radius: 8px;
2102+ border: 2px solid #dd8448;
2103+ margin-top: -4px;
2104+ margin-left: -4px;
2105+ filter: url(#turbulent-displace);
2106+ position: relative;
2107+ width: calc(100% + 4px);
2108+ height: calc(100% + 4px);
2109+ background: ${ colors . background } ;
2110+ }
2111+
2112+ .electric-border-active .glow-layer-1 {
2113+ border: 2px solid rgba(221, 132, 72, 0.6);
2114+ border-radius: 8px;
2115+ width: 100%;
2116+ height: 100%;
2117+ position: absolute;
2118+ top: 0;
2119+ left: 0;
2120+ right: 0;
2121+ bottom: 0;
2122+ filter: blur(1px);
2123+ pointer-events: none;
2124+ }
2125+
2126+ .electric-border-active .glow-layer-2 {
2127+ border: 2px solid #dd8448;
2128+ border-radius: 8px;
2129+ width: 100%;
2130+ height: 100%;
2131+ position: absolute;
2132+ top: 0;
2133+ left: 0;
2134+ right: 0;
2135+ bottom: 0;
2136+ filter: blur(4px);
2137+ pointer-events: none;
20692138 }
20702139
2071- .electric-border-active::before {
2072- content: '';
2140+ .electric-border-active .overlay-1 {
20732141 position: absolute;
2074- top: -3px;
2075- left: -3px;
2076- right: -3px;
2077- bottom: -3px;
2078- background: conic-gradient(
2079- from 0deg,
2080- transparent 0%,
2081- #00f3ff 10%,
2082- #00d4ff 20%,
2142+ width: 100%;
2143+ height: 100%;
2144+ top: 0;
2145+ left: 0;
2146+ right: 0;
2147+ bottom: 0;
2148+ border-radius: 8px;
2149+ opacity: 1;
2150+ mix-blend-mode: overlay;
2151+ transform: scale(1.1);
2152+ filter: blur(16px);
2153+ background: linear-gradient(
2154+ -30deg,
2155+ white,
20832156 transparent 30%,
20842157 transparent 70%,
2085- #ff00ea 80%,
2086- #d400ff 90%,
2087- transparent 100%
2158+ white
20882159 );
2089- border-radius: 10px;
2090- z-index: -1;
2091- animation: electric-rotate 4s linear infinite;
2092- filter: blur(8px);
2093- opacity: 0.8;
2160+ pointer-events: none;
20942161 }
20952162
2096- .electric-border-active::after {
2097- content: '';
2163+ .electric-border-active .overlay-2 {
20982164 position: absolute;
2099- top: -2px;
2100- left: -2px;
2101- right: -2px;
2102- bottom: -2px;
2103- background: conic-gradient(
2104- from 180deg,
2105- transparent 0%,
2106- #ff00ea 10%,
2107- #d400ff 20%,
2165+ width: 100%;
2166+ height: 100%;
2167+ top: 0;
2168+ left: 0;
2169+ right: 0;
2170+ bottom: 0;
2171+ border-radius: 8px;
2172+ opacity: 0.5;
2173+ mix-blend-mode: overlay;
2174+ transform: scale(1.1);
2175+ filter: blur(16px);
2176+ background: linear-gradient(
2177+ -30deg,
2178+ white,
21082179 transparent 30%,
21092180 transparent 70%,
2110- #00f3ff 80%,
2111- #00d4ff 90%,
2112- transparent 100%
2181+ white
21132182 );
2114- border-radius: 10px;
2115- z-index: -1;
2116- animation: electric-rotate-reverse 3s linear infinite;
2117- filter: blur(6px);
2118- opacity: 0.6;
2183+ pointer-events: none;
21192184 }
21202185
2121- @keyframes electric-rotate {
2122- 0% {
2123- transform: rotate(0deg);
2124- }
2125- 100% {
2126- transform: rotate(360deg);
2127- }
2128- }
2129-
2130- @keyframes electric-rotate-reverse {
2131- 0% {
2132- transform: rotate(360deg);
2133- }
2134- 100% {
2135- transform: rotate(0deg);
2136- }
2186+ .electric-border-active .background-glow {
2187+ position: absolute;
2188+ width: 100%;
2189+ height: 100%;
2190+ top: 0;
2191+ left: 0;
2192+ right: 0;
2193+ bottom: 0;
2194+ border-radius: 8px;
2195+ filter: blur(32px);
2196+ transform: scale(1.1);
2197+ opacity: 0.3;
2198+ z-index: -1;
2199+ background: linear-gradient(
2200+ -30deg,
2201+ #dd8448,
2202+ transparent,
2203+ #dd8448
2204+ );
2205+ pointer-events: none;
21372206 }
21382207 </style>
21392208
2209+ <!-- SVG filter for electric turbulence effect -->
2210+ <svg style="position: absolute; width: 0; height: 0;">
2211+ <defs>
2212+ <filter id="turbulent-displace">
2213+ <feTurbulence
2214+ type="turbulence"
2215+ baseFrequency="0.009 0.009"
2216+ numOctaves="2"
2217+ result="turbulence"
2218+ seed="1">
2219+ <animate
2220+ attributeName="baseFrequency"
2221+ dur="40s"
2222+ values="0.009 0.009;0.012 0.009;0.009 0.009"
2223+ repeatCount="indefinite" />
2224+ </feTurbulence>
2225+ <feDisplacementMap
2226+ in="SourceGraphic"
2227+ in2="turbulence"
2228+ scale="8"
2229+ xChannelSelector="R"
2230+ yChannelSelector="G" />
2231+ </filter>
2232+ </defs>
2233+ </svg>
2234+
21402235 <div class="winamp-container">
21412236 <!-- Main Player Window -->
21422237 <div class="main-window ${ this . _electricBorder ? 'electric-border-active' : '' } ">
2143- <div class="track-title">Radio Browser</div>
2144- <div class="station-info" style="display: none;"></div>
2145- <div class="sleep-timer-display" style="display: none;"></div>
2146-
2147- <!-- Visualizer -->
2148- <canvas class="visualizer" width="80" height="32"></canvas>
2149-
2150- <!-- Control Buttons -->
2151- <div class="control-buttons">
2152- <button class="control-btn btn-prev" onclick="this.getRootNode().host.playPrevious()" title="Previous (←)"></button>
2153- <button class="control-btn btn-play" onclick="this.getRootNode().host.togglePlay()" title="Play (Space)"></button>
2154- <button class="control-btn btn-pause" onclick="this.getRootNode().host.togglePlay()" title="Pause (Space)"></button>
2155- <button class="control-btn btn-stop" onclick="this.getRootNode().host.stop()" title="Stop"></button>
2156- <button class="control-btn btn-next" onclick="this.getRootNode().host.playNext()" title="Next (→)"></button>
2157- </div>
2158-
2159- <!-- Volume Control -->
2160- <div class="volume-control">
2161- <input type="range" class="volume-slider" min="0" max="100" value="${ currentVolume } "
2162- oninput="this.getRootNode().host.handleVolumeChange(event)"
2163- title="Volume (↑↓)">
2238+ ${ this . _electricBorder ? `
2239+ <div class="main-window-inner">
2240+ <div class="border-outer">
2241+ <div class="main-card-layer">
2242+ <div class="glow-layer-1"></div>
2243+ <div class="glow-layer-2"></div>
2244+ <div class="overlay-1"></div>
2245+ <div class="overlay-2"></div>
2246+ <div class="background-glow"></div>
2247+ ` : '' }
2248+
2249+ <div class="track-title">Radio Browser</div>
2250+ <div class="station-info" style="display: none;"></div>
2251+ <div class="sleep-timer-display" style="display: none;"></div>
2252+
2253+ <!-- Visualizer -->
2254+ <canvas class="visualizer" width="80" height="32"></canvas>
2255+
2256+ <!-- Control Buttons -->
2257+ <div class="control-buttons">
2258+ <button class="control-btn btn-prev" onclick="this.getRootNode().host.playPrevious()" title="Previous (←)"></button>
2259+ <button class="control-btn btn-play" onclick="this.getRootNode().host.togglePlay()" title="Play (Space)"></button>
2260+ <button class="control-btn btn-pause" onclick="this.getRootNode().host.togglePlay()" title="Pause (Space)"></button>
2261+ <button class="control-btn btn-stop" onclick="this.getRootNode().host.stop()" title="Stop"></button>
2262+ <button class="control-btn btn-next" onclick="this.getRootNode().host.playNext()" title="Next (→)"></button>
2263+ </div>
2264+
2265+ <!-- Volume Control -->
2266+ <div class="volume-control">
2267+ <input type="range" class="volume-slider" min="0" max="100" value="${ currentVolume } "
2268+ oninput="this.getRootNode().host.handleVolumeChange(event)"
2269+ title="Volume (↑↓)">
2270+ </div>
2271+
2272+ ${ this . _electricBorder ? `
2273+ </div>
2274+ </div>
21642275 </div>
2276+ ` : '' }
21652277 </div>
21662278
21672279 <!-- Playlist Window -->
@@ -2376,8 +2488,10 @@ class RadioBrowserCard extends HTMLElement {
23762488 }
23772489
23782490 // Check if playback state indicates playing
2379- if ( entity . state !== 'playing' ) {
2380- console . warn ( 'Player state is not "playing", attempting recovery...' ) ;
2491+ // BUT only attempt recovery if we THINK we're still playing
2492+ // If user pressed STOP, _isPlaying will be false and we shouldn't recover
2493+ if ( entity . state !== 'playing' && this . _isPlaying ) {
2494+ console . warn ( 'Player state is not "playing" but _isPlaying=true, attempting recovery...' ) ;
23812495 this . _recoverPlayback ( ) ;
23822496 return ;
23832497 }
0 commit comments