diff --git a/main.py b/main.py
index 416ceee..8bfd406 100644
--- a/main.py
+++ b/main.py
@@ -1,9 +1,558 @@
import time
-
import flet as ft
-
from components.functions import *
from components.widgets import *
+import os
+import shutil
+import socket
+from typing import Dict
+from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
+import threading
+import webbrowser
+from functools import partial
+
+os.environ["FLET_SECRET_KEY"] = "zxczxczxcSS"
+os.environ["FLET_UPLOAD_DIR"] = "assets/uploads"
+os.environ["FLET_ASSETS_DIR"] = "assets"
+assets_dir = "assets"
+upload_dir = os.path.join(assets_dir, "uploads")
+
+# Ensure upload directory exists
+os.makedirs(upload_dir, exist_ok=True)
+
+def get_local_ip():
+ try:
+ # Crear un socket UDP para obtener la IP local
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.connect(("8.8.8.8", 80))
+ ip = s.getsockname()[0]
+ s.close()
+ return ip
+ except Exception:
+ return "0.0.0.0" # Fallback a todas las interfaces si no se puede determinar la IP
+
+class CustomHandler(SimpleHTTPRequestHandler):
+ def __init__(self, *args, directory=None, **kwargs):
+ super().__init__(*args, directory=directory, **kwargs)
+
+ def do_GET(self):
+ if self.path == '/':
+ self.send_response(200)
+ self.send_header("Content-type", "text/html")
+ self.end_headers()
+ self.wfile.write(self.generate_html().encode())
+ else:
+ # Manejar el error de favicon.ico
+ if self.path == '/favicon.ico':
+ self.send_response(404)
+ self.end_headers()
+ return
+ super().do_GET()
+
+ def handle_one_request(self):
+ try:
+ super().handle_one_request()
+ except BrokenPipeError:
+ # Ignorar errores de pipe roto
+ pass
+
+ def generate_html(self):
+ files = os.listdir(upload_dir)
+ html_content = f"""
+
+
+
+
+
+ Servidor July
+
+
+
+
+
+
+
Servidor Archivos (july 🐎)
+ {"
No hay archivos disponibles para descargar.
" if not files else f'''
+
+
+ Nombre del archivo |
+ Acciones |
+
+ {"".join([self.generate_file_row(file) for file in files])}
+
+ '''}
+
+
+
+
+
+ """
+ return html_content
+
+ def generate_file_row(self, filename):
+ return f"""
+
+ {filename} |
+ Descargar |
+
+ """
+
+class FileUploader(ft.Container):
+ def __init__(
+ self,
+ ring_size:int = 20,
+ server_port:int = 8000
+ ):
+ super().__init__()
+
+ self.server_port = server_port
+ self.server = None
+ self.server_thread = None
+ self.is_server_running = False
+ self.local_ip = get_local_ip()
+
+ self.ring_size = ring_size
+ self.icon_size = self.ring_size * 0.75
+ self.progs_ring: Dict[str, ft.ProgressBar] = {}
+ self.anim_switchers: Dict[str, ft.AnimatedSwitcher] = {}
+ self.files_column = ft.Column()
+
+ self.fp = ft.FilePicker(
+ on_result=self.handle_file_picked
+ )
+
+ self.upload_button = ft.ElevatedButton("Subir", on_click=self.handle_upload, visible=False)
+
+ # Server controls con IP local
+ self.server_status = ft.Text("Apagado", color="red")
+ self.ip_text = ft.Text(f"IP: {self.local_ip}", visible=False)
+ self.server_button = ft.ElevatedButton(
+ "Encender",
+ icon=ft.icons.PLAY_ARROW,
+ on_click=self.toggle_server,
+ bgcolor=ft.colors.GREEN
+ )
+
+ self.server_url = ft.TextButton(
+ "Abrir en el navegador",
+ icon=ft.icons.OPEN_IN_BROWSER,
+ on_click=lambda _: webbrowser.open(f"http://{self.local_ip}:{self.server_port}"),
+ visible=False
+ )
+
+ self.content = ft.Column(
+ [
+ ft.ElevatedButton(
+ "Seleccionar archivos",
+ icon=ft.icons.UPLOAD_FILE,
+ on_click=lambda _: self.fp.pick_files(
+ allow_multiple=True,
+ allowed_extensions=["jpg", "png", "jpeg", "pdf", "mp4", "mp3", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "bat", "sh", "py", "c", "cpp", "java", "css", "js", "html", "php", "rb", "go", "js", "py", "cs", "json", "xml", "svg", "psd", "ai", "eps", "indd", "ps", "pdf"]
+ ),
+ ),
+ self.server_button,
+ ft.Row(
+ [
+ self.server_status,
+ self.ip_text,
+ ],
+ alignment=ft.MainAxisAlignment.START
+ ),
+ self.server_url,
+ self.upload_button,
+ self.files_column
+ ]
+ )
+
+ def handle_file_picked(self, e: ft.FilePickerResultEvent):
+ if not e.files:
+ return
+
+ # first of all we remove evry file from assets/uploads
+ for f in os.listdir(upload_dir):
+ os.remove(os.path.join(upload_dir, f))
+
+
+ self.upload_button.visible = True
+ self.upload_button.update()
+ self.progs_ring = {}
+ self.anim_switchers = {}
+ self.files_column.controls.clear()
+
+ for f in e.files:
+ progress_ring = ft.ProgressRing(
+ value=0,
+ width=self.ring_size,
+ height=self.ring_size,
+ color=ft.colors.GREEN
+ )
+
+ switcher = ft.AnimatedSwitcher(
+ content=ft.Container(
+ width=self.ring_size,
+ height=self.ring_size,
+ content=ft.Icon(
+ ft.icons.UPLOAD_FILE,
+ expand=True,
+ size=self.icon_size
+ ),
+ ),
+ duration=300,
+ reverse_duration=100,
+ transition=ft.AnimatedSwitcherTransition.SCALE
+ )
+
+ self.progs_ring[f.name] = progress_ring
+ self.anim_switchers[f.name] = switcher
+
+ self.files_column.controls.append(
+ ft.Row(
+ [
+ ft.Stack([progress_ring, switcher]),
+ ft.Text(f.name),
+ ]
+ )
+ )
+ self.update()
+
+ def handle_upload(self, e):
+ if self.fp.result and self.fp.result.files:
+ for f in self.fp.result.files:
+ try:
+ dest_path = os.path.join(upload_dir, f.name)
+ shutil.copy2(f.path, dest_path)
+
+ self.progs_ring[f.name].value = 100
+ self.anim_switchers[f.name].content = ft.Container(
+ width=self.ring_size,
+ height=self.ring_size,
+ content=ft.Icon(
+ ft.icons.DONE,
+ expand=True,
+ size=self.icon_size
+ ),
+ )
+ except Exception as ex:
+ print(f"Error subiendo {f.name}: {str(ex)}")
+ self.progs_ring[f.name].color = ft.colors.RED
+ self.anim_switchers[f.name].content = ft.Container(
+ width=self.ring_size,
+ height=self.ring_size,
+ content=ft.Icon(
+ ft.icons.ERROR,
+ expand=True,
+ size=self.icon_size
+ ),
+ )
+ finally:
+ self.progs_ring[f.name].update()
+ self.anim_switchers[f.name].update()
+
+ def start_server(self):
+ if not os.path.exists(upload_dir):
+ os.makedirs(upload_dir)
+
+ handler = partial(CustomHandler, directory=assets_dir)
+ # Cambiar 'localhost' por '0.0.0.0' para escuchar en todas las interfaces
+ self.server = ThreadingHTTPServer(('0.0.0.0', self.server_port), handler)
+ self.server_thread = threading.Thread(target=self.server.serve_forever)
+ self.server_thread.daemon = True
+ self.server_thread.start()
+ self.is_server_running = True
+
+ self.server_status.value = f"Server: Running on port {self.server_port}"
+ self.server_status.color = "green"
+ self.server_button.text = "Stop Server"
+ self.server_button.icon = ft.icons.STOP
+ self.server_button.bgcolor = ft.colors.RED
+ self.server_url.visible = True
+ self.ip_text.visible = True
+ self.update()
+
+ def stop_server(self):
+ if self.server:
+ self.server.shutdown()
+ self.server.server_close()
+ self.is_server_running = False
+
+ self.server_status.value = "Apagado"
+ self.server_status.color = "red"
+ self.server_button.text = "Encender"
+ self.server_button.icon = ft.icons.PLAY_ARROW
+ self.server_button.bgcolor = ft.colors.GREEN
+ self.server_url.visible = False
+ self.ip_text.visible = False
+
+ self.update()
+
+ def toggle_server(self, e):
+ if self.is_server_running:
+ self.stop_server()
+ else:
+ self.start_server()
+
+ def did_mount(self):
+ self.page.overlay.append(self.fp)
+ self.page.update()
+ return super().did_mount()
+
+ def will_unmount(self):
+ self.stop_server()
+ return super().did_mount()
class RusContactos(ft.Tabs):
@@ -458,14 +1007,29 @@ def on_resize(e):
content=RusContactos(),
),
),
+ ft.Tab(
+ text="FED",
+ content=ft.Container(
+ ft.Tabs(
+ [
+ TabFederacionFranquicias(),
+
+ ],
+ tab_alignment=ft.TabAlignment.CENTER,
+ expand=True,
+ ),
+ expand=True,
+ ),
+ ),
ft.Tab(
text="GENERAL",
content=ft.Container(
ft.Tabs(
[
TabGeneralPatentes(),
- TabFederacionFranquicias(),
ft.Tab(text="Contador Billetes", content=Billetes()),
+ ft.Tab(text="Server", content=FileUploader()),
+
],
tab_alignment=ft.TabAlignment.CENTER,
expand=True,