Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import requests, zipfile, os, shutil, io, json
|
3 |
+
|
4 |
+
# FILE DI CONFIGURAZIONE
|
5 |
+
SETTINGS_FILE = "settings.json"
|
6 |
+
DEFAULT_SETTINGS = {
|
7 |
+
"videolibrarypath": "special://profile/addon_data/plugin.video.s4me/videolibrary/",
|
8 |
+
"folder_tvshows": "Serie TV",
|
9 |
+
"folder_movies": "Film",
|
10 |
+
"videolibrary_kodi": False
|
11 |
+
}
|
12 |
+
|
13 |
+
def load_settings():
|
14 |
+
"""Carica le impostazioni dal file JSON, se presente, altrimenti usa quelle di default."""
|
15 |
+
if os.path.exists(SETTINGS_FILE):
|
16 |
+
try:
|
17 |
+
with open(SETTINGS_FILE, "r", encoding="utf-8") as f:
|
18 |
+
return json.load(f)
|
19 |
+
except Exception:
|
20 |
+
return DEFAULT_SETTINGS
|
21 |
+
else:
|
22 |
+
return DEFAULT_SETTINGS
|
23 |
+
|
24 |
+
def save_settings(new_settings):
|
25 |
+
"""Salva le impostazioni su file JSON."""
|
26 |
+
try:
|
27 |
+
with open(SETTINGS_FILE, "w", encoding="utf-8") as f:
|
28 |
+
json.dump(new_settings, f, indent=4, ensure_ascii=False)
|
29 |
+
return "Impostazioni salvate correttamente."
|
30 |
+
except Exception as e:
|
31 |
+
return f"Errore nel salvataggio delle impostazioni: {str(e)}"
|
32 |
+
|
33 |
+
def get_current_settings():
|
34 |
+
"""Restituisce le impostazioni correnti in formato JSON formattato."""
|
35 |
+
settings = load_settings()
|
36 |
+
return json.dumps(settings, indent=4, ensure_ascii=False)
|
37 |
+
|
38 |
+
def update_settings(videolibrarypath, folder_tvshows, folder_movies, videolibrary_kodi):
|
39 |
+
"""Aggiorna le impostazioni a partire dai valori forniti."""
|
40 |
+
new_settings = {
|
41 |
+
"videolibrarypath": videolibrarypath,
|
42 |
+
"folder_tvshows": folder_tvshows,
|
43 |
+
"folder_movies": folder_movies,
|
44 |
+
"videolibrary_kodi": videolibrary_kodi
|
45 |
+
}
|
46 |
+
return save_settings(new_settings)
|
47 |
+
|
48 |
+
def get_branches():
|
49 |
+
"""
|
50 |
+
Recupera la lista dei branch disponibili dal repository GitHub.
|
51 |
+
Priorità a 'stable' e 'master', il resto in ordine.
|
52 |
+
"""
|
53 |
+
url = "https://api.github.com/repos/stream4me/addon/branches"
|
54 |
+
try:
|
55 |
+
response = requests.get(url)
|
56 |
+
response.raise_for_status()
|
57 |
+
branches = [branch["name"] for branch in response.json()]
|
58 |
+
ordered = []
|
59 |
+
for b in ['stable', 'master']:
|
60 |
+
if b in branches:
|
61 |
+
ordered.append(b)
|
62 |
+
ordered.extend([b for b in branches if b not in ['stable', 'master']])
|
63 |
+
return ordered
|
64 |
+
except Exception:
|
65 |
+
return ["stable", "master"]
|
66 |
+
|
67 |
+
def update_from_zip(branch: str) -> str:
|
68 |
+
"""
|
69 |
+
Scarica lo zip del branch selezionato da GitHub,
|
70 |
+
lo estrae e sostituisce la cartella target 'plugin.video.s4me'.
|
71 |
+
Viene restituito un log delle operazioni.
|
72 |
+
"""
|
73 |
+
log = []
|
74 |
+
try:
|
75 |
+
url = f"https://github.com/stream4me/addon/archive/{branch}.zip"
|
76 |
+
log.append(f"Avvio download da: {url}")
|
77 |
+
response = requests.get(url, stream=True)
|
78 |
+
response.raise_for_status()
|
79 |
+
zip_filename = f"{branch}.zip"
|
80 |
+
with open(zip_filename, "wb") as f:
|
81 |
+
for chunk in response.iter_content(chunk_size=8192):
|
82 |
+
if chunk:
|
83 |
+
f.write(chunk)
|
84 |
+
log.append("Download completato.")
|
85 |
+
|
86 |
+
# Definizione delle directory
|
87 |
+
extract_base = f"plugin.video.s4me_{branch}"
|
88 |
+
target_dir = "plugin.video.s4me"
|
89 |
+
|
90 |
+
# Rimozione della cartella target se esiste già
|
91 |
+
if os.path.isdir(target_dir):
|
92 |
+
shutil.rmtree(target_dir)
|
93 |
+
log.append(f"Vecchia cartella '{target_dir}' rimossa.")
|
94 |
+
|
95 |
+
log.append("Inizio estrazione...")
|
96 |
+
with zipfile.ZipFile(zip_filename, "r") as zip_ref:
|
97 |
+
zip_ref.extractall(extract_base)
|
98 |
+
log.append("Estrazione completata.")
|
99 |
+
|
100 |
+
# GitHub estrae in una cartella denominata 'addon-<branch>'
|
101 |
+
extracted_folder = os.path.join(extract_base, f"addon-{branch}")
|
102 |
+
if os.path.isdir(extracted_folder):
|
103 |
+
os.rename(extracted_folder, target_dir)
|
104 |
+
log.append(f"Cartella rinominata in '{target_dir}'.")
|
105 |
+
else:
|
106 |
+
log.append("Errore: cartella estratta non trovata.")
|
107 |
+
|
108 |
+
# Pulizia finale
|
109 |
+
os.remove(zip_filename)
|
110 |
+
shutil.rmtree(extract_base)
|
111 |
+
log.append("Pulizia completata. Aggiornamento eseguito con successo.")
|
112 |
+
except Exception as e:
|
113 |
+
log.append(f"Errore durante l'aggiornamento: {str(e)}")
|
114 |
+
return "\n".join(log)
|
115 |
+
|
116 |
+
def perform_update(selected_branch: str) -> str:
|
117 |
+
"""Funzione chiamata dal pulsante di aggiornamento nell’interfaccia."""
|
118 |
+
return update_from_zip(selected_branch)
|
119 |
+
|
120 |
+
def build_interface():
|
121 |
+
"""Costruisce l’interfaccia completa utilizzando Gradio."""
|
122 |
+
branches = get_branches()
|
123 |
+
|
124 |
+
# CSS personalizzato per Smart TV: font ingranditi e margini per una migliore usabilità
|
125 |
+
css_custom = """
|
126 |
+
body { font-size: 32px; }
|
127 |
+
.gradio-container { font-size: 32px; }
|
128 |
+
.gr-button { font-size: 32px; padding: 20px; }
|
129 |
+
.gr-input { font-size: 32px; padding: 10px; }
|
130 |
+
.gr-dropdown { font-size: 32px; padding: 10px; }
|
131 |
+
/* Aggiungi margini maggiori per facilitare la navigazione con telecomando */
|
132 |
+
.gradio-container * {
|
133 |
+
margin: 20px;
|
134 |
+
}
|
135 |
+
"""
|
136 |
+
# Script JS per navigazione via tasti freccia (arrow keys)
|
137 |
+
js_script = """
|
138 |
+
<script>
|
139 |
+
document.addEventListener('keydown', function(event) {
|
140 |
+
let focused = document.activeElement;
|
141 |
+
if (!focused) return;
|
142 |
+
let focusable = Array.from(document.querySelectorAll('button, input, select, textarea, a'));
|
143 |
+
let index = focusable.indexOf(focused);
|
144 |
+
if (event.key === 'ArrowDown') {
|
145 |
+
index = (index + 1) % focusable.length;
|
146 |
+
focusable[index].focus();
|
147 |
+
} else if (event.key === 'ArrowUp') {
|
148 |
+
index = (index - 1 + focusable.length) % focusable.length;
|
149 |
+
focusable[index].focus();
|
150 |
+
}
|
151 |
+
});
|
152 |
+
</script>
|
153 |
+
"""
|
154 |
+
|
155 |
+
with gr.Blocks(css=css_custom, title="Stream4Me Update per Smart TV") as demo:
|
156 |
+
gr.Markdown("# Stream4Me - Aggiornamento e Configurazione per Smart TV")
|
157 |
+
gr.HTML(js_script) # Iniezione dello script JS per la navigazione via tasti
|
158 |
+
with gr.Tabs():
|
159 |
+
with gr.TabItem("Aggiornamento"):
|
160 |
+
gr.Markdown("## Aggiorna l'addon dal repository GitHub")
|
161 |
+
branch_dropdown = gr.Dropdown(choices=branches, value=branches[0], label="Seleziona Branch")
|
162 |
+
update_button = gr.Button("Avvia Aggiornamento")
|
163 |
+
update_log = gr.Textbox(label="Log di Aggiornamento", lines=10)
|
164 |
+
update_button.click(fn=perform_update, inputs=branch_dropdown, outputs=update_log)
|
165 |
+
|
166 |
+
with gr.TabItem("Impostazioni"):
|
167 |
+
gr.Markdown("## Configura le impostazioni")
|
168 |
+
settings = load_settings()
|
169 |
+
input_videolibrarypath = gr.Textbox(value=settings.get("videolibrarypath", ""), label="Percorso Video Library")
|
170 |
+
input_folder_tvshows = gr.Textbox(value=settings.get("folder_tvshows", ""), label="Cartella Serie TV")
|
171 |
+
input_folder_movies = gr.Textbox(value=settings.get("folder_movies", ""), label="Cartella Film")
|
172 |
+
input_videolibrary_kodi = gr.Checkbox(value=settings.get("videolibrary_kodi", False), label="Integrazione Video Library Kodi")
|
173 |
+
save_button = gr.Button("Salva Impostazioni")
|
174 |
+
settings_status = gr.Textbox(label="Stato Impostazioni", lines=2)
|
175 |
+
save_button.click(fn=update_settings,
|
176 |
+
inputs=[input_videolibrarypath, input_folder_tvshows, input_folder_movies, input_videolibrary_kodi],
|
177 |
+
outputs=settings_status)
|
178 |
+
gr.Markdown("### Impostazioni Correnti")
|
179 |
+
current_settings_box = gr.Textbox(value=get_current_settings(), label="Impostazioni Correnti", lines=8)
|
180 |
+
refresh_button = gr.Button("Ricarica Impostazioni")
|
181 |
+
refresh_button.click(fn=get_current_settings, inputs=[], outputs=current_settings_box)
|
182 |
+
|
183 |
+
with gr.TabItem("Informazioni"):
|
184 |
+
gr.Markdown("## Informazioni sul Progetto")
|
185 |
+
gr.Markdown("""
|
186 |
+
**Stream4Me** è un progetto per gestire e aggiornare l'addon per Kodi, ora migrato ad una interfaccia web ottimizzata per Smart TV.
|
187 |
+
|
188 |
+
**Funzionalità:**
|
189 |
+
- Aggiornamento automatico dall'API GitHub.
|
190 |
+
- Configurazione personalizzata tramite interfaccia web.
|
191 |
+
- Ottimizzato per dispositivi Smart TV con interfaccia responsive e navigazione semplificata.
|
192 |
+
|
193 |
+
**Repository:** [Stream4Me su GitHub](https://github.com/Stream4me/addon)
|
194 |
+
|
195 |
+
**Autore:** S4Me Team
|
196 |
+
""")
|
197 |
+
|
198 |
+
return demo
|
199 |
+
|
200 |
+
if __name__ == "__main__":
|
201 |
+
demo = build_interface()
|
202 |
+
demo.launch()
|