website-htmx-rustelo-code/scripts/sh/content/generate-content.sh
2026-07-10 03:44:13 +01:00

579 lines
14 KiB
Bash
Executable file
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# Content generation script for localized blog and recipe content
# Generates new content files from templates with proper localization
set -e
# Configuration
CONTENT_DIR="content"
TEMPLATES_DIR="scripts/content/templates"
SUPPORTED_LANGUAGES=("en" "es")
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Helper functions
log_error() {
echo -e "${RED}❌ ERROR: $1${NC}"
}
log_warning() {
echo -e "${YELLOW}⚠️ WARNING: $1${NC}"
}
log_success() {
echo -e "${GREEN}$1${NC}"
}
log_info() {
echo -e "${BLUE} $1${NC}"
}
log_step() {
echo -e "${CYAN}🔧 $1${NC}"
}
# Display usage information
show_usage() {
echo -e "${BLUE}📝 Content Generation Tool${NC}"
echo "================================"
echo ""
echo "Usage: $0 [COMMAND] [OPTIONS]"
echo ""
echo "Commands:"
echo " blog-post Generate a new blog post"
echo " recipe Generate a new recipe"
echo " templates Initialize template files"
echo " index Update content indices"
echo " help Show this help message"
echo ""
echo "Blog Post Options:"
echo " --title Post title (required)"
echo " --author Author name (default: 'Jesús Pérez')"
echo " --category Post category"
echo " --tags Comma-separated tags"
echo " --featured Mark as featured (true/false)"
echo " --language Target language (en/es/all)"
echo ""
echo "Recipe Options:"
echo " --title Recipe title (required)"
echo " --category Recipe category (required)"
echo " --difficulty Difficulty level (Beginner/Intermediate/Advanced/Expert)"
echo " --prep-time Preparation time (e.g., '30 min', '2 hours')"
echo " --tags Comma-separated tags"
echo " --featured Mark as featured (true/false)"
echo " --language Target language (en/es/all)"
echo ""
echo "Examples:"
echo " $0 blog-post --title 'Advanced Rust Patterns' --category 'Programming' --tags 'rust,patterns'"
echo " $0 recipe --title 'Docker Security Setup' --category 'Security' --difficulty 'Advanced'"
echo " $0 templates"
echo ""
}
# Generate unique ID from title
generate_id() {
local title="$1"
echo "$title" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g'
}
# Generate current date
get_current_date() {
date +"%Y-%m-%d"
}
# Create templates directory and files
create_templates() {
log_step "Creating template files..."
mkdir -p "$TEMPLATES_DIR"
# Blog post template
cat > "$TEMPLATES_DIR/blog-post.md" << 'EOF'
---
id: "{{ID}}"
title: "{{TITLE}}"
subtitle: "{{SUBTITLE}}"
excerpt: "{{EXCERPT}}"
author: "{{AUTHOR}}"
date: "{{DATE}}"
read_time: "{{READ_TIME}}"
tags: [{{TAGS}}]
featured: {{FEATURED}}
published: true
language: "{{LANGUAGE}}"
translations: [{{TRANSLATIONS}}]
---
# {{TITLE}}
{{SUBTITLE}}
## Introduction
{{EXCERPT}}
## Content
Write your blog post content here...
## Conclusion
Summarize your key points here...
---
*Published on {{DATE}} by {{AUTHOR}}*
EOF
# Recipe template
cat > "$TEMPLATES_DIR/recipe.md" << 'EOF'
---
id: "{{ID}}"
title: "{{TITLE}}"
subtitle: "{{SUBTITLE}}"
excerpt: "{{EXCERPT}}"
category: "{{CATEGORY}}"
difficulty: "{{DIFFICULTY}}"
prep_time: "{{PREP_TIME}}"
tags: [{{TAGS}}]
featured: {{FEATURED}}
language: "{{LANGUAGE}}"
translations: [{{TRANSLATIONS}}]
---
# {{TITLE}}
{{SUBTITLE}}
## Overview
{{EXCERPT}}
## Prerequisites
- List prerequisites here
- Required tools and knowledge
- System requirements
## Difficulty Level: {{DIFFICULTY}}
**Estimated Time:** {{PREP_TIME}}
## Steps
### Step 1: Initial Setup
Describe the first step...
### Step 2: Configuration
Describe configuration steps...
### Step 3: Implementation
Describe implementation details...
## Testing
How to test the implementation...
## Troubleshooting
Common issues and solutions...
## Additional Resources
- Link to relevant documentation
- Related recipes or articles
- Community resources
---
*Category: {{CATEGORY}} | Difficulty: {{DIFFICULTY}} | Time: {{PREP_TIME}}*
EOF
# JSON templates
cat > "$TEMPLATES_DIR/blog-post.json" << 'EOF'
{
"id": "{{ID}}",
"title": "{{TITLE}}",
"subtitle": "{{SUBTITLE}}",
"excerpt": "{{EXCERPT}}",
"author": "{{AUTHOR}}",
"date": "{{DATE}}",
"read_time": "{{READ_TIME}}",
"tags": [{{TAGS_JSON}}],
"featured": {{FEATURED}},
"published": true,
"language": "{{LANGUAGE}}",
"source_file": "content/blog/{{LANGUAGE}}/{{ID}}.md",
"html_file": "/blog/{{LANGUAGE}}/{{ID}}.html",
"translations": [{{TRANSLATIONS_JSON}}]
}
EOF
cat > "$TEMPLATES_DIR/recipe.json" << 'EOF'
{
"id": "{{ID}}",
"title": "{{TITLE}}",
"subtitle": "{{SUBTITLE}}",
"excerpt": "{{EXCERPT}}",
"category": "{{CATEGORY}}",
"difficulty": "{{DIFFICULTY}}",
"prep_time": "{{PREP_TIME}}",
"tags": [{{TAGS_JSON}}],
"featured": {{FEATURED}},
"language": "{{LANGUAGE}}",
"source_file": "content/recipes/{{LANGUAGE}}/{{ID}}.md",
"html_file": "/recipes/{{LANGUAGE}}/{{ID}}.html",
"translations": [{{TRANSLATIONS_JSON}}]
}
EOF
log_success "Template files created in $TEMPLATES_DIR"
}
# Parse command line arguments
parse_args() {
TITLE=""
AUTHOR="Jesús Pérez"
CATEGORY=""
DIFFICULTY=""
PREP_TIME=""
TAGS=""
FEATURED="false"
LANGUAGE="all"
SUBTITLE=""
EXCERPT=""
while [[ $# -gt 0 ]]; do
case $1 in
--title)
TITLE="$2"
shift 2
;;
--author)
AUTHOR="$2"
shift 2
;;
--category)
CATEGORY="$2"
shift 2
;;
--difficulty)
DIFFICULTY="$2"
shift 2
;;
--prep-time)
PREP_TIME="$2"
shift 2
;;
--tags)
TAGS="$2"
shift 2
;;
--featured)
FEATURED="$2"
shift 2
;;
--language)
LANGUAGE="$2"
shift 2
;;
--subtitle)
SUBTITLE="$2"
shift 2
;;
--excerpt)
EXCERPT="$2"
shift 2
;;
*)
log_error "Unknown option: $1"
show_usage
exit 1
;;
esac
done
}
# Convert tags to JSON array format
tags_to_json() {
local tags="$1"
if [[ -z "$tags" ]]; then
echo ""
return
fi
echo "$tags" | sed 's/,/", "/g' | sed 's/^/"/' | sed 's/$/"/'
}
# Get translation languages
get_translations() {
local current_lang="$1"
local translations=""
for lang in "${SUPPORTED_LANGUAGES[@]}"; do
if [[ "$lang" != "$current_lang" ]]; then
if [[ -z "$translations" ]]; then
translations="\"$lang\""
else
translations="$translations, \"$lang\""
fi
fi
done
echo "$translations"
}
# Replace template variables
replace_template_vars() {
local template_file="$1"
local output_file="$2"
local lang="$3"
local id=$(generate_id "$TITLE")
local date=$(get_current_date)
local tags_json=$(tags_to_json "$TAGS")
local translations_json=$(get_translations "$lang")
# Set defaults if not provided
if [[ -z "$SUBTITLE" ]]; then
SUBTITLE="A comprehensive guide"
fi
if [[ -z "$EXCERPT" ]]; then
EXCERPT="This content provides detailed information and practical examples."
fi
# Calculate read time (rough estimate: 200 words per minute)
local read_time="5 min read"
cp "$template_file" "$output_file"
# Replace all template variables
sed -i "s|{{ID}}|$id|g" "$output_file"
sed -i "s|{{TITLE}}|$TITLE|g" "$output_file"
sed -i "s|{{SUBTITLE}}|$SUBTITLE|g" "$output_file"
sed -i "s|{{EXCERPT}}|$EXCERPT|g" "$output_file"
sed -i "s|{{AUTHOR}}|$AUTHOR|g" "$output_file"
sed -i "s|{{DATE}}|$date|g" "$output_file"
sed -i "s|{{READ_TIME}}|$read_time|g" "$output_file"
sed -i "s|{{CATEGORY}}|$CATEGORY|g" "$output_file"
sed -i "s|{{DIFFICULTY}}|$DIFFICULTY|g" "$output_file"
sed -i "s|{{PREP_TIME}}|$PREP_TIME|g" "$output_file"
sed -i "s|{{TAGS}}|$TAGS|g" "$output_file"
sed -i "s|{{TAGS_JSON}}|$tags_json|g" "$output_file"
sed -i "s|{{FEATURED}}|$FEATURED|g" "$output_file"
sed -i "s|{{LANGUAGE}}|$lang|g" "$output_file"
sed -i "s|{{TRANSLATIONS}}|$(get_translations "$lang" | sed 's/"//g')|g" "$output_file"
sed -i "s|{{TRANSLATIONS_JSON}}|$translations_json|g" "$output_file"
}
# Generate blog post
generate_blog_post() {
if [[ -z "$TITLE" ]]; then
log_error "Title is required for blog posts"
show_usage
exit 1
fi
local id=$(generate_id "$TITLE")
local languages=("${SUPPORTED_LANGUAGES[@]}")
if [[ "$LANGUAGE" != "all" ]]; then
languages=("$LANGUAGE")
fi
log_step "Generating blog post: $TITLE"
for lang in "${languages[@]}"; do
local blog_dir="${CONTENT_DIR}/blog/${lang}"
mkdir -p "$blog_dir"
local md_file="${blog_dir}/${id}.md"
local json_file="/tmp/${id}.json"
# Generate markdown file
replace_template_vars "$TEMPLATES_DIR/blog-post.md" "$md_file" "$lang"
log_success "Created markdown file: $md_file"
# Generate JSON entry
replace_template_vars "$TEMPLATES_DIR/blog-post.json" "$json_file" "$lang"
# Update index file
local index_file="${blog_dir}/index.json"
if [[ -f "$index_file" ]]; then
# Add to existing index
local temp_file="/tmp/blog_index_temp.json"
jq --argjson new_post "$(cat "$json_file")" '.blog += [$new_post]' "$index_file" > "$temp_file"
mv "$temp_file" "$index_file"
else
# Create new index
cat > "$index_file" << EOF
{
"blog": [$(cat "$json_file")],
"language": "$lang"
}
EOF
fi
log_success "Updated blog index for language: $lang"
rm "$json_file"
done
log_success "Blog post '$TITLE' generated successfully!"
}
# Generate recipe
generate_recipe() {
if [[ -z "$TITLE" ]]; then
log_error "Title is required for recipes"
show_usage
exit 1
fi
if [[ -z "$CATEGORY" ]]; then
log_error "Category is required for recipes"
show_usage
exit 1
fi
# Set defaults
if [[ -z "$DIFFICULTY" ]]; then
DIFFICULTY="Intermediate"
fi
if [[ -z "$PREP_TIME" ]]; then
PREP_TIME="30 min"
fi
local id=$(generate_id "$TITLE")
local languages=("${SUPPORTED_LANGUAGES[@]}")
if [[ "$LANGUAGE" != "all" ]]; then
languages=("$LANGUAGE")
fi
log_step "Generating recipe: $TITLE"
for lang in "${languages[@]}"; do
local recipe_dir="${CONTENT_DIR}/recipes/${lang}"
mkdir -p "$recipe_dir"
local md_file="${recipe_dir}/${id}.md"
local json_file="/tmp/${id}.json"
# Generate markdown file
replace_template_vars "$TEMPLATES_DIR/recipe.md" "$md_file" "$lang"
log_success "Created markdown file: $md_file"
# Generate JSON entry
replace_template_vars "$TEMPLATES_DIR/recipe.json" "$json_file" "$lang"
# Update index file
local index_file="${recipe_dir}/index.json"
if [[ -f "$index_file" ]]; then
# Add to existing index
local temp_file="/tmp/recipe_index_temp.json"
jq --argjson new_recipe "$(cat "$json_file")" '.recipes += [$new_recipe]' "$index_file" > "$temp_file"
mv "$temp_file" "$index_file"
else
# Create new index
cat > "$index_file" << EOF
{
"recipes": [$(cat "$json_file")],
"language": "$lang"
}
EOF
fi
log_success "Updated recipe index for language: $lang"
rm "$json_file"
done
log_success "Recipe '$TITLE' generated successfully!"
}
# Update all indices
update_indices() {
log_step "Updating all content indices..."
for lang in "${SUPPORTED_LANGUAGES[@]}"; do
# Update blog index
local blog_dir="${CONTENT_DIR}/blog/${lang}"
local blog_index="${blog_dir}/index.json"
if [[ -d "$blog_dir" ]] && [[ ! -f "$blog_index" ]]; then
log_info "Creating missing blog index for language: $lang"
cat > "$blog_index" << EOF
{
"blog": [],
"language": "$lang"
}
EOF
fi
# Update recipe index
local recipe_dir="${CONTENT_DIR}/recipes/${lang}"
local recipe_index="${recipe_dir}/index.json"
if [[ -d "$recipe_dir" ]] && [[ ! -f "$recipe_index" ]]; then
log_info "Creating missing recipe index for language: $lang"
cat > "$recipe_index" << EOF
{
"recipes": [],
"language": "$lang"
}
EOF
fi
done
log_success "Indices updated successfully!"
}
# Main function
main() {
local command="$1"
shift
case "$command" in
"blog-post")
parse_args "$@"
generate_blog_post
;;
"recipe")
parse_args "$@"
generate_recipe
;;
"templates")
create_templates
;;
"index")
update_indices
;;
"help"|"")
show_usage
;;
*)
log_error "Unknown command: $command"
show_usage
exit 1
;;
esac
}
# Check if templates exist, create if not
if [[ ! -d "$TEMPLATES_DIR" ]] && [[ "$1" != "templates" ]] && [[ "$1" != "help" ]] && [[ "$1" != "" ]]; then
log_info "Templates not found, creating them first..."
create_templates
fi
# Run main function
main "$@"