feat(browser): set_chrome_profile_appearance — avatar + color de perfiles Chrome
Nueva función Bash del dominio browser para personalizar la apariencia de un perfil Chrome/Chromium y diferenciarlo de un vistazo. Edita `profile.info_cache.<perfil>` en el Local State del user-data-dir: - `--avatar <N>`: avatar built-in de Chrome (índice 0..55) vía `avatar_icon = chrome://theme/IDR_PROFILE_AVATAR_<N>`. Camino robusto. - `--avatar <ruta.png>`: avatar custom best-effort (copia la imagen al perfil y marca `is_using_default_avatar=false`); ver gotchas del .md. - `--color <#rrggbb>`: color del perfil. Convierte el hex a int32 con signo en formato ARGB (0xAARRGGBB) y lo aplica a `profile_highlight_color`, `profile_color_seed` y `default_avatar_fill_color`. Sigue el patrón de create/delete_chrome_profile: backup del Local State antes de escribir, validación del JSON resultante con restauración del backup si queda inválido, guard de SingletonLock (chromium debe estar cerrado), idempotente y con --dry-run. No crea perfiles (eso es create_chrome_profile); requiere que el perfil ya exista. Probada con --avatar 26 --color #1f6feb y casos edge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
name: set_chrome_profile_appearance
|
||||||
|
kind: function
|
||||||
|
lang: bash
|
||||||
|
domain: browser
|
||||||
|
version: "1.0.0"
|
||||||
|
purity: impure
|
||||||
|
signature: "set_chrome_profile_appearance --user-data-dir <dir> --profile <dir-name> [--avatar <N|ruta.png>] [--color <#rrggbb>] [--dry-run]"
|
||||||
|
description: "Personaliza la apariencia visual de un perfil Chrome/Chromium existente: asigna un avatar built-in (índice 0..55) o una imagen PNG/JPG custom, y/o un color de acento (hex #rrggbb). Edita los campos avatar_icon, is_using_default_avatar, profile_highlight_color, profile_color_seed y default_avatar_fill_color en profile.info_cache de Local State. Requiere que Chromium esté cerrado sobre el user-data-dir. Hace backup de Local State antes de escribir y valida el JSON resultante."
|
||||||
|
tags: [navegator, chromium, profile, browser, cdp, scraping, appearance, avatar, color]
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: "error_go_core"
|
||||||
|
imports: []
|
||||||
|
tested: false
|
||||||
|
tests: []
|
||||||
|
test_file_path: ""
|
||||||
|
file_path: "bash/functions/browser/set_chrome_profile_appearance.sh"
|
||||||
|
params:
|
||||||
|
- name: --user-data-dir
|
||||||
|
desc: "Raíz del user-data-dir de Chrome/Chromium donde vive el perfil. El directorio y Local State deben existir. Obligatorio."
|
||||||
|
- name: --profile
|
||||||
|
desc: "Nombre de la carpeta del perfil dentro de user-data-dir, por ejemplo: Default, Automation, \"Profile 1\". El perfil debe existir previamente en info_cache de Local State. Obligatorio."
|
||||||
|
- name: --avatar
|
||||||
|
desc: "Índice entero 0..55 del avatar built-in de Chrome (56 avatares: animales, objetos, personas) o ruta absoluta/relativa a un archivo PNG/JPG para avatar custom. Con índice: sets avatar_icon=IDR_PROFILE_AVATAR_<N> e is_using_default_avatar=true. Con imagen: copia el archivo al perfil como 'Google Profile Picture.png' y sets is_using_default_avatar=false. Opcional; al menos uno de --avatar o --color debe darse."
|
||||||
|
- name: --color
|
||||||
|
desc: "Color de acento del perfil en hex #rrggbb, con o sin el '#' inicial. Se convierte internamente a int32 con signo en formato ARGB 0xFFRRGGBB y se aplica a profile_highlight_color, profile_color_seed y default_avatar_fill_color. Opcional; al menos uno de --avatar o --color debe darse."
|
||||||
|
- name: --dry-run
|
||||||
|
desc: "Describe las acciones que se ejecutarían (campos a modificar, conversión de color) sin escribir nada ni verificar si Chromium está corriendo. Emite JSON de resultado con dry_run:true."
|
||||||
|
output: "JSON en stdout con los campos resultantes del perfil: {\"profile\":\"<dir>\",\"avatar_icon\":\"...\",\"is_using_default_avatar\":true|false,\"profile_highlight_color\":<int>,\"profile_color_seed\":<int>,\"default_avatar_fill_color\":<int>,\"backup\":\"Local State.bak.YYYYMMDD\"}. En dry-run: {\"profile\":\"...\",\"avatar_applied\":true|false,\"color_applied\":true|false,\"dry_run\":true}. Mensajes de diagnóstico a stderr. Exit 0 en éxito."
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ejemplo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source $HOME/fn_registry/bash/functions/browser/set_chrome_profile_appearance.sh
|
||||||
|
|
||||||
|
# Asignar avatar built-in #26 (oso panda) y color azul GitHub al perfil Automation
|
||||||
|
set_chrome_profile_appearance \
|
||||||
|
--user-data-dir ~/.config/chromium-cdp \
|
||||||
|
--profile Automation \
|
||||||
|
--avatar 26 \
|
||||||
|
--color "#1f6feb"
|
||||||
|
# Salida JSON con los valores aplicados
|
||||||
|
|
||||||
|
# Solo cambiar color (sin tocar avatar)
|
||||||
|
set_chrome_profile_appearance \
|
||||||
|
--user-data-dir ~/.config/chromium-cdp \
|
||||||
|
--profile Default \
|
||||||
|
--color "16a34a"
|
||||||
|
|
||||||
|
# Solo cambiar avatar built-in
|
||||||
|
set_chrome_profile_appearance \
|
||||||
|
--user-data-dir ~/.config/chromium-cdp \
|
||||||
|
--profile "Profile 1" \
|
||||||
|
--avatar 5
|
||||||
|
|
||||||
|
# Avatar custom desde imagen
|
||||||
|
set_chrome_profile_appearance \
|
||||||
|
--user-data-dir ~/.config/chromium-cdp \
|
||||||
|
--profile Personal \
|
||||||
|
--avatar /tmp/foto.png \
|
||||||
|
--color "#0ea5e9"
|
||||||
|
|
||||||
|
# Dry-run: ver qué se aplicaría sin escribir
|
||||||
|
set_chrome_profile_appearance \
|
||||||
|
--user-data-dir ~/.config/chromium-cdp \
|
||||||
|
--profile Automation \
|
||||||
|
--avatar 26 \
|
||||||
|
--color "#1f6feb" \
|
||||||
|
--dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cuando usarla
|
||||||
|
|
||||||
|
Úsala para diferenciar visualmente los perfiles de un user-data-dir de automatización — un color y avatar distintos por perfil hacen inmediata la identificación en el selector de Chrome. Ejecútala justo después de `create_chrome_profile` (con `--no-launch`) o como paso independiente de personalización batch antes de lanzar sesiones CDP.
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
|
||||||
|
- **Chromium debe estar cerrado**: Chrome reescribe `Local State` completo desde memoria al cerrar; si se ejecuta mientras hay un proceso chromium vivo sobre el mismo user-data-dir, Chrome sobreescribirá los cambios al salir. La función detecta esto con `pgrep -x chromium` filtrando por cmdline y sale con exit 2 antes de modificar nada. Usa `pkill -TERM chromium` para cerrar y espera unos segundos.
|
||||||
|
- **El perfil debe existir en info_cache**: esta función personaliza perfiles existentes; no los crea. Usa `create_chrome_profile` primero (con `--no-launch` basta para que aparezca en Local State) y luego `set_chrome_profile_appearance`.
|
||||||
|
- **color es int32 con signo en ARGB**: Chrome almacena el color como entero con signo de 32 bits en formato `0xAARRGGBB`. Un color como `#1f6feb` (azul) da ARGB `0xFF1F6FEB` → signed int32 `-14713877`. La función hace la conversión internamente; tú pasas siempre hex `#rrggbb`.
|
||||||
|
- **Avatar custom (imagen) es best-effort**: el campo `gaia_picture_file_name` y `is_using_default_avatar=false` se aplican correctamente en Local State y la imagen se copia al directorio del perfil. Sin embargo, Chrome puede ignorar la foto de perfil en perfiles sin sesión Google activa (Chromium sin cuenta). El camino robusto y garantizado es usar el índice built-in (`--avatar 0..55`): 56 avatares (animales, objetos, personas) son más que suficientes para diferenciar perfiles de automatización.
|
||||||
|
- **Backup diario**: se crea `Local State.bak.YYYYMMDD` en el user-data-dir antes de cualquier escritura. Si ya existe el backup del día no se sobreescribe. Si el JSON resultante es inválido, se restaura automáticamente el backup.
|
||||||
|
- **is_using_default_avatar con índice built-in**: Chrome considera los avatares IDR_PROFILE_AVATAR_* como "avatares por defecto" del sistema, por eso `is_using_default_avatar` permanece `true` con índice numérico. Esto es correcto y es lo que Chrome haría internamente.
|
||||||
|
|
||||||
|
## Exit codes
|
||||||
|
|
||||||
|
| Código | Significado |
|
||||||
|
|--------|------------|
|
||||||
|
| 0 | Éxito |
|
||||||
|
| 1 | Argumento obligatorio faltante, rango inválido o archivo de imagen no encontrado |
|
||||||
|
| 2 | Lock: hay un chromium usando el mismo user-data-dir |
|
||||||
|
| 3 | El perfil no existe en info_cache de Local State |
|
||||||
|
| 4 | Error editando Local State (JSON inválido tras escritura, restaurado backup) |
|
||||||
@@ -0,0 +1,298 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# set_chrome_profile_appearance — personaliza la apariencia visual de un perfil
|
||||||
|
# Chrome/Chromium existente: asigna un avatar built-in (índice 0..55) o una imagen
|
||||||
|
# PNG/JPG custom, y/o un color de acento (hex #rrggbb). Edita Local State.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
set_chrome_profile_appearance() {
|
||||||
|
# ── defaults ──────────────────────────────────────────────────────────────
|
||||||
|
local _udd=""
|
||||||
|
local _profile_dir=""
|
||||||
|
local _avatar=""
|
||||||
|
local _color=""
|
||||||
|
local _dry_run=0
|
||||||
|
|
||||||
|
# ── parse args ─────────────────────────────────────────────────────────────
|
||||||
|
_usage() {
|
||||||
|
cat >&2 <<'EOF'
|
||||||
|
Usage: set_chrome_profile_appearance --user-data-dir <dir> --profile <dir-name>
|
||||||
|
[--avatar <N|ruta.png>] [--color <#rrggbb>] [--dry-run]
|
||||||
|
|
||||||
|
--user-data-dir Raíz del user-data-dir de Chrome/Chromium (obligatorio).
|
||||||
|
--profile Nombre de la carpeta del perfil, ej: Default, Automation,
|
||||||
|
"Profile 1" (obligatorio). El perfil debe existir.
|
||||||
|
--avatar Índice entero 0..55 del avatar built-in de Chrome, o ruta a
|
||||||
|
un archivo PNG/JPG para avatar custom (opcional).
|
||||||
|
--color Color de acento del perfil en formato hex #rrggbb, con o sin
|
||||||
|
el '#' inicial (opcional).
|
||||||
|
--dry-run Describe las acciones sin modificar nada.
|
||||||
|
|
||||||
|
Al menos uno de --avatar o --color debe indicarse.
|
||||||
|
|
||||||
|
Exit codes:
|
||||||
|
0 éxito
|
||||||
|
1 error de argumento o validación
|
||||||
|
2 lock: hay un chromium corriendo con este user-data-dir
|
||||||
|
3 el perfil no existe en info_cache de Local State
|
||||||
|
4 error editando Local State (JSON inválido tras escritura)
|
||||||
|
EOF
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--user-data-dir) _udd="$2"; shift 2 ;;
|
||||||
|
--profile) _profile_dir="$2"; shift 2 ;;
|
||||||
|
--avatar) _avatar="$2"; shift 2 ;;
|
||||||
|
--color) _color="$2"; shift 2 ;;
|
||||||
|
--dry-run) _dry_run=1; shift ;;
|
||||||
|
-h|--help) _usage; return 0 ;;
|
||||||
|
*) echo "set_chrome_profile_appearance: argumento desconocido: $1" >&2; return 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── validaciones obligatorias ──────────────────────────────────────────────
|
||||||
|
if [[ -z "$_udd" ]]; then
|
||||||
|
echo "set_chrome_profile_appearance: --user-data-dir es obligatorio" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ -z "$_profile_dir" ]]; then
|
||||||
|
echo "set_chrome_profile_appearance: --profile es obligatorio" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ -z "$_avatar" && -z "$_color" ]]; then
|
||||||
|
echo "set_chrome_profile_appearance: al menos --avatar o --color debe indicarse" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Expandir ~ en el user-data-dir
|
||||||
|
_udd="${_udd/#\~/$HOME}"
|
||||||
|
|
||||||
|
local _local_state="${_udd}/Local State"
|
||||||
|
|
||||||
|
# Verificar que user-data-dir y Local State existen
|
||||||
|
if [[ ! -d "$_udd" ]]; then
|
||||||
|
echo "set_chrome_profile_appearance: user-data-dir no encontrado: ${_udd}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ ! -f "$_local_state" ]]; then
|
||||||
|
echo "set_chrome_profile_appearance: Local State no encontrado: ${_local_state}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── validar --avatar ──────────────────────────────────────────────────────
|
||||||
|
local _avatar_index=-1
|
||||||
|
local _avatar_image_path=""
|
||||||
|
|
||||||
|
if [[ -n "$_avatar" ]]; then
|
||||||
|
if [[ "$_avatar" =~ ^[0-9]+$ ]]; then
|
||||||
|
# Índice built-in
|
||||||
|
_avatar_index=$(( _avatar ))
|
||||||
|
if [[ $_avatar_index -lt 0 || $_avatar_index -gt 55 ]]; then
|
||||||
|
echo "set_chrome_profile_appearance: índice de avatar fuera de rango (0..55): ${_avatar}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Ruta a imagen custom
|
||||||
|
local _img_path="${_avatar/#\~/$HOME}"
|
||||||
|
if [[ ! -f "$_img_path" ]]; then
|
||||||
|
echo "set_chrome_profile_appearance: archivo de imagen no encontrado: ${_img_path}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_avatar_image_path="$_img_path"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── validar --color ───────────────────────────────────────────────────────
|
||||||
|
local _color_hex=""
|
||||||
|
if [[ -n "$_color" ]]; then
|
||||||
|
_color_hex="${_color/#\#/}" # quitar # inicial si lo hay
|
||||||
|
if ! [[ "$_color_hex" =~ ^[0-9a-fA-F]{6}$ ]]; then
|
||||||
|
echo "set_chrome_profile_appearance: color hex inválido (espera rrggbb): ${_color}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── guard: ningún chromium debe tener ESTE user-data-dir abierto ──────────
|
||||||
|
# pgrep -x chromium lista solo procesos cuyo comm es exactamente "chromium",
|
||||||
|
# nunca grep/pgrep/bash. Así evitamos auto-matchear el propio script cuando
|
||||||
|
# el path del udd contiene "chromium" (p.ej. ~/.config/chromium-cdp).
|
||||||
|
if [[ $_dry_run -eq 0 ]]; then
|
||||||
|
local _p _busy=0
|
||||||
|
for _p in $(pgrep -x chromium 2>/dev/null); do
|
||||||
|
if tr '\0' ' ' < "/proc/$_p/cmdline" 2>/dev/null | grep -qF -- "$_udd"; then
|
||||||
|
_busy=1; break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ $_busy -eq 1 ]]; then
|
||||||
|
echo "set_chrome_profile_appearance: hay un chromium corriendo con este user-data-dir — ciérralo primero:" >&2
|
||||||
|
echo " pkill -TERM chromium" >&2
|
||||||
|
echo " (Chrome reescribe Local State al cerrar y pierde los cambios)" >&2
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── verificar que el perfil existe en info_cache ──────────────────────────
|
||||||
|
if [[ $_dry_run -eq 0 ]]; then
|
||||||
|
local _profile_exists
|
||||||
|
_profile_exists="$(python3 -c "
|
||||||
|
import json, sys
|
||||||
|
data = json.load(open(sys.argv[1]))
|
||||||
|
ic = data.get('profile', {}).get('info_cache', {})
|
||||||
|
print('yes' if sys.argv[2] in ic else 'no')
|
||||||
|
" "$_local_state" "$_profile_dir" 2>/dev/null || echo "no")"
|
||||||
|
if [[ "$_profile_exists" != "yes" ]]; then
|
||||||
|
echo "set_chrome_profile_appearance: perfil '${_profile_dir}' no existe en info_cache de Local State" >&2
|
||||||
|
echo " Perfiles disponibles:" >&2
|
||||||
|
python3 -c "
|
||||||
|
import json, sys
|
||||||
|
data = json.load(open(sys.argv[1]))
|
||||||
|
ic = data.get('profile', {}).get('info_cache', {})
|
||||||
|
for k in ic: print(' ', k)
|
||||||
|
" "$_local_state" >&2 2>/dev/null || true
|
||||||
|
return 3
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── modo dry-run ──────────────────────────────────────────────────────────
|
||||||
|
if [[ $_dry_run -eq 1 ]]; then
|
||||||
|
echo "=== set_chrome_profile_appearance DRY-RUN ===" >&2
|
||||||
|
echo " user-data-dir : ${_udd}" >&2
|
||||||
|
echo " profile : ${_profile_dir}" >&2
|
||||||
|
if [[ $_avatar_index -ge 0 ]]; then
|
||||||
|
echo " avatar : built-in #${_avatar_index} → avatar_icon=chrome://theme/IDR_PROFILE_AVATAR_${_avatar_index}" >&2
|
||||||
|
echo " is_using_default_avatar=true" >&2
|
||||||
|
elif [[ -n "$_avatar_image_path" ]]; then
|
||||||
|
local _dest_img="${_udd}/${_profile_dir}/Google Profile Picture.png"
|
||||||
|
echo " avatar : imagen custom ${_avatar_image_path}" >&2
|
||||||
|
echo " copiaría a ${_dest_img}" >&2
|
||||||
|
echo " is_using_default_avatar=false" >&2
|
||||||
|
echo " gaia_picture_file_name=Google Profile Picture.png" >&2
|
||||||
|
fi
|
||||||
|
if [[ -n "$_color_hex" ]]; then
|
||||||
|
local _signed_preview
|
||||||
|
_signed_preview="$(python3 -c "
|
||||||
|
rgb = int('${_color_hex}', 16)
|
||||||
|
argb = 0xFF000000 | rgb
|
||||||
|
signed = argb - 0x100000000 if argb >= 0x80000000 else argb
|
||||||
|
print(signed)
|
||||||
|
" 2>/dev/null || echo '?')"
|
||||||
|
echo " color : #${_color_hex} → signed int32 ${_signed_preview}" >&2
|
||||||
|
echo " profile_highlight_color, profile_color_seed, default_avatar_fill_color" >&2
|
||||||
|
fi
|
||||||
|
echo " archivo : ${_local_state}" >&2
|
||||||
|
printf '{"profile":"%s","avatar_applied":%s,"color_applied":%s,"dry_run":true}\n' \
|
||||||
|
"$_profile_dir" \
|
||||||
|
"$([[ -n "$_avatar" ]] && echo 'true' || echo 'false')" \
|
||||||
|
"$([[ -n "$_color_hex" ]] && echo 'true' || echo 'false')"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── backup de Local State (no sobreescribir el del mismo día) ────────────
|
||||||
|
local _today
|
||||||
|
_today="$(date +%Y%m%d)"
|
||||||
|
local _backup="${_local_state}.bak.${_today}"
|
||||||
|
if [[ ! -f "$_backup" ]]; then
|
||||||
|
cp "$_local_state" "$_backup"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── copiar imagen custom si es necesario ──────────────────────────────────
|
||||||
|
local _copy_image_done=false
|
||||||
|
if [[ -n "$_avatar_image_path" ]]; then
|
||||||
|
local _profile_path="${_udd}/${_profile_dir}"
|
||||||
|
mkdir -p "$_profile_path"
|
||||||
|
cp "$_avatar_image_path" "${_profile_path}/Google Profile Picture.png"
|
||||||
|
_copy_image_done=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── editar Local State con python3 ────────────────────────────────────────
|
||||||
|
if ! python3 - \
|
||||||
|
"$_local_state" \
|
||||||
|
"$_profile_dir" \
|
||||||
|
"${_avatar_index}" \
|
||||||
|
"${_avatar_image_path}" \
|
||||||
|
"${_color_hex}" <<'PY'; then
|
||||||
|
import sys, json
|
||||||
|
|
||||||
|
ls_path = sys.argv[1]
|
||||||
|
prof_dir = sys.argv[2]
|
||||||
|
avatar_index = int(sys.argv[3]) # -1 = no cambiar avatar
|
||||||
|
avatar_img = sys.argv[4] # "" = no usar imagen
|
||||||
|
color_hex = sys.argv[5] # "" = no cambiar color
|
||||||
|
|
||||||
|
with open(ls_path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
profile_section = data.setdefault("profile", {})
|
||||||
|
info_cache = profile_section.setdefault("info_cache", {})
|
||||||
|
|
||||||
|
# El perfil debe existir (ya validado en bash, pero doble check)
|
||||||
|
if prof_dir not in info_cache:
|
||||||
|
print(f"error: perfil '{prof_dir}' no existe en info_cache", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
entry = info_cache[prof_dir]
|
||||||
|
|
||||||
|
# ── Avatar ────────────────────────────────────────────────────────────────────
|
||||||
|
if avatar_index >= 0:
|
||||||
|
# Avatar built-in: IDR_PROFILE_AVATAR_<N>
|
||||||
|
entry["avatar_icon"] = f"chrome://theme/IDR_PROFILE_AVATAR_{avatar_index}"
|
||||||
|
entry["is_using_default_avatar"] = True
|
||||||
|
elif avatar_img:
|
||||||
|
# Avatar custom imagen: Chrome necesita gaia_picture_file_name
|
||||||
|
entry["avatar_icon"] = "chrome://theme/IDR_PROFILE_AVATAR_0"
|
||||||
|
entry["is_using_default_avatar"] = False
|
||||||
|
entry["gaia_picture_file_name"] = "Google Profile Picture.png"
|
||||||
|
|
||||||
|
# ── Color ─────────────────────────────────────────────────────────────────────
|
||||||
|
if color_hex:
|
||||||
|
rgb = int(color_hex, 16) # 0xRRGGBB
|
||||||
|
argb = 0xFF000000 | rgb # alpha=FF opaco → 0xFFRRGGBB
|
||||||
|
# Convertir a int32 con signo (Python usa enteros arbitrarios)
|
||||||
|
signed = argb - 0x100000000 if argb >= 0x80000000 else argb
|
||||||
|
|
||||||
|
entry["profile_highlight_color"] = signed
|
||||||
|
entry["profile_color_seed"] = signed
|
||||||
|
entry["default_avatar_fill_color"] = signed
|
||||||
|
|
||||||
|
with open(ls_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, separators=(",", ":"))
|
||||||
|
PY
|
||||||
|
echo "set_chrome_profile_appearance: error editando Local State con python3" >&2
|
||||||
|
return 4
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── validar JSON tras escritura ───────────────────────────────────────────
|
||||||
|
if ! python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$_local_state" 2>/dev/null; then
|
||||||
|
echo "set_chrome_profile_appearance: JSON inválido tras escribir Local State; restaurando backup" >&2
|
||||||
|
cp "$_backup" "$_local_state"
|
||||||
|
return 4
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── leer valores resultantes para el JSON de salida ───────────────────────
|
||||||
|
local _result_json
|
||||||
|
_result_json="$(python3 - "$_local_state" "$_profile_dir" <<'PY'
|
||||||
|
import json, sys
|
||||||
|
data = json.load(open(sys.argv[1]))
|
||||||
|
entry = data.get("profile", {}).get("info_cache", {}).get(sys.argv[2], {})
|
||||||
|
out = {
|
||||||
|
"profile": sys.argv[2],
|
||||||
|
"avatar_icon": entry.get("avatar_icon", ""),
|
||||||
|
"is_using_default_avatar": entry.get("is_using_default_avatar", True),
|
||||||
|
"profile_highlight_color": entry.get("profile_highlight_color", 0),
|
||||||
|
"profile_color_seed": entry.get("profile_color_seed", 0),
|
||||||
|
"default_avatar_fill_color": entry.get("default_avatar_fill_color", 0),
|
||||||
|
"backup": "Local State.bak." + __import__("datetime").date.today().strftime("%Y%m%d"),
|
||||||
|
}
|
||||||
|
print(json.dumps(out, separators=(",",":")))
|
||||||
|
PY
|
||||||
|
)"
|
||||||
|
|
||||||
|
echo "$_result_json"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── auto-ejecución ────────────────────────────────────────────────────────────
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
set_chrome_profile_appearance "$@"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user