// BASHEK.DE

SCRIPTS_ARCHIVE

POWERSHELL · BASH · READ ONLY

// BASH

GDRIVE_SYNC.SH

Moves recorded MP4 and MP3 files from the Douyin recorder output directory to Google Drive via rclone. Separates raw video and audio assets into dedicated archive folders.

BASHRCLONE
4.81.1k
READ ONLY
#!/bin/bash

GDRIVE="gdrive_real:Archive/Projects/TikTok/your-project"
DOWNLOADS="/home/user/projects/DouyinLiveRecorder/downloads"
STREAMER="抖音直播/StreamerName"

# MP4 → 01_Raw_Data
rclone move "$DOWNLOADS/$STREAMER" "$GDRIVE/01_Raw_Data" \
  --include "*.mp4" \
  --progress \
  --log-file /home/user/gdrive_sync.log \
  --log-level INFO

# MP3 aus Streamer-Ordner → 02_Audio_Assets
rclone move "$DOWNLOADS/$STREAMER" "$GDRIVE/02_Audio_Assets" \
  --include "*.mp3" \
  --progress \
  --log-file /home/user/gdrive_sync.log \
  --log-level INFO

# MP3-Chunks direkt in downloads/ → 02_Audio_Assets
rclone move "$DOWNLOADS" "$GDRIVE/02_Audio_Assets" \
  --include "*.mp3" \
  --exclude "抖音直播/**" \
  --progress \
  --log-file /home/user/gdrive_sync.log \
  --log-level INFO

echo "Sync completed: $(date)" >> /home/user/gdrive_sync.log

// BASH

DAILY_SYNC.SH

Daily cron job that pushes CLAUDE.md context files and project learnings/error logs to Google Drive via rclone. Runs at 03:00, keeps all VPS project docs in sync with BashekVault.

BASHRCLONE
4.7834
READ ONLY
#!/bin/bash
TIMESTAMP=$(date '+%Y-%m-%d')
GDRIVE_TARGET="gdrive_real:BashekVault/VPS-Sync"

rclone copy /home/user/.claude/CLAUDE.md "$GDRIVE_TARGET/claude/" --update
rclone copy /home/user/stream-intel/CLAUDE.md "$GDRIVE_TARGET/stream-intel/" --update
rclone copy /home/user/nexus/CLAUDE.md "$GDRIVE_TARGET/nexus/" --update
rclone copy /home/user/stream-intel/.learnings/ "$GDRIVE_TARGET/stream-intel/learnings/" --update

echo "[$TIMESTAMP] Daily sync complete" >> /home/user/logs/daily_sync.log

// BASH

VPS_BACKUP.SH

Full VPS backup via rclone to Google Drive. Dumps PostgreSQL, syncs Docker project directories (nexus, openclaw-core, stream-intel), excludes node_modules. Runs as a scheduled cron job.

BASHRCLONEDOCKER
4.91.4k
READ ONLY
#!/bin/bash

GDRIVE="gdrive_real:Archive/VPS_Backup"
DATE=$(date +%Y-%m-%d)

# PostgreSQL Dump
sudo -u postgres pg_dumpall > /tmp/postgres_backup.sql

# Backup project directories
rclone sync /home/user/nexus --exclude "node_modules/**" "$GDRIVE/nexus" \
  --log-file /home/user/vps_backup.log --log-level INFO
rclone sync /home/user/openclaw-core --exclude "node_modules/**" "$GDRIVE/openclaw-core" \
  --log-file /home/user/vps_backup.log --log-level INFO
rclone sync /home/user/stream-intel --exclude "node_modules/**" "$GDRIVE/stream-intel" \
  --log-file /home/user/vps_backup.log --log-level INFO
rclone copyto /tmp/postgres_backup.sql "$GDRIVE/postgresql/postgres_$DATE.sql" \
  --log-file /home/user/vps_backup.log --log-level INFO

rm -f /tmp/postgres_backup.sql
echo "Backup completed: $(date)" >> /home/user/vps_backup.log

// BASH

PREP_MP4_TO_GCS.SH

Converts Douyin stream recordings to 60-second MP3 chunks and uploads them to Google Cloud Storage for Speech-to-Text processing. Accepts multiple dates as arguments, processes all MP4s found in each day's folder.

