directory changes and restructuring
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Ulysses Export Helper
|
||||
|
||||
Exports markdown files to iCloud folder for Ulysses sync.
|
||||
Designed for OurDigital research-to-publisher workflow.
|
||||
|
||||
Usage:
|
||||
python export_to_ulysses.py --content "# Title\n\nContent..." --filename "my-post.md"
|
||||
python export_to_ulysses.py --file /path/to/draft.md --channel blog
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
# Default iCloud paths (user should customize)
|
||||
ICLOUD_BASE = Path.home() / "Library/Mobile Documents/com~apple~CloudDocs"
|
||||
|
||||
# Channel-specific folders (customize based on Ulysses library structure)
|
||||
CHANNEL_FOLDERS = {
|
||||
"blog": "Ulysses/OurDigital/Blog Drafts",
|
||||
"journal": "Ulysses/OurDigital/Journal Drafts",
|
||||
"ourstory": "Ulysses/OurDigital/OurStory Drafts",
|
||||
"medium": "Ulysses/OurDigital/Medium Drafts",
|
||||
"default": "Ulysses/Blog Drafts"
|
||||
}
|
||||
|
||||
def get_export_path(channel: str = "default") -> Path:
|
||||
"""Get the export path for a specific channel."""
|
||||
folder = CHANNEL_FOLDERS.get(channel, CHANNEL_FOLDERS["default"])
|
||||
export_path = ICLOUD_BASE / folder
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
export_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
return export_path
|
||||
|
||||
def generate_filename(title: str = None) -> str:
|
||||
"""Generate a filename with timestamp."""
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M")
|
||||
if title:
|
||||
# Clean title for filename
|
||||
clean_title = "".join(c if c.isalnum() or c in "- " else "" for c in title)
|
||||
clean_title = clean_title.replace(" ", "-").lower()[:50]
|
||||
return f"{timestamp}_{clean_title}.md"
|
||||
return f"{timestamp}_draft.md"
|
||||
|
||||
def export_content(content: str, filename: str, channel: str = "default") -> Path:
|
||||
"""Export content to the appropriate iCloud folder."""
|
||||
export_path = get_export_path(channel)
|
||||
file_path = export_path / filename
|
||||
|
||||
# Add Ulysses-friendly frontmatter if not present
|
||||
if not content.startswith("---"):
|
||||
frontmatter = f"""---
|
||||
created: {datetime.now().isoformat()}
|
||||
channel: {channel}
|
||||
status: draft
|
||||
---
|
||||
|
||||
"""
|
||||
content = frontmatter + content
|
||||
|
||||
file_path.write_text(content, encoding="utf-8")
|
||||
return file_path
|
||||
|
||||
def export_file(source_path: str, channel: str = "default") -> Path:
|
||||
"""Copy an existing file to the iCloud folder."""
|
||||
source = Path(source_path)
|
||||
if not source.exists():
|
||||
raise FileNotFoundError(f"Source file not found: {source_path}")
|
||||
|
||||
content = source.read_text(encoding="utf-8")
|
||||
filename = source.name
|
||||
|
||||
return export_content(content, filename, channel)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Export markdown to Ulysses via iCloud")
|
||||
parser.add_argument("--content", help="Markdown content to export")
|
||||
parser.add_argument("--file", help="Path to existing markdown file")
|
||||
parser.add_argument("--filename", help="Output filename (auto-generated if not provided)")
|
||||
parser.add_argument("--channel", choices=list(CHANNEL_FOLDERS.keys()),
|
||||
default="default", help="Target channel/folder")
|
||||
parser.add_argument("--list-paths", action="store_true",
|
||||
help="List configured export paths")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.list_paths:
|
||||
print("Configured export paths:")
|
||||
for channel, folder in CHANNEL_FOLDERS.items():
|
||||
full_path = ICLOUD_BASE / folder
|
||||
exists = "✓" if full_path.exists() else "✗"
|
||||
print(f" [{exists}] {channel}: {full_path}")
|
||||
return
|
||||
|
||||
if args.file:
|
||||
result = export_file(args.file, args.channel)
|
||||
print(f"✓ Exported to: {result}")
|
||||
elif args.content:
|
||||
filename = args.filename or generate_filename()
|
||||
result = export_content(args.content, filename, args.channel)
|
||||
print(f"✓ Exported to: {result}")
|
||||
else:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user