Skip to content

Commit ee69c9e

Browse files
committed
Mejora navegación en nube y cambio de fecha por grupos y en la nube
1 parent 218a0eb commit ee69c9e

File tree

5 files changed

+660
-101
lines changed

5 files changed

+660
-101
lines changed

db_manager.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ def bulk_upsert_drive_photos(self, photos_list, root_folder_id=None):
403403
p.get('thumbnailLink'),
404404
p.get('webContentLink'),
405405
parent,
406-
root_folder_id # <--- Guardamos el ID de la carpeta origen
406+
root_folder_id
407407
))
408408

409409
with self.conn:
@@ -413,5 +413,28 @@ def bulk_upsert_drive_photos(self, photos_list, root_folder_id=None):
413413
ON CONFLICT(id) DO UPDATE SET
414414
name=excluded.name,
415415
thumbnail_link=excluded.thumbnail_link,
416-
root_folder_id=excluded.root_folder_id
416+
root_folder_id=excluded.root_folder_id,
417+
parent_id=excluded.parent_id -- <--- ¡ESTA LÍNEA ES NUEVA Y CRÍTICA!
417418
""", data_to_insert)
419+
420+
421+
def clear_drive_data(self):
422+
"""
423+
SEGURIDAD: Elimina todos los registros de fotos de la nube de la base de datos local.
424+
"""
425+
with self.conn:
426+
self.conn.execute("DELETE FROM drive_photos")
427+
# Opcional: VACUUM para reducir tamaño del archivo, aunque puede ser lento
428+
# self.conn.execute("VACUUM")
429+
430+
def get_drive_photos_by_parent(self, parent_id):
431+
# IMPORTANTE: Esta consulta busca fotos cuyo PADRE DIRECTO sea la carpeta clickeada
432+
cursor = self.conn.execute("SELECT * FROM drive_photos WHERE parent_id = ?", (parent_id,))
433+
return [dict(row) for row in cursor.fetchall()]
434+
435+
def update_drive_photo_date(self, file_id, new_iso_date):
436+
"""
437+
Actualiza la fecha de una foto de Drive localmente para reorganizarla.
438+
"""
439+
with self.conn:
440+
self.conn.execute("UPDATE drive_photos SET created_time = ? WHERE id = ?", (new_iso_date, file_id))

drive_auth.py

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,74 @@
1313
class DriveAuthenticator:
1414
def __init__(self):
1515
self.creds = None
16-
# Ruta donde guardaremos la sesión del usuario para no pedirla siempre
1716
self.token_path = os.path.join(os.path.dirname(__file__), 'user_token.pickle')
18-
# Tu archivo maestro (que el usuario no toca)
1917
self.secrets_path = resource_path('client_secrets.json')
2018

21-
def get_service(self):
22-
"""Intenta loguear silenciosamente, o abre navegador si hace falta."""
19+
def has_credentials(self):
20+
"""Verifica si existe el archivo de token."""
21+
return os.path.exists(self.token_path)
2322

24-
# 1. Cargar sesión guardada si existe
23+
def logout(self):
24+
"""Cierra sesión borrando el token local."""
25+
self.creds = None
26+
if os.path.exists(self.token_path):
27+
try:
28+
os.remove(self.token_path)
29+
return True
30+
except Exception as e:
31+
print(f"Error borrando token: {e}")
32+
return False
33+
return True
34+
35+
def get_service(self, silent=False):
36+
"""
37+
Obtiene el servicio de Drive.
38+
Si silent=True, NO abrirá el navegador si el token es inválido,
39+
simplemente devolverá None.
40+
"""
41+
# 1. Cargar sesión guardada
2542
if os.path.exists(self.token_path):
26-
with open(self.token_path, 'rb') as token:
27-
self.creds = pickle.load(token)
43+
try:
44+
with open(self.token_path, 'rb') as token:
45+
self.creds = pickle.load(token)
46+
except Exception:
47+
self.creds = None
2848

29-
# 2. Si no hay credenciales válidas, hacemos el login visual
49+
# 2. Validar credenciales
3050
if not self.creds or not self.creds.valid:
3151
if self.creds and self.creds.expired and self.creds.refresh_token:
3252
try:
33-
# Intenta refrescar el token sin abrir el navegador
3453
self.creds.refresh(Request())
3554
except Exception:
36-
self._start_browser_login()
55+
self.creds = None
3756
else:
57+
self.creds = None
58+
59+
# 3. Si no hay credenciales válidas:
60+
if not self.creds:
61+
if silent:
62+
# Si es modo silencioso (arranque), no hacemos nada más
63+
return None
64+
else:
65+
# Si NO es silencioso (botón conectar), abrimos navegador
3866
self._start_browser_login()
3967

40-
# 3. Guardar la sesión para la próxima vez
68+
# Guardar token refrescado o nuevo
69+
if self.creds and self.creds.valid:
4170
with open(self.token_path, 'wb') as token:
4271
pickle.dump(self.creds, token)
72+
return build('drive', 'v3', credentials=self.creds)
4373

44-
# 4. Retornar el servicio listo para usar
45-
return build('drive', 'v3', credentials=self.creds)
74+
return None
4675

4776
def _start_browser_login(self):
48-
"""Lanza el flujo de 'Logueate con Google' en el navegador."""
4977
if not os.path.exists(self.secrets_path):
50-
raise FileNotFoundError("Falta el archivo de configuración interna (client_secrets.json).")
51-
52-
flow = InstalledAppFlow.from_client_secrets_file(
53-
self.secrets_path, SCOPES)
78+
raise FileNotFoundError("Falta client_secrets.json")
5479

55-
# CAMBIO CLAVE: Especificar success_message para asegurar que el usuario vea que acabó
56-
# y el servidor sepa que debe cerrarse.
80+
flow = InstalledAppFlow.from_client_secrets_file(self.secrets_path, SCOPES)
5781
self.creds = flow.run_local_server(
5882
port=0,
59-
success_message='La autenticación se ha completado. Puedes cerrar esta ventana.'
83+
success_message='Autenticación completada. Puedes cerrar esta ventana.'
6084
)
6185

6286

drive_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ def list_images_recursively(self, folder_id):
196196
q=query,
197197
pageSize=1000,
198198
pageToken=page_token,
199-
fields="nextPageToken, files(id, name, mimeType, thumbnailLink, webContentLink, createdTime)"
199+
fields="nextPageToken, files(id, name, mimeType, thumbnailLink, webContentLink, createdTime, parents)"
200200
).execute()
201201
except Exception as e:
202202
print(f"⚠️ Error listando imágenes en {folder_id}: {e}")

visagevault.db-shm

-32 KB
Binary file not shown.

0 commit comments

Comments
 (0)