SuperAPI AI Directory

wjs-mining-voicedrop

Writing & research

View repository

Updated 10-15 23:32

About this skill

wjs-mining-voicedrop

VoiceDrop inbox (jianshuo.dev/files on VoiceDrop-*.m4a) → transcribe one by one → hand to wjs-mining-articles to produce 公众号 drafts. This is the Mac-side closed loop for the VoiceDrop iOS app (record on open, upload on stop).

This skill's own output = ① 公众号 drafts (~/code/wechat-publish/) + ② local audio/SRT archive (~/code/voicedrop/archive/) + ③ processing markers on R2 (articles/<stem>.json or .empty) + ④ a batch report (how many processed, how many articles each produced, which were marked as no speech and why, how many remain unprocessed). The full interface contract is at agents/interface.yaml。

Core Principle

Reuse, don't rewrite. This skill only does the two things that don't exist on their own:inbox in/out(list/download/mark) andper-item orchestration. Transcription goes to wjs-transcribing-audio, article writing goes to wjs-mining-articles, not one line is rewritten.

R2 is never deleted; marker files indicate processing status. Audio stays on R2 until the user deletes it in the app. "Unprocessed" = no articles/<stem>.json(article written) or articles/<stem>.empty(no speech) marker yet of VoiceDrop-*.m4a;list automatically lists only unprocessed ones. Once an itemsucceedsand becomes an article, write mark-done,no speech/corruptedwrite mark-empty——both prevent this item from being processed again.never delete(delete is reserved for the user to manually clean up in the app).

When This Skill Fires

  • User says "process VoiceDrop recordings", "dig new recordings into articles", "process my dictations"
  • User runs /wjs-mining-voicedrop

When NOT to use

  • already has SRT → directly wjs-mining-articles
  • audio not in R2 inbox(local loose files) → directly wjs-transcribing-audio to produce SRT, then wjs-mining-articles
  • the bucket contains non-recording files uploaded by other machines → this skill only recognizes the VoiceDrop-*.m4a prefix, touches nothing else

Prerequisites

  • ~/code/.env contains FILES_TOKEN(inbox authentication) and Volcano ASR creds (VOLC_ASR_* / VOLC_TTS_*, for transcription).set -a; source ~/code/.env; set +a。

Workflow

The only new code:scripts/voicedrop-inbox.sh(list / download / mark-done / mark-empty / delete, token is read from ~/code/.env at runtime, never committed to code).list lists only unprocessed;mark-done/mark-empty writes processing markers;delete is only for manual cleanup, the article-writing flow doesn't call it.

Step 0 · Locate script + load environment (does not depend on current directory)

INBOX=~/.claude/skills/wjs-mining-voicedrop/scripts/voicedrop-inbox.sh
set -a; source ~/code/.env; set +a    # FILES_TOKEN + 火山 ASR creds

Call the script with the absolute path $INBOX ——do notwrite it as scripts/voicedrop-inbox.sh, that relies on the hidden assumption that "the person happens to be in the skill root directory", and it breaks when the directory changes.

Step 1 · List the inbox

"$INBOX" list      # 打印未处理的 VoiceDrop-*.m4a,一行一个
  • Commandexits non-zero(network unreachable / token invalid) → report "inbox unreachable or FILES_TOKEN invalid, check ~/code/.env" and stop,do not enter the loop。
  • Output empty → report "no new recordings in the inbox" and end.
  • Non-empty → get this batch's file names.

Step 2 · Close the loop one by one (serial, finish one before the next)

