package main import ( "encoding/json" "fmt" "io" "net/http" "os" "os/signal" "regexp" "strings" "time" epub "github.com/go-shiori/go-epub" "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/html" "github.com/gomarkdown/markdown/parser" ) type Parameters struct { KitchenOwlURL string KitchenOwlKey string KitchenOwlHousehold string RecipeAuthors string RecipeChapters string RecipeOutput string } type Household struct { Name string `json:"name"` Photo string `json:"photo"` } type Item struct { Name string `json:"name"` Description string `json:"description"` Icon string `json:"icon"` Slug string `json:"-"` } type Tag struct { Name string `json:"name"` } type Recipe struct { Name string `json:"name"` ID int `json:"id"` Tags []Tag `json:"tags"` Description string `json:"description"` Items []Item `json:"items"` } func Slugify(s string) string { // Convert to lowercase result := strings.ToLower(s) // Replace spaces with hyphens result = strings.ReplaceAll(result, " ", "_") // Return the slug return result } func MarkdownToHTML(md string) string { // Create markdown parser with extensions extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock p := parser.NewWithExtensions(extensions) doc := p.Parse([]byte(md)) // Create HTML renderer with extensions htmlFlags := html.UseXHTML | html.HrefTargetBlank opts := html.RendererOptions{Flags: htmlFlags} renderer := html.NewRenderer(opts) return string(markdown.Render(doc, renderer)) } func FetchHousehold(param Parameters) (*Household, error) { client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest(http.MethodGet, param.KitchenOwlURL+"/api/household/"+param.KitchenOwlHousehold, nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+param.KitchenOwlKey) req.Header.Set("Accept", "application/json") res, err := client.Do(req) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode != 200 { return nil, fmt.Errorf("status code %d", res.StatusCode) } body, err := io.ReadAll(res.Body) if err != nil { return nil, err } var household Household if err := json.Unmarshal(body, &household); err != nil { return nil, err } return &household, nil } func FetchImage(param Parameters, image string) (*string, error) { client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest(http.MethodGet, param.KitchenOwlURL+"/api/upload/"+image, nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+param.KitchenOwlKey) req.Header.Set("Accept", "application/json") res, err := client.Do(req) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode != 200 { return nil, fmt.Errorf("status code %d", res.StatusCode) } // Open a file for writing filename := "/tmp/" + image file, err := os.Create(filename) if err != nil { return nil, err } defer file.Close() // Dump the response body to the file _, err = io.Copy(file, res.Body) if err != nil { return nil, err } return &filename, nil } func FetchRecipes(param Parameters) ([]Recipe, error) { client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest(http.MethodGet, param.KitchenOwlURL+"/api/household/"+param.KitchenOwlHousehold+"/recipe", nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+param.KitchenOwlKey) req.Header.Set("Accept", "application/json") res, err := client.Do(req) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode != 200 { return nil, fmt.Errorf("status code %d", res.StatusCode) } body, err := io.ReadAll(res.Body) if err != nil { return nil, err } var recipes []Recipe if err := json.Unmarshal(body, &recipes); err != nil { return nil, err } for recipe := range recipes { for item := range recipes[recipe].Items { recipes[recipe].Items[item].Slug = Slugify(recipes[recipe].Items[item].Name) } } return recipes, nil } // Create a new EBook with chapters and recipes func CreateEbook(param Parameters, household Household, recipes []Recipe) (*epub.Epub, error) { book, err := epub.NewEpub("KitchenOwl Recipes") if err != nil { fmt.Printf("Error creating epub: %v\n", err) } // Set author if param.RecipeAuthors != "" { book.SetAuthor(param.RecipeAuthors) } // Set description book.SetDescription("Recipes from KitchenOwl server " + param.KitchenOwlURL) // Add cover image filename, err := FetchImage(param, household.Photo) if err != nil { fmt.Printf("Error fetching cover image: %v\n", err) } else { coverImagePath, err := book.AddImage(*filename, "cover.png") if err != nil { fmt.Printf("Error setting cover: %v\n", err) } else { err = book.SetCover(coverImagePath, "") if err != nil { fmt.Printf("Error setting cover: %v\n", err) } } } // Add an empty section at the beginning content := "