@@ -306,19 +306,20 @@ class RadioBrowserCard extends HTMLElement {
306306 return this . _favorites . some ( fav => fav . media_content_id === station . media_content_id ) ;
307307 }
308308
309- // Export/Import favorites
309+ // Export/Import favorites + custom stations
310310 exportFavorites ( ) {
311311 const data = {
312- version : '1 .0' ,
312+ version : '2 .0' ,
313313 exported : new Date ( ) . toISOString ( ) ,
314- favorites : this . _favorites
314+ favorites : this . _favorites ,
315+ custom_stations : this . _customStations
315316 } ;
316317 const json = JSON . stringify ( data , null , 2 ) ;
317318 const blob = new Blob ( [ json ] , { type : 'application/json' } ) ;
318319 const url = URL . createObjectURL ( blob ) ;
319320 const a = document . createElement ( 'a' ) ;
320321 a . href = url ;
321- a . download = `radio-favorites -${ new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] } .json` ;
322+ a . download = `radio-backup -${ new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] } .json` ;
322323 a . click ( ) ;
323324 URL . revokeObjectURL ( url ) ;
324325 }
@@ -335,16 +336,25 @@ class RadioBrowserCard extends HTMLElement {
335336 reader . onload = ( event ) => {
336337 try {
337338 const data = JSON . parse ( event . target . result ) ;
339+ const parts = [ ] ;
338340 if ( data . favorites && Array . isArray ( data . favorites ) ) {
339341 this . _favorites = data . favorites ;
340342 this . saveFavorites ( ) ;
343+ parts . push ( `${ data . favorites . length } favorites` ) ;
344+ }
345+ if ( data . custom_stations && Array . isArray ( data . custom_stations ) ) {
346+ this . _customStations = data . custom_stations ;
347+ this . saveCustomStations ( ) ;
348+ parts . push ( `${ data . custom_stations . length } custom stations` ) ;
349+ }
350+ if ( parts . length > 0 ) {
341351 this . updatePlaylist ( ) ;
342- alert ( `Imported ${ data . favorites . length } favorite stations !` ) ;
352+ alert ( `Imported: ${ parts . join ( ', ' ) } !` ) ;
343353 } else {
344354 alert ( 'Invalid file format' ) ;
345355 }
346356 } catch ( error ) {
347- alert ( 'Error importing favorites : ' + error . message ) ;
357+ alert ( 'Error importing: ' + error . message ) ;
348358 }
349359 } ;
350360 reader . readAsText ( file ) ;
@@ -2239,10 +2249,10 @@ class RadioBrowserCard extends HTMLElement {
22392249 </div>
22402250 </div>
22412251 <div class="settings-group">
2242- <div class="settings-label">Favorites </div>
2252+ <div class="settings-label">Backup & Restore </div>
22432253 <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>
2254+ <button class="settings-option" onclick="this.getRootNode().host.exportFavorites()">📤 Backup </button>
2255+ <button class="settings-option" onclick="this.getRootNode().host.importFavorites()">📥 Restore </button>
22462256 </div>
22472257 </div>
22482258 </div>
@@ -3004,6 +3014,61 @@ class RadioBrowserCardCompact extends HTMLElement {
30043014 return div . innerHTML ;
30053015 }
30063016
3017+ // --- Export/Import (shared format with main card) ---
3018+ _exportFavorites ( ) {
3019+ const favorites = this . _loadFavorites ( ) ;
3020+ const customStations = this . _loadCustomStations ( ) ;
3021+ const data = {
3022+ version : '2.0' ,
3023+ exported : new Date ( ) . toISOString ( ) ,
3024+ favorites : favorites ,
3025+ custom_stations : customStations
3026+ } ;
3027+ const json = JSON . stringify ( data , null , 2 ) ;
3028+ const blob = new Blob ( [ json ] , { type : 'application/json' } ) ;
3029+ const url = URL . createObjectURL ( blob ) ;
3030+ const a = document . createElement ( 'a' ) ;
3031+ a . href = url ;
3032+ a . download = `radio-backup-${ new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] } .json` ;
3033+ a . click ( ) ;
3034+ URL . revokeObjectURL ( url ) ;
3035+ }
3036+
3037+ _importFavorites ( ) {
3038+ const input = document . createElement ( 'input' ) ;
3039+ input . type = 'file' ;
3040+ input . accept = 'application/json' ;
3041+ input . onchange = ( e ) => {
3042+ const file = e . target . files [ 0 ] ;
3043+ if ( ! file ) return ;
3044+ const reader = new FileReader ( ) ;
3045+ reader . onload = ( event ) => {
3046+ try {
3047+ const data = JSON . parse ( event . target . result ) ;
3048+ const parts = [ ] ;
3049+ if ( data . favorites && Array . isArray ( data . favorites ) ) {
3050+ localStorage . setItem ( 'radio_favorites' , JSON . stringify ( data . favorites ) ) ;
3051+ parts . push ( `${ data . favorites . length } favorites` ) ;
3052+ }
3053+ if ( data . custom_stations && Array . isArray ( data . custom_stations ) ) {
3054+ localStorage . setItem ( 'radio_custom_stations' , JSON . stringify ( data . custom_stations ) ) ;
3055+ parts . push ( `${ data . custom_stations . length } custom stations` ) ;
3056+ }
3057+ if ( parts . length > 0 ) {
3058+ this . _updateStationSelect ( ) ;
3059+ alert ( `Imported: ${ parts . join ( ', ' ) } !` ) ;
3060+ } else {
3061+ alert ( 'Invalid file format' ) ;
3062+ }
3063+ } catch ( error ) {
3064+ alert ( 'Error importing: ' + error . message ) ;
3065+ }
3066+ } ;
3067+ reader . readAsText ( file ) ;
3068+ } ;
3069+ input . click ( ) ;
3070+ }
3071+
30073072 // --- Render ---
30083073 render ( ) {
30093074 const c = this . _getThemeColors ( ) ;
@@ -3213,6 +3278,34 @@ class RadioBrowserCardCompact extends HTMLElement {
32133278 color: ${ c . primary } ;
32143279 text-decoration: none;
32153280 }
3281+
3282+ /* Backup row */
3283+ .compact-backup-row {
3284+ display: flex;
3285+ gap: 4px;
3286+ margin-top: 8px;
3287+ }
3288+ .compact-backup-btn {
3289+ flex: 1;
3290+ height: 26px;
3291+ border: 1px solid ${ c . surfaceLighter } ;
3292+ border-radius: 6px;
3293+ background: ${ c . surfaceLight } ;
3294+ color: ${ c . textSecondary } ;
3295+ font-size: 10px;
3296+ font-family: inherit;
3297+ cursor: pointer;
3298+ display: flex;
3299+ align-items: center;
3300+ justify-content: center;
3301+ gap: 4px;
3302+ transition: all 0.15s;
3303+ }
3304+ .compact-backup-btn:hover {
3305+ background: ${ c . surfaceLighter } ;
3306+ color: ${ c . text } ;
3307+ border-color: ${ c . primary } ;
3308+ }
32163309 </style>
32173310
32183311 <div class="compact-card">
@@ -3244,6 +3337,12 @@ class RadioBrowserCardCompact extends HTMLElement {
32443337 <input type="range" class="compact-volume" min="0" max="100" value="${ this . _volume } " style="--vol: ${ this . _volume } %;">
32453338 </div>
32463339
3340+ <!-- Backup buttons -->
3341+ <div class="compact-backup-row">
3342+ <button class="compact-backup-btn compact-export-btn" title="Export favorites & stations to JSON file">📤 Backup</button>
3343+ <button class="compact-backup-btn compact-import-btn" title="Import favorites & stations from JSON file">📥 Restore</button>
3344+ </div>
3345+
32473346 <audio class="compact-audio" style="display:none;"></audio>
32483347 </div>
32493348 ` ;
@@ -3296,6 +3395,16 @@ class RadioBrowserCardCompact extends HTMLElement {
32963395 if ( muteBtn ) {
32973396 muteBtn . addEventListener ( 'click' , ( ) => this . _toggleMute ( ) ) ;
32983397 }
3398+
3399+ // Backup buttons
3400+ const exportBtn = root . querySelector ( '.compact-export-btn' ) ;
3401+ if ( exportBtn ) {
3402+ exportBtn . addEventListener ( 'click' , ( ) => this . _exportFavorites ( ) ) ;
3403+ }
3404+ const importBtn = root . querySelector ( '.compact-import-btn' ) ;
3405+ if ( importBtn ) {
3406+ importBtn . addEventListener ( 'click' , ( ) => this . _importFavorites ( ) ) ;
3407+ }
32993408 }
33003409
33013410 disconnectedCallback ( ) {
0 commit comments