BASHGCS
4.8672
READ ONLY
#!/bin/bash
# prep_mp4_to_gcs.sh — MP4 → 60s MP3 Chunks → GCS upload
# Usage: bash prep_mp4_to_gcs.sh 2026-04-18 2026-04-19 2026-04-20

BUCKET="gs://YOUR_BUCKET_NAME/historical"
BASE="/home/user/stream-intel/raw_incoming"
GCP_KEY="/home/user/stream-intel/bridge/gcp-key.json"
CHUNK=60

export GOOGLE_APPLICATION_CREDENTIALS="$GCP_KEY"

for DATE in "$@"; do
    DIR="$BASE/$DATE"
    echo "=== $DATE ==="
    for mp4 in "$DIR"/*.mp4; do
        [ -f "$mp4" ] || continue
        name=$(basename "$mp4")
        dur=$(ffprobe -v error -show_entries format=duration \
              -of default=noprint_wrappers=1:nokey=1 "$mp4" 2>/dev/null)
        dur=${dur%.*}
        echo "  $name — ${dur}s"
        for ((start=0; start<dur; start+=CHUNK)); do
            out="/tmp/${name}_part_${start}s.mp3"
            ffmpeg -v error -ss $start -t $CHUNK -i "$mp4" \
                   -vn -ar 16000 -ac 1 -b:a 32k "$out" -y
            gsutil -q cp "$out" "$BUCKET/$DATE/"
            rm "$out"
        done
        echo "  → $name done"
    done
    echo "=== $DATE fertig ==="
done
echo "### ALLE TAGE FERTIG ###"

// PYTHON

CHECK_LIVE_STATUS.PY

Polls the Douyin live API to detect stream state transitions (online/offline). Fires n8n webhooks on state change with a 35-minute cooldown. Designed to run every 5 minutes via cron, persists state to a JSON file.

PYTHONWEBHOOKAPI
4.7923
READ ONLY
#!/usr/bin/env python3
import json, os, time, urllib.request, urllib.error

DOUYIN_API_URL = (
    "https://live.douyin.com/webcast/room/web/enter/"
    "?aid=6383&live_id=1&device_platform=web&language=zh-CN&web_rid=YOUR_ROOM_ID"
)
HEADERS = {
    "Cookie": "YOUR_DOUYIN_COOKIE_HERE",
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
    ),
    "Referer": "https://live.douyin.com/",
}

WEBHOOK_URL = "https://n8n.your-domain.com/webhook/your-webhook-path"
STATE_FILE = "/tmp/douyin_state.json"
COOLDOWN_SEC = 35 * 60


def load_state():
    if os.path.exists(STATE_FILE):
        try:
            with open(STATE_FILE) as f:
                return json.load(f)
        except Exception:
            pass
    return {"wasLive": False, "lastOnlineSent": 0, "lastOfflineSent": 0}


def save_state(state):
    with open(STATE_FILE, "w") as f:
        json.dump(state, f)


def check_is_live():
    req = urllib.request.Request(DOUYIN_API_URL, headers=HEADERS)
    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read().decode())
    except Exception as e:
        print(f"API error: {e}")
        return None

    d = data.get("data", {})
    is_live = (
        d.get("is_living") == 1
        or d.get("room_status") == 2
        or (isinstance(d.get("data"), list) and len(d["data"]) > 0 and (
            d["data"][0].get("room", {}).get("status") == 2
        ))
    )
    return is_live


def post_webhook(event):
    payload = json.dumps({"event": event}).encode()
    req = urllib.request.Request(
        WEBHOOK_URL,
        data=payload,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            print(f"Webhook {event}: {resp.status}")
    except Exception as e:
        print(f"Webhook error ({event}): {e}")


def main():
    state = load_state()
    is_live = check_is_live()

    if is_live is None:
        print("No valid response — skipping state update")
        return

    was_live = state.get("wasLive", False)
    now = time.time()

    if is_live and not was_live:
        last_sent = state.get("lastOnlineSent", 0)
        if (now - last_sent) > COOLDOWN_SEC:
            print("online transition → firing webhook")
            post_webhook("online")
            state["lastOnlineSent"] = now
    elif not is_live and was_live:
        last_sent = state.get("lastOfflineSent", 0)
        if (now - last_sent) > COOLDOWN_SEC:
            print("offline transition → firing webhook")
            post_webhook("offline")
            state["lastOfflineSent"] = now
    else:
        print(f"no transition (isLive={is_live}, wasLive={was_live})")

    state["wasLive"] = is_live
    save_state(state)


if __name__ == "__main__":
    main()

// PYTHON

CREATE_DAILY_SHEET.PY

Creates a new dated tab in the stream analytics Google Sheet with all required column headers and conditional formatting rules. Skips creation if the tab already exists. Run once per day before stream processing begins.

PYTHONGOOGLE SHEETS
4.6541
READ ONLY
#!/usr/bin/env python3
import datetime
from google.oauth2 import service_account
from googleapiclient.discovery import build

SPREADSHEET_ID = "YOUR_SHEET_ID"
GCP_KEY = "/home/user/stream-intel/bridge/gcp-key.json"
SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]
HEADERS = [
    "Timestamp", "Streamer", "Transcript_Original", "Translation_EN",
    "Gemini_Analysis", "Audio_GCS_URI", "Translation_DE", "Keywords",
    "Topics", "Sentiment", "Sentiment_Score", "Flags", "Music_Detected",
    "Summary_EN", "Summary_DE", "Language_Detected", "Chunk_Index"
]

def color(r, g, b):
    return {"red": r/255, "green": g/255, "blue": b/255}

def apply_formatting(sheets, sid):
    requests = [
        {"updateSheetProperties": {
            "properties": {"sheetId": sid, "gridProperties": {"frozenRowCount": 1}},
            "fields": "gridProperties.frozenRowCount"
        }},
        {"addConditionalFormatRule": {"rule": {
            "ranges": [{"sheetId": sid, "startRowIndex": 1, "startColumnIndex": 10, "endColumnIndex": 11}],
            "booleanRule": {
                "condition": {"type": "NUMBER_GREATER", "values": [{"userEnteredValue": "0,3"}]},
                "format": {"backgroundColor": color(105, 219, 124)}
            }
        }, "index": 0}},
        {"addConditionalFormatRule": {"rule": {
            "ranges": [{"sheetId": sid, "startRowIndex": 1, "startColumnIndex": 10, "endColumnIndex": 11}],
            "booleanRule": {
                "condition": {"type": "NUMBER_LESS", "values": [{"userEnteredValue": "-0,3"}]},
                "format": {"backgroundColor": color(255, 107, 107)}
            }
        }, "index": 0}},
        {"addConditionalFormatRule": {"rule": {
            "ranges": [{"sheetId": sid, "startRowIndex": 1, "startColumnIndex": 11, "endColumnIndex": 12}],
            "booleanRule": {
                "condition": {"type": "TEXT_CONTAINS", "values": [{"userEnteredValue": "PARSE_ERROR"}]},
                "format": {"backgroundColor": color(255, 212, 59)}
            }
        }, "index": 0}},
    ]
    sheets.batchUpdate(spreadsheetId=SPREADSHEET_ID, body={"requests": requests}).execute()
    print(f"Formatting applied (freeze + 3 conditional rules)")

def main():
    today = datetime.date.today().strftime("%Y-%m-%d")
    creds = service_account.Credentials.from_service_account_file(GCP_KEY, scopes=SCOPES)
    service = build("sheets", "v4", credentials=creds)
    sheets = service.spreadsheets()

    meta = sheets.get(spreadsheetId=SPREADSHEET_ID).execute()
    existing = [s["properties"]["title"] for s in meta["sheets"]]

    if today in existing:
        print(f"Sheet '{today}' already exists. Nothing to do.")
        return

    body = {"requests": [{"addSheet": {"properties": {"title": today}}}]}
    resp = sheets.batchUpdate(spreadsheetId=SPREADSHEET_ID, body=body).execute()
    sheet_id = resp["replies"][0]["addSheet"]["properties"]["sheetId"]
    print(f"Created sheet '{today}' (id={sheet_id})")

    sheets.values().update(
        spreadsheetId=SPREADSHEET_ID,
        range=f"'{today}'!A1",
        valueInputOption="RAW",
        body={"values": [HEADERS]}
    ).execute()
    print(f"Header written ({len(HEADERS)} columns)")

    apply_formatting(sheets, sheet_id)

if __name__ == "__main__":
    main()