podops

package module
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 20, 2022 License: MIT Imports: 10 Imported by: 1

README

podops

PodOps is a set of backend services and command line utilities to create podcast feeds from simple markdown files.

TL;DR

... or how to build a podcast in 5 steps:

cd /the/podcast/location

# prepare the podcast repo
po new
<... do some yaml editing>

# create an episode
po template episode
<... more yaml editing>

# build the feed
po build

# register the podcast with the CDN
po init

# sync the podcast with the CDN
po sync
Installation

TBD

Local development

Get the source code

$ git clone https://github.com/podops/podops.git <some_location>

$ cd <some_location>
$ go mod tidy
$ make test_build
Command Line

Run the CLI from local source code:

cd cmd/cli
go run cli.go <comand>

In order to target a local API service, set the PODOPS_API_ENDPOINT environment variable:

cd cmd/cli
PODOPS_API_ENDPOINT=http://localhost:8080 go run cli.go <comand>
API Service

Run the API service from local source code:

cd cmd/api
go run main.go

In order to set the location of the CDN folders, use environment variables PODOPS_STORAGE_LOCATION and PODOPS_STATIC_LOCATION, e.g.


PODOPS_STORAGE_LOCATION=/path/to/cdn PODOPS_STATIC_LOCATION=/path/to/public go run main.go

Documentation

Index

Constants

View Source
const (
	MsgStatus = "status: %d"

	// messages used in the validations
	MsgResourceInvalidName      = "invalid resource name '%s'"
	MsgResourceInvalidReference = "invalid reference type '%s'"
	MsgResourceIsInvalid        = "invalid resource '%s'"
	MsgResourceInvalidGUID      = "resource '%s': invalid guid '%s'" // FIXME swap order and update code accordingly -> "invalid guid '%s' for resource '%s'"
	MsgInvalidEmail             = "invalid email '%s'"
	MsgMissingCategory          = "missing categories"

	MsgResourceUnsupportedKind = "unsupported kind '%s'"
	MsgResourceImportError     = "error transfering '%s'"
	MsgResourceUploadError     = "error uploading '%s'"

	MsgBuildSuccess    = "Sucessfully built podcast '%s'"
	MsgAssembleSuccess = "Sucessfully collected all resources"
	MsgGenerateSuccess = "Sucessfully generated markdown resources"
	MsgSyncSuccess     = "Sucessfully synced all resources"

	MsgConfigInit = "Created new config for client id '%s' with token '%s'"
)
View Source
const (

	// LabelLanguage ISO-639 two-letter language code. channel.language
	LabelLanguage = "language"
	// LabelExplicit ["true"|"false"] channel.itunes.explicit
	LabelExplicit = "explicit"
	// LabelType ["Episodic"|"Serial"] channel.itunes.type
	LabelType = "type"
	// LabelBlock ["Yes"] channel.itunes.block
	LabelBlock = "block"
	// LabelComplete ["Yes"] channel.itunes.complete
	LabelComplete = "complete"
	// LabelSeason defaults to "1"
	LabelSeason = "season"
	// LabelEpisode positive integer 1..
	LabelEpisode = ResourceEpisode

	// ShowTypeEpisodic type of podcast is episodic
	ShowTypeEpisodic = "Episodic"
	// ShowTypeSerial type of podcast is serial
	ShowTypeSerial = "Serial"

	// EpisodeTypeFull type of episode is 'full'
	EpisodeTypeFull = "Full"
	// EpisodeTypeTrailer type of episode is 'trailer'
	EpisodeTypeTrailer = "Trailer"
	// EpisodeTypeBonus type of episode is 'bonus'
	EpisodeTypeBonus = "Bonus"

	// ResourceTypeExternal references an external URL
	ResourceTypeExternal = "external"
	// ResourceTypeLocal references a local resource
	ResourceTypeLocal = "local"
	// ResourceTypeImport references an external resources that will be imported into the CDN
	ResourceTypeImport = "import"

	// ResourceShow is referencing a resource of type "show"
	ResourceShow = "show"
	// ResourceEpisode is referencing a resource of type "episode"
	ResourceEpisode = "episode"
	// ResourceAsset is referencing any media or binary resource e.g. .mp3 or .png
	ResourceAsset = "asset"
	// ResourceALL is a wildcard for any kind of resource
	ResourceALL = "all"
)

Variables

