Version 2.1 #41
|
@ -648,6 +648,9 @@ type EditPostModel = {
|
|||
/// The explicit rating for this episode (optional, defaults to podcast setting)
|
||||
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)
|
||||
ChapterFile: string
|
||||
|
||||
|
@ -713,6 +716,9 @@ type EditPostModel = {
|
|||
ImageUrl = defaultArg episode.ImageUrl ""
|
||||
Subtitle = defaultArg episode.Subtitle ""
|
||||
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 ""
|
||||
ChapterType = defaultArg episode.ChapterType ""
|
||||
ContainsWaypoints = defaultArg episode.ChapterWaypoints false
|
||||
|
@ -773,10 +779,19 @@ type EditPostModel = {
|
|||
ImageUrl = noneIfBlank this.ImageUrl
|
||||
Subtitle = noneIfBlank this.Subtitle
|
||||
Explicit = noneIfBlank this.Explicit |> Option.map ExplicitRating.Parse
|
||||
Chapters = match post.Episode with Some e -> e.Chapters | None -> None
|
||||
ChapterFile = noneIfBlank this.ChapterFile
|
||||
ChapterType = noneIfBlank this.ChapterType
|
||||
ChapterWaypoints = if this.ContainsWaypoints then Some true else None
|
||||
Chapters = if this.ChapterSource = "internal" then
|
||||
match post.Episode with
|
||||
| Some e when Option.isSome e.Chapters -> e.Chapters
|
||||
| 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
|
||||
TranscriptType = noneIfBlank this.TranscriptType
|
||||
TranscriptLang = noneIfBlank this.TranscriptLang
|
||||
|
|
|
@ -621,7 +621,7 @@ let editPostModelTests = testList "EditPostModel" [
|
|||
ImageUrl = Some "uploads/podcast-cover.jpg"
|
||||
Subtitle = Some "Narration"
|
||||
Explicit = Some Clean
|
||||
Chapters = None // for future implementation
|
||||
Chapters = None
|
||||
ChapterFile = Some "uploads/1970/01/chapters.txt"
|
||||
ChapterType = Some "chapters"
|
||||
ChapterWaypoints = Some true
|
||||
|
@ -661,6 +661,7 @@ let editPostModelTests = testList "EditPostModel" [
|
|||
Expect.equal model.ImageUrl "" "ImageUrl not filled properly"
|
||||
Expect.equal model.Subtitle "" "Subtitle 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.ChapterType "" "ChapterType not filled properly"
|
||||
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.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
|
||||
Expect.equal model.PostId "a-post" "PostId 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.Subtitle "Narration" "Subtitle 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.ChapterType "chapters" "ChapterType not filled properly"
|
||||
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.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" [
|
||||
test "succeeds for a new post" {
|
||||
|
@ -747,6 +762,7 @@ let editPostModelTests = testList "EditPostModel" [
|
|||
ImageUrl = "updated-cover.png"
|
||||
Subtitle = "Talking"
|
||||
Explicit = "no"
|
||||
ChapterSource = "external"
|
||||
ChapterFile = "updated-chapters.txt"
|
||||
ChapterType = "indexes"
|
||||
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.Subtitle (Some "Talking") "Subtitle 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.ChapterType (Some "indexes") "ChapterType not filled properly"
|
||||
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.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" {
|
||||
let post = { updatedModel with IsEpisode = false; Template = "" }.UpdatePost fullPost Noda.epoch
|
||||
Expect.isNone post.Template "Template not filled properly"
|
||||
|
|
|
@ -13,6 +13,12 @@
|
|||
<a href="{{ entity_url_base | append: "/permalinks" | relative_link }}">Manage Permalinks</a>
|
||||
<span class=text-muted> • </span>
|
||||
<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>
|
||||
{%- endunless -%}
|
||||
</div>
|
||||
|
|
|
@ -107,13 +107,43 @@
|
|||
</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="col-12 col-md-8 pb-3">
|
||||
<div class=form-floating>
|
||||
<input type=text name=ChapterFile id=chapterFile class=form-control placeholder="Chapter File"
|
||||
value="{{ model.chapter_file }}">
|
||||
<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 class="col-12 col-md-4 pb-3">
|
||||
|
|
|
@ -212,15 +212,37 @@ this.Admin = {
|
|||
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
|
||||
*/
|
||||
toggleEpisodeFields() {
|
||||
const disabled = !document.getElementById("isEpisode").checked
|
||||
;[ "media", "mediaType", "length", "duration", "subtitle", "imageUrl", "explicit", "chapterFile", "chapterType",
|
||||
"transcriptUrl", "transcriptType", "transcriptLang", "transcriptCaptions", "seasonNumber", "seasonDescription",
|
||||
"episodeNumber", "episodeDescription"
|
||||
].forEach(it => document.getElementById(it).disabled = disabled)
|
||||
let fields = [
|
||||
"media", "mediaType", "length", "duration", "subtitle", "imageUrl", "explicit", "transcriptUrl", "transcriptType",
|
||||
"transcriptLang", "transcriptCaptions", "seasonNumber", "seasonDescription", "episodeNumber", "episodeDescription"
|
||||
]
|
||||
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