Compare commits

..

2 Commits

316 changed files with 10736 additions and 3270 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

350
audio_iot.py Normal file
View File

@ -0,0 +1,350 @@
import asyncio
from pydub import AudioSegment
import os
import threading
import http.server
import socketserver
import socket
import time
import subprocess
import urllib.request # Para enviar HTTP GET a la ESP32 (sin librerías extra)
# ================= CONFIGURACIÓN =================
IP_BOCINA = "192.168.15.135"
PUERTO_ESP32 = 80
PUERTO_HTTP = 8000
CARPETA_AUDIO = "public_audio"
if not os.path.exists(CARPETA_AUDIO):
os.makedirs(CARPETA_AUDIO)
# ================= AUTO-DETECTAR MI IP =================
def obtener_mi_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect((IP_BOCINA, 80))
IP = s.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
s.close()
return IP
MI_IP = obtener_mi_ip()
print(f"[RED] IP auto-detectada para el servidor de audio: {MI_IP}")
def iniciar_servidor_http():
class Handler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=CARPETA_AUDIO, **kwargs)
def log_message(self, format, *args):
print(f"[SERVIDOR HTTP] Petición de ESP32: {args[0]}")
def handle(self):
try:
super().handle()
except (BrokenPipeError, ConnectionResetError):
pass
def copyfile(self, source, outputfile):
try:
super().copyfile(source, outputfile)
except (BrokenPipeError, ConnectionResetError):
pass
class ReusableTCPServer(socketserver.TCPServer):
allow_reuse_address = True
try:
with ReusableTCPServer(("", PUERTO_HTTP), Handler) as httpd:
print(f"Servidor de Audio activo en http://{MI_IP}:{PUERTO_HTTP}")
httpd.serve_forever()
except Exception as e:
print(f"ERROR CRÍTICO AL LEVANTAR EL SERVIDOR: {e}")
# ================= ORDEN A LA ESP32 =================
# La ESP32 usa HTTP GET en /play?url=..., NO WebSocket.
# Antes usábamos WebSocket al puerto 81 → la ESP32 nunca recibió nada.
def _mandar_orden_reproduccion(nombre_archivo):
url_mp3 = f"http://{MI_IP}:{PUERTO_HTTP}/{nombre_archivo}"
# Construimos la URL exacta que espera el handlePlay() de la ESP32
url_orden = f"http://{IP_BOCINA}:{PUERTO_ESP32}/play?url={url_mp3}"
try:
print(f"Ordenando a ESP32 reproducir: {url_mp3}")
with urllib.request.urlopen(url_orden, timeout=5) as respuesta:
estado = respuesta.read().decode()
print(f"ESP32 respondió: {estado}")
return True
except Exception as e:
print(f" Error enviando orden a la bocina: {e}")
return False
# ================= CONTROL DE VOLUMEN =================
def _mandar_orden_volumen(nivel=18):
"""Establece volumen directamente (0-21)"""
url_orden = f"http://{IP_BOCINA}:{PUERTO_ESP32}/volume?level={nivel}"
try:
with urllib.request.urlopen(url_orden, timeout=5) as respuesta:
print(f"Volumen: {respuesta.read().decode()}")
return True
except Exception as e:
print(f"Error cambiando volumen: {e}")
return False
def subir_volumen():
"""Sube 1 nivel de volumen"""
url_orden = f"http://{IP_BOCINA}:{PUERTO_ESP32}/volume_up"
try:
with urllib.request.urlopen(url_orden, timeout=5) as respuesta:
print(f" {respuesta.read().decode()}")
return True
except Exception as e:
print(f"Error: {e}")
return False
def bajar_volumen():
"""Baja 1 nivel de volumen"""
url_orden = f"http://{IP_BOCINA}:{PUERTO_ESP32}/volume_down"
try:
with urllib.request.urlopen(url_orden, timeout=5) as respuesta:
print(f"{respuesta.read().decode()}")
return True
except Exception as e:
print(f"Error: {e}")
return False
# ================= LÓGICA PRINCIPAL =================
def enviar_archivos_bocina(lista_archivos):
if not lista_archivos:
return
try:
# 1. Unimos los audios
audio_final = AudioSegment.empty()
for archivo in lista_archivos:
if os.path.exists(archivo):
audio_final += AudioSegment.from_file(archivo)
# 2. WAV temporal
ruta_temp = os.path.join(CARPETA_AUDIO, "temp.wav")
audio_final.export(ruta_temp, format="wav")
# 3. Nombre único anti-caché
timestamp = int(time.time())
nombre_salida = f"saludo_{timestamp}.mp3"
ruta_salida = os.path.join(CARPETA_AUDIO, nombre_salida)
# 4. MP3 ligero para reducir la presión sobre el buffer de Audio.h
# El fix completo del OOM va en el .ino (ver abajo)
comando_ffmpeg = [
"/usr/bin/ffmpeg", "-y",
"-i", ruta_temp,
"-codec:a", "libmp3lame",
"-b:a", "48k",
"-ar", "24000",
"-ac", "1",
# FILTRO ACÚSTICO: Corta todo lo que no sea voz humana
"-af", "highpass=f=150, lowpass=f=7500",
"-map_metadata", "-1",
"-id3v2_version", "0",
"-write_xing", "0",
ruta_salida
]
subprocess.run(comando_ffmpeg, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# 5. Limpieza del temporal
if os.path.exists(ruta_temp):
os.remove(ruta_temp)
# 6. Enviamos la orden en un hilo separado
# (evita conflicto con el runtime de InsightFace/ONNX)
def _lanzar_orden():
time.sleep(0.5) # Aseguramos que el archivo ya está en disco
_mandar_orden_reproduccion(nombre_salida)
# Limpiamos MP3 viejos DESPUÉS de que la ESP32 confirmó
for archivo in os.listdir(CARPETA_AUDIO):
if archivo.startswith("saludo_") and archivo != nombre_salida:
try:
os.remove(os.path.join(CARPETA_AUDIO, archivo))
except Exception:
pass
hilo = threading.Thread(target=_lanzar_orden, daemon=True)
hilo.start()
except Exception as e:
print(f"Error procesando el audio: {e}")
# ==============================================================================
# AUTO-ARRANQUE DEL SERVIDOR (Hilo en segundo plano)
# ==============================================================================
servidor_thread = threading.Thread(target=iniciar_servidor_http, daemon=True)
servidor_thread.start()
"""
import asyncio
from pydub import AudioSegment
import os
import threading
import http.server
import socketserver
import socket
import time
import subprocess
import urllib.request # Para enviar HTTP GET a la ESP32 (sin librerías extra)
# ================= CONFIGURACIÓN =================
IP_BOCINA = "192.168.1.77" # La IP de la ESP32 que se definio como estatica
PUERTO_ESP32 = 80 # La ESP32 escucha HTTP en el puerto 80
PUERTO_HTTP = 8000
CARPETA_AUDIO = "public_audio"
if not os.path.exists(CARPETA_AUDIO):
os.makedirs(CARPETA_AUDIO)
# ================= SERVIDOR HTTP =================
def obtener_mi_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect(('10.255.255.255', 1))
IP = s.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
s.close()
return IP
MI_IP = obtener_mi_ip()
def iniciar_servidor_http():
class Handler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=CARPETA_AUDIO, **kwargs)
def log_message(self, format, *args):
print(f"[SERVIDOR HTTP] Petición de ESP32: {args[0]}")
def handle(self):
try:
super().handle()
except (BrokenPipeError, ConnectionResetError):
pass
def copyfile(self, source, outputfile):
try:
super().copyfile(source, outputfile)
except (BrokenPipeError, ConnectionResetError):
pass
class ReusableTCPServer(socketserver.TCPServer):
allow_reuse_address = True
try:
with ReusableTCPServer(("", PUERTO_HTTP), Handler) as httpd:
print(f"Servidor de Audio activo en http://{MI_IP}:{PUERTO_HTTP}")
httpd.serve_forever()
except Exception as e:
print(f"ERROR CRÍTICO AL LEVANTAR EL SERVIDOR: {e}")
# ================= ORDEN A LA ESP32 =================
# La ESP32 usa HTTP GET en /play?url=..., NO WebSocket.
# Antes usábamos WebSocket al puerto 81 → la ESP32 nunca recibió nada.
def _mandar_orden_reproduccion(nombre_archivo):
url_mp3 = f"http://{MI_IP}:{PUERTO_HTTP}/{nombre_archivo}"
# Construimos la URL exacta que espera el handlePlay() de la ESP32
url_orden = f"http://{IP_BOCINA}:{PUERTO_ESP32}/play?url={url_mp3}"
try:
print(f"Ordenando a ESP32 reproducir: {url_mp3}")
with urllib.request.urlopen(url_orden, timeout=5) as respuesta:
estado = respuesta.read().decode()
print(f"ESP32 respondió: {estado}")
return True
except Exception as e:
print(f" Error enviando orden a la bocina: {e}")
return False
# ================= LÓGICA PRINCIPAL =================
def enviar_archivos_bocina(lista_archivos):
if not lista_archivos:
return
try:
# 1. Unimos los audios
audio_final = AudioSegment.empty()
for archivo in lista_archivos:
if os.path.exists(archivo):
audio_final += AudioSegment.from_file(archivo)
# 2. WAV temporal
ruta_temp = os.path.join(CARPETA_AUDIO, "temp.wav")
audio_final.export(ruta_temp, format="wav")
# 3. Nombre único anti-caché
timestamp = int(time.time())
nombre_salida = f"saludo_{timestamp}.mp3"
ruta_salida = os.path.join(CARPETA_AUDIO, nombre_salida)
# 4. MP3 ligero para reducir la presión sobre el buffer de Audio.h
# El fix completo del OOM va en el .ino (ver abajo)
comando_ffmpeg = [
"/usr/bin/ffmpeg", "-y",
"-i", ruta_temp,
"-codec:a", "libmp3lame",
"-b:a", "48k",
"-ar", "24000",
"-ac", "1",
# FILTRO ACÚSTICO: Corta todo lo que no sea voz humana
"-af", "highpass=f=150, lowpass=f=7500",
"-map_metadata", "-1",
"-id3v2_version", "0",
"-write_xing", "0",
ruta_salida
]
subprocess.run(comando_ffmpeg, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# 5. Limpieza del temporal
if os.path.exists(ruta_temp):
os.remove(ruta_temp)
# 6. Enviamos la orden en un hilo separado
# (evita conflicto con el runtime de InsightFace/ONNX)
def _lanzar_orden():
time.sleep(0.5) # Aseguramos que el archivo ya está en disco
_mandar_orden_reproduccion(nombre_salida)
# Limpiamos MP3 viejos DESPUÉS de que la ESP32 confirmó
for archivo in os.listdir(CARPETA_AUDIO):
if archivo.startswith("saludo_") and archivo != nombre_salida:
try:
os.remove(os.path.join(CARPETA_AUDIO, archivo))
except Exception:
pass
hilo = threading.Thread(target=_lanzar_orden, daemon=True)
hilo.start()
except Exception as e:
print(f"Error procesando el audio: {e}")
# ==============================================================================
# AUTO-ARRANQUE DEL SERVIDOR (Hilo en segundo plano)
# ==============================================================================
servidor_thread = threading.Thread(target=iniciar_servidor_http, daemon=True)
servidor_thread.start()
"""

Binary file not shown.

225
bocina_esp32.ino Normal file
View File

@ -0,0 +1,225 @@
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include "Audio.h"
const char* ssid = "SmartEstancia";
const char* password = "Smart12345";
IPAddress local_IP(192, 168, 1, 77);
IPAddress gateway(192, 168, 1, 254);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8);
IPAddress secondaryDNS(8, 8, 4, 4);
#define I2S_BCLK 26
#define I2S_LRC 25
#define I2S_DOUT 33
Audio audio;
WebServer server(80);
void handlePlay() {
if (server.hasArg("url")) {
String url = server.arg("url");
Serial.print("🎵 Python ordenó reproducir: ");
Serial.println(url);
audio.stopSong();
audio.connecttohost(url.c_str());
server.send(200, "text/plain", "Reproduciendo OK");
} else {
server.send(400, "text/plain", "Error: Falta la URL");
}
}
// --- NUEVO: Control de Volumen ---
void handleVolume() {
if (server.hasArg("level")) {
int nivel = server.arg("level").toInt();
if (nivel >= 0 && nivel <= 21) {
audio.setVolume(nivel);
Serial.print("🔊 Volumen cambiado a: ");
Serial.println(nivel);
server.send(200, "text/plain", "Volumen OK: " + String(nivel));
} else {
server.send(400, "text/plain", "Error: Nivel debe ser 0-21");
}
} else {
server.send(400, "text/plain", "Error: Falta parametro 'level'");
}
}
void handleVolumeUp() {
int actual = audio.getVolume();
if (actual < 21) audio.setVolume(actual + 1);
server.send(200, "text/plain", "Volumen: " + String(audio.getVolume()));
}
void handleVolumeDown() {
int actual = audio.getVolume();
if (actual > 0) audio.setVolume(actual - 1);
server.send(200, "text/plain", "Volumen: " + String(audio.getVolume()));
}
void setup() {
Serial.begin(115200);
Serial.println("\n--- INICIANDO SISTEMA DE AUDIO ESP32 ---");
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
Serial.println("Fallo al configurar la IP Estatica");
}
WiFi.begin(ssid, password);
Serial.print("Conectando a WiFi");
int intento = 0;
while (WiFi.status() != WL_CONNECTED && intento < 20) {
delay(500);
Serial.print(".");
intento++;
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("\nFallo de conexion.");
while(true){ delay(1000); }
}
Serial.println("\nWiFi Conectado!");
Serial.print("IP FIJA: ");
Serial.println(WiFi.localIP());
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
audio.setVolume(15); // Volumen inicial medio
server.on("/play", HTTP_GET, handlePlay);
server.on("/volume", HTTP_GET, handleVolume);
server.on("/volume_up", HTTP_GET, handleVolumeUp);
server.on("/volume_down", HTTP_GET, handleVolumeDown);
server.begin();
Serial.println("Listo para recibir audio...");
}
void loop() {
audio.loop();
server.handleClient();
if (Serial.available()) {
String url = Serial.readStringUntil('\n');
url.trim();
if (url.length() > 5 && url.startsWith("http")) {
audio.stopSong();
audio.connecttohost(url.c_str());
}
}
}
void audio_info(const char *info){ Serial.print("INFO AUDIO: "); Serial.println(info); }
void audio_id3data(const char *info){ Serial.print("METADATOS: "); Serial.println(info); }
void audio_eof_mp3(const char *info){ Serial.print("FIN DEL AUDIO: "); Serial.println(info); }
"""
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include "Audio.h"
// =======================================================
// 1. CONFIGURACIÓN DE RED
// =======================================================
const char* ssid = "SmartEstancia";
const char* password = "Smart12345";
// --- CONFIGURACIÓN DE IP ESTÁTICA ---
IPAddress local_IP(192, 168, 1, 77); // La IP que le asiganamos a la bocina
IPAddress gateway(192, 168, 1, 254); // la IP del router
IPAddress subnet(255, 255, 255, 0); // Máscara de subred
IPAddress primaryDNS(8, 8, 8, 8); // DNS primario (Google)
IPAddress secondaryDNS(8, 8, 4, 4); // DNS secundario (Google)
// =======================================================
// 2. CONFIGURACIÓN DE PINES CORRECTOS
// =======================================================
#define I2S_BCLK 26
#define I2S_LRC 25
#define I2S_DOUT 33
Audio audio;
WebServer server(80);
void handlePlay() {
if (server.hasArg("url")) {
String url = server.arg("url");
Serial.print("🎵 Python ordenó reproducir: ");
Serial.println(url);
audio.stopSong();
audio.connecttohost(url.c_str());
server.send(200, "text/plain", "Reproduciendo OK");
} else {
server.send(400, "text/plain", "Error: Falta la URL");
}
}
void setup() {
Serial.begin(115200);
Serial.println("\n--- INICIANDO SISTEMA DE AUDIO ESP32 ---");
if (psramFound()) {
Serial.println("PSRAM encontrada.");
} else {
Serial.println(" Sin PSRAM. Usando RAM interna.");
}
// ⚡ APLICAR IP ESTÁTICA ANTES DE CONECTAR
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
Serial.println(" Fallo al configurar la IP Estática");
}
WiFi.begin(ssid, password);
Serial.print("Conectando a WiFi");
int intento = 0;
while (WiFi.status() != WL_CONNECTED && intento < 20) {
delay(500);
Serial.print(".");
intento++;
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("\n Fallo de conexión.");
while(true){ delay(1000); }
}
Serial.println("\n WiFi Conectado de verdad!");
Serial.print("Tu IP es FIJA: ");
Serial.println(WiFi.localIP());
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
audio.setVolume(18);
server.on("/play", HTTP_GET, handlePlay);
server.begin();
Serial.println("Listo para recibir audio...");
}
void loop() {
audio.loop();
server.handleClient();
if (Serial.available()) {
String url = Serial.readStringUntil('\n');
url.trim();
if (url.length() > 5 && url.startsWith("http")) {
audio.stopSong();
audio.connecttohost(url.c_str());
}
}
}
void audio_info(const char *info){ Serial.print("INFO AUDIO: "); Serial.println(info); }
void audio_id3data(const char *info){ Serial.print("METADATOS: "); Serial.println(info); }
void audio_eof_mp3(const char *info){ Serial.print("FIN DEL AUDIO: "); Serial.println(info); }
"""

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Some files were not shown because too many files have changed in this diff Show More