Initial commit: Thread Ripper containerized application
This commit is contained in:
commit
a648a59c0f
16 files changed
+2213
No files matched your search
@@ -0,0 +1,76 @@
|
||||
import os
|
||||
import json
|
||||
from typing import List, Optional
|
||||
from pathlib import Path
|
||||
from .models import RippedThread, HistorySummary
|
||||
|
||||
class ThreadStorage:
|
||||
def __init__(self, data_dir: str = "data"):
|
||||
self.data_dir = Path(data_dir)
|
||||
self.threads_dir = self.data_dir / "threads"
|
||||
self.threads_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def save(self, thread: RippedThread) -> str:
|
||||
"""Saves a ripped thread to disk in JSON, MD, and HTML formats."""
|
||||
file_path = self.threads_dir / f"{thread.id}.json"
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(thread.model_dump_json(indent=2))
|
||||
|
||||
# Also save standalone .md and .html files for direct browsing
|
||||
md_path = self.threads_dir / f"{thread.id}.md"
|
||||
with open(md_path, "w", encoding="utf-8") as f:
|
||||
f.write(thread.stitched_markdown)
|
||||
|
||||
html_path = self.threads_dir / f"{thread.id}.html"
|
||||
with open(html_path, "w", encoding="utf-8") as f:
|
||||
f.write(thread.stitched_html)
|
||||
|
||||
txt_path = self.threads_dir / f"{thread.id}.txt"
|
||||
with open(txt_path, "w", encoding="utf-8") as f:
|
||||
f.write(thread.stitched_text)
|
||||
|
||||
return thread.id
|
||||
|
||||
def get(self, thread_id: str) -> Optional[RippedThread]:
|
||||
"""Loads a saved ripped thread by ID."""
|
||||
file_path = self.threads_dir / f"{thread_id}.json"
|
||||
if not file_path.exists():
|
||||
return None
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
return RippedThread(**data)
|
||||
|
||||
def list_history(self) -> List[HistorySummary]:
|
||||
"""Lists summaries of all previously ripped threads, newest first."""
|
||||
history = []
|
||||
for file in self.threads_dir.glob("*.json"):
|
||||
try:
|
||||
with open(file, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
history.append(
|
||||
HistorySummary(
|
||||
id=data["id"],
|
||||
title=data.get("title", "Untitled Thread"),
|
||||
author_handle=data.get("author_handle", "unknown"),
|
||||
author_name=data.get("author_name", "Unknown"),
|
||||
segment_count=data.get("segment_count", len(data.get("segments", []))),
|
||||
total_words=data.get("total_words", 0),
|
||||
saved_at=str(data.get("saved_at", ""))[:19],
|
||||
original_url=data.get("original_url", "")
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
continue
|
||||
# Sort by saved_at descending
|
||||
history.sort(key=lambda x: x.saved_at, reverse=True)
|
||||
return history
|
||||
|
||||
def delete(self, thread_id: str) -> bool:
|
||||
"""Deletes a saved thread and all associated format files."""
|
||||
deleted = False
|
||||
for ext in [".json", ".md", ".html", ".txt"]:
|
||||
f = self.threads_dir / f"{thread_id}{ext}"
|
||||
if f.exists():
|
||||
f.unlink()
|
||||
deleted = True
|
||||
return deleted
|
||||
Reference in new issue
Block a user