View Source
var (
	// ErrNotImplemented indicates that a function is not yet implemented
	//ErrNotImplemented = errors.New("not implemented")
	// ErrInternalError indicates everything else
	ErrInternalError = errors.New("internal error")
	// ErrApiError indicates an error in an API call
	ErrApiError = errors.New("api error")
	// ErrInvalidRoute indicates that the route and/or its parameters are not valid
	ErrInvalidRoute = errors.New("invalid route")
	// ErrMissingPayloadSecret indicates that no secret was provided
	//ErrMissingPayloadSecret = errors.New("missing payload secret")
	// ErrUnsupportedWebhookEvent indicates that the wrong type of webhook was received
	ErrUnsupportedWebhookEvent = errors.New("unsupported webhook")

	// ErrInvalidResourceName indicates that the resource name is invalid
	ErrInvalidResourceName = errors.New("invalid resource name")
	// ErrMissingResourceName indicates that a resource type is missing
	ErrMissingResourceName = errors.New("missing resource type")
	// ErrResourceNotFound indicates that the resource does not exist
	ErrResourceNotFound = errors.New("resource does not exist")
	// ErrResourceExists indicates that the resource does not exist
	//ErrResourceExists = errors.New("resource already exists")
	// ErrInvalidGUID indicates that the GUID is invalid
	ErrInvalidGUID = errors.New("invalid GUID")
	// ErrInvalidParameters indicates that parameters used in an API call are not valid
	ErrInvalidParameters = errors.New("invalid parameters")
	// ErrInvalidNumArguments indicates that the number of arguments in an API call is not valid
	ErrInvalidNumArguments = errors.New("invalid arguments")
	// ErrInvalidPassPhrase indicates that the pass phrase is too short
	ErrInvalidPassPhrase = errors.New("invalid pass phrase")

	// ErrBuildFailed indicates that there was an error while building the feed
	ErrBuildFailed = errors.New("build failed")
	// ErrBuildNoShow indicates that no show.yaml could be found
	ErrBuildNoShow = errors.New("missing show.yaml")
	// ErrBuildNoEpisodes indicates that no episodes could be found
	ErrBuildNoEpisodes = errors.New("missing episodes")

	// ErrAssembleNoResources indicates that no resources could be found
	ErrAssembleNoResources = errors.New("missing resource cache")
)

Functions

func CreateETag2 added in v0.10.0

func CreateETag2(name string, size, timestamp int64) string

CreateETag calculates an etag based on a file's name, size and timestamp. It does not inspect the actual content of the file though.

func CreateRandomAssetGUID2 added in v0.10.0

func CreateRandomAssetGUID2() string

CreateRandomAssetGUID returns a random ID for assets references. The GUID is 6 bytes / 12 char long, covering 16^12 address space. The GUID is most likely unique but there is not guarantee.

func CreateSimpleID2 added in v0.10.0

func CreateSimpleID2() string

func CreateSimpleToken2 added in v0.10.0

func CreateSimpleToken2() string

func DefaultEpisodeMetadata added in v0.9.12

func DefaultEpisodeMetadata() map[string]string

DefaultEpisodeMetadata creates a default set of labels etc for a Episode resource

season: 	<season number> OPTIONAL 'item.itunes.season'
episode:	<episode number> REQUIRED 'item.itunes.episode'
explicit:	True | False REQUIRED 'channel.itunes.explicit'
type:		Full | Trailer | Bonus REQUIRED 'item.itunes.episodeType'
block:		Yes OPTIONAL 'item.itunes.block' Anything else than 'Yes' has no effect

func DefaultShowMetadata added in v0.9.12

func DefaultShowMetadata() map[string]string

DefaultShowMetadata creates a default set of labels etc for a Show resource

language:	<ISO639 two-letter-code> REQUIRED 'channel.language'
explicit:	True | False REQUIRED 'channel.itunes.explicit'
type:		Episodic | Serial REQUIRED 'channel. itunes.type'
block:		Yes OPTIONAL 'channel.itunes.block' Anything else than 'Yes' has no effect
complete:	Yes OPTIONAL 'channel.itunes.complete' Anything else than 'Yes' has no effect

func NormalizeKind added in v0.10.0

func NormalizeKind(kind string) (string, error)

func ValidEmail added in v0.10.0

func ValidEmail(e string) bool

ValidEmail checks if the email provided passes the required structure and length.

func ValidGUID added in v0.10.0

func ValidGUID(guid string) bool

