chore: auto-commit from dashboard
This commit is contained in:
@@ -269,9 +269,9 @@
|
|||||||
"prompt": "Heartbeat check. Rulează src/heartbeat.py printr-un scurt raport de status.\nDacă nu e nimic de raportat (email=0, calendar nu are evenimente <2h, kb ok), răspunde doar cu HEARTBEAT_OK și oprește-te — nu trimite mesaj.\nDacă e ceva: raport scurt pe Discord #echo-work.",
|
"prompt": "Heartbeat check. Rulează src/heartbeat.py printr-un scurt raport de status.\nDacă nu e nimic de raportat (email=0, calendar nu are evenimente <2h, kb ok), răspunde doar cu HEARTBEAT_OK și oprește-te — nu trimite mesaj.\nDacă e ceva: raport scurt pe Discord #echo-work.",
|
||||||
"allowed_tools": [],
|
"allowed_tools": [],
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"last_run": "2026-04-29T16:00:00.003223+00:00",
|
"last_run": "2026-04-29T18:00:00.001439+00:00",
|
||||||
"last_status": "ok",
|
"last_status": "ok",
|
||||||
"next_run": "2026-04-29T18:00:00+00:00"
|
"next_run": "2026-04-30T06:00:00+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "night-execute",
|
"name": "night-execute",
|
||||||
|
|||||||
@@ -1128,6 +1128,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderNoteCard(note) {
|
function renderNoteCard(note) {
|
||||||
|
if (!note.file) return '';
|
||||||
// Domains (portocaliu), Types (mov), Tags colapsate cu expand
|
// Domains (portocaliu), Types (mov), Tags colapsate cu expand
|
||||||
const tags = note.tags || [];
|
const tags = note.tags || [];
|
||||||
const noteId = note.file.replace(/[^a-zA-Z0-9]/g, '_');
|
const noteId = note.file.replace(/[^a-zA-Z0-9]/g, '_');
|
||||||
|
|||||||
@@ -1,5 +1,25 @@
|
|||||||
{
|
{
|
||||||
"notes": [
|
"notes": [
|
||||||
|
{
|
||||||
|
"file": "notes-data/facebook/2026-04-29_julien-blanc-love-yourself.md",
|
||||||
|
"title": "Love Yourself Even If They Don't",
|
||||||
|
"date": "2026-04-29",
|
||||||
|
"tags": [
|
||||||
|
"self-love",
|
||||||
|
"self-esteem",
|
||||||
|
"social-approval",
|
||||||
|
"mindset"
|
||||||
|
],
|
||||||
|
"domains": [],
|
||||||
|
"types": [
|
||||||
|
"coaching"
|
||||||
|
],
|
||||||
|
"category": "facebook",
|
||||||
|
"project": null,
|
||||||
|
"subdir": null,
|
||||||
|
"video": "",
|
||||||
|
"tldr": "Julien Blanc face un exercițiu pe stradă: îl întreabă pe un trecător care e artistul lui preferat. Răspunsul: Glenn Gould. Nimeni din grup nu știa cine e, apoi toți l-au criticat. Întrebarea lui Julie..."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"file": "notes-data/youtube/2026-04-29_ternary-models-local-ai.md",
|
"file": "notes-data/youtube/2026-04-29_ternary-models-local-ai.md",
|
||||||
"title": "I Just Tried The Brand New Ternary Model And It's Great!",
|
"title": "I Just Tried The Brand New Ternary Model And It's Great!",
|
||||||
@@ -8559,8 +8579,8 @@
|
|||||||
"title": "Proiect: Vending Master - Integrare Website → ROA",
|
"title": "Proiect: Vending Master - Integrare Website → ROA",
|
||||||
"date": "2026-01-30",
|
"date": "2026-01-30",
|
||||||
"tags": [
|
"tags": [
|
||||||
"integrare",
|
"vending-master",
|
||||||
"vending-master"
|
"integrare"
|
||||||
],
|
],
|
||||||
"domains": [
|
"domains": [
|
||||||
"work"
|
"work"
|
||||||
@@ -9037,21 +9057,6 @@
|
|||||||
"subdir": null,
|
"subdir": null,
|
||||||
"video": "",
|
"video": "",
|
||||||
"tldr": "Tutorial complet pentru a vorbi cu Claude Code prin telefon, folosind 3CX (sistem telefonic cloud gratuit) + un proiect GitHub custom. Setup-ul implică: 3CX cloud (gratuit, <10 useri), un voice server..."
|
"tldr": "Tutorial complet pentru a vorbi cu Claude Code prin telefon, folosind 3CX (sistem telefonic cloud gratuit) + un proiect GitHub custom. Setup-ul implică: 3CX cloud (gratuit, <10 useri), un voice server..."
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "facebook/2026-04-29_julien-blanc-love-yourself",
|
|
||||||
"title": "Love Yourself Even If They Don't — Julien Blanc",
|
|
||||||
"path": "memory/kb/facebook/2026-04-29_julien-blanc-love-yourself.md",
|
|
||||||
"date": "2026-04-29",
|
|
||||||
"tags": [
|
|
||||||
"coaching",
|
|
||||||
"self-love",
|
|
||||||
"self-esteem",
|
|
||||||
"social-approval",
|
|
||||||
"mindset"
|
|
||||||
],
|
|
||||||
"source": "facebook-reel",
|
|
||||||
"creator": "Julien Blanc"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stats": {
|
"stats": {
|
||||||
@@ -9069,6 +9074,7 @@
|
|||||||
"conversations": 0,
|
"conversations": 0,
|
||||||
"emails": 18,
|
"emails": 18,
|
||||||
"exercitii": 4,
|
"exercitii": 4,
|
||||||
|
"facebook": 1,
|
||||||
"health": 6,
|
"health": 6,
|
||||||
"insights": 46,
|
"insights": 46,
|
||||||
"projects": 233,
|
"projects": 233,
|
||||||
@@ -9103,6 +9109,7 @@
|
|||||||
"conversations",
|
"conversations",
|
||||||
"emails",
|
"emails",
|
||||||
"exercitii",
|
"exercitii",
|
||||||
|
"facebook",
|
||||||
"health",
|
"health",
|
||||||
"insights",
|
"insights",
|
||||||
"projects",
|
"projects",
|
||||||
@@ -9112,4 +9119,4 @@
|
|||||||
"youtube",
|
"youtube",
|
||||||
"memory"
|
"memory"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,31 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Descarcă un video (Facebook, YouTube etc.), extrage audio, transcrie cu Whisper.
|
# Descarcă un video (Facebook, YouTube etc.), extrage audio, transcrie cu Whisper.
|
||||||
# Usage: ./transcribe_video.sh <URL> [language]
|
# Usage: ./transcribe_video.sh <URL> [language]
|
||||||
|
# ./transcribe_video.sh <URL> [language] --save-kb
|
||||||
# Exemple:
|
# Exemple:
|
||||||
# ./transcribe_video.sh "https://www.facebook.com/share/v/1EdPt3q2sq/"
|
# ./transcribe_video.sh "https://www.facebook.com/share/v/1EdPt3q2sq/"
|
||||||
|
# ./transcribe_video.sh "https://www.facebook.com/share/r/1akfPJYvTw/" ro --save-kb
|
||||||
# ./transcribe_video.sh "https://youtu.be/xyz" ro
|
# ./transcribe_video.sh "https://youtu.be/xyz" ro
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
URL="${1:-}"
|
URL="${1:-}"
|
||||||
LANG="${2:-en}"
|
LANG="${2:-en}"
|
||||||
|
SAVE_KB=0
|
||||||
|
|
||||||
|
# Parse flags
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ "$arg" == "--save-kb" ]]; then
|
||||||
|
SAVE_KB=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
WORKDIR="/tmp/transcribe_$$"
|
WORKDIR="/tmp/transcribe_$$"
|
||||||
|
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
KB_DIR="$PROJECT_ROOT/memory/kb"
|
||||||
|
|
||||||
if [[ -z "$URL" ]]; then
|
if [[ -z "$URL" ]]; then
|
||||||
echo "Usage: $0 <URL> [language (default: en)]"
|
echo "Usage: $0 <URL> [language (default: en)] [--save-kb]"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -21,7 +34,13 @@ export PATH="/home/moltbot/bin:$PATH"
|
|||||||
mkdir -p "$WORKDIR"
|
mkdir -p "$WORKDIR"
|
||||||
trap 'rm -rf "$WORKDIR"' EXIT
|
trap 'rm -rf "$WORKDIR"' EXIT
|
||||||
|
|
||||||
echo "→ Descarc video..."
|
echo "→ Obțin informații video..."
|
||||||
|
INFO_JSON=$(yt-dlp "$URL" --dump-json --no-download -q 2>/dev/null || echo "{}")
|
||||||
|
TITLE=$(echo "$INFO_JSON" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('title','Unknown'))" 2>/dev/null || echo "Unknown")
|
||||||
|
CREATOR=$(echo "$INFO_JSON" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('uploader') or d.get('channel') or '')" 2>/dev/null || echo "")
|
||||||
|
DURATION=$(echo "$INFO_JSON" | python3 -c "import json,sys; d=json.load(sys.stdin); s=d.get('duration',0); print(f'{s//60}:{s%60:02d}')" 2>/dev/null || echo "?")
|
||||||
|
|
||||||
|
echo "→ Descarc video: $TITLE..."
|
||||||
yt-dlp "$URL" -o "$WORKDIR/video.%(ext)s" --no-playlist -q
|
yt-dlp "$URL" -o "$WORKDIR/video.%(ext)s" --no-playlist -q
|
||||||
|
|
||||||
VIDEO_FILE=$(ls "$WORKDIR"/video.* 2>/dev/null | head -1)
|
VIDEO_FILE=$(ls "$WORKDIR"/video.* 2>/dev/null | head -1)
|
||||||
@@ -34,12 +53,77 @@ echo "→ Extrag audio..."
|
|||||||
ffmpeg -i "$VIDEO_FILE" -vn -acodec pcm_s16le -ar 16000 -ac 1 "$WORKDIR/audio.wav" -y -loglevel error
|
ffmpeg -i "$VIDEO_FILE" -vn -acodec pcm_s16le -ar 16000 -ac 1 "$WORKDIR/audio.wav" -y -loglevel error
|
||||||
|
|
||||||
echo "→ Transcriu cu Whisper (model: small, limbă: $LANG)..."
|
echo "→ Transcriu cu Whisper (model: small, limbă: $LANG)..."
|
||||||
python3 -c "
|
TRANSCRIPT=$(python3 -c "
|
||||||
import whisper
|
import whisper
|
||||||
model = whisper.load_model('small')
|
model = whisper.load_model('small')
|
||||||
result = model.transcribe('$WORKDIR/audio.wav', language='$LANG')
|
result = model.transcribe('$WORKDIR/audio.wav', language='$LANG')
|
||||||
print(result['text'])
|
print(result['text'])
|
||||||
" 2>/dev/null
|
" 2>/dev/null)
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "✓ Gata."
|
echo "=== $TITLE ==="
|
||||||
|
echo "$TRANSCRIPT"
|
||||||
|
echo ""
|
||||||
|
echo "✓ Transcriere completă."
|
||||||
|
|
||||||
|
if [[ "$SAVE_KB" == "1" ]]; then
|
||||||
|
DATE=$(date +%Y-%m-%d)
|
||||||
|
|
||||||
|
# Slug din titlu: lowercase, fără diacritice, doar alfanumerice și cratime
|
||||||
|
SLUG=$(echo "$TITLE" | python3 -c "
|
||||||
|
import sys, re, unicodedata
|
||||||
|
s = sys.stdin.read().strip()
|
||||||
|
s = unicodedata.normalize('NFD', s)
|
||||||
|
s = ''.join(c for c in s if unicodedata.category(c) != 'Mn')
|
||||||
|
s = s.lower()
|
||||||
|
s = re.sub(r'[^a-z0-9]+', '-', s)
|
||||||
|
s = s.strip('-')[:50]
|
||||||
|
print(s)
|
||||||
|
")
|
||||||
|
|
||||||
|
# Detectează categoria din URL
|
||||||
|
if echo "$URL" | grep -qi "facebook\.com"; then
|
||||||
|
CATEGORY="facebook"
|
||||||
|
FORMAT="Reel (~${DURATION} min)"
|
||||||
|
elif echo "$URL" | grep -qi "youtube\.com\|youtu\.be"; then
|
||||||
|
CATEGORY="youtube"
|
||||||
|
FORMAT="Video (~${DURATION} min)"
|
||||||
|
else
|
||||||
|
CATEGORY="media"
|
||||||
|
FORMAT="Video (~${DURATION} min)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
NOTE_DIR="$KB_DIR/$CATEGORY"
|
||||||
|
mkdir -p "$NOTE_DIR"
|
||||||
|
NOTE_FILE="$NOTE_DIR/${DATE}_${SLUG}.md"
|
||||||
|
|
||||||
|
cat > "$NOTE_FILE" << NOTEEOF
|
||||||
|
# $TITLE
|
||||||
|
|
||||||
|
**Sursa:** $URL
|
||||||
|
**Data:** $DATE
|
||||||
|
**Creator:** $CREATOR
|
||||||
|
**Format:** $FORMAT
|
||||||
|
**Tags:** @coaching
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TL;DR
|
||||||
|
|
||||||
|
<!-- Completează un rezumat de 2-3 rânduri -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Transcrierea
|
||||||
|
|
||||||
|
$TRANSCRIPT
|
||||||
|
NOTEEOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "→ Notiță salvată: $NOTE_FILE"
|
||||||
|
|
||||||
|
echo "→ Reindexez KB..."
|
||||||
|
python3 "$PROJECT_ROOT/tools/update_notes_index.py"
|
||||||
|
|
||||||
|
echo "✓ KB actualizat. Link: /echo/files.html#memory/kb/$CATEGORY/${DATE}_${SLUG}.md"
|
||||||
|
fi
|
||||||
|
|||||||
@@ -16,3 +16,10 @@ Lecții capturate din corectările lui Marius. Citește acest fișier la începu
|
|||||||
---
|
---
|
||||||
|
|
||||||
<!-- Lecțiile se adaugă mai jos, cele mai noi sus. -->
|
<!-- Lecțiile se adaugă mai jos, cele mai noi sus. -->
|
||||||
|
|
||||||
|
## Nu scrie manual în index.json — rulează update_notes_index.py
|
||||||
|
**Data:** 2026-04-29
|
||||||
|
**Context:** Salvam o notiță din Facebook reel în memory/kb/. Am adăugat manual o intrare în index.json cu schema greșită (`id` + `path` în loc de `file`), ceea ce a blocat notes.html pe "Se încarcă..." cu un TypeError în renderNoteCard.
|
||||||
|
**Greșeala:** Am editat index.json direct, cu o schemă diferită față de ce produce update_notes_index.py.
|
||||||
|
**Regula:** Niciodată nu scriei manual în `memory/kb/index.json`. Fluxul corect: (1) creezi fișierul `.md` în `memory/kb/<categorie>/`, (2) rulezi `python3 tools/update_notes_index.py`. Dacă ai nevoie să salvezi o notiță din Facebook/video, folosești `scripts/transcribe_video.sh <URL> <lang> --save-kb` care face totul corect.
|
||||||
|
**Când se aplică:** Orice salvare de notiță în KB (Facebook, YouTube, coaching, insights, orice). Dacă ești tentat să `json.dump` în index.json — stop, rulează scriptul.
|
||||||
|
|||||||
Reference in New Issue
Block a user