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:
53
scripts/asr_processor.py
Normal file
53
scripts/asr_processor.py
Normal 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
78
scripts/install_mongodb.sh
Executable 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)"
|
||||
97
scripts/thumbnail_extractor.py
Normal file
97
scripts/thumbnail_extractor.py
Normal 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
151
scripts/troubleshoot.sh
Executable 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
|
||||
Reference in New Issue
Block a user