func ValidName added in v0.10.0

func ValidName(name string) bool

ValidName verifies that a name is valid for a resource. The following rules apply:

'name' must contain only lowercase letters, numbers, dashes (-), underscores (_). 'name' must contain 8-64 characters. Spaces and dots (.) are not allowed.

Types

type AssetRef added in v0.10.0

type AssetRef struct {
	URI       string `json:"uri" yaml:"uri" binding:"required"`              // REQUIRED
	Rel       string `json:"rel" yaml:"rel" binding:"required"`              // REQUIRED
	Type      string `json:"type,omitempty" yaml:"type,omitempty"`           // OPTIONAL
	ETag      string `json:"etag,omitempty" yaml:"etag,omitempty"`           // OPTIONAL
	Duration  int    `json:"duration,omitempty" yaml:"duration,omitempty"`   // OPTIONAL
	Timestamp int64  `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` // OPTIONAL
	Size      int    `json:"size,omitempty" yaml:"size,omitempty"`           // OPTIONAL
}

func (*AssetRef) AssetReference added in v0.10.0

func (r *AssetRef) AssetReference(parent string) string

AssetReference creates a unique asset reference based on the assets parent GUID and its URI. The reference is a CRC32 checksum and assumed to be static once the asset has been created. The media file the asset refers to might change over time.

func (*AssetRef) CanonicalReference added in v0.10.0

func (r *AssetRef) CanonicalReference(cdn, parent string) string

CanonicalReference creates the full URI for the asset, as it can be found in the CDN

func (*AssetRef) LocalNamePart added in v0.10.0

func (r *AssetRef) LocalNamePart() string

LocalNamePart returns the part after the last /, if any

func (*AssetRef) MediaReference added in v0.10.0

func (r *AssetRef) MediaReference() string

MediaReference creates reference to a media file based on its current ETag. The MediaReference can change over time as the referenced file changes.

func (*AssetRef) Validate added in v0.10.0

func (r *AssetRef) Validate(root string, v *validate.Validator) *validate.Validator

Validate verifies the integrity of struct Resource

URI    string `json:"uri" yaml:"uri" binding:"required"`        // REQUIRED
Rel    string `json:"rel,omitempty" yaml:"rel,omitempty"`       // REQUIRED
Type   string `json:"type,omitempty" yaml:"type,omitempty"`     // OPTIONAL
Size   int    `json:"size,omitempty" yaml:"size,omitempty"`     // OPTIONAL

type Category added in v0.9.10

type Category struct {
	Name        string   `json:"name" yaml:"name" binding:"required"`      // REQUIRED
	SubCategory []string `json:"subcategory" yaml:"subcategory,omitempty"` // OPTIONAL
}

Category is the show/episodes category and it's subcategories

func (*Category) Validate added in v0.9.10

func (c *Category) Validate(root string, v *validate.Validator) *validate.Validator

Validate verifies the integrity of struct Category

Name        string   `json:"name" yaml:"name" binding:"required"`      // REQUIRED
SubCategory []string `json:"subcategory" yaml:"subcategory,omitempty"` // OPTIONAL

type Episode added in v0.9.10

type Episode struct {
	APIVersion  string             `json:"apiVersion" yaml:"apiVersion" binding:"required"`   // REQUIRED default: v1.0
	Kind        string             `json:"kind" yaml:"kind" binding:"required"`               // REQUIRED default: episode
	Metadata    Metadata           `json:"metadata" yaml:"metadata" binding:"required"`       // REQUIRED
	Description EpisodeDescription `json:"description" yaml:"description" binding:"required"` // REQUIRED
	Image       AssetRef           `json:"image" yaml:"image" binding:"required"`             // REQUIRED 'item.itunes.image'
	Enclosure   AssetRef           `json:"enclosure" yaml:"enclosure" binding:"required"`     // REQUIRED
}

Episode holds all metadata related to a podcast episode

func DefaultEpisode added in v0.9.12

func DefaultEpisode(name, parentName, guid, parent, portal, cdn string) *Episode

DefaultEpisode creates a default episode struc

func (*Episode) GUID added in v0.9.10

func (e *Episode) GUID() string

GUID is a convenience method to access the resources guid

func (*Episode) Parent added in v0.9.10

func (e *Episode) Parent() string

ParentGUID is a convenience method to access the resources parent guid

func (*Episode) PublishDate added in v0.9.10

func (e *Episode) PublishDate() string