Serial。Batch resilience: if any single step of one item fails → log the reason, skip to the next item, never abort the whole batch, never leave an item unmarked. Each recording must ultimately end up in one of three terminal states:transcribed (mark-done) / no speech (mark-empty) / failed (not marked, left for next time)——never "processed but marked nothing". For each <name>:

  1. Download and archive:
    "$INBOX" download <name> ~/code/voicedrop/archive
    
    Audio lands in ~/code/voicedrop/archive/<name>. The original on R2 is always retained; this local copy is just an offline duplicate.
  2. Take a quick look to check whether it's a real recording:
    dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$audio" 2>/dev/null)
    
    dur is empty (not audio/corrupt) → "$INBOX" mark-empty <name> corrupt;< 1.0 seconds (misupload/silence) → "$INBOX" mark-empty <name> silent. After marking, report to the user,skip to the next item——this item is already in a terminal state, do not process it again.
  3. Transcribe → SRT: load wjs-transcribing-audio(for Chinese, use Volcano Doubao volc_asr_stream.py + build_srt_from_asr.py + do AI polishing within the session to fix typos). SRT lands in ~/code/voicedrop/archive/<stem>.srt。
    • the transcription comes out empty(there's sound but the ASR outputs not a single word, usually ambient noise) → "$INBOX" mark-empty <name> no-speech, report, skip to the next item.
  4. Mine articles: hand this SRT to wjs-mining-articles to run its full pipeline—produce a topic list (its human gate, follow it as usual, don't skip), write the article, create the WeChat draft. Voice memos are mostly short single-topic monologues, the list often has only 1 item, still let the user confirm as usual.
  5. After successful transcription, write the processing marker(at least one draft produced, user didn't abort): assemble the articles mined from this item into a v2 JSON ({"schema":2,"status":"ready","sourceAudio":"<name>","articles":[{"title","body"},…]}, may include transcript/srt) write it to a temp file, then:
    "$INBOX" mark-done <name> /tmp/<stem>.json
    
    This item is then marked as transcribed, visible in the app, and won't be mined again by the server or next time. Transcription failed / user didn't check any topic / no article mined → mark neither done nor empty, leave it unprocessed, come back next time, report the reason.

Step 3 · Report

How many items were processed, how many drafts each mined (landing in ~/code/wechat-publish/), which were marked no-speech and why (corrupt/silent/no-speech), local archive paths, how many items remain unprocessed on R2.

Marking safety red lines

download(存档) → 判别 → 成文 ? mark-done : 无语音 ? mark-empty : 留着不标
                                                     ↑ 绝不 delete
  • Never delete. Audio always stays on R2; deletion is only a manual action by the user in the app.
  • Every item has a terminal state. Transcribed → mark-done; corrupt/silent/no-speech → mark-empty(with reason); true failure (transcription error, user abort, no article mined) → not marked, left unprocessed to retry next time.Never let "ran through a pass but left no marker at all" happen——that would cause this item to be reprocessed every time.

Reuse boundaries

Reuse Usage
wjs-transcribing-audio Each audio → SRT (Chinese Volcano Doubao, including polishing to fix typos)
wjs-mining-articles Each SRT → topic list → article → WeChat draft (including its own human gate)
~/code/.env FILES_TOKEN + Volcano ASR creds
VoiceDrop app Upstream: filename looks like VoiceDrop-<时间戳>-<时长>-<星期>-<时段>[-<城市-城区>].m4a(all ASCII). This skill claims items by the VoiceDrop- prefix + .m4a suffix; the duration/weekday/time period/location in the middle are context, which can be used when writing to judge when and where this recording was dictated.
Server miner (~/code/voicedrop/mining/mine.py, every 2h) Same set of marking conventions: written as articles/<stem>.json, no speech written as articles/<stem>.empty, never delete audio. It automatically processes the inbox, so when this skill runs, list is often already empty—this is expected, this skill is a manual fill-in

The only new code in this skill:scripts/voicedrop-inbox.sh。

Common Mistakes

  • delete audio — red line. The writing workflow never deletes, only mark-done/mark-empty; delete is only used manually by the user in the app.
  • Ran one but didn't mark it — no-speech/corrupted ones also need to be mark-empty, otherwise it gets re-downloaded and re-transcribed every time, forever "pending".
  • Transcription failed/user didn't check it but still force-marked done — if it truly failed, leave it unprocessed (don't mark it), try again next time; only mark mark-done。
  • skip wjs-mining-articles 's topic gate and force-write yourself — that gate is its design, follow it.
  • Treating non-VoiceDrop files in the bucket as sources too — only recognize the VoiceDrop-*.m4a prefix;list also only lists unprocessed ones.
  • Mistakenly uploaded/0-second/ambient sound treated as real recordings and retried repeatedly — first ffprobe to check the duration, if empty/corrupted/<1s directly mark-empty, don't send it for transcription.