44 < meta charset ="UTF-8 " />
55 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 " />
66 < title > Sito Streaming</ title >
7- <!-- Tailwind CSS CDN -->
87 < script src ="https://cdn.tailwindcss.com "> </ script >
98 < style >
109 [tabindex ]: focus { outline : 3px solid # 3B82F6 ; }
1413 </ style >
1514</ head >
1615< body class ="bg-gray-100 text-gray-800 font-sans leading-normal ">
17- <!-- Navbar -->
1816 < header class ="bg-gray-900 text-white sticky top-0 z-50 ">
1917 < nav class ="max-w-screen-2xl mx-auto flex flex-wrap items-center justify-between p-4 ">
2018 < h1 class ="text-2xl sm:text-3xl font-bold "> My Stream</ h1 >
@@ -32,7 +30,6 @@ <h1 class="text-2xl sm:text-3xl font-bold">My Stream</h1>
3230 </ nav >
3331 </ header >
3432
35- <!-- Content -->
3633 < main class ="max-w-screen-2xl mx-auto p-4 ">
3734 < div id ="gallery "> </ div >
3835 </ main >
@@ -43,91 +40,117 @@ <h1 class="text-2xl sm:text-3xl font-bold">My Stream</h1>
4340 film : 'https://raw.githubusercontent.com/Server21/IPTV-Pro/refs/heads/master/Random/film.json' ,
4441 series_tv : 'https://raw.githubusercontent.com/Server21/IPTV-Pro/refs/heads/master/Random/series_tv.json'
4542 } ;
46- let currentType = 'anime' , rawData = [ ] , flatItems = [ ] ;
47- const gallery = document . getElementById ( 'gallery' ) ;
48- const searchInput = document . getElementById ( 'search' ) ;
49- const genreFilter = document . getElementById ( 'genreFilter' ) ;
50- const navButtons = document . querySelectorAll ( 'nav button' ) ;
5143
52- async function loadData ( type ) {
53- currentType = type ; rawData = [ ] ; flatItems = [ ] ;
54- const res = await fetch ( dataFiles [ type ] ) ; rawData = await res . json ( ) ;
55- if ( [ 'anime' , 'series_tv' ] . includes ( type ) ) rawData . forEach ( item => {
56- item . seasons . forEach ( season => season . episodes . forEach ( ep => flatItems . push ( {
57- title :item . title ,
58- season :season . name ,
59- episode :ep . episode_number ,
60- name :ep . name ,
61- file :ep . file ,
62- overview :ep . overview ,
63- poster :ep . still_path || item . poster_path ,
64- genres :item . genres
65- } ) ) ) ;
66- } ) ; else flatItems = rawData . map ( f => ( {
67- title :f . title ,
68- season :'' ,
69- episode :'' ,
70- name :f . title ,
71- file :f . url_video ,
72- overview :f . overview ,
73- poster :f . poster_path ,
74- genres :f . genres
75- } ) ) ;
44+ let currentType = 'anime' , rawData = [ ] , flatItems = [ ] ;
45+ const gallery = document . getElementById ( 'gallery' ) ;
46+ const searchInput = document . getElementById ( 'search' ) ;
47+ const genreFilter = document . getElementById ( 'genreFilter' ) ;
48+ const navButtons = document . querySelectorAll ( 'nav button' ) ;
49+
50+ async function loadData ( type ) {
51+ currentType = type ; rawData = [ ] ; flatItems = [ ] ;
52+ const res = await fetch ( dataFiles [ type ] ) ; rawData = await res . json ( ) ;
53+ if ( [ 'anime' , 'series_tv' ] . includes ( type ) ) {
54+ rawData . forEach ( ( item , seriesIndex ) => {
55+ item . seasons . forEach ( ( season , seasonIndex ) => {
56+ season . episodes . forEach ( ( ep , episodeIndex ) => {
57+ flatItems . push ( {
58+ ...ep ,
59+ title : item . title ,
60+ seasonName : season . name ,
61+ seasonIndex,
62+ seriesIndex,
63+ episodeIndex,
64+ poster : ep . still_path || item . poster_path ,
65+ genres : item . genres
66+ } ) ;
67+ } ) ;
68+ } ) ;
69+ } ) ;
70+ } else {
71+ flatItems = rawData . map ( ( f , idx ) => ( {
72+ ...f ,
73+ seasonName : '' ,
74+ seasonIndex : - 1 ,
75+ seriesIndex : idx ,
76+ episodeIndex : - 1 ,
77+ poster : f . poster_path ,
78+ genres : f . genres
79+ } ) ) ;
80+ }
7681 populateGenres ( ) ; renderGallery ( ) ;
7782 }
78- function populateGenres ( ) {
79- const opts = [ '<option value="">Tutti i generi</option>' ] ;
80- new Set ( flatItems . flatMap ( i => i . genres ) ) . forEach ( g => opts . push ( `<option value="${ g } ">${ g } </option>` ) ) ;
81- genreFilter . innerHTML = opts . join ( '' ) ;
83+
84+ function populateGenres ( ) {
85+ const opts = [ '<option value="">Tutti i generi</option>' ] ;
86+ new Set ( flatItems . flatMap ( i => i . genres ) ) . forEach ( g => opts . push ( `<option value="${ g } ">${ g } </option>` ) ) ;
87+ genreFilter . innerHTML = opts . join ( '' ) ;
8288 }
83- function renderGallery ( ) {
84- gallery . innerHTML = '' ; const q = searchInput . value . toLowerCase ( ) , g = genreFilter . value ;
85- if ( [ 'anime' , 'series_tv' ] . includes ( currentType ) ) rawData . forEach ( item => {
86- const section = document . createElement ( 'section' ) ;
87- const h2 = document . createElement ( 'h2' ) ;
88- h2 . textContent = item . title ; h2 . className = 'text-2xl font-bold mt-6' ;
89- section . append ( h2 ) ;
90- item . seasons . forEach ( season => {
91- const h3 = document . createElement ( 'h3' ) ;
92- h3 . textContent = season . name ; h3 . className = 'text-xl mt-3 font-semibold' ;
93- section . append ( h3 ) ;
94- const list = document . createElement ( 'div' ) ; list . className = 'grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3' ;
95- season . episodes . forEach ( ep => {
96- if ( ep . name . toLowerCase ( ) . includes ( q ) && ( ! g || item . genres . includes ( g ) ) ) {
97- const card = document . createElement ( 'div' ) ;
98- card . className = 'cursor-pointer' ;
99- card . innerHTML = `
100- <img src="${ ep . still_path || item . poster_path } " class="w-full h-24 object-cover rounded" />
101- <p class="mt-1 text-sm truncate">${ ep . name } </p>
102- ` ;
103- card . addEventListener ( 'click' , ( ) => {
104- const params = new URLSearchParams ( {
105- video : ep . file ,
106- title : item . title ,
107- season : season . name ,
108- episode : ep . episode_number ,
109- name : ep . name ,
110- overview : ep . overview
89+
90+ function renderGallery ( ) {
91+ gallery . innerHTML = '' ;
92+ const q = searchInput . value . toLowerCase ( ) , g = genreFilter . value ;
93+ if ( [ 'anime' , 'series_tv' ] . includes ( currentType ) ) {
94+ rawData . forEach ( ( item , seriesIndex ) => {
95+ const sec = document . createElement ( 'section' ) ;
96+ const h2 = document . createElement ( 'h2' ) ;
97+ h2 . textContent = item . title ;
98+ h2 . className = 'text-2xl font-bold mt-6' ;
99+ sec . append ( h2 ) ;
100+ item . seasons . forEach ( ( season , seasonIndex ) => {
101+ const h3 = document . createElement ( 'h3' ) ;
102+ h3 . textContent = season . name ;
103+ h3 . className = 'text-xl mt-3 font-semibold' ;
104+ sec . append ( h3 ) ;
105+ const list = document . createElement ( 'div' ) ;
106+ list . className = 'grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3' ;
107+ season . episodes . forEach ( ( ep , episodeIndex ) => {
108+ if ( ep . name . toLowerCase ( ) . includes ( q ) && ( ! g || item . genres . includes ( g ) ) ) {
109+ const card = document . createElement ( 'div' ) ;
110+ card . className = 'cursor-pointer' ;
111+ card . innerHTML = `
112+ <img src="${ ep . still_path || item . poster_path } " class="w-full h-24 object-cover rounded" />
113+ <p class="mt-1 text-sm truncate">${ ep . name } </p>
114+ ` ;
115+ card . addEventListener ( 'click' , ( ) => {
116+ const params = new URLSearchParams ( {
117+ videoUrl : ep . file ,
118+ contentType : currentType ,
119+ title : item . title ,
120+ seasonName : season . name ,
121+ seasonIndex,
122+ episodeIndex,
123+ skipIntroStart : ep . skipIntroStart ,
124+ skipIntroEnd : ep . skipIntroEnd ,
125+ outroStart : ep . outroStart ,
126+ returnTo : window . location . pathname
127+ } ) ;
128+ window . location . href = `player.html?${ params . toString ( ) } ` ;
111129 } ) ;
112- window . location . href = `player.html? ${ params . toString ( ) } ` ;
113- } ) ;
114- list . append ( card ) ;
115- }
130+ list . append ( card ) ;
131+ }
132+ } ) ;
133+ sec . append ( list ) ;
116134 } ) ;
117- section . append ( list ) ;
135+ gallery . append ( sec ) ;
118136 } ) ;
119- gallery . append ( section ) ;
120- } ) ; else {
121- const grid = document . createElement ( 'div' ) ; grid . className = 'grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4' ;
122- flatItems . forEach ( i => {
123- if ( i . title . toLowerCase ( ) . includes ( q ) && ( ! g || i . genres . includes ( g ) ) ) {
124- const c = document . createElement ( 'div' ) ; c . className = 'bg-white p-2 rounded shadow cursor-pointer hover:shadow-lg' ;
125- c . innerHTML = `<img src="${ i . poster } " class="w-full h-32 object-cover rounded"/><h3 class="mt-2 text-lg truncate">${ i . title } </h3>` ;
126- c . addEventListener ( 'click' , ( ) => {
137+ } else {
138+ const grid = document . createElement ( 'div' ) ;
139+ grid . className = 'grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4' ;
140+ flatItems . forEach ( i => {
141+ if ( i . title . toLowerCase ( ) . includes ( q ) && ( ! g || i . genres . includes ( g ) ) ) {
142+ const c = document . createElement ( 'div' ) ;
143+ c . className = 'bg-white p-2 rounded shadow cursor-pointer hover:shadow-lg' ;
144+ c . innerHTML = `
145+ <img src="${ i . poster } " class="w-full h-32 object-cover rounded"/>
146+ <h3 class="mt-2 text-lg truncate">${ i . title } </h3>
147+ ` ;
148+ c . addEventListener ( 'click' , ( ) => {
127149 const params = new URLSearchParams ( {
128- video : i . file ,
150+ videoUrl : i . url_video ,
151+ contentType : 'film' ,
129152 title : i . title ,
130- overview : i . overview
153+ returnTo : window . location . pathname
131154 } ) ;
132155 window . location . href = `player.html?${ params . toString ( ) } ` ;
133156 } ) ;
@@ -137,9 +160,10 @@ <h1 class="text-2xl sm:text-3xl font-bold">My Stream</h1>
137160 gallery . append ( grid ) ;
138161 }
139162 }
140- searchInput . addEventListener ( 'input' , renderGallery ) ;
141- genreFilter . addEventListener ( 'change' , renderGallery ) ;
142- navButtons . forEach ( b => b . addEventListener ( 'click' , ( ) => loadData ( b . dataset . type ) ) ) ;
163+
164+ searchInput . addEventListener ( 'input' , renderGallery ) ;
165+ genreFilter . addEventListener ( 'change' , renderGallery ) ;
166+ navButtons . forEach ( b => b . addEventListener ( 'click' , ( ) => loadData ( b . dataset . type ) ) ) ;
143167 loadData ( currentType ) ;
144168 </ script >
145169</ body >
0 commit comments