func (c *LineImageCache) GetLinePath(id string) string {
	imgPath := filepath.Join(c.path, id+".png")
	if _, err := os.Stat(imgPath); os.IsNotExist(err) {
		return ""
	}
	absPath, _ := filepath.Abs(imgPath)
	return absPath
}
GetLinePath returns the file path for a given line image
package lib
import (
	"encoding/json"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"time"
	"github.com/rs/zerolog/log"
)
type IdentifierCacheEntry struct {
	Identifier string `json:"id"`
	NumPages   int    `json:"numPages"`
}
type IdentifierCache struct {
	path    string
	entries map[int][]IdentifierCacheEntry
}
func NewIdentifierCache(path string) *IdentifierCache {
	return &IdentifierCache{
		path:    path,
		entries: map[int][]IdentifierCacheEntry{}}
}
func LoadIdentifierCache(path string) *IdentifierCache {
	cacheJSON, _ := ioutil.ReadFile(path)
	cache := IdentifierCache{path: path}
	json.Unmarshal(cacheJSON, &cache.entries)
	return &cache
}
func (c *IdentifierCache) Write() {
	cacheJSON, _ := json.Marshal(c.entries)
	ioutil.WriteFile(c.path, cacheJSON, 0644)
}
func (c *IdentifierCache) Add(ident string, numPages int, year int) {
	c.entries[year] = append(c.entries[year], IdentifierCacheEntry{
		Identifier: ident,
		NumPages:   numPages})
}
func (c *IdentifierCache) Random(year int) IdentifierCacheEntry {
	pickIdx := rand.Intn(len(c.entries[year]))
	entry := c.entries[year][pickIdx]
	c.entries[year] = append(c.entries[year][:pickIdx], c.entries[year][pickIdx+1:]...)
	c.Write()
	return entry
}
type LineImageCache struct {
	path string
}
func NewLineImageCache(cacheDir string) *LineImageCache {
	path := filepath.Join(cacheDir, "line_images")
	if _, err := os.Stat(path); os.IsNotExist(err) {
		os.MkdirAll(path, 0755)
	}
	cache := LineImageCache{
		path: filepath.Join(cacheDir, "line_images"),
	}
	go cache.purgeCacheWorker()
	return &cache
}
func (c *LineImageCache) purgeCacheWorker() {
	for {
		currentTime := time.Now()
		files, _ := ioutil.ReadDir(c.path)
		for _, finfo := range files {
			if currentTime.Sub(finfo.ModTime()).Hours() >= 7.0*24 {
				os.Remove(filepath.Join(c.path, finfo.Name()))
			}
		}
		time.Sleep(24 * time.Hour)
	}
}
func (c *LineImageCache) CacheLine(url string, id string) (string, error) {
	imgPath := filepath.Join(c.path, id+".png")
	imgOut, err := os.Create(imgPath)
	if err != nil {
		return "", err
	}
	defer imgOut.Close()
	imgResp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer imgResp.Body.Close()
	if _, err := io.Copy(imgOut, imgResp.Body); err != nil {
		return "", err
	}
	return imgPath, nil
}
func (c *LineImageCache) CacheLines(lines []OCRLine, ident string) {
	log.Info().
		Str("identifier", ident).
		Int("numLines", len(lines)).
		Msg("Caching lines")
	for _, line := range lines {
		c.CacheLine(line.ImageURL, MakeLineIdentifier(ident, line))
	}
	log.Info().
		Str("identifier", ident).
		Int("numLines", len(lines)).
		Msg("Cached lines")
}