Obsidian 노트를 자연스러운 음성으로 변환하고, 여러 기기에서 이어 들을 수 있는 TTS 솔루션. Docker 한 줄이면 로컬에서 바로 시작할 수 있습니다.
English | 한국어
- Obsidian 노트의 텍스트를 **음성(MP3)**으로 변환합니다
- 변환된 음성을 캐시해서 같은 노트를 다시 들을 때 즉시 재생합니다
- PC, 태블릿, 스마트폰 간 재생 위치를 실시간 동기화합니다
- 모든 처리가 로컬 Docker에서 이루어져 클라우드 비용이 없습니다
graph TB
subgraph "Obsidian (프론트엔드)"
A[tts-config] --> B[tts-engine]
C[tts-text] --> B
D[tts-cache] --> B
E[tts-ui] --> B
F[tts-position] --> G[sse-sync]
H[scroll-manager] --> G
I[ConfigResolver] --> A
I --> F
I --> H
end
subgraph "Docker tts-proxy :5051"
J[Flask Server]
K[Cache Manager]
L[SSE Manager]
M[VAD Processor]
N[Normalizer]
J --> K
J --> L
J --> M
J --> N
end
subgraph "TTS Backend (택 1)"
O["openai-edge-tts :5050
(클라우드, 무료)"]
P["CosyVoice3 :5052
(로컬 GPU)"]
Q["MLX TTS
(Apple Silicon)"]
end
B -->|"POST /api/tts"| J
G -->|"SSE /api/events/*"| L
J -->|"POST /v1/audio/speech"| O
J -->|"POST /v1/audio/speech"| P
J -->|"POST /v1/audio/speech"| Q
Obsidian 노트 → tts-text (텍스트 추출)
→ tts-cache (IndexedDB 캐시 확인)
→ tts-proxy (서버 캐시 확인 → TTS 백엔드 호출)
→ VAD (무음 제거) → 캐시 저장 → 오디오 재생
- Docker & Docker Compose (설치 가이드)
- Obsidian + Dataview 플러그인 활성화
git clone https://github.com/turtlesoup0/obsidian-tts.git
cd obsidian-ttstts-proxy는 OpenAI 호환 TTS 백엔드를 필요로 합니다. 가장 간단한 무료 백엔드는 openai-edge-tts (Microsoft Edge TTS 프록시)입니다.
# 1) openai-edge-tts 를 자체 docker-compose 로 먼저 실행
# → 이 과정에서 docker 네트워크 'openai-edge-tts_default' 와
# 컨테이너 'openai-edge-tts' 가 생성됩니다.
# (openai-edge-tts 저장소의 설치 안내를 따르세요)
# 2) 백엔드 프리셋 복사 후 tts-proxy 실행 (위 네트워크에 연결)
cd docker/tts-proxy
cp .env.edge-tts.example .env.edge-tts # 처음 한 번만
docker compose --env-file .env.edge-tts up -d백엔드 프리셋은 시크릿이 아니지만
.gitignore(.env.*)로 로컬 전용입니다. 저장소에는.env.edge-tts.example/.env.cosyvoice3.example가 포함되어 있으니 복사해서 사용하세요.
중요: 제공된
docker-compose.yml은 외부 네트워크openai-edge-tts_default를 참조하며, 백엔드를 호스트명openai-edge-tts:5050으로 찾습니다. 따라서 openai-edge-tts를 먼저 실행해야 합니다.백엔드를 호스트에서 직접 실행 중이거나 다른 포트를 쓴다면
TTS_BACKEND_URL을http://host.docker.internal:5050같은 값으로 지정하세요. 이때는docker-compose.yml의openai-edge-tts_default네트워크 참조를 제거해야 합니다.
# 헬스 체크
curl http://localhost:5051/health
# TTS 테스트 — 음성 파일 생성
curl -X POST http://localhost:5051/api/tts \
-H "Content-Type: application/json" \
-d '{"text":"안녕하세요. TTS 테스트입니다.","voice":"ko-KR-SunHiNeural"}' \
--output test.mp3
# 재생 (macOS)
afplay test.mp3Obsidian 쪽은 모듈형(dv.view) 방식으로 동작합니다. 핵심 3가지:
views/폴더를 vault의3_Resource/obsidian/views/에 복사 (경로가 코드에 하드코딩됨)3_Resource/obsidian/views/obsidian-tts-config.md생성 후 서버 주소(<서버-IP>:5051) 설정- 리더 노트에서 모듈을 의존성 순서로
dv.view(...)로 로드 → 재생
전체 단계·정확한 경로·실제 동작 예시는 아래 Obsidian Vault 설정에 있습니다.
필수: Dataview 플러그인 + "Enable JavaScript Queries" 활성화.
집이나 사무실의 한 컴퓨터에서만 사용합니다.
Obsidian ──→ localhost:5051 (tts-proxy) ──→ localhost:5050 (edge-tts)
# (openai-edge-tts 가 먼저 실행 중이어야 함 — Quick Start 2단계 참조)
cd docker/tts-proxy
docker compose --env-file .env.edge-tts up -d- 장점: 설정 최소, 비용 0원
- 단점: 같은 컴퓨터에서만 접근 가능
여러 기기(PC, iPad, iPhone)에서 TTS를 공유합니다.
iPhone ──┐
iPad ───┤── Tailscale VPN ──→ 100.x.x.x:5051 (tts-proxy)
Mac ───┘
# 1. 서버와 모든 기기에 Tailscale 설치
# https://tailscale.com/download
# 2. TTS 백엔드 + tts-proxy 실행 (서버에서, Quick Start 2단계 참조)
cd docker/tts-proxy
docker compose --env-file .env.edge-tts up -d
# 3. Obsidian 설정에서 Tailscale IP 사용
# 예: http://100.107.208.106:5051- 장점: 어디서든 접근, 보안(VPN), 비용 0원
- 단점: Tailscale 설정 필요
인터넷 어디서든 접근 가능하게 합니다.
⚠️ 보안 경고: 이 tts-proxy 는 인증이 없습니다. Cloudflare Tunnel 은 HTTPS 만 제공할 뿐 인증을 제공하지 않으므로, 호스트명을 아는 누구나 캐시를 삭제(DELETE /api/cache-clear)하거나 백엔드에 무제한 합성 요청을 보낼 수 있습니다. 공개 노출이 꼭 필요하면 Cloudflare Access(또는 mTLS·공유 시크릿 헤더) 같은 인증 계층을 반드시 앞단에 두십시오. 그렇지 않다면 원격 접근은 시나리오 B(Tailscale)를 권장합니다. 또한 공개 노출 시CORS_ORIGINS=*를 쓰지 마십시오(아래 CORS 항목 참고).
어디서든 ──→ https://tts.yourdomain.com ──→ Cloudflare Tunnel ──→ :5051
# 1. Cloudflare Tunnel 설정
cloudflared tunnel create obsidian-tts
cloudflared tunnel route dns obsidian-tts tts.yourdomain.com
# 2. config.yml 작성
# tunnel: <tunnel-id>
# ingress:
# - hostname: tts.yourdomain.com
# service: http://localhost:5051
# - service: http_status:404
# 3. 실행
cloudflared tunnel run obsidian-tts- 장점: HTTPS 자동, 어디서든 접근
- 단점: 도메인 필요, Cloudflare 계정, 인증 계층 직접 구성 필요(미구성 시 공개 금지)
tts-proxy는 OpenAI Audio Speech API 호환 (/v1/audio/speech) 백엔드라면 무엇이든 연결할 수 있습니다.
| 백엔드 | 타입 | 품질 | 속도 | 비용 | 적합한 경우 |
|---|---|---|---|---|---|
| openai-edge-tts | 클라우드 | 높음 | 빠름 | 무료 | 처음 시작하는 분 |
| CosyVoice3 | 로컬 GPU | 매우 높음 | 보통 | 무료 | GPU가 있는 분 |
| MLX TTS | 로컬 Apple Silicon | 높음 | 보통 | 무료 | Mac 사용자 |
# 프리셋은 .example 에서 복사 (처음 한 번)
cp .env.edge-tts.example .env.edge-tts
cp .env.cosyvoice3.example .env.cosyvoice3
# Edge TTS (기본)
docker compose --env-file .env.edge-tts up -d
# CosyVoice3 (로컬 GPU)
docker compose --env-file .env.cosyvoice3 up -d
# 또는 환경변수로 직접 지정 (예: MLX TTS 기본 포트 8000)
TTS_BACKEND_URL=http://host.docker.internal:8000 docker compose up -d.env 파일 하나만 바꾸면 백엔드가 전환됩니다. 클라이언트(Obsidian) 설정은 변경 불필요.
Obsidian의 Dataview 플러그인 위에서 동작하는 JavaScript 모듈들입니다.
views/
├── common/ # 공용 유틸리티
│ ├── constants.js # 전역 상수
│ ├── device-id.js # 기기 식별자 생성
│ ├── fetch-helpers.js # HTTP 요청 헬퍼 (타임아웃 등)
│ └── ui-helpers.js # UI 유틸리티
│
├── tts-config/view.js # 설정 로딩 (config 파일, Keychain, 기본값)
├── tts-core/view.js # 핵심 초기화 (전역 네임스페이스, 로깅)
├── tts-text/view.js # 텍스트 추출 (마크다운 제거, 볼드 강조)
├── tts-cache/view.js # 3단계 캐시 (IndexedDB → 서버 → 새 생성)
├── tts-engine/view.js # 재생 엔진 (play/pause/stop, iOS 백그라운드)
│ └── modules/
│ ├── audio-state-machine.js # 오디오 상태 머신 (중단/복구)
│ └── audio-cache-resolver.js # 캐시 해결 전략
├── tts-ui/view.js # UI 렌더링 (플레이어 컨트롤, 노트 목록)
│ └── modules/
│ ├── tts-styles.js # CSS 스타일
│ ├── tts-usage.js # 사용량 표시
│ └── tts-bulk.js # 전체 노트 일괄 생성
├── tts-position/view.js # 재생 위치 동기화
├── tts-debug/view.js # 디버그 패널
├── sse-sync/view.js # SSE 실시간 동기화 클라이언트
├── scroll-manager/view.js # 스크롤 위치 동기화
└── integrated-ui/view.js # 통합 노트 UI
tts-core → tts-config → tts-text → tts-cache → tts-engine → tts-ui
↑
tts-position
sse-sync
scroll-manager
docker/tts-proxy/
├── server.py # Flask 메인 서버 (18개 엔드포인트)
├── cache_manager.py # 파일 기반 캐시 + 통계
├── sse_manager.py # SSE 브로드캐스트 (인메모리/Redis)
├── vad_processor.py # Silero VAD 무음 트리밍
├── normalizer.py # 영문 축약어 발음 정규화
├── requirements.txt # Python 의존성
├── Dockerfile
├── docker-compose.yml
├── .env.edge-tts # Edge TTS 프리셋
└── .env.cosyvoice3 # CosyVoice3 프리셋
설정은 두 계층으로 나뉩니다. 코드를 그대로 반영하면:
1) 리더 TTS 설정 — views/tts-config/view.js
TTS 엔드포인트·음성·캐시 옵션은 obsidian-tts-config.md의 window.ObsidianTTSConfig에서 로드합니다(파일이 없으면 코드 내 기본값 사용). operationMode(local/server/hybrid)가 TTS_OPERATION_MODES(tts-config/view.js)에 정의되어 TTS 엔드포인트를 결정합니다:
| Mode | TTS | 캐시 | 위치 동기화 |
|---|---|---|---|
local |
로컬 | 로컬 | 로컬 |
server |
Azure | Azure | Azure (레거시) |
hybrid |
로컬 | 하이브리드 | 로컬 (Cloudflare/Tailscale 경유) |
2) 동기화 엔드포인트 해석 — shared/configResolver.js (SPEC-ARCH-001)
재생위치·스크롤·SSE 모듈(tts-position, scroll-manager, sse-sync)이 참조하도록 설계된 별도 모듈로, 4가지 소스를 우선순위로 병합합니다:
| 우선순위 | 소스 | 설명 |
|---|---|---|
| 1 (최우선) | Runtime Config | window.ttsEndpointConfig 직접 설정 |
| 2 | Config File | obsidian-tts-config.md 파일 |
| 3 | Keychain | Obsidian Keychain API (shared/configResolver.js의 _loadKeychainConfig) |
| 4 (폴백) | Defaults | 하드코딩된 기본값 |
두 계층은 별개입니다. 메인 리더(tts-config)는 1번 경로로 설정을 읽고, 동기화 엔드포인트는 ConfigResolver(2번)가 해석합니다. Keychain은 ConfigResolver 모듈에만 존재합니다(리더 설정 로더에는 없음).
| 메서드 | 경로 | 설명 |
|---|---|---|
GET |
/api/tts?text=...&voice=... |
TTS 생성 (쿼리 파라미터) |
POST |
/api/tts |
TTS 생성 (JSON body) |
POST |
/api/tts-stream |
TTS 생성 (Azure 호환) |
POST |
/v1/audio/speech |
TTS 생성 (OpenAI 호환) |
POST /api/tts 요청 예시:
{
"text": "안녕하세요. TTS 테스트입니다.",
"voice": "ko-KR-SunHiNeural",
"rate": 1.0,
"useCache": true
}지원 음성:
- 한국어 (9종):
ko-KR-SunHiNeural(여성, 기본),ko-KR-InJoonNeural,ko-KR-BongJinNeural,ko-KR-GookMinNeural,ko-KR-JiMinNeural,ko-KR-SeoHyeonNeural,ko-KR-SoonBokNeural,ko-KR-YuJinNeural,ko-KR-HyunsuNeural - 영어:
en-US-JennyNeural,en-US-GuyNeural,en-US-AriaNeural - 범용:
alloy,echo,fable,onyx,nova,shimmer
응답: audio/mpeg 바이너리 + 헤더 X-Cache: HIT|MISS
| 메서드 | 경로 | 설명 |
|---|---|---|
GET |
/api/cache/<key> |
캐시 조회 |
PUT |
/api/cache/<key> |
캐시 저장 |
DELETE |
/api/cache/<key> |
캐시 삭제 |
DELETE |
/api/cache-clear |
전체 캐시 삭제 |
| 메서드 | 경로 | 설명 |
|---|---|---|
GET |
/api/events/playback |
재생 위치 SSE 스트림 |
GET |
/api/events/scroll |
스크롤 위치 SSE 스트림 |
GET |
/api/playback-position |
재생 위치 조회 |
PUT |
/api/playback-position |
재생 위치 저장 + SSE 브로드캐스트 |
GET |
/api/scroll-position |
스크롤 위치 조회 |
PUT |
/api/scroll-position |
스크롤 위치 저장 + SSE 브로드캐스트 |
| 메서드 | 경로 | 설명 |
|---|---|---|
GET |
/health |
서버 상태 (SSE 클라이언트 수, VAD 상태 등) |
GET |
/api/stats |
요청 통계 |
GET |
/api/usage |
일별 사용량 |
GET |
/api/cache-stats |
캐시 히트율, 파일 수, 총 크기 |
중요 — 경로 규칙이 코드에 하드코딩되어 있습니다.
tts-config/view.js·tts-engine/view.js·tts-ui/view.js·integrated-ui/view.js는 설정 파일과 보조 모듈을3_Resource/obsidian/views/경로에서 로드합니다 (하드코딩).- 따라서 리더 노트의
dv.view(...)도 동일하게3_Resource/obsidian/views/<모듈>전체 경로로 호출해야 두 경로가 일치합니다 (dv.view인자는 vault 루트 기준 해석).아래 안내는 작성자의 실제 동작 구조(
3_Resource/obsidian/views/)를 단일 기준으로 합니다. 다른 위치를 쓰려면dv.view인자와 위 4개 파일의 하드코딩 경로를 둘 다 바꿔야 합니다(3단계 끝 주석 참고).
views/ 폴더 전체를 vault의 3_Resource/obsidian/views/ 아래로 복사합니다:
VAULT="$HOME/obsidian/my-vault"
mkdir -p "$VAULT/3_Resource/obsidian"
cp -r views "$VAULT/3_Resource/obsidian/views"<vault 루트>/3_Resource/obsidian/views/obsidian-tts-config.md 를 생성합니다
(이 경로는 tts-config/view.js:31 에 하드코딩되어 있습니다 — vault 루트가 아닙니다):
---
해시태그: "#tts-config"
---
```dataviewjs
window.ObsidianTTSConfig = {
operationMode: 'local', // local | server | hybrid
localEdgeTtsUrl: 'http://<서버-IP>:5051/api/tts',
edgeServerUrl: 'http://<서버-IP>:5051',
ttsEndpoint: '/api/tts-stream',
cacheEndpoint: '/api/cache',
playbackPositionEndpoint: '/api/playback-position',
scrollPositionEndpoint: '/api/scroll-position',
defaultVoice: 'ko-KR-SunHiNeural',
defaultRate: 1.0,
enableOfflineCache: true,
cacheTtlDays: 30,
debugMode: false
};
```보안 검증:
tts-config/view.js는 이 블록을 화이트리스트로 검사 후 실행합니다 (eval/fetch/import/setTimeout등 금지,window.ObsidianTTSConfig = {...}할당만 허용).
<서버-IP>: 로컬=localhost· Tailscale=100.x.x.x· Cloudflare=https://tts.yourdomain.com
새 노트에 아래 dataviewjs 블록을 넣습니다. 모듈을 의존성 순서로 로드한 뒤 페이지를 조회해 엔진·UI에 전달합니다 (작성자의 실제 운영 노트와 동일한 구조):
```dataviewjs
// 모듈 로딩 (의존성 순서). dv.view 경로는 vault 루트 기준이므로,
// 1단계에서 복사한 위치(3_Resource/obsidian/views/)를 그대로 전체 경로로 적는다.
await dv.view("3_Resource/obsidian/views/tts-core"); // 공유 유틸리티
await dv.view("3_Resource/obsidian/views/tts-config"); // 설정 로딩
await dv.view("3_Resource/obsidian/views/tts-text"); // 텍스트 정제
await dv.view("3_Resource/obsidian/views/tts-cache"); // 3단계 캐시
await dv.view("3_Resource/obsidian/views/tts-position"); // 재생 위치 동기화
// 읽을 노트 선택 (폴더·태그를 본인 환경에 맞게 수정)
const pages = dv.pages('"내폴더/경로" and -#검색제외 and #읽기대상')
.sort(b => [b.file.folder, b.file.name], 'asc')
.array();
// 엔진 + UI (pages 전달)
await dv.view("3_Resource/obsidian/views/tts-engine", { pages });
await dv.view("3_Resource/obsidian/views/tts-ui", { pages, dv });
```경로를 하나로 통일하세요.
dv.view(...)인자는 Dataview가 vault 루트 기준으로 해석하고, 모듈 코드 내부(tts-config·tts-engine·tts-ui·integrated-ui)는 보조 모듈·설정 파일을3_Resource/obsidian/views/에서 로드합니다(하드코딩). 따라서 모듈을3_Resource/obsidian/views/에 복사하고dv.view에도 위처럼 전체 경로를 적으면 두 경로가 일치해 그대로 동작합니다. 다른 위치(예: vault 루트views/)에 두려면dv.view인자와 위 4개 파일의 하드코딩 경로를 둘 다 같은 위치로 바꿔야 합니다 — 한쪽만 바꾸면 모듈 로드 404 또는 설정/보조 모듈 로드 실패가 조용히 발생합니다.
리더가 노트를 음성으로 변환할 때 사용하는 필드:
---
정의: 이 노트의 핵심 정의 (TTS 본문에 포함)
키워드: ["키워드1", "키워드2"]
---정의·키워드는 TTS 본문 생성 및 캐시 키 계산에 사용됩니다.- 어떤 노트가 목록에 들어갈지는 위
dv.pages(...)쿼리(폴더·태그 필터)가 결정합니다.
- Dataview: 모듈 실행 엔진
- Community Plugins → "Dataview" 설치
- Dataview 설정에서 "Enable JavaScript Queries" 활성화 필수
templates/tts-reader.md 와 templates/sample-tts-note.md 는 옛 Azure 기반(v5) 예시입니다
(azureFunctionUrl·Azure Blob 캐시 가정). 현재 로컬 Docker 구조에서는 위 모듈형 방식을 사용하세요.
레거시 파일은 참고용으로만 남겨두었습니다.
1. IndexedDB (오프라인) → 가장 빠름, 기기별 저장
2. 서버 캐시 (tts-proxy) → 기기 간 공유
3. TTS 백엔드 생성 → 캐시 없을 때만 호출
폴링(5초 간격) 대신 Server-Sent Events로 100ms 이내 동기화:
기기 A: 42번 노트 재생 중 → PUT /api/playback-position
→ SSE 브로드캐스트
기기 B: SSE 수신 → 자동으로 42번 노트 위치로 이동
Silero VAD 모델이 TTS 출력의 앞뒤 불필요한 무음/숨소리를 자동 제거합니다.
edge-tts가 JWT, HTTP, API 같은 약어를 뭉개는 문제를 해결:
JWT → J W T (글자별 분리)
JWTs → J W T s (복수형도 처리)
API → A P I (forcelist)
JSON → JSON (단어로 발음, whitelist)
TTS_NORMALIZE_ENABLED=true 일 때만 동작하며(기본 off), 사전 없이도 휴리스틱 단독 모드로
동작합니다. 선택적으로 TTS_NORMALIZE_DICT_PATH(기본 data/acronym-dict.json)에 vault 약어
사전을 두면 우선 적용됩니다. 사전을 생성·갱신하는 배치 파이프라인은 이 저장소의 범위 밖이며,
없을 경우 정규화는 내장 휴리스틱(화이트리스트/forcelist/모음 비율)으로 graceful degradation 합니다.
iPhone/iPad에서 다른 앱으로 전환해도 재생이 계속됩니다:
visibilitychange이벤트 기반 백그라운드 감지- 오디오 세션 유지를 위한 3중 가드 패턴
AudioPlaybackWatchdog로 상태 불일치 자동 복구
| 변수 | 기본값 | 설명 |
|---|---|---|
TTS_BACKEND_URL |
http://localhost:5050 |
TTS 백엔드 주소 |
TTS_MODEL |
(빈 값) | 모델명 오버라이드 (MLX 등) |
TTS_TIMEOUT |
120 |
백엔드 요청 타임아웃 (초) |
TTS_MAX_RETRIES |
3 |
재시도 횟수 |
TTS_DISABLE_INTERNAL_CACHE |
false |
백엔드 전환 시 캐시 우회 |
TTS_NORMALIZE_ENABLED |
false |
축약어 정규화 활성화 |
TTS_NORMALIZE_DICT_PATH |
/app/data/acronym-dict.json |
축약어 사전 경로 |
CORS_ORIGINS |
app://obsidian.md,capacitor://localhost,http://localhost:*,http://127.0.0.1:* |
CORS 허용 출처. host:* 는 server.py가 앵커드 정규식으로 변환(포트만 와일드카드). *=전체(사설망 전용) |
REDIS_ENABLED |
false |
Redis SSE 모드 |
REDIS_HOST |
localhost |
Redis 호스트 |
REDIS_PORT |
6379 |
Redis 포트 |
TTS_PROXY_PORT |
5051 |
서버 포트 |
TTS_DATA_DIR |
./data/tts-cache |
데이터 저장 경로 |
위 기본값은
server.py가 환경변수 부재 시 사용하는 코드 기본값입니다. 실제 실행 시 일부는 덮어써집니다:
docker-compose.yml자체가TTS_BACKEND_URL→openai-edge-tts:5050기본값을 주고,CORS_ORIGINS를 Obsidian/로컬 allowlist(server.py 코드 기본값과 동일)로 고정합니다..env.edge-tts프리셋은 추가로TTS_TIMEOUT·TTS_MODEL·TTS_MAX_RETRIES를 공급합니다.정규화는 기본 opt-in(off)이며 켜려면
TTS_NORMALIZE_ENABLED=true, 사설망에서 모든 origin 을 허용하려면CORS_ORIGINS=*를.env로 직접 설정하세요(공개 노출 시*금지 — 시나리오 C 경고 참고).
# 1. tts-proxy 헬스 체크
curl http://localhost:5051/health
# 2. TTS 백엔드 직접 테스트
curl -X POST http://localhost:5050/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","input":"테스트","voice":"alloy"}' \
--output test.mp3
# 3. Docker 로그 확인
docker logs obsidian-tts-proxy- 브라우저 콘솔 (F12 → Console)에서 빨간 에러 확인
obsidian-tts-config.md의 서버 주소가 정확한지 확인- CORS 설정 확인 (Obsidian →
app://obsidian.md) - 방화벽에서 5051 포트가 열려있는지 확인
# 서버 캐시 전체 삭제
curl -X DELETE http://localhost:5051/api/cache-clear
# IndexedDB 캐시 (Obsidian 콘솔에서)
indexedDB.deleteDatabase('obsidian-tts-offline');obsidian-tts/
├── docker/tts-proxy/ # 메인 백엔드 서버 (Python/Flask)
├── views/ # Obsidian 프론트엔드 모듈 (JavaScript)
├── shared/ # 레거시 공유 모듈 (configResolver 등)
├── src/functions/ # 레거시 Azure Functions (비활성)
├── templates/ # Obsidian 템플릿 파일
├── scripts/
│ ├── sync-to-vault.sh # Projects → vault 단방향 동기화
│ └── setup-obsidian.sh # Obsidian 자동 설정 (레거시)
├── docs/ # 추가 문서
├── README.md # 이 파일
├── README_EN.md # English version
├── CHANGELOG.md # 변경 이력
└── CONTRIBUTING.md # 기여 가이드
이슈 리포트나 Pull Request를 환영합니다!
- Fork
- Feature branch 생성 (
git checkout -b feat/my-feature) - 커밋 (
git commit -m 'feat: Add my feature') - Push (
git push origin feat/my-feature) - Pull Request 생성
MIT License. LICENSE 참조.
저장소: github.com/turtlesoup0/obsidian-tts 최종 업데이트: 2026-05-29