Initial commit: Momentry Core v0.1

- Rust-based digital asset management system
- Video analysis: ASR, OCR, YOLO, Face, Pose
- RAG capabilities with Qdrant vector database
- Multi-database support: PostgreSQL, Redis, MongoDB
- Monitoring system with launchd plists
- n8n workflow automation integration
This commit is contained in:
accusys
2026-03-16 15:07:33 +08:00
commit de14bd6afa
101 changed files with 19858 additions and 0 deletions

53
scripts/asr_processor.py Normal file
View File

@@ -0,0 +1,53 @@
#!/opt/homebrew/bin/python3.11
import sys
import json
import tempfile
import os
from faster_whisper import WhisperModel
def run_asr(video_path, output_path):
print(f"ASR_START", file=sys.stderr)
print(f"Loading Whisper model...", file=sys.stderr)
model = WhisperModel("tiny", device="cpu", compute_type="int8")
print(f"Transcribing: {video_path}", file=sys.stderr)
segments, info = model.transcribe(video_path, beam_size=5)
print(f"ASR_LANGUAGE:{info.language}", file=sys.stderr)
print(
f"Detected language: {info.language} (probability: {info.language_probability:.2f})",
file=sys.stderr,
)
results = []
total_segments = 0
for segment in segments:
results.append(
{"start": segment.start, "end": segment.end, "text": segment.text.strip()}
)
total_segments += 1
if total_segments % 100 == 0:
print(f"ASR_PROGRESS:{total_segments}", file=sys.stderr)
output = {
"language": info.language,
"language_probability": info.language_probability,
"segments": results,
}
with open(output_path, "w") as f:
json.dump(output, f, indent=2)
print(f"ASR_COMPLETE:{total_segments}", file=sys.stderr)
print(f"ASR complete. {len(results)} segments.", file=sys.stderr)
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: asr_processor.py <video_path> <output_json_path>")
sys.exit(1)
run_asr(sys.argv[1], sys.argv[2])

78
scripts/install_mongodb.sh Executable file
View File

@@ -0,0 +1,78 @@
#!/bin/bash
# MongoDB Installation Script
echo "=== MongoDB Installation ==="
echo ""
# Step 1: Create directories
echo "Step 1: Creating directories..."
sudo mkdir -p /Users/accusys/momentry/var
sudo mkdir -p /Users/accusys/momentry/log
sudo chown -R accusys:staff /Users/accusys/momentry
echo " ✓ Directories created"
# Step 2: Check and backup existing plist
echo ""
echo "Step 2: Checking existing plist..."
PLIST_PATH="/Library/LaunchDaemons/com.momentry.mongodb.plist"
if [ -f "$PLIST_PATH" ]; then
BACKUP_NAME="${PLIST_PATH}.$(date +%Y%m%d%H%M%S).bak"
echo " Backing up existing plist to: $BACKUP_NAME"
sudo mv "$PLIST_PATH" "$BACKUP_NAME"
echo " ✓ Existing plist backed up"
else
echo " ✓ No existing plist found"
fi
# Step 3: Copy new plist
echo ""
echo "Step 3: Copying new plist..."
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SOURCE_PLIST="$SCRIPT_DIR/momentry_runtime/plist/com.momentry.mongodb.plist"
if [ -f "$SOURCE_PLIST" ]; then
sudo cp "$SOURCE_PLIST" /Library/LaunchDaemons/
echo " ✓ plist copied"
else
echo " ✗ Source plist not found: $SOURCE_PLIST"
exit 1
fi
# Step 4: Start MongoDB
echo ""
echo "Step 4: Starting MongoDB..."
sudo launchctl load /Library/LaunchDaemons/com.momentry.mongodb.plist
echo " ✓ MongoDB started"
# Step 5: Verify
echo ""
echo "Step 5: Verifying..."
sleep 2
echo ""
echo " Port 27017 status:"
lsof -i :27017 || echo " (checking...)"
echo ""
echo " Service status:"
sudo launchctl list | grep momentry || echo " No service found"
# Step 6: Create user (without auth first)
echo ""
echo "Step 6: Creating database user..."
mongosh --eval '
use admin
db.createUser({
user: "accusys",
pwd: "Test3200Test3200",
roles: [{ role: "root", db: "admin" }]
})
' 2>/dev/null || echo " (user creation deferred - MongoDB may still be starting)"
echo ""
echo "=== Installation Complete ==="
echo ""
echo "Connection string:"
echo " mongodb://accusys:Test3200Test3200@localhost:27017/momentry"
echo ""
echo "Remote access enabled (bind_ip: 0.0.0.0)"

View File

