func DownloadCatalog(ctx context.Context, prgrs chan Progress, dst string) error {
	if prgrs != nil {
		defer close(prgrs)
	}
	tmp, err := ioutil.TempDir("", "go-jwlm")
	if err != nil {
		return errors.Wrap(err, "Error while creating temporary directory")
	}
	defer os.RemoveAll(tmp)
	mfst, err := fetchManifest(ctx)
	if err != nil {
		return errors.Wrap(err, "Could not fetch catalog manifest")
	}
	url := fmt.Sprintf(CatalogURL, mfst.Current)
	client := grab.NewClient()
	req, err := grab.NewRequest(tmp, url)
	if err != nil {
		return errors.Wrapf(err, "Error while creating request for %s", url)
	}
	req = req.WithContext(ctx)
	resp := client.Do(req)
	progress := Progress{}
	t := time.NewTicker(250 * time.Millisecond)
	defer t.Stop()
Loop:
	for {
		progress := Progress{
			Size:           resp.Size(),
			BytesComplete:  resp.BytesComplete(),
			BytesPerSecond: resp.BytesPerSecond(),
			Progress:       resp.Progress(),
			Duration:       resp.Duration(),
			ETA:            resp.ETA(),
		}
		select {
		case <-t.C:
			select {
			case prgrs <- progress:
				continue
			default:
				continue
			}
		case <-resp.Done:
			break Loop
		}
	}
	if err := resp.Err(); err != nil {
		progress.Done = true
		select {
		case prgrs <- progress:
		default:
		}
		return errors.Wrapf(err, "Error while downloading catalog from %s", url)
	}
	data, err := ioutil.ReadFile(resp.Filename)
	if err != nil {
		return errors.Wrap(err, "Error while reading catalog.db.gz")
	}
	buffer := bytes.NewBuffer(data)
	err = extract.Gz(ctx, buffer, dst, nil)
	if err != nil {
		return errors.Wrap(err, "Error while extracting catalog.db")
	}
	progress.Done = true
	select {
	case prgrs <- progress:
	default:
	}
	return nil
}
DownloadCatalog downloads the newest catalog.db and saves it at dst.
The prgrs channel informs about the progress of the download.
package publication
import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"
	"github.com/cavaliercoder/grab"
	"github.com/codeclysm/extract/v3"
	"github.com/pkg/errors"
)
var ManifestURL = "https://app.jw-cdn.org/catalogs/publications/v4/manifest.json"
var CatalogURL = "https://app.jw-cdn.org/catalogs/publications/v4/%s/catalog.db.gz"
type Progress struct {
	Size           int64
	BytesComplete  int64
	BytesPerSecond float64
	Progress       float64
	Duration       time.Duration
	ETA            time.Time
	Done           bool
}
type catalogManifest struct {
	Version int    `json:"version"`
	Current string `json:"current"`
}
func CatalogNeedsUpdate(path string) bool {
	stat, err := os.Stat(path)
	if err == nil {
		old := time.Now().Add(-time.Hour * 24 * 30)
		if !stat.ModTime().Before(old) {
			return false
		}
	}
	return true
}
func CatalogExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}
func CatalogSize(path string) int64 {
	info, err := os.Stat(path)
	if err != nil {
		return 0
	}
	return info.Size()
}