PublishDate is a convenience method to access the pub date

func (*Episode) PublishDateTimestamp added in v0.9.10

func (e *Episode) PublishDateTimestamp() int64

PublishDateTimestamp converts a RFC1123Z formatted timestamp into UNIX timestamp

func (*Episode) Validate added in v0.9.10

func (e *Episode) Validate(root string, v *validate.Validator) *validate.Validator

Validate verifies the integrity of struct Episode

APIVersion  string             `json:"apiVersion" yaml:"apiVersion" binding:"required"`   // REQUIRED default: v1.0
Kind        string             `json:"kind" yaml:"kind" binding:"required"`               // REQUIRED default: episode
Metadata    Metadata           `json:"metadata" yaml:"metadata" binding:"required"`       // REQUIRED
Description EpisodeDescription `json:"description" yaml:"description" binding:"required"` // REQUIRED
Image       Resource           `json:"image" yaml:"image" binding:"required"`             // REQUIRED 'item.itunes.image'
Enclosure   Resource           `json:"enclosure" yaml:"enclosure" binding:"required"`     // REQUIRED

type EpisodeDescription added in v0.9.10

type EpisodeDescription struct {
	Title       string   `json:"title" yaml:"title" binding:"required"`                                 // REQUIRED 'item.title' 'item.itunes.title'
	Summary     string   `json:"summary" yaml:"summary" binding:"required"`                             // REQUIRED 'item.description'
	EpisodeText string   `json:"episodeText,omitempty" yaml:"episodeText,omitempty" binding:"required"` // REQUIRED 'item.itunes.summary'
	Link        AssetRef `json:"link" yaml:"link"`                                                      // RECOMMENDED 'item.link'
	Duration    int      `json:"duration" yaml:"duration" binding:"required"`                           // REQUIRED 'item.itunes.duration'
}

EpisodeDescription holds essential episode metadata

func (*EpisodeDescription) Validate added in v0.9.10

Validate verifies the integrity of struct EpisodeDescription

Title       string   `json:"title" yaml:"title" binding:"required"`                                 // REQUIRED 'item.title' 'item.itunes.title'
Summary     string   `json:"summary" yaml:"summary" binding:"required"`                             // REQUIRED 'item.description'
EpisodeText string   `json:"episodeText,omitempty" yaml:"episodeText,omitempty" binding:"required"` // REQUIRED 'item.itunes.summary'
Link        Resource `json:"link" yaml:"link"`                                                      // RECOMMENDED 'item.link'
Duration    int      `json:"duration" yaml:"duration" binding:"required"`                           // REQUIRED 'item.itunes.duration'

type GenericResource added in v0.10.0

type GenericResource struct {
	APIVersion string   `json:"apiVersion" yaml:"apiVersion" binding:"required"` // REQUIRED default: v1.0
	Kind       string   `json:"kind" yaml:"kind" binding:"required"`             // REQUIRED default: show
	Metadata   Metadata `json:"metadata" yaml:"metadata" binding:"required"`     // REQUIRED
}

GenericResource holds only the kind and metadata of a resource

func (*GenericResource) GUID added in v0.10.0

func (r *GenericResource) GUID() string

GUID is a convenience method to access the resources guid

type Metadata added in v0.9.10

