summaryrefslogtreecommitdiff
path: root/internal/feed/feed.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/feed/feed.go')
-rw-r--r--internal/feed/feed.go40
1 files changed, 34 insertions, 6 deletions
diff --git a/internal/feed/feed.go b/internal/feed/feed.go
index 02a016e..642d8f3 100644
--- a/internal/feed/feed.go
+++ b/internal/feed/feed.go
@@ -28,6 +28,13 @@ type Options struct {
// successively later dates so that episode order is unambiguous; see
// pubDate handling in Build.
Epoch time.Time
+ // Token, if set, is appended to every enclosure URL.
+ //
+ // This is not optional bookkeeping: a podcast client fetches an enclosure as
+ // a plain GET with nothing carried over from the feed request. If the server
+ // requires a token and the enclosure URLs lack one, the feed loads, shows
+ // every episode, and no episode will ever download.
+ Token string
}
// Item is one episode.
@@ -106,6 +113,22 @@ var audioTypes = map[string]string{
".wav": "audio/wav",
}
+// IsAudio reports whether a filename is one podkit treats as an episode.
+//
+// Exported because the server and the uploader need exactly the same answer as
+// the feed generator: a file the server accepts but the generator ignores would
+// upload successfully and then never appear in the feed.
+func IsAudio(filename string) bool {
+ _, ok := audioTypes[strings.ToLower(filepath.Ext(filename))]
+ return ok
+}
+
+// MIMEType returns the enclosure content type for a filename, or "" if it is
+// not audio podkit recognises.
+func MIMEType(filename string) string {
+ return audioTypes[strings.ToLower(filepath.Ext(filename))]
+}
+
// ParseName splits "03 - Great Conservation Principles.mp3" into its track
// number and title. Files without a numeric prefix keep their whole stem as the
// title and report track 0.
@@ -126,7 +149,9 @@ func ParseName(filename string) (track int, title string) {
// contain spaces and ampersands, and pasting them raw produces both an invalid
// URL and invalid XML. Only the final path segment is escaped — escaping the
// whole base would mangle its scheme and host.
-func EnclosureURL(baseURL, filename string) (string, error) {
+// A token, when supplied, is carried on the URL, because that is the only thing
+// a podcast client will send back.
+func EnclosureURL(baseURL, filename, token string) (string, error) {
base, err := url.Parse(baseURL)
if err != nil {
return "", fmt.Errorf("parsing base URL %q: %w", baseURL, err)
@@ -135,6 +160,11 @@ func EnclosureURL(baseURL, filename string) (string, error) {
return "", fmt.Errorf("base URL %q needs a scheme and host", baseURL)
}
base.Path = path.Join(base.Path, filename)
+ if token != "" {
+ q := base.Query()
+ q.Set("k", token)
+ base.RawQuery = q.Encode()
+ }
// url.URL.String() escapes the path for us; RawPath stays unset so the
// escaping is derived from Path.
return base.String(), nil
@@ -167,11 +197,9 @@ func Build(opts Options, readTags TagReader) (*RSS, error) {
if e.IsDir() {
continue
}
- mime, isAudio := audioTypes[strings.ToLower(filepath.Ext(e.Name()))]
- if !isAudio {
+ if !IsAudio(e.Name()) {
continue
}
- _ = mime
info, err := e.Info()
if err != nil {
// A file that vanished mid-scan is not a reason to abandon the
@@ -209,7 +237,7 @@ func Build(opts Options, readTags TagReader) (*RSS, error) {
items := make([]Item, 0, len(found))
for i, c := range found {
- encURL, err := EnclosureURL(opts.BaseURL, c.name)
+ encURL, err := EnclosureURL(opts.BaseURL, c.name, opts.Token)
if err != nil {
return nil, err
}
@@ -229,7 +257,7 @@ func Build(opts Options, readTags TagReader) (*RSS, error) {
PubDate: pub.Format(time.RFC1123Z),
Enclosure: Enclosure{
URL: encURL,
- Type: audioTypes[strings.ToLower(filepath.Ext(c.name))],
+ Type: MIMEType(c.name),
Length: c.size,
},
Episode: episode,