@@ -246,24 +246,42 @@ async function renderBtr() {
246246 data = await r . json ( ) ;
247247 } catch { sec . hidden = true ; return ; }
248248
249- const tv = data . tv || [ ] ;
250- const pods = data . podcasts || [ ] ;
249+ const tv = data . tv || [ ] , radio = data . radio || [ ] , pods = data . podcasts || [ ] , movies = data . movies || [ ] ;
251250 sec . hidden = false ;
252- const item = ( href , img , ph , label ) =>
253- `<a class="btr-item" href="${ escAttr ( href ) } " target="_blank">` +
251+
252+ // Playable tiles carry data-player (open in a modal); the rest link out.
253+ const tile = ( player , url , img , ph , label ) =>
254+ `<a class="btr-item" ${ player ? `href="#" data-player="${ escAttr ( player ) } "` : `href="${ escAttr ( url ) } " target="_blank"` } >` +
254255 ( img ? `<img src="${ escAttr ( img ) } " alt="" loading="lazy" referrerpolicy="no-referrer" />` : `<span class="btr-ph">${ ph } </span>` ) +
255256 `<span class="btr-label">${ escapeHtml ( label ) } </span></a>` ;
256257 const group = ( title , items ) => items . length
257258 ? `<div class="btr-group"><h3>${ title } </h3><div class="btr-items">${ items . join ( '' ) } </div></div>` : '' ;
258259
259- if ( ! tv . length && ! pods . length ) {
260- el ( 'btr' ) . innerHTML = '<p class="muted">Connected — add Live TV or podcast favorites on bittorrented.com and they’ll show up here.</p>' ;
260+ if ( ! tv . length && ! radio . length && ! pods . length && ! movies . length ) {
261+ el ( 'btr' ) . innerHTML = '<p class="muted">Connected — add Live TV, radio, podcast or movie favorites on bittorrented.com and they’ll show up here.</p>' ;
261262 return ;
262263 }
263264 el ( 'btr' ) . innerHTML =
264- group ( 'Live TV' , tv . slice ( 0 , 12 ) . map ( ( c ) => item ( c . url , c . logo , '📺' , c . name ) ) ) +
265- group ( 'Podcasts' , pods . slice ( 0 , 12 ) . map ( ( p ) => item ( p . url , p . image , '🎙' , p . title ) ) ) ;
265+ group ( 'Live TV' , tv . slice ( 0 , 12 ) . map ( ( c ) => tile ( c . player , c . url , c . logo , '📺' , c . name ) ) ) +
266+ group ( 'Podcasts' , pods . slice ( 0 , 12 ) . map ( ( p ) => tile ( ( p . episodes || [ ] ) [ 0 ] ?. player , p . url , p . image , '🎙' , p . title ) ) ) +
267+ group ( 'Radio' , radio . slice ( 0 , 12 ) . map ( ( s ) => tile ( null , s . url , s . logo , '📻' , s . name ) ) ) +
268+ group ( 'Movies' , movies . slice ( 0 , 12 ) . map ( ( m ) => tile ( null , m . url , m . poster , '🎬' , m . title ) ) ) ;
269+ }
270+
271+ // Open a bittorrented /api/player URL in a themed modal iframe.
272+ function openPlayer ( url ) {
273+ const sep = url . includes ( '?' ) ? '&' : '?' ;
274+ el ( 'player-frame' ) . src = `${ url } ${ sep } theme=dark&bg=${ encodeURIComponent ( '#05070d' ) } &accent=${ encodeURIComponent ( '#34e7ff' ) } ` ;
275+ el ( 'player-modal' ) . hidden = false ;
266276}
277+ function closePlayer ( ) { el ( 'player-modal' ) . hidden = true ; el ( 'player-frame' ) . src = 'about:blank' ; }
278+ el ( 'btr' ) . addEventListener ( 'click' , ( e ) => {
279+ const a = e . target . closest ( '[data-player]' ) ;
280+ if ( a ) { e . preventDefault ( ) ; openPlayer ( a . getAttribute ( 'data-player' ) ) ; }
281+ } ) ;
282+ el ( 'player-close' ) . addEventListener ( 'click' , closePlayer ) ;
283+ el ( 'player-modal' ) . addEventListener ( 'click' , ( e ) => { if ( e . target === el ( 'player-modal' ) ) closePlayer ( ) ; } ) ;
284+ document . addEventListener ( 'keydown' , ( e ) => { if ( e . key === 'Escape' ) closePlayer ( ) ; } ) ;
267285
268286renderMotd ( ) ;
269287renderBtr ( ) ;
0 commit comments