type Metadata struct {
	Name   string            `json:"name" yaml:"name" binding:"required"`       // REQUIRED
	GUID   string            `json:"guid" yaml:"guid" binding:"required"`       // REQUIRED
	Parent string            `json:"parent,omitempty" yaml:"parent,omitempty" ` // OPTIONAL
	Date   string            `json:"date,omitempty" yaml:"date,omitempty" `     // RECOMMENDED
	Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`  // REQUIRED
	Tags   string            `json:"tags,omitempty" yaml:"tags,omitempty" `     // OPTIONAL
}

Metadata contains information describing a resource

func (*Metadata) Validate added in v0.9.10

func (m *Metadata) Validate(root string, v *validate.Validator) *validate.Validator

Validate verifies the integrity of struct Metadata

	Name   string            `json:"name" yaml:"name" binding:"required"` // REQUIRED
 GUID   string            `json:"guid" yaml:"guid" binding:"required"` // REQUIRED

type Owner added in v0.9.10

type Owner struct {
	Name  string `json:"name" yaml:"name" binding:"required"`   // REQUIRED
	Email string `json:"email" yaml:"email" binding:"required"` // REQUIRED
}

Owner describes the owner of the show/podcast

func (*Owner) Validate added in v0.9.10

func (o *Owner) Validate(root string, v *validate.Validator) *validate.Validator

Validate verifies the integrity of struct Owner

Name  string `json:"name" yaml:"name" binding:"required"`   // REQUIRED
Email string `json:"email" yaml:"email" binding:"required"` // REQUIRED

type Show added in v0.9.10

type Show struct {
	APIVersion  string          `json:"apiVersion" yaml:"apiVersion" binding:"required"`   // REQUIRED default: v1.0
	Kind        string          `json:"kind" yaml:"kind" binding:"required"`               // REQUIRED default: show
	Metadata    Metadata        `json:"metadata" yaml:"metadata" binding:"required"`       // REQUIRED
	Description ShowDescription `json:"description" yaml:"description" binding:"required"` // REQUIRED
	Image       AssetRef        `json:"image" yaml:"image" binding:"required"`             // REQUIRED 'channel.itunes.image'
}

Show holds all metadata related to a podcast/show

func DefaultShow added in v0.9.12

func DefaultShow(name, title, summary, guid, portal, cdn string) *Show

DefaultShow creates a default show struc

func (*Show) GUID added in v0.9.10

func (s *Show) GUID() string

GUID is a convenience method to access the resources guid

func (*Show) Validate added in v0.9.10

func (s *Show) Validate(root string, v *validate.Validator) *validate.Validator

Validate verifies the integrity of struct Show

APIVersion  string          `json:"apiVersion" yaml:"apiVersion" binding:"required"`   // REQUIRED default: v1.0
Kind        string          `json:"kind" yaml:"kind" binding:"required"`               // REQUIRED default: show
Metadata    Metadata        `json:"metadata" yaml:"metadata" binding:"required"`       // REQUIRED
Description ShowDescription `json:"description" yaml:"description" binding:"required"` // REQUIRED
Image       Resource        `json:"image" yaml:"image" binding:"required"`             // REQUIRED 'channel.itunes.image'

type ShowDescription added in v0.9.10

type ShowDescription struct {
	Title     string     `json:"title" yaml:"title" binding:"required"`          // REQUIRED 'channel.title' 'channel.itunes.title'
	Summary   string     `json:"summary" yaml:"summary" binding:"required"`      // REQUIRED 'channel.description'
	Link      AssetRef   `json:"link" yaml:"link"`                               // RECOMMENDED 'channel.link'
	Category  []Category `json:"category" yaml:"category" binding:"required"`    // REQUIRED channel.category
	Owner     Owner      `json:"owner" yaml:"owner"`                             // RECOMMENDED 'channel.itunes.owner'
	Author    string     `json:"author" yaml:"author"`                           // RECOMMENDED 'channel.itunes.author'
	Copyright string     `json:"copyright,omitempty" yaml:"copyright,omitempty"` // OPTIONAL 'channel.copyright'
	NewFeed   *AssetRef  `json:"newFeed,omitempty" yaml:"newFeed,omitempty"`     // OPTIONAL channel.itunes.new-feed-url -> move to label
}

ShowDescription holds essential show metadata

func (*ShowDescription) Validate added in v0.9.10

func (d *ShowDescription) Validate(root string, v *validate.Validator) *validate.Validator

Validate verifies the integrity of struct ShowDescription

Title     string    `json:"title" yaml:"title" binding:"required"`          // REQUIRED 'channel.title' 'channel.itunes.title'
Summary   string    `json:"summary" yaml:"summary" binding:"required"`      // REQUIRED 'channel.description'
Link      Resource  `json:"link" yaml:"link"`                               // RECOMMENDED 'channel.link'
Category  Category  `json:"category" yaml:"category" binding:"required"`    // REQUIRED channel.category
Owner     Owner     `json:"owner" yaml:"owner"`                             // RECOMMENDED 'channel.itunes.owner'
Author    string    `json:"author" yaml:"author"`                           // RECOMMENDED 'channel.itunes.author'
Copyright string    `json:"copyright,omitempty" yaml:"copyright,omitempty"` // OPTIONAL 'channel.copyright'
NewFeed   *Resource `json:"newFeed,omitempty" yaml:"newFeed,omitempty"`     // OPTIONAL channel.itunes.new-feed-url -> move to label

Directories

Path Synopsis
cmd
api
cdn
cli
api
cdn
cli
rss

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL