ServerX commited on
Commit
08414ba
·
verified ·
1 Parent(s): b9551f2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +186 -33
app.py CHANGED
@@ -1,5 +1,5 @@
1
  import gradio as gr
2
- import requests, zipfile, os, shutil, json
3
 
4
  # -----------------------
5
  # Free API keys (TMDb e OMDb) ottenute automaticamente
@@ -17,8 +17,10 @@ free_keys = get_free_keys()
17
  TMDB_API_KEY = free_keys["tmdb_key"]
18
 
19
  # -----------------------
20
- # FILE DI CONFIGURAZIONE
21
  SETTINGS_FILE = "settings.json"
 
 
22
  DEFAULT_SETTINGS = {
23
  "videolibrarypath": "path/to/videolibrary",
24
  "folder_tvshows": "Serie TV",
@@ -61,6 +63,46 @@ def update_settings(videolibrarypath, folder_tvshows, folder_movies, videolibrar
61
  }
62
  return save_settings(new_settings)
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  # -----------------------
65
  # FUNZIONALITÀ DI AGGIORNAMENTO DELL'APPLICAZIONE
66
  def get_branches():
@@ -85,8 +127,7 @@ def get_branches():
85
  def update_from_zip(branch: str) -> str:
86
  """
87
  Scarica il file zip del branch selezionato da GitHub,
88
- lo estrae in una cartella target ("s4me_app") e restituisce un log delle operazioni.
89
- Questa funzione è completamente stand-alone e non utilizza dipendenze da Kodi.
90
  """
91
  log = []
92
  try:
@@ -101,11 +142,9 @@ def update_from_zip(branch: str) -> str:
101
  f.write(chunk)
102
  log.append("Download completato.")
103
 
104
- # Definizione delle directory: estraiamo in una cartella temporanea
105
  extract_base = f"s4me_app_{branch}"
106
  target_dir = "s4me_app"
107
 
108
- # Rimuovo la cartella target se esiste già
109
  if os.path.isdir(target_dir):
110
  shutil.rmtree(target_dir)
111
  log.append(f"Vecchia cartella '{target_dir}' rimossa.")
@@ -115,7 +154,6 @@ def update_from_zip(branch: str) -> str:
115
  zip_ref.extractall(extract_base)
116
  log.append("Estrazione completata.")
117
 
118
- # GitHub estrae in una cartella denominata 'addon-<branch>'
119
  extracted_folder = os.path.join(extract_base, f"addon-{branch}")
120
  if os.path.isdir(extracted_folder):
121
  os.rename(extracted_folder, target_dir)
@@ -123,7 +161,6 @@ def update_from_zip(branch: str) -> str:
123
  else:
124
  log.append("Errore: cartella estratta non trovata.")
125
 
126
- # Pulizia: rimozione del file zip e della cartella temporanea
127
  os.remove(zip_filename)
128
  shutil.rmtree(extract_base)
129
  log.append("Pulizia completata. Aggiornamento eseguito con successo.")
@@ -136,39 +173,116 @@ def perform_update(selected_branch: str) -> str:
136
  return update_from_zip(selected_branch)
137
 
138
  # -----------------------
139
- # FUNZIONALITÀ DI RICERCA FILM
140
- def search_movie(movie_title: str) -> str:
141
  """
142
- Cerca il film utilizzando l'API di TMDb e restituisce una lista formattata dei risultati.
143
- Utilizza automaticamente la free API key ottenuta.
144
  """
145
  if not movie_title:
146
- return "Inserisci il nome del film da cercare."
147
  url = f"https://api.themoviedb.org/3/search/movie?api_key={TMDB_API_KEY}&query={movie_title}"
148
  try:
149
  response = requests.get(url)
150
  response.raise_for_status()
151
  data = response.json()
152
  results = data.get("results", [])
153
- if not results:
154
- return "Nessun risultato trovato."
155
- output = []
156
  for movie in results:
157
  title = movie.get("title", "Sconosciuto")
158
  release_date = movie.get("release_date", "Data sconosciuta")
159
- overview = movie.get("overview", "")
160
- output.append(f"{title} ({release_date})\n{overview}\n")
161
- return "\n".join(output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  except Exception as e:
163
- return f"Errore durante la ricerca: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
  # -----------------------
166
  # COSTRUZIONE DELL'INTERFACCIA CON GRADIO
167
  def build_interface():
168
- """Costruisce l’interfaccia completa utilizzando Gradio."""
169
  branches = get_branches()
170
 
171
- # CSS personalizzato per Smart TV: font e padding maggiorati per una migliore usabilità
172
  css_custom = """
173
  body { font-size: 32px; }
174
  .gradio-container { font-size: 32px; }
@@ -177,7 +291,6 @@ def build_interface():
177
  .gr-dropdown { font-size: 32px; padding: 10px; }
178
  .gradio-container * { margin: 20px; }
179
  """
180
- # Script JS per navigazione tramite tasti freccia (utile con telecomando)
181
  js_script = """
182
  <script>
183
  document.addEventListener('keydown', function(event) {
@@ -196,9 +309,9 @@ def build_interface():
196
  </script>
197
  """
198
 
199
- with gr.Blocks(css=css_custom, title="Stream4Me Update per Smart TV") as demo:
200
- gr.Markdown("# Stream4Me - Aggiornamento, Configurazione e Ricerca Film")
201
- gr.HTML(js_script) # Iniezione dello script JS per la navigazione con telecomando
202
  with gr.Tabs():
203
  with gr.TabItem("Aggiornamento"):
204
  gr.Markdown("## Aggiorna l'applicazione dal repository GitHub")
@@ -224,23 +337,63 @@ def build_interface():
224
  refresh_button = gr.Button("Ricarica Impostazioni")
225
  refresh_button.click(fn=get_current_settings, inputs=[], outputs=current_settings_box)
226
 
227
- with gr.TabItem("Ricerca Film"):
228
  gr.Markdown("## Cerca un Film")
229
  film_input = gr.Textbox(placeholder="Inserisci il nome del film...", label="Nome del Film")
230
- search_button = gr.Button("Cerca")
231
- search_results = gr.Textbox(label="Risultati", lines=10)
232
- search_button.click(fn=search_movie, inputs=film_input, outputs=search_results)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
 
234
  with gr.TabItem("Informazioni"):
235
  gr.Markdown("## Informazioni sul Progetto")
236
  gr.Markdown("""
237
- **Stream4Me** è un progetto stand-alone per gestire l’aggiornamento e la configurazione di un’applicazione,
238
- completamente indipendente da Kodi e ottimizzata per Smart TV.
239
 
240
  **Funzionalità:**
241
  - Aggiornamento automatico dal repository GitHub.
242
  - Configurazione personalizzata tramite interfaccia web.
243
- - Ricerca di film tramite l'API di TMDb con free API key automatica.
 
 
244
  - Interfaccia responsive, con navigazione tramite telecomando.
245
 
246
  **Repository:** [Stream4Me su GitHub](https://github.com/Stream4me/addon)
 
1
  import gradio as gr
2
+ import requests, zipfile, os, shutil, json, re
3
 
4
  # -----------------------
5
  # Free API keys (TMDb e OMDb) ottenute automaticamente
 
17
  TMDB_API_KEY = free_keys["tmdb_key"]
18
 
19
  # -----------------------
20
+ # FILE DI CONFIGURAZIONE E VIDEOTECA
21
  SETTINGS_FILE = "settings.json"
22
+ LIBRARY_FILE = "videoteca.json"
23
+
24
  DEFAULT_SETTINGS = {
25
  "videolibrarypath": "path/to/videolibrary",
26
  "folder_tvshows": "Serie TV",
 
63
  }
64
  return save_settings(new_settings)
65
 
66
+ def load_library():
67
+ """Carica la videoteca dal file JSON."""
68
+ if os.path.exists(LIBRARY_FILE):
69
+ try:
70
+ with open(LIBRARY_FILE, "r", encoding="utf-8") as f:
71
+ return json.load(f)
72
+ except Exception:
73
+ return []
74
+ else:
75
+ return []
76
+
77
+ def save_library(lib):
78
+ """Salva la videoteca nel file JSON."""
79
+ with open(LIBRARY_FILE, "w", encoding="utf-8") as f:
80
+ json.dump(lib, f, indent=4, ensure_ascii=False)
81
+
82
+ def add_to_library(item_str: str, item_type: str) -> str:
83
+ """
84
+ Aggiunge un elemento (film o serie TV) alla videoteca.
85
+ L'elemento è una stringa formattata con [ID: ...].
86
+ """
87
+ lib = load_library()
88
+ match = re.search(r'\[ID: (\d+)\]', item_str)
89
+ if not match:
90
+ return "Impossibile estrarre l'ID dall'elemento."
91
+ item_id = match.group(1)
92
+ # Controlla duplicati
93
+ for item in lib:
94
+ if item["id"] == item_id and item["type"] == item_type:
95
+ return "Elemento già presente in videoteca."
96
+ new_item = {"id": item_id, "title": item_str, "type": item_type}
97
+ lib.append(new_item)
98
+ save_library(lib)
99
+ return "Elemento aggiunto in videoteca."
100
+
101
+ def get_library_items(item_type: str) -> list:
102
+ """Restituisce una lista di elementi della videoteca filtrata per tipo."""
103
+ lib = load_library()
104
+ return [item["title"] for item in lib if item["type"] == item_type]
105
+
106
  # -----------------------
107
  # FUNZIONALITÀ DI AGGIORNAMENTO DELL'APPLICAZIONE
108
  def get_branches():
 
127
  def update_from_zip(branch: str) -> str:
128
  """
129
  Scarica il file zip del branch selezionato da GitHub,
130
+ lo estrae in una cartella target ("s4me_app") e restituisce un log.
 
131
  """
132
  log = []
133
  try:
 
142
  f.write(chunk)
143
  log.append("Download completato.")
144
 
 
145
  extract_base = f"s4me_app_{branch}"
146
  target_dir = "s4me_app"
147
 
 
148
  if os.path.isdir(target_dir):
149
  shutil.rmtree(target_dir)
150
  log.append(f"Vecchia cartella '{target_dir}' rimossa.")
 
154
  zip_ref.extractall(extract_base)
155
  log.append("Estrazione completata.")
156
 
 
157
  extracted_folder = os.path.join(extract_base, f"addon-{branch}")
158
  if os.path.isdir(extracted_folder):
159
  os.rename(extracted_folder, target_dir)
 
161
  else:
162
  log.append("Errore: cartella estratta non trovata.")
163
 
 
164
  os.remove(zip_filename)
165
  shutil.rmtree(extract_base)
166
  log.append("Pulizia completata. Aggiornamento eseguito con successo.")
 
173
  return update_from_zip(selected_branch)
174
 
175
  # -----------------------
176
+ # FUNZIONALITÀ DI RICERCA E STREAMING PER FILM
177
+ def search_movie_list(movie_title: str):
178
  """
179
+ Cerca film tramite TMDb e restituisce una lista per il dropdown.
180
+ Formato: "Titolo (Data) [ID: movie_id]".
181
  """
182
  if not movie_title:
183
+ return []
184
  url = f"https://api.themoviedb.org/3/search/movie?api_key={TMDB_API_KEY}&query={movie_title}"
185
  try:
186
  response = requests.get(url)
187
  response.raise_for_status()
188
  data = response.json()
189
  results = data.get("results", [])
190
+ choices = []
 
 
191
  for movie in results:
192
  title = movie.get("title", "Sconosciuto")
193
  release_date = movie.get("release_date", "Data sconosciuta")
194
+ movie_id = movie.get("id")
195
+ choices.append(f"{title} ({release_date}) [ID: {movie_id}]")
196
+ return choices
197
+ except Exception as e:
198
+ return [f"Errore durante la ricerca: {str(e)}"]
199
+
200
+ def get_streaming_providers(movie_choice: str) -> str:
201
+ """
202
+ Data la scelta del film (contenente l'ID),
203
+ richiama l'endpoint TMDb per ottenere i provider streaming.
204
+ """
205
+ match = re.search(r'\[ID: (\d+)\]', movie_choice)
206
+ if not match:
207
+ return "Impossibile estrarre l'ID del film."
208
+ movie_id = match.group(1)
209
+ url = f"https://api.themoviedb.org/3/movie/{movie_id}/watch/providers?api_key={TMDB_API_KEY}"
210
+ try:
211
+ response = requests.get(url)
212
+ response.raise_for_status()
213
+ data = response.json()
214
+ results = data.get("results", {})
215
+ if "IT" in results:
216
+ providers = results["IT"].get("flatrate", [])
217
+ elif "US" in results:
218
+ providers = results["US"].get("flatrate", [])
219
+ else:
220
+ providers = []
221
+ if not providers:
222
+ return "Nessun canale streaming diretto trovato."
223
+ output = [provider.get("provider_name", "Sconosciuto") for provider in providers]
224
+ return "Canali streaming trovati:\n" + "\n".join(output)
225
  except Exception as e:
226
+ return f"Errore durante la ricerca dei canali: {str(e)}"
227
+
228
+ # -----------------------
229
+ # FUNZIONALITÀ DI RICERCA PER SERIE TV
230
+ def search_tv_list(tv_title: str):
231
+ """
232
+ Cerca serie TV tramite TMDb e restituisce una lista per il dropdown.
233
+ Formato: "Nome Serie (Data) [ID: tv_id]".
234
+ """
235
+ if not tv_title:
236
+ return []
237
+ url = f"https://api.themoviedb.org/3/search/tv?api_key={TMDB_API_KEY}&query={tv_title}"
238
+ try:
239
+ response = requests.get(url)
240
+ response.raise_for_status()
241
+ data = response.json()
242
+ results = data.get("results", [])
243
+ choices = []
244
+ for tv in results:
245
+ name = tv.get("name", "Sconosciuto")
246
+ first_air_date = tv.get("first_air_date", "Data sconosciuta")
247
+ tv_id = tv.get("id")
248
+ choices.append(f"{name} ({first_air_date}) [ID: {tv_id}]")
249
+ return choices
250
+ except Exception as e:
251
+ return [f"Errore durante la ricerca: {str(e)}"]
252
+
253
+ def load_tv_episodes(tv_series_choice: str) -> str:
254
+ """
255
+ Data la serie TV selezionata (con l'ID incluso), carica gli episodi della stagione 1.
256
+ Restituisce una lista formattata degli episodi.
257
+ """
258
+ match = re.search(r'\[ID: (\d+)\]', tv_series_choice)
259
+ if not match:
260
+ return "Impossibile estrarre l'ID della serie TV."
261
+ tv_id = match.group(1)
262
+ url = f"https://api.themoviedb.org/3/tv/{tv_id}/season/1?api_key={TMDB_API_KEY}"
263
+ try:
264
+ response = requests.get(url)
265
+ response.raise_for_status()
266
+ data = response.json()
267
+ episodes = data.get("episodes", [])
268
+ if not episodes:
269
+ return "Nessun episodio trovato per la stagione 1."
270
+ output = []
271
+ for ep in episodes:
272
+ ep_num = ep.get("episode_number")
273
+ name = ep.get("name", "Sconosciuto")
274
+ overview = ep.get("overview", "")
275
+ output.append(f"Episodio {ep_num}: {name}\n{overview}\n")
276
+ return "\n".join(output)
277
+ except Exception as e:
278
+ return f"Errore durante il caricamento degli episodi: {str(e)}"
279
 
280
  # -----------------------
281
  # COSTRUZIONE DELL'INTERFACCIA CON GRADIO
282
  def build_interface():
283
+ """Costruisce l’interfaccia completa con più tab."""
284
  branches = get_branches()
285
 
 
286
  css_custom = """
287
  body { font-size: 32px; }
288
  .gradio-container { font-size: 32px; }
 
291
  .gr-dropdown { font-size: 32px; padding: 10px; }
292
  .gradio-container * { margin: 20px; }
293
  """
 
294
  js_script = """
295
  <script>
296
  document.addEventListener('keydown', function(event) {
 
309
  </script>
310
  """
311
 
312
+ with gr.Blocks(css=css_custom, title="Stream4Me per Smart TV") as demo:
313
+ gr.Markdown("# Stream4Me - Aggiornamento, Configurazione, Ricerca e Videoteca")
314
+ gr.HTML(js_script)
315
  with gr.Tabs():
316
  with gr.TabItem("Aggiornamento"):
317
  gr.Markdown("## Aggiorna l'applicazione dal repository GitHub")
 
337
  refresh_button = gr.Button("Ricarica Impostazioni")
338
  refresh_button.click(fn=get_current_settings, inputs=[], outputs=current_settings_box)
339
 
340
+ with gr.TabItem("Ricerca Film & Streaming"):
341
  gr.Markdown("## Cerca un Film")
342
  film_input = gr.Textbox(placeholder="Inserisci il nome del film...", label="Nome del Film")
343
+ film_search_button = gr.Button("Cerca Film")
344
+ film_dropdown = gr.Dropdown(choices=[], label="Seleziona il Film")
345
+ film_search_button.click(fn=search_movie_list, inputs=film_input, outputs=film_dropdown)
346
+ gr.Markdown("### Streaming / Aggiungi in Videoteca")
347
+ film_stream_button = gr.Button("Guarda Film (Streaming)")
348
+ film_stream_results = gr.Textbox(label="Risultati Streaming", lines=10)
349
+ film_stream_button.click(fn=get_streaming_providers, inputs=film_dropdown, outputs=film_stream_results)
350
+ film_add_button = gr.Button("Aggiungi Film in Videoteca")
351
+ film_add_status = gr.Textbox(label="Stato Aggiunta", lines=2)
352
+ film_add_button.click(fn=lambda x: add_to_library(x, "film"), inputs=film_dropdown, outputs=film_add_status)
353
+
354
+ with gr.TabItem("Ricerca Serie TV & Videoteca"):
355
+ gr.Markdown("## Cerca una Serie TV")
356
+ tv_input = gr.Textbox(placeholder="Inserisci il nome della serie TV...", label="Nome Serie TV")
357
+ tv_search_button = gr.Button("Cerca Serie TV")
358
+ tv_dropdown = gr.Dropdown(choices=[], label="Seleziona la Serie TV")
359
+ tv_search_button.click(fn=search_tv_list, inputs=tv_input, outputs=tv_dropdown)
360
+ gr.Markdown("### Aggiungi Serie TV in Videoteca")
361
+ tv_add_button = gr.Button("Aggiungi Serie TV in Videoteca")
362
+ tv_add_status = gr.Textbox(label="Stato Aggiunta", lines=2)
363
+ tv_add_button.click(fn=lambda x: add_to_library(x, "tv"), inputs=tv_dropdown, outputs=tv_add_status)
364
+
365
+ with gr.TabItem("Videoteca"):
366
+ gr.Markdown("## Film in Videoteca")
367
+ film_library_dropdown = gr.Dropdown(choices=get_library_items("film"), label="Film in Videoteca")
368
+ film_library_button = gr.Button("Guarda Film (Streaming)")
369
+ film_library_results = gr.Textbox(label="Streaming Film", lines=10)
370
+ film_library_button.click(fn=get_streaming_providers, inputs=film_library_dropdown, outputs=film_library_results)
371
+
372
+ gr.Markdown("## Serie TV in Videoteca")
373
+ tv_library_dropdown = gr.Dropdown(choices=get_library_items("tv"), label="Serie TV in Videoteca")
374
+ tv_library_button = gr.Button("Carica Episodi (Stagione 1)")
375
+ tv_library_results = gr.Textbox(label="Episodi", lines=10)
376
+ tv_library_button.click(fn=load_tv_episodes, inputs=tv_library_dropdown, outputs=tv_library_results)
377
+
378
+ gr.Markdown("### Ricarica Videoteca")
379
+ refresh_library_button = gr.Button("Ricarica Lista Videoteca")
380
+ def refresh_library():
381
+ return get_library_items("film"), get_library_items("tv")
382
+ film_lib_out, tv_lib_out = gr.Dropdown(choices=[], label=""), gr.Dropdown(choices=[], label="")
383
+ refresh_library_button.click(fn=refresh_library, inputs=[], outputs=[film_library_dropdown, tv_library_dropdown])
384
 
385
  with gr.TabItem("Informazioni"):
386
  gr.Markdown("## Informazioni sul Progetto")
387
  gr.Markdown("""
388
+ **Stream4Me** è un progetto stand-alone per gestire l’aggiornamento, la configurazione, la ricerca e la videoteca di film e serie TV,
389
+ completamente indipendente da Kodi e ottimizzato per Smart TV.
390
 
391
  **Funzionalità:**
392
  - Aggiornamento automatico dal repository GitHub.
393
  - Configurazione personalizzata tramite interfaccia web.
394
+ - Ricerca film e serie TV tramite TMDb.
395
+ - Aggiunta in videoteca e visualizzazione dei provider streaming per i film.
396
+ - Caricamento degli episodi (es. stagione 1) per le serie TV, per poterle guardare in sequenza.
397
  - Interfaccia responsive, con navigazione tramite telecomando.
398
 
399
  **Repository:** [Stream4Me su GitHub](https://github.com/Stream4me/addon)