@@ -69,6 +69,11 @@ class RadioBrowserCard extends HTMLElement {
6969 }
7070 }
7171
72+ // On first hass connection, auto-restore from HA server if cache was cleared
73+ if ( ! oldHass && hass ) {
74+ this . _autoRestoreFromHA ( ) ;
75+ }
76+
7277 if ( ! oldHass || Object . keys ( oldHass . states ) . length !== Object . keys ( hass . states ) . length ) {
7378 this . updateMediaPlayers ( ) ;
7479 }
@@ -93,6 +98,7 @@ class RadioBrowserCard extends HTMLElement {
9398 saveFavorites ( ) {
9499 try {
95100 localStorage . setItem ( 'radio_favorites' , JSON . stringify ( this . _favorites ) ) ;
101+ this . _autoBackupToHA ( ) ;
96102 } catch ( e ) {
97103 console . error ( 'Error saving favorites:' , e ) ;
98104 }
@@ -111,11 +117,66 @@ class RadioBrowserCard extends HTMLElement {
111117 saveCustomStations ( ) {
112118 try {
113119 localStorage . setItem ( 'radio_custom_stations' , JSON . stringify ( this . _customStations ) ) ;
120+ this . _autoBackupToHA ( ) ;
114121 } catch ( e ) {
115122 console . error ( 'Error saving custom stations:' , e ) ;
116123 }
117124 }
118125
126+ // Auto-backup to Home Assistant server (survives cache clear)
127+ async _autoBackupToHA ( ) {
128+ if ( ! this . _hass ) return ;
129+ try {
130+ await this . _hass . callWS ( {
131+ type : 'frontend/set_user_data' ,
132+ key : 'radio_browser_backup' ,
133+ value : {
134+ favorites : this . _favorites ,
135+ custom_stations : this . _customStations ,
136+ timestamp : Date . now ( )
137+ }
138+ } ) ;
139+ console . log ( 'Auto-backup to HA server OK' ) ;
140+ } catch ( e ) {
141+ console . log ( 'Auto-backup to HA server failed:' , e ) ;
142+ }
143+ }
144+
145+ // Auto-restore from Home Assistant server (after cache clear)
146+ async _autoRestoreFromHA ( ) {
147+ if ( ! this . _hass ) return ;
148+ try {
149+ const result = await this . _hass . callWS ( {
150+ type : 'frontend/get_user_data' ,
151+ key : 'radio_browser_backup'
152+ } ) ;
153+ if ( ! result || ! result . value ) return ;
154+
155+ const data = result . value ;
156+ const localFavs = localStorage . getItem ( 'radio_favorites' ) ;
157+ const localCustom = localStorage . getItem ( 'radio_custom_stations' ) ;
158+ const hasLocalData = ( localFavs && JSON . parse ( localFavs ) . length > 0 ) ||
159+ ( localCustom && JSON . parse ( localCustom ) . length > 0 ) ;
160+
161+ // Only restore if localStorage is empty (cache was cleared) and server has data
162+ if ( ! hasLocalData && ( data . favorites ?. length > 0 || data . custom_stations ?. length > 0 ) ) {
163+ console . log ( 'Cache cleared detected - restoring from HA server backup...' ) ;
164+ if ( data . favorites ?. length > 0 ) {
165+ this . _favorites = data . favorites ;
166+ localStorage . setItem ( 'radio_favorites' , JSON . stringify ( data . favorites ) ) ;
167+ }
168+ if ( data . custom_stations ?. length > 0 ) {
169+ this . _customStations = data . custom_stations ;
170+ localStorage . setItem ( 'radio_custom_stations' , JSON . stringify ( data . custom_stations ) ) ;
171+ }
172+ console . log ( `Restored: ${ data . favorites ?. length || 0 } favorites, ${ data . custom_stations ?. length || 0 } custom stations` ) ;
173+ this . updatePlaylist ( ) ;
174+ }
175+ } catch ( e ) {
176+ console . log ( 'Auto-restore from HA server not available:' , e ) ;
177+ }
178+ }
179+
119180 // YouTube URL handling
120181 parseYouTubeUrl ( url ) {
121182 // Support various YouTube URL formats
@@ -306,19 +367,20 @@ class RadioBrowserCard extends HTMLElement {
306367 return this . _favorites . some ( fav => fav . media_content_id === station . media_content_id ) ;
307368 }
308369
309- // Export/Import favorites
370+ // Export/Import favorites + custom stations
310371 exportFavorites ( ) {
311372 const data = {
312- version : '1 .0' ,
373+ version : '2 .0' ,
313374 exported : new Date ( ) . toISOString ( ) ,
314- favorites : this . _favorites
375+ favorites : this . _favorites ,
376+ custom_stations : this . _customStations
315377 } ;
316378 const json = JSON . stringify ( data , null , 2 ) ;
317379 const blob = new Blob ( [ json ] , { type : 'application/json' } ) ;
318380 const url = URL . createObjectURL ( blob ) ;
319381 const a = document . createElement ( 'a' ) ;
320382 a . href = url ;
321- a . download = `radio-favorites -${ new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] } .json` ;
383+ a . download = `radio-backup -${ new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] } .json` ;
322384 a . click ( ) ;
323385 URL . revokeObjectURL ( url ) ;
324386 }
@@ -335,16 +397,25 @@ class RadioBrowserCard extends HTMLElement {
335397 reader . onload = ( event ) => {
336398 try {
337399 const data = JSON . parse ( event . target . result ) ;
400+ const parts = [ ] ;
338401 if ( data . favorites && Array . isArray ( data . favorites ) ) {
339402 this . _favorites = data . favorites ;
340403 this . saveFavorites ( ) ;
404+ parts . push ( `${ data . favorites . length } favorites` ) ;
405+ }
406+ if ( data . custom_stations && Array . isArray ( data . custom_stations ) ) {
407+ this . _customStations = data . custom_stations ;
408+ this . saveCustomStations ( ) ;
409+ parts . push ( `${ data . custom_stations . length } custom stations` ) ;
410+ }
411+ if ( parts . length > 0 ) {
341412 this . updatePlaylist ( ) ;
342- alert ( `Imported ${ data . favorites . length } favorite stations !` ) ;
413+ alert ( `Imported: ${ parts . join ( ', ' ) } !` ) ;
343414 } else {
344415 alert ( 'Invalid file format' ) ;
345416 }
346417 } catch ( error ) {
347- alert ( 'Error importing favorites : ' + error . message ) ;
418+ alert ( 'Error importing: ' + error . message ) ;
348419 }
349420 } ;
350421 reader . readAsText ( file ) ;
@@ -2239,10 +2310,10 @@ class RadioBrowserCard extends HTMLElement {
22392310 </div>
22402311 </div>
22412312 <div class="settings-group">
2242- <div class="settings-label">Favorites </div>
2313+ <div class="settings-label">Backup & Restore </div>
22432314 <div class="settings-options">
2244- <button class="settings-option" onclick="this.getRootNode().host.exportFavorites()">📤 Export </button>
2245- <button class="settings-option" onclick="this.getRootNode().host.importFavorites()">📥 Import </button>
2315+ <button class="settings-option" onclick="this.getRootNode().host.exportFavorites()">📤 Backup </button>
2316+ <button class="settings-option" onclick="this.getRootNode().host.importFavorites()">📥 Restore </button>
22462317 </div>
22472318 </div>
22482319 </div>
@@ -2590,6 +2661,11 @@ class RadioBrowserCardCompact extends HTMLElement {
25902661 const oldHass = this . _hass ;
25912662 this . _hass = hass ;
25922663
2664+ // On first hass connection, auto-restore from HA server if cache was cleared
2665+ if ( ! oldHass && hass ) {
2666+ this . _autoRestoreFromHA ( ) ;
2667+ }
2668+
25932669 if ( ! oldHass || Object . keys ( oldHass . states ) . length !== Object . keys ( hass . states ) . length ) {
25942670 this . _updateMediaPlayers ( ) ;
25952671 }
@@ -2628,6 +2704,37 @@ class RadioBrowserCardCompact extends HTMLElement {
26282704 return all ;
26292705 }
26302706
2707+ // Auto-restore from Home Assistant server (after cache clear)
2708+ async _autoRestoreFromHA ( ) {
2709+ if ( ! this . _hass ) return ;
2710+ try {
2711+ const result = await this . _hass . callWS ( {
2712+ type : 'frontend/get_user_data' ,
2713+ key : 'radio_browser_backup'
2714+ } ) ;
2715+ if ( ! result || ! result . value ) return ;
2716+
2717+ const data = result . value ;
2718+ const localFavs = localStorage . getItem ( 'radio_favorites' ) ;
2719+ const localCustom = localStorage . getItem ( 'radio_custom_stations' ) ;
2720+ const hasLocalData = ( localFavs && JSON . parse ( localFavs ) . length > 0 ) ||
2721+ ( localCustom && JSON . parse ( localCustom ) . length > 0 ) ;
2722+
2723+ if ( ! hasLocalData && ( data . favorites ?. length > 0 || data . custom_stations ?. length > 0 ) ) {
2724+ console . log ( 'Compact card: Restoring from HA server backup...' ) ;
2725+ if ( data . favorites ?. length > 0 ) {
2726+ localStorage . setItem ( 'radio_favorites' , JSON . stringify ( data . favorites ) ) ;
2727+ }
2728+ if ( data . custom_stations ?. length > 0 ) {
2729+ localStorage . setItem ( 'radio_custom_stations' , JSON . stringify ( data . custom_stations ) ) ;
2730+ }
2731+ this . _updateStationSelect ( ) ;
2732+ }
2733+ } catch ( e ) {
2734+ console . log ( 'Compact card: Auto-restore not available:' , e ) ;
2735+ }
2736+ }
2737+
26312738 // --- State persistence ---
26322739 _saveState ( ) {
26332740 try {
@@ -3004,6 +3111,61 @@ class RadioBrowserCardCompact extends HTMLElement {
30043111 return div . innerHTML ;
30053112 }
30063113
3114+ // --- Export/Import (shared format with main card) ---
3115+ _exportFavorites ( ) {
3116+ const favorites = this . _loadFavorites ( ) ;
3117+ const customStations = this . _loadCustomStations ( ) ;
3118+ const data = {
3119+ version : '2.0' ,
3120+ exported : new Date ( ) . toISOString ( ) ,
3121+ favorites : favorites ,
3122+ custom_stations : customStations
3123+ } ;
3124+ const json = JSON . stringify ( data , null , 2 ) ;
3125+ const blob = new Blob ( [ json ] , { type : 'application/json' } ) ;
3126+ const url = URL . createObjectURL ( blob ) ;
3127+ const a = document . createElement ( 'a' ) ;
3128+ a . href = url ;
3129+ a . download = `radio-backup-${ new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] } .json` ;
3130+ a . click ( ) ;
3131+ URL . revokeObjectURL ( url ) ;
3132+ }
3133+
3134+ _importFavorites ( ) {
3135+ const input = document . createElement ( 'input' ) ;
3136+ input . type = 'file' ;
3137+ input . accept = 'application/json' ;
3138+ input . onchange = ( e ) => {
3139+ const file = e . target . files [ 0 ] ;
3140+ if ( ! file ) return ;
3141+ const reader = new FileReader ( ) ;
3142+ reader . onload = ( event ) => {
3143+ try {
3144+ const data = JSON . parse ( event . target . result ) ;
3145+ const parts = [ ] ;
3146+ if ( data . favorites && Array . isArray ( data . favorites ) ) {
3147+ localStorage . setItem ( 'radio_favorites' , JSON . stringify ( data . favorites ) ) ;
3148+ parts . push ( `${ data . favorites . length } favorites` ) ;
3149+ }
3150+ if ( data . custom_stations && Array . isArray ( data . custom_stations ) ) {
3151+ localStorage . setItem ( 'radio_custom_stations' , JSON . stringify ( data . custom_stations ) ) ;
3152+ parts . push ( `${ data . custom_stations . length } custom stations` ) ;
3153+ }
3154+ if ( parts . length > 0 ) {
3155+ this . _updateStationSelect ( ) ;
3156+ alert ( `Imported: ${ parts . join ( ', ' ) } !` ) ;
3157+ } else {
3158+ alert ( 'Invalid file format' ) ;
3159+ }
3160+ } catch ( error ) {
3161+ alert ( 'Error importing: ' + error . message ) ;
3162+ }
3163+ } ;
3164+ reader . readAsText ( file ) ;
3165+ } ;
3166+ input . click ( ) ;
3167+ }
3168+
30073169 // --- Render ---
30083170 render ( ) {
30093171 const c = this . _getThemeColors ( ) ;
@@ -3213,6 +3375,34 @@ class RadioBrowserCardCompact extends HTMLElement {
32133375 color: ${ c . primary } ;
32143376 text-decoration: none;
32153377 }
3378+
3379+ /* Backup row */
3380+ .compact-backup-row {
3381+ display: flex;
3382+ gap: 4px;
3383+ margin-top: 8px;
3384+ }
3385+ .compact-backup-btn {
3386+ flex: 1;
3387+ height: 26px;
3388+ border: 1px solid ${ c . surfaceLighter } ;
3389+ border-radius: 6px;
3390+ background: ${ c . surfaceLight } ;
3391+ color: ${ c . textSecondary } ;
3392+ font-size: 10px;
3393+ font-family: inherit;
3394+ cursor: pointer;
3395+ display: flex;
3396+ align-items: center;
3397+ justify-content: center;
3398+ gap: 4px;
3399+ transition: all 0.15s;
3400+ }
3401+ .compact-backup-btn:hover {
3402+ background: ${ c . surfaceLighter } ;
3403+ color: ${ c . text } ;
3404+ border-color: ${ c . primary } ;
3405+ }
32163406 </style>
32173407
32183408 <div class="compact-card">
@@ -3244,6 +3434,12 @@ class RadioBrowserCardCompact extends HTMLElement {
32443434 <input type="range" class="compact-volume" min="0" max="100" value="${ this . _volume } " style="--vol: ${ this . _volume } %;">
32453435 </div>
32463436
3437+ <!-- Backup buttons -->
3438+ <div class="compact-backup-row">
3439+ <button class="compact-backup-btn compact-export-btn" title="Export favorites & stations to JSON file">📤 Backup</button>
3440+ <button class="compact-backup-btn compact-import-btn" title="Import favorites & stations from JSON file">📥 Restore</button>
3441+ </div>
3442+
32473443 <audio class="compact-audio" style="display:none;"></audio>
32483444 </div>
32493445 ` ;
@@ -3296,6 +3492,16 @@ class RadioBrowserCardCompact extends HTMLElement {
32963492 if ( muteBtn ) {
32973493 muteBtn . addEventListener ( 'click' , ( ) => this . _toggleMute ( ) ) ;
32983494 }
3495+
3496+ // Backup buttons
3497+ const exportBtn = root . querySelector ( '.compact-export-btn' ) ;
3498+ if ( exportBtn ) {
3499+ exportBtn . addEventListener ( 'click' , ( ) => this . _exportFavorites ( ) ) ;
3500+ }
3501+ const importBtn = root . querySelector ( '.compact-import-btn' ) ;
3502+ if ( importBtn ) {
3503+ importBtn . addEventListener ( 'click' , ( ) => this . _importFavorites ( ) ) ;
3504+ }
32993505 }
33003506
33013507 disconnectedCallback ( ) {
0 commit comments