@@ -0,0 +1,97 @@
#!/opt/homebrew/bin/python3.11
import sys
import os
import subprocess
import json
import argparse
def get_duration(video_path):
result = subprocess.run(
["ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", video_path],
capture_output=True,
text=True,
)
data = json.loads(result.stdout)
return float(data["format"]["duration"])
def extract_thumbnails(video_path, uuid, output_dir, count=6):
"""Extract evenly-spaced thumbnails from video."""
if not os.path.exists(video_path):
print(f"Error: Video file not found: {video_path}", file=sys.stderr)
return None
duration = get_duration(video_path)
print(f"Video duration: {duration:.2f}s", file=sys.stderr)
# Output directory
thumb_dir = os.path.join(output_dir, uuid)
os.makedirs(thumb_dir, exist_ok=True)
# Calculate timestamps (skip first 10%, skip last 10%)
start_ts = duration * 0.1
end_ts = duration * 0.9
interval = (end_ts - start_ts) / (count - 1) if count > 1 else 0
extracted = []
for i in range(count):
if count == 1:
ts = duration / 2
else:
ts = start_ts + (interval * i)
output_file = os.path.join(thumb_dir, f"thumb_{i:03d}.jpg")
result = subprocess.run(
[
"ffmpeg",
"-y",
"-ss",
str(ts),
"-i",
video_path,
"-vframes",
"1",
"-q:v",
"2",
"-vf",
"scale=320:-1",
output_file,
],
capture_output=True,
)
if result.returncode == 0 and os.path.exists(output_file):
extracted.append(output_file)
print(f" Extracted: {output_file} at {ts:.1f}s", file=sys.stderr)
else:
print(f" Failed to extract frame at {ts:.1f}s", file=sys.stderr)
return extracted
def main():
parser = argparse.ArgumentParser(
description="Extract keyframe thumbnails from video"
)
parser.add_argument("video_path", help="Path to video file")
parser.add_argument("uuid", help="Video UUID")
parser.add_argument("-o", "--output", default="thumbnails", help="Output directory")
parser.add_argument(
"-c", "--count", type=int, default=6, help="Number of thumbnails"
)
args = parser.parse_args()
result = extract_thumbnails(args.video_path, args.uuid, args.output, args.count)
if result:
print(json.dumps({"uuid": args.uuid, "count": len(result), "files": result}))
else:
print(json.dumps({"error": "Failed to extract thumbnails"}))
sys.exit(1)
if __name__ == "__main__":
main()

151
scripts/troubleshoot.sh Executable file
View File

@@ -0,0 +1,151 @@
#!/bin/bash
# Momentry 服務故障排除快速參考
# 用法: ./troubleshoot.sh [service]
# 例如: ./troubleshoot.sh n8n
SERVICE=${1:-all}
echo "========================================"
echo "Momentry 故障排除快速檢查"
echo "========================================"
echo ""
case "$SERVICE" in
all)
echo "執行所有服務健康檢查..."
/Users/accusys/momentry_core_0.1/monitor/service/health_check.sh
;;
postgresql|postgres|pg)
echo "=== PostgreSQL 診斷 ==="
echo "狀態: $(pg_isready -h localhost -p 5432 -U accusys 2>&1)"
echo "連線測試: $(psql -U accusys -h localhost -d momentry -c 'SELECT 1' 2>&1 | head -1)"
echo "數據庫列表:"
psql -U accusys -h localhost -l 2>/dev/null | grep -v "rows)" || echo " 無法連線"
echo ""
echo "日誌: tail -50 /Users/accusys/momentry/log/postgresql.log"
;;
redis)
echo "=== Redis 診斷 ==="
echo "狀態: $(redis-cli -a accusys ping 2>&1)"
echo "記憶體: $(redis-cli -a accusys INFO memory 2>/dev/null | grep used_memory_human | head -1)"
echo "連線數: $(redis-cli -a accusys INFO clients 2>/dev/null | grep connected_clients | head -1)"
echo "Keys: $(redis-cli -a accusys DBSIZE 2>/dev/null)"
echo ""
echo "日誌: tail -50 /Users/accusys/momentry/log/redis.log"
;;
mariadb|mysql)
echo "=== MariaDB 診斷 ==="
echo "狀態: $(mysql -u accusys -e 'SELECT 1' 2>&1 | head -1)"
echo "用戶:"
mysql -u accusys -e "SELECT user, host FROM mysql.user;" 2>/dev/null || echo " 無法連線"
echo ""
echo "日誌: tail -50 /Users/accusys/momentry/log/mariadb.log"
;;
n8n)
echo "=== n8n 診斷 ==="
echo "Web 訪問: $(curl -s -o /dev/null -w '%{http_code}' http://localhost:8085/ 2>&1)"
echo "Port 8085: $(lsof -i :8085 | tail -1)"
echo ""
echo "PostgreSQL 連線:"
psql -U n8n -h localhost -d n8n -c "SELECT COUNT(*) as workflows FROM workflow_entity;" 2>&1
psql -U n8n -h localhost -d n8n -c "SELECT email, \"firstName\", \"roleSlug\" FROM \"user\";" 2>&1
echo ""
echo "Redis Queue:"
redis-cli -a accusys LLEN bull:n8n:wait 2>/dev/null || echo " 無法連線"
echo ""
echo "日誌: tail -50 /Users/accusys/momentry/log/n8n-main.log"
;;
ollama)
echo "=== Ollama 診斷 ==="
echo "API 狀態: $(curl -s http://localhost:11434/api/tags 2>&1 | head -1)"
echo "模型列表:"
curl -s http://localhost:11434/api/tags 2>/dev/null | jq -r '.models[].name' 2>/dev/null || echo " 無法獲取"
echo ""
echo "日誌: tail -50 /Users/accusys/momentry/log/ollama.log"
;;
qdrant)
echo "=== Qdrant 診斷 ==="
echo "API 狀態: $(curl -s -o /dev/null -w '%{http_code}' -H 'api-key: Test3200Test3200' http://localhost:6333/ 2>&1)"
echo "Collections:"
curl -s -H "api-key: Test3200Test3200" http://localhost:6333/collections 2>/dev/null | jq -r '.result[].name' 2>/dev/null || echo " 無法獲取"
echo ""
echo "日誌: tail -50 /Users/accusys/momentry/log/qdrant.log"
;;
caddy)
echo "=== Caddy 診斷 ==="
echo "Admin API: $(curl -s http://localhost:2019/config/ 2>&1 | head -1)"
echo "Port 2019: $(lsof -i :2019 | tail -1)"
echo "Port 443: $(lsof -i :443 | tail -1)"
echo ""
echo "配置: cat /Users/accusys/momentry/etc/Caddyfile"
echo "日誌: tail -50 /Users/accusys/momentry/log/caddy.log"
;;
gitea)
echo "=== Gitea 診斷 ==="
echo "Web: $(curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/ 2>&1)"
echo "Port 3000: $(lsof -i :3000 | tail -1)"
echo ""
echo "日誌: tail -50 /Users/accusys/momentry/log/gitea.log"
;;
sftpgo)
echo "=== SFTPGo 診斷 ==="
echo "HTTP: $(curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/ 2>&1)"
echo "SFTP Port 2022: $(lsof -i :2022 | tail -1)"
echo ""
echo "日誌: tail -50 /Users/accusys/momentry/log/sftpgo.log"
;;
mongodb|mongo)
echo "=== MongoDB 診斷 ==="
mongosh --quiet --username accusys --password Test3200Test3200 --authenticationDatabase admin --eval "db.adminCommand('ping')" 2>&1
echo "數據庫:"
mongosh --quiet --username accusys --password Test3200Test3200 --authenticationDatabase admin --eval "db.adminCommand({listDatabases:1})" 2>&1 | grep name || echo " 無法獲取"
echo ""
echo "日誌: tail -50 /Users/accusys/momentry/log/mongodb.log"
;;
php)
echo "=== PHP-FPM 診斷 ==="
echo "進程: $(pgrep -a php-fpm | head -1)"
echo "Port 9000: $(lsof -i :9000 | tail -1)"
echo ""
echo "日誌: tail -50 /Users/accusys/momentry/log/php.error.log"
;;
rustdesk)
echo "=== RustDesk 診斷 ==="
echo "hbbs: $(pgrep -a hbbs | head -1)"
echo "hbbr: $(pgrep -a hbbr | head -1)"
echo "Port 21116: $(lsof -i :21116 | tail -1)"
echo "Port 21117: $(lsof -i :21117 | tail -1)"
;;
*)
echo "用法: $0 [service]"
echo ""
echo "可用服務:"
echo " all - 執行所有健康檢查"
echo " postgresql - PostgreSQL 診斷"
echo " redis - Redis 診斷"
echo " mariadb - MariaDB 診斷"
echo " n8n - n8n 診斷"
echo " ollama - Ollama 診斷"
echo " qdrant - Qdrant 診斷"
echo " caddy - Caddy 診斷"
echo " gitea - Gitea 診斷"
echo " sftpgo - SFTPGo 診斷"
echo " mongodb - MongoDB 診斷"
echo " php - PHP-FPM 診斷"
echo " rustdesk - RustDesk 診斷"
;;
esac