Add chapter source fields (#6)
This commit is contained in:
parent
f25426db5c
commit
d378f690e4
@ -648,6 +648,9 @@ type EditPostModel = {
|
|||||||
/// The explicit rating for this episode (optional, defaults to podcast setting)
|
/// The explicit rating for this episode (optional, defaults to podcast setting)
|
||||||
Explicit: string
|
Explicit: string
|
||||||
|
|
||||||
|
/// The chapter source ("internal" for chapters defined here, "external" for a file link, "none" if none defined)
|
||||||
|
ChapterSource: string
|
||||||
|
|
||||||
/// The URL for the chapter file for the episode (may be permalink; optional)
|
/// The URL for the chapter file for the episode (may be permalink; optional)
|
||||||
ChapterFile: string
|
ChapterFile: string
|
||||||
|
|
||||||
@ -713,6 +716,9 @@ type EditPostModel = {
|
|||||||
ImageUrl = defaultArg episode.ImageUrl ""
|
ImageUrl = defaultArg episode.ImageUrl ""
|
||||||
Subtitle = defaultArg episode.Subtitle ""
|
Subtitle = defaultArg episode.Subtitle ""
|
||||||
Explicit = defaultArg (episode.Explicit |> Option.map string) ""
|
Explicit = defaultArg (episode.Explicit |> Option.map string) ""
|
||||||
|
ChapterSource = if Option.isSome episode.Chapters then "internal"
|
||||||
|
elif Option.isSome episode.ChapterFile then "external"
|
||||||
|
else "none"
|
||||||
ChapterFile = defaultArg episode.ChapterFile ""
|
ChapterFile = defaultArg episode.ChapterFile ""
|
||||||
ChapterType = defaultArg episode.ChapterType ""
|
ChapterType = defaultArg episode.ChapterType ""
|
||||||
ContainsWaypoints = defaultArg episode.ChapterWaypoints false
|
ContainsWaypoints = defaultArg episode.ChapterWaypoints false
|
||||||
@ -773,10 +779,19 @@ type EditPostModel = {
|
|||||||
ImageUrl = noneIfBlank this.ImageUrl
|
ImageUrl = noneIfBlank this.ImageUrl
|
||||||
Subtitle = noneIfBlank this.Subtitle
|
Subtitle = noneIfBlank this.Subtitle
|
||||||
Explicit = noneIfBlank this.Explicit |> Option.map ExplicitRating.Parse
|
Explicit = noneIfBlank this.Explicit |> Option.map ExplicitRating.Parse
|
||||||
Chapters = match post.Episode with Some e -> e.Chapters | None -> None
|
Chapters = if this.ChapterSource = "internal" then
|
||||||
ChapterFile = noneIfBlank this.ChapterFile
|
match post.Episode with
|
||||||
ChapterType = noneIfBlank this.ChapterType
|
| Some e when Option.isSome e.Chapters -> e.Chapters
|
||||||
ChapterWaypoints = if this.ContainsWaypoints then Some true else None
|
| Some _
|
||||||
|
| None -> Some []
|
||||||
|
else None
|
||||||
|
ChapterFile = if this.ChapterSource = "external" then noneIfBlank this.ChapterFile
|
||||||
|
else None
|
||||||
|
ChapterType = if this.ChapterSource = "external" then noneIfBlank this.ChapterType
|
||||||
|
else None
|
||||||
|
ChapterWaypoints = if this.ChapterSource = "none" then None
|
||||||
|
elif this.ContainsWaypoints then Some true
|
||||||
|
else None
|
||||||
TranscriptUrl = noneIfBlank this.TranscriptUrl
|
TranscriptUrl = noneIfBlank this.TranscriptUrl
|
||||||
TranscriptType = noneIfBlank this.TranscriptType
|
TranscriptType = noneIfBlank this.TranscriptType
|
||||||
TranscriptLang = noneIfBlank this.TranscriptLang
|
TranscriptLang = noneIfBlank this.TranscriptLang
|
||||||
|
@ -621,7 +621,7 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
ImageUrl = Some "uploads/podcast-cover.jpg"
|
ImageUrl = Some "uploads/podcast-cover.jpg"
|
||||||
Subtitle = Some "Narration"
|
Subtitle = Some "Narration"
|
||||||
Explicit = Some Clean
|
Explicit = Some Clean
|
||||||
Chapters = None // for future implementation
|
Chapters = None
|
||||||
ChapterFile = Some "uploads/1970/01/chapters.txt"
|
ChapterFile = Some "uploads/1970/01/chapters.txt"
|
||||||
ChapterType = Some "chapters"
|
ChapterType = Some "chapters"
|
||||||
ChapterWaypoints = Some true
|
ChapterWaypoints = Some true
|
||||||
@ -661,6 +661,7 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
Expect.equal model.ImageUrl "" "ImageUrl not filled properly"
|
Expect.equal model.ImageUrl "" "ImageUrl not filled properly"
|
||||||
Expect.equal model.Subtitle "" "Subtitle not filled properly"
|
Expect.equal model.Subtitle "" "Subtitle not filled properly"
|
||||||
Expect.equal model.Explicit "" "Explicit not filled properly"
|
Expect.equal model.Explicit "" "Explicit not filled properly"
|
||||||
|
Expect.equal model.ChapterSource "none" "ChapterSource not filled properly"
|
||||||
Expect.equal model.ChapterFile "" "ChapterFile not filled properly"
|
Expect.equal model.ChapterFile "" "ChapterFile not filled properly"
|
||||||
Expect.equal model.ChapterType "" "ChapterType not filled properly"
|
Expect.equal model.ChapterType "" "ChapterType not filled properly"
|
||||||
Expect.isFalse model.ContainsWaypoints "ContainsWaypoints should not have been set"
|
Expect.isFalse model.ContainsWaypoints "ContainsWaypoints should not have been set"
|
||||||
@ -673,7 +674,7 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
Expect.equal model.EpisodeNumber "" "EpisodeNumber not filled properly"
|
Expect.equal model.EpisodeNumber "" "EpisodeNumber not filled properly"
|
||||||
Expect.equal model.EpisodeDescription "" "EpisodeDescription not filled properly"
|
Expect.equal model.EpisodeDescription "" "EpisodeDescription not filled properly"
|
||||||
}
|
}
|
||||||
test "succeeds for full post" {
|
test "succeeds for full post with external chapters" {
|
||||||
let model = EditPostModel.FromPost { WebLog.Empty with TimeZone = "Etc/GMT+1" } fullPost
|
let model = EditPostModel.FromPost { WebLog.Empty with TimeZone = "Etc/GMT+1" } fullPost
|
||||||
Expect.equal model.PostId "a-post" "PostId not filled properly"
|
Expect.equal model.PostId "a-post" "PostId not filled properly"
|
||||||
Expect.equal model.Title "A Post" "Title not filled properly"
|
Expect.equal model.Title "A Post" "Title not filled properly"
|
||||||
@ -704,6 +705,7 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
Expect.equal model.ImageUrl "uploads/podcast-cover.jpg" "ImageUrl not filled properly"
|
Expect.equal model.ImageUrl "uploads/podcast-cover.jpg" "ImageUrl not filled properly"
|
||||||
Expect.equal model.Subtitle "Narration" "Subtitle not filled properly"
|
Expect.equal model.Subtitle "Narration" "Subtitle not filled properly"
|
||||||
Expect.equal model.Explicit "clean" "Explicit not filled properly"
|
Expect.equal model.Explicit "clean" "Explicit not filled properly"
|
||||||
|
Expect.equal model.ChapterSource "external" "ChapterSource not filled properly"
|
||||||
Expect.equal model.ChapterFile "uploads/1970/01/chapters.txt" "ChapterFile not filled properly"
|
Expect.equal model.ChapterFile "uploads/1970/01/chapters.txt" "ChapterFile not filled properly"
|
||||||
Expect.equal model.ChapterType "chapters" "ChapterType not filled properly"
|
Expect.equal model.ChapterType "chapters" "ChapterType not filled properly"
|
||||||
Expect.isTrue model.ContainsWaypoints "ContainsWaypoints should have been set"
|
Expect.isTrue model.ContainsWaypoints "ContainsWaypoints should have been set"
|
||||||
@ -716,6 +718,19 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
Expect.equal model.EpisodeNumber "322" "EpisodeNumber not filled properly"
|
Expect.equal model.EpisodeNumber "322" "EpisodeNumber not filled properly"
|
||||||
Expect.equal model.EpisodeDescription "Episode 322" "EpisodeDescription not filled properly"
|
Expect.equal model.EpisodeDescription "Episode 322" "EpisodeDescription not filled properly"
|
||||||
}
|
}
|
||||||
|
test "succeeds for full post with internal chapters" {
|
||||||
|
let model =
|
||||||
|
EditPostModel.FromPost
|
||||||
|
{ WebLog.Empty with TimeZone = "Etc/GMT+1" }
|
||||||
|
{ fullPost with
|
||||||
|
Episode =
|
||||||
|
Some
|
||||||
|
{ fullPost.Episode.Value with
|
||||||
|
Chapters = Some []
|
||||||
|
ChapterFile = None
|
||||||
|
ChapterType = None } }
|
||||||
|
Expect.equal model.ChapterSource "internal" "ChapterSource not filled properly"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
testList "IsNew" [
|
testList "IsNew" [
|
||||||
test "succeeds for a new post" {
|
test "succeeds for a new post" {
|
||||||
@ -747,6 +762,7 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
ImageUrl = "updated-cover.png"
|
ImageUrl = "updated-cover.png"
|
||||||
Subtitle = "Talking"
|
Subtitle = "Talking"
|
||||||
Explicit = "no"
|
Explicit = "no"
|
||||||
|
ChapterSource = "external"
|
||||||
ChapterFile = "updated-chapters.txt"
|
ChapterFile = "updated-chapters.txt"
|
||||||
ChapterType = "indexes"
|
ChapterType = "indexes"
|
||||||
TranscriptUrl = "updated-transcript.txt"
|
TranscriptUrl = "updated-transcript.txt"
|
||||||
@ -787,6 +803,7 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
Expect.equal ep.ImageUrl (Some "updated-cover.png") "ImageUrl not filled properly"
|
Expect.equal ep.ImageUrl (Some "updated-cover.png") "ImageUrl not filled properly"
|
||||||
Expect.equal ep.Subtitle (Some "Talking") "Subtitle not filled properly"
|
Expect.equal ep.Subtitle (Some "Talking") "Subtitle not filled properly"
|
||||||
Expect.equal ep.Explicit (Some No) "ExplicitRating not filled properly"
|
Expect.equal ep.Explicit (Some No) "ExplicitRating not filled properly"
|
||||||
|
Expect.isNone ep.Chapters "Chapters should have had no value"
|
||||||
Expect.equal ep.ChapterFile (Some "updated-chapters.txt") "ChapterFile not filled properly"
|
Expect.equal ep.ChapterFile (Some "updated-chapters.txt") "ChapterFile not filled properly"
|
||||||
Expect.equal ep.ChapterType (Some "indexes") "ChapterType not filled properly"
|
Expect.equal ep.ChapterType (Some "indexes") "ChapterType not filled properly"
|
||||||
Expect.equal ep.ChapterWaypoints (Some true) "ChapterWaypoints should have been set"
|
Expect.equal ep.ChapterWaypoints (Some true) "ChapterWaypoints should have been set"
|
||||||
@ -840,6 +857,32 @@ let editPostModelTests = testList "EditPostModel" [
|
|||||||
Expect.isNone ep.EpisodeNumber "EpisodeNumber not filled properly"
|
Expect.isNone ep.EpisodeNumber "EpisodeNumber not filled properly"
|
||||||
Expect.isNone ep.EpisodeDescription "EpisodeDescription not filled properly"
|
Expect.isNone ep.EpisodeDescription "EpisodeDescription not filled properly"
|
||||||
}
|
}
|
||||||
|
test "succeeds for a podcast episode with internal chapters" {
|
||||||
|
let minModel =
|
||||||
|
{ updatedModel with
|
||||||
|
ChapterSource = "internal"
|
||||||
|
ChapterFile = ""
|
||||||
|
ChapterType = "" }
|
||||||
|
let post = minModel.UpdatePost fullPost (Noda.epoch + Duration.FromDays 500)
|
||||||
|
Expect.isSome post.Episode "There should have been a podcast episode"
|
||||||
|
let ep = post.Episode.Value
|
||||||
|
Expect.equal ep.Chapters (Some []) "Chapters not filled properly"
|
||||||
|
Expect.isNone ep.ChapterFile "ChapterFile not filled properly"
|
||||||
|
Expect.isNone ep.ChapterType "ChapterType not filled properly"
|
||||||
|
}
|
||||||
|
test "succeeds for a podcast episode with no chapters" {
|
||||||
|
let minModel = { updatedModel with ChapterSource = "none" }
|
||||||
|
let post =
|
||||||
|
minModel.UpdatePost
|
||||||
|
{ fullPost with Episode = Some { fullPost.Episode.Value with Chapters = Some [] } }
|
||||||
|
(Noda.epoch + Duration.FromDays 500)
|
||||||
|
Expect.isSome post.Episode "There should have been a podcast episode"
|
||||||
|
let ep = post.Episode.Value
|
||||||
|
Expect.isNone ep.Chapters "Chapters not filled properly"
|
||||||
|
Expect.isNone ep.ChapterFile "ChapterFile not filled properly"
|
||||||
|
Expect.isNone ep.ChapterType "ChapterType not filled properly"
|
||||||
|
Expect.isNone ep.ChapterWaypoints "ChapterWaypoints not filled properly"
|
||||||
|
}
|
||||||
test "succeeds for no podcast episode and no template" {
|
test "succeeds for no podcast episode and no template" {
|
||||||
let post = { updatedModel with IsEpisode = false; Template = "" }.UpdatePost fullPost Noda.epoch
|
let post = { updatedModel with IsEpisode = false; Template = "" }.UpdatePost fullPost Noda.epoch
|
||||||
Expect.isNone post.Template "Template not filled properly"
|
Expect.isNone post.Template "Template not filled properly"
|
||||||
|
@ -13,6 +13,12 @@
|
|||||||
<a href="{{ entity_url_base | append: "/permalinks" | relative_link }}">Manage Permalinks</a>
|
<a href="{{ entity_url_base | append: "/permalinks" | relative_link }}">Manage Permalinks</a>
|
||||||
<span class=text-muted> • </span>
|
<span class=text-muted> • </span>
|
||||||
<a href="{{ entity_url_base | append: "/revisions" | relative_link }}">Manage Revisions</a>
|
<a href="{{ entity_url_base | append: "/revisions" | relative_link }}">Manage Revisions</a>
|
||||||
|
{% if model.chapter_source == "internal" %}
|
||||||
|
<span id="chapterEditLink">
|
||||||
|
<span class=text-muted> • </span>
|
||||||
|
<a href="{{ entity_url_base | append: "/chapters" | relative_link }}">Manage Chapters</a>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
{%- endunless -%}
|
{%- endunless -%}
|
||||||
</div>
|
</div>
|
||||||
|
@ -107,13 +107,43 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class=row>
|
||||||
|
<div class="col-12 col-md-8 pb-3">
|
||||||
|
<div class="form-text">Chapters</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input type=radio name=ChapterSource id=chapterSourceNone value=none
|
||||||
|
class=form-check-input{% if model.chapter_source == "none" %} checked{% endif %}
|
||||||
|
onclick="Admin.setChapterSource('none')">
|
||||||
|
<label for=chapterSourceNone>None</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input type=radio name=ChapterSource id=chapterSourceInternal value=internal
|
||||||
|
class=form-check-input{% if model.chapter_source == "internal" %} checked{% endif %}
|
||||||
|
onclick="Admin.setChapterSource('internal')">
|
||||||
|
<label for=chapterSourceInternal>Defined Here</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input type=radio name=ChapterSource id=chapterSourceExternal value=none
|
||||||
|
class=form-check-input{% if model.chapter_source == "external" %} checked{% endif %}
|
||||||
|
onclick="Admin.setChapterSource('external')">
|
||||||
|
<label for=chapterSourceExternal>Separate File</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-flex justify-content-center">
|
||||||
|
<div class="form-check form-switch align-self-center pb-3">
|
||||||
|
<input type=checkbox name=ContainsWaypoints id=containsWaypoints class=form-check-input
|
||||||
|
value=true{% if model.contains_waypoints %} checked{% endif %}>
|
||||||
|
<label for=containsWaypoints>Chapters contain waypoints</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class=row>
|
<div class=row>
|
||||||
<div class="col-12 col-md-8 pb-3">
|
<div class="col-12 col-md-8 pb-3">
|
||||||
<div class=form-floating>
|
<div class=form-floating>
|
||||||
<input type=text name=ChapterFile id=chapterFile class=form-control placeholder="Chapter File"
|
<input type=text name=ChapterFile id=chapterFile class=form-control placeholder="Chapter File"
|
||||||
value="{{ model.chapter_file }}">
|
value="{{ model.chapter_file }}">
|
||||||
<label for=chapterFile>Chapter File</label>
|
<label for=chapterFile>Chapter File</label>
|
||||||
<div class=form-text>Optional; relative URL served from this web log</div>
|
<div class=form-text>Relative URL served from this web log</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4 pb-3">
|
<div class="col-12 col-md-4 pb-3">
|
||||||
|
@ -212,15 +212,37 @@ this.Admin = {
|
|||||||
this.nextPermalink++
|
this.nextPermalink++
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the chapter type for a podcast episode
|
||||||
|
* @param {"none"|"internal"|"external"} src The source for chapters for this episode
|
||||||
|
*/
|
||||||
|
setChapterSource(src) {
|
||||||
|
document.getElementById("containsWaypoints").disabled = src === "none"
|
||||||
|
const isDisabled = src === "none" || src === "internal"
|
||||||
|
const chapterFile = document.getElementById("chapterFile")
|
||||||
|
chapterFile.disabled = isDisabled
|
||||||
|
chapterFile.required = !isDisabled
|
||||||
|
document.getElementById("chapterType").disabled = isDisabled
|
||||||
|
const link = document.getElementById("chapterEditLink")
|
||||||
|
if (link) link.style.display = src === "none" || src === "external" ? "none" : ""
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable or disable podcast fields
|
* Enable or disable podcast fields
|
||||||
*/
|
*/
|
||||||
toggleEpisodeFields() {
|
toggleEpisodeFields() {
|
||||||
const disabled = !document.getElementById("isEpisode").checked
|
const disabled = !document.getElementById("isEpisode").checked
|
||||||
;[ "media", "mediaType", "length", "duration", "subtitle", "imageUrl", "explicit", "chapterFile", "chapterType",
|
let fields = [
|
||||||
"transcriptUrl", "transcriptType", "transcriptLang", "transcriptCaptions", "seasonNumber", "seasonDescription",
|
"media", "mediaType", "length", "duration", "subtitle", "imageUrl", "explicit", "transcriptUrl", "transcriptType",
|
||||||
"episodeNumber", "episodeDescription"
|
"transcriptLang", "transcriptCaptions", "seasonNumber", "seasonDescription", "episodeNumber", "episodeDescription"
|
||||||
].forEach(it => document.getElementById(it).disabled = disabled)
|
]
|
||||||
|
if (disabled) {
|
||||||
|
fields.push("chapterFile", "chapterType", "containsWaypoints")
|
||||||
|
} else {
|
||||||
|
const src = [...document.getElementsByName("ChapterSource")].filter(it => it.checked)[0].value
|
||||||
|
this.setChapterSource(src)
|
||||||
|
}
|
||||||
|
fields.forEach(it => document.getElementById(it).disabled = disabled)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user