You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(notes): ship a Bases-friendly default episode note template (#160) (#201)
* feat(notes): ship a Bases-friendly default episode note template (#160)
The episode note path/template both defaulted to "" (empty), which left the
"Create episode note" command disabled on a fresh install and gave new users
no structured metadata. Ship sensible defaults:
- note.path: PodNotes/{{podcast}}/{{title}}.md (matches the download convention)
- note.template: Bases-friendly frontmatter (type/podcast-link/url/date/tags +
user-fillable status/rating/favorite), with the raw title as the body H1 so
YAML rules never apply to it. {{podcastlink}} ties each episode to its #163
feed note for Bases/Dataview rollups.
Make the frontmatter bulletproof: NoteTemplateEngine now sanitizes the URL tags
({{url}}/{{stream}}/{{artwork}}/{{episodeurl}}/{{episodeartwork}}/{{feedurl}}/
{{feedartwork}}) by stripping " and \\ (lossless for valid URLs), mirroring
FeedNoteTemplateEngine, so a quoted scalar like url: "{{url}}" stays valid.
Migrate existing users without clobbering customizations: migrateNotePath /
migrateNoteTemplate upgrade ONLY the exact legacy empty value (or absent), so
any non-empty path/template the user configured is preserved. Mirrors the #183
migrateDownloadPath precedent and is wired into loadSettings.
Update the e2e provisioning seed to match (keeps the seed/DEFAULT_SETTINGS drift
test green and seeds the new default for verification), refresh the settings
placeholder, and document the default + an example .base view in templates.md.
Verified in the isolated worktree Obsidian vault: notes created via the real
create-podcast-note command (incl. a hostile title/URL and a no-date episode)
parse into clean frontmatter via Obsidian's properties parser.
* fix(notes): harden URL-tag sanitization and correct Bases docs (#160 review)
Address ultracode review findings on the #160 default template:
- NoteTemplateEngine: guard {{url}}/{{stream}}/{{episodeurl}} with `?? ""` before
sanitizeUrlForTemplate, matching the other URL tags. A corrupted/hand-edited
data.json could surface a null/undefined on these typed-string fields, where
the unguarded `.replace` would throw and abort note creation. Add a regression
test covering a corrupted episode.
- docs/templates.md: the default puts {{artwork}} in the body as a Markdown image,
not a quoted frontmatter scalar; correct the YAML-safety bullet to say so.
- docs/templates.md: quote the example .base filter condition
('type == "podcastEpisode"') to match Obsidian's canonical Bases syntax.
* fix(notes): tighten note migration and Bases docs (#160 adversarial review)
Address findings from the adversarial review of the #160 default template:
- Migration: only upgrade the legacy empty note when the WHOLE note is the old
default (path AND template both empty/absent). Replace the per-field
migrateNotePath/migrateNoteTemplate with migrateNoteSettings so a user who
configured one field and deliberately left the other empty (e.g. a custom path
with an empty template to keep "Create episode note" disabled) is never
silently re-enabled. Coalesce null fields so a corrupted data.json can't reach
the path/template engines. Update tests to cover the preserve-partial case.
- docs/templates.md: the example .base used a `sort:` view key, which is not part
of the documented Obsidian Bases schema (sorting is view UI state; only
`order` + `groupBy` are documented). Switch to `groupBy` (by listening status)
and `note.`-prefixed properties, matching the canonical syntax.
- Qualify the "always valid YAML" claim (docs + constants comment): the guarantee
holds for the default and any ordinary feed-note path; a feed-note path with a
literal quote/backslash in a folder segment would flow into {{podcastlink}}.
* fix(notes): render URL tags verbatim, don't mutate them globally (#160 review)
Codex review (P2): globally stripping "/\ from NoteTemplateEngine's URL tags
corrupts {{url}} for local-file episodes, where episode.url is a wikilink from
generateMarkdownLink (e.g. a file named Talk "A".mp3 would link to Talk A.mp3),
and changes existing custom templates that use {{url}} outside YAML.
Revert the URL-tag sanitization added earlier in this branch back to master's
verbatim behavior. The default frontmatter stays valid YAML for the common case
without mutating tag values: feed episode URLs are well-formed (no quotes), the
{{podcastlink}} name is already sanitized, and ordinary local-file links contain
no quotes. A URL or file name with a literal quote is the one case that would
need adjusting, which is documented. sanitizeUrlForTemplate stays in use by
FeedNoteTemplateEngine, where {{url}} is always a real website URL.
Add a test guarding that {{url}}/{{episodeurl}} pass through verbatim (local-file
wikilinks preserved), and update the docs/comment claims accordingly.
* fix(notes): keep {{url}} out of the default frontmatter scalar (#160 review)
Codex review (P2): after rendering URL tags verbatim, a local-file episode whose
name contains a double quote (e.g. Talk "A".mp3, playable on macOS/Linux) makes
{{url}} a wikilink with a quote, so url: "{{url}}" renders
url: "[[Talk "A".mp3]]" and the whole frontmatter fails to parse, hiding every
property from Bases.
Move {{url}} (and it was already true for {{artwork}}) into the note body, where
YAML rules don't apply, so the verbatim tag value can never break the
frontmatter. The frontmatter now contains only values that can't carry a
YAML-hostile character (type, the sanitized {{podcastlink}}, an ISO/empty date,
tags, and the user-fillable status/rating/favorite). Feed-only users can add
url: "{{url}}" back; documented. Update the example, the e2e seed, and the test
(now exercises a quote-bearing local-file url to prove the frontmatter stays
parseable).
Copy file name to clipboardExpand all lines: docs/docs/templates.md
+47-5Lines changed: 47 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,6 +3,8 @@ PodNotes can create notes from templates. These templates can contain certain sy
3
3
To use templates, you can use the `Create episode note` Obsidian command (previously named `Create podcast note`).
4
4
This requires you to have defined a template for both the file path and note text.
5
5
6
+
PodNotes ships a sensible default for both, so the command works out of the box on a fresh install. The default note template uses structured frontmatter properties that [Obsidian Bases](https://help.obsidian.md/bases) can sort, filter, and group on (see [Using the default template with Bases](#using-the-default-template-with-bases) below). You can customize either template under PodNotes settings at any time, and your customizations are always preserved across updates.
7
+
6
8
PodNotes can also create a note for a whole podcast (the feed). See [Podcast feed notes](#podcast-feed-notes) below.
7
9
8
10
## File path
@@ -28,7 +30,7 @@ This template will be used to create the note text. You can use the following sy
28
30
- You can use `{{content:> }}` to prepend each new line with a `>` (to put the entire content in a blockquote).
29
31
30
32
-`{{podcast}}`: The name of the podcast.
31
-
-`{{url}}`: The URL of the podcast episode.
33
+
-`{{url}}`: The URL of the podcast episode. For a local-file episode this is a link to the file rather than a web URL. Tag values are inserted verbatim, so when you place one in a quoted YAML property (e.g. `url: "{{url}}"`) it stays valid for well-formed feed URLs and ordinary file names (a URL or file name containing a literal `"` would need escaping).
32
34
-`{{stream}}`: The direct URL of the episode's audio file — the RSS `<enclosure>` URL for podcast feeds, or the underlying audio source for Pocket Casts and local-file episodes. Handy for embedding the raw audio or linking to the source. An empty string is used in the rare case no audio URL is available. Available in episode note templates only.
33
35
-`{{date}}`: The publish date of the podcast episode.
34
36
- You can use `{{date:format}}` to specify a custom [Moment.js](https://momentjs.com) format. E.g. `{{date:YYYY-MM-DD}}`.
@@ -51,22 +53,62 @@ In an episode note, `{{url}}` and `{{artwork}}` always describe the **episode**.
51
53
-`{{feedartwork}}`: The podcast's (feed) artwork. Falls back to the episode artwork if the feed isn't saved.
52
54
-`{{podcastlink}}`: A ready-made wikilink to the podcast's [feed note](#podcast-feed-notes). It points at the same file the feed note is created at, so episodes and the feed note link up automatically. When the feed-note path has a folder it is path-qualified (e.g. `[[PodNotes/Podcasts/My Show|My Show]]`) so it can't resolve to an unrelated note that shares the basename; otherwise it's a plain `[[My Show]]`. (Avoid putting a `{{date}}` in the feed-note path, since the episode side can't reproduce the feed note's creation date.)
53
55
54
-
A Bases-friendly episode template that links back to the feed note:
56
+
### Using the default template with Bases
57
+
PodNotes ships this default episode note template, which links each episode back to its feed note and exposes structured frontmatter properties for [Obsidian Bases](https://help.obsidian.md/bases):
55
58
56
59
```
57
60
---
58
61
type: podcastEpisode
59
62
podcast: "{{podcastlink}}"
60
-
title: "{{title}}"
61
-
image: "{{artwork}}"
62
-
url: "{{url}}"
63
63
date: {{date:YYYY-MM-DD}}
64
64
tags:
65
65
- podcastEpisode
66
+
status:
67
+
rating:
68
+
favorite: false
66
69
---
70
+
# {{title}}
71
+
72
+

73
+
74
+
[Resume in PodNotes]({{episodelink}})
75
+
76
+
{{url}}
77
+
67
78
{{description}}
68
79
```
69
80
81
+
The frontmatter is built so it stays **valid YAML** for every episode, because only values that can never contain a YAML-hostile character go into it:
82
+
83
+
- The full episode title goes in the body as the `# {{title}}` heading, where YAML rules don't apply (a raw title can contain `"` or `:`, which would break a frontmatter scalar).
84
+
-`{{podcastlink}}` is quoted so its leading `[[` isn't read as a YAML flow sequence, and the linked name is sanitized. (If you customize *Feed note file path* to a folder containing a literal `"` or `\`, that character flows into the link verbatim — keep the feed-note path to ordinary path characters.)
85
+
-`{{url}}` and `{{artwork}}` are kept in the **body** (a bare link and a Markdown image), not in the frontmatter. Tag values are inserted verbatim, and for a local-file episode `{{url}}` is a vault link whose name can contain a `"`; keeping it out of a quoted scalar means an awkward URL or file name can never break the note's properties. If your episodes all come from feeds (so `{{url}}` is always a well-formed web URL), you can add `url: "{{url}}"` back to the frontmatter.
86
+
-`date:` is a bare `YYYY-MM-DD` (or empty/null when the feed has no publish date).
87
+
-`status`, `rating`, and `favorite` are left for you to fill in — they give Bases columns to sort and filter on (e.g. mark an episode `favorite: true`, or set `status` to `to-listen`/`listening`/`listened`).
88
+
89
+
A starter Bases view (save as e.g. `Podcast Episodes.base`) that lists every episode note, grouped by listening status, with a column for each property:
90
+
91
+
```yaml
92
+
filters:
93
+
and:
94
+
- 'type == "podcastEpisode"'
95
+
views:
96
+
- type: table
97
+
name: Episodes
98
+
order:
99
+
- file.name
100
+
- note.podcast
101
+
- note.date
102
+
- note.status
103
+
- note.rating
104
+
- note.favorite
105
+
groupBy:
106
+
property: note.status
107
+
direction: ASC
108
+
```
109
+
110
+
Open the base in Obsidian to sort, filter, or add views from the view options. Because `note.podcast` resolves to the linked [feed note](#podcast-feed-notes), you can also group by show or pivot from a feed note to all of its episodes.
111
+
70
112
## Podcast feed notes
71
113
A *feed note* is a single parent note for an entire podcast (the feed), which episode notes can link to (great for [Obsidian Bases](https://help.obsidian.md/bases) / Dataview rollups).
0 commit comments