Add episode fields to UI (#9)
- Add chapter fields (#5) - Add transcript fields (#8)
This commit is contained in:
parent
9fbb1bb14d
commit
80c65bcad6
|
@ -141,7 +141,7 @@ type SQLitePostData (conn : SqliteConnection) =
|
||||||
let updatePostEpisode (post : Post) = backgroundTask {
|
let updatePostEpisode (post : Post) = backgroundTask {
|
||||||
use cmd = conn.CreateCommand ()
|
use cmd = conn.CreateCommand ()
|
||||||
cmd.CommandText <- "SELECT COUNT(post_id) FROM post_episode WHERE post_id = @postId"
|
cmd.CommandText <- "SELECT COUNT(post_id) FROM post_episode WHERE post_id = @postId"
|
||||||
cmd.Parameters.AddWithValue ("@postId", post.id) |> ignore
|
cmd.Parameters.AddWithValue ("@postId", PostId.toString post.id) |> ignore
|
||||||
let! count = count cmd
|
let! count = count cmd
|
||||||
if count = 1 then
|
if count = 1 then
|
||||||
match post.episode with
|
match post.episode with
|
||||||
|
@ -176,13 +176,13 @@ type SQLitePostData (conn : SqliteConnection) =
|
||||||
| Some ep ->
|
| Some ep ->
|
||||||
cmd.CommandText <-
|
cmd.CommandText <-
|
||||||
"""INSERT INTO post_episode (
|
"""INSERT INTO post_episode (
|
||||||
post_id, media, length, duration, media_type, image_url, subtitle, explicit, chapter_file,
|
post_id, media, length, duration, media_type, image_url, subtitle, explicit,
|
||||||
chapter_type, transcript_url, transcript_type, transcript_lang, transcript_captions,
|
chapter_file, chapter_type, transcript_url, transcript_type, transcript_lang,
|
||||||
season_number, season_description, episode_number, episode_description
|
transcript_captions, season_number, season_description, episode_number, episode_description
|
||||||
) VALUES (
|
) VALUES (
|
||||||
@media, @length, @duration, @mediaType, @imageUrl, @subtitle, @explicit, @chapterFile,
|
@postId, @media, @length, @duration, @mediaType, @imageUrl, @subtitle, @explicit,
|
||||||
@chapterType, @transcriptUrl, @transcriptType, @transcriptLang, @transcriptCaptions,
|
@chapterFile, @chapterType, @transcriptUrl, @transcriptType, @transcriptLang,
|
||||||
@seasonNumber, @seasonDescription, @episodeNumber, @episodeDescription
|
@transcriptCaptions, @seasonNumber, @seasonDescription, @episodeNumber, @episodeDescription
|
||||||
)"""
|
)"""
|
||||||
addEpisodeParameters cmd ep
|
addEpisodeParameters cmd ep
|
||||||
do! write cmd
|
do! write cmd
|
||||||
|
@ -530,7 +530,7 @@ type SQLitePostData (conn : SqliteConnection) =
|
||||||
use cmd = conn.CreateCommand ()
|
use cmd = conn.CreateCommand ()
|
||||||
cmd.CommandText <-
|
cmd.CommandText <-
|
||||||
"""UPDATE post
|
"""UPDATE post
|
||||||
SET author_id = @author_id,
|
SET author_id = @authorId,
|
||||||
status = @status,
|
status = @status,
|
||||||
title = @title,
|
title = @title,
|
||||||
permalink = @permalink,
|
permalink = @permalink,
|
||||||
|
|
|
@ -9,7 +9,7 @@ module private Helpers =
|
||||||
|
|
||||||
/// Create a string option if a string is blank
|
/// Create a string option if a string is blank
|
||||||
let noneIfBlank (it : string) =
|
let noneIfBlank (it : string) =
|
||||||
match it.Trim () with "" -> None | trimmed -> Some trimmed
|
match (defaultArg (Option.ofObj it) "").Trim () with "" -> None | trimmed -> Some trimmed
|
||||||
|
|
||||||
|
|
||||||
/// Details about a category, used to display category lists
|
/// Details about a category, used to display category lists
|
||||||
|
@ -448,10 +448,10 @@ type EditPostModel =
|
||||||
transcriptLang : string
|
transcriptLang : string
|
||||||
|
|
||||||
/// Whether the provided transcript should be presented as captions
|
/// Whether the provided transcript should be presented as captions
|
||||||
transcriptCaptions : bool option
|
transcriptCaptions : bool
|
||||||
|
|
||||||
/// The season number (optional)
|
/// The season number (optional)
|
||||||
seasonNumber : int option
|
seasonNumber : int
|
||||||
|
|
||||||
/// A description of this season (optional, ignored if season number is not provided)
|
/// A description of this season (optional, ignored if season number is not provided)
|
||||||
seasonDescription : string
|
seasonDescription : string
|
||||||
|
@ -489,7 +489,7 @@ type EditPostModel =
|
||||||
isEpisode = Option.isSome post.episode
|
isEpisode = Option.isSome post.episode
|
||||||
media = episode.media
|
media = episode.media
|
||||||
length = episode.length
|
length = episode.length
|
||||||
duration = defaultArg (episode.duration |> Option.map (fun it -> it.ToString "HH:mm:SS")) ""
|
duration = defaultArg (episode.duration |> Option.map (fun it -> it.ToString """hh\:mm\:ss""")) ""
|
||||||
mediaType = defaultArg episode.mediaType ""
|
mediaType = defaultArg episode.mediaType ""
|
||||||
imageUrl = defaultArg episode.imageUrl ""
|
imageUrl = defaultArg episode.imageUrl ""
|
||||||
subtitle = defaultArg episode.subtitle ""
|
subtitle = defaultArg episode.subtitle ""
|
||||||
|
@ -499,8 +499,8 @@ type EditPostModel =
|
||||||
transcriptUrl = defaultArg episode.transcriptUrl ""
|
transcriptUrl = defaultArg episode.transcriptUrl ""
|
||||||
transcriptType = defaultArg episode.transcriptType ""
|
transcriptType = defaultArg episode.transcriptType ""
|
||||||
transcriptLang = defaultArg episode.transcriptLang ""
|
transcriptLang = defaultArg episode.transcriptLang ""
|
||||||
transcriptCaptions = episode.transcriptCaptions
|
transcriptCaptions = defaultArg episode.transcriptCaptions false
|
||||||
seasonNumber = episode.seasonNumber
|
seasonNumber = defaultArg episode.seasonNumber 0
|
||||||
seasonDescription = defaultArg episode.seasonDescription ""
|
seasonDescription = defaultArg episode.seasonDescription ""
|
||||||
episodeNumber = defaultArg (episode.episodeNumber |> Option.map string) ""
|
episodeNumber = defaultArg (episode.episodeNumber |> Option.map string) ""
|
||||||
episodeDescription = defaultArg episode.episodeDescription ""
|
episodeDescription = defaultArg episode.episodeDescription ""
|
||||||
|
@ -536,27 +536,24 @@ type EditPostModel =
|
||||||
Some {
|
Some {
|
||||||
media = this.media
|
media = this.media
|
||||||
length = this.length
|
length = this.length
|
||||||
duration = match this.duration.Trim () with
|
duration = noneIfBlank this.duration |> Option.map TimeSpan.Parse
|
||||||
| "" -> None
|
mediaType = noneIfBlank this.mediaType
|
||||||
| dur -> Some (TimeSpan.Parse dur)
|
imageUrl = noneIfBlank this.imageUrl
|
||||||
mediaType = match this.mediaType.Trim () with "" -> None | mt -> Some mt
|
subtitle = noneIfBlank this.subtitle
|
||||||
imageUrl = match this.imageUrl.Trim () with "" -> None | iu -> Some iu
|
explicit = noneIfBlank this.explicit |> Option.map ExplicitRating.parse
|
||||||
subtitle = match this.subtitle.Trim () with "" -> None | sub -> Some sub
|
chapterFile = noneIfBlank this.chapterFile
|
||||||
explicit = match this.explicit.Trim () with
|
chapterType = noneIfBlank this.chapterType
|
||||||
| "" -> None
|
transcriptUrl = noneIfBlank this.transcriptUrl
|
||||||
| exp -> Some (ExplicitRating.parse exp)
|
transcriptType = noneIfBlank this.transcriptType
|
||||||
chapterFile = match this.chapterFile.Trim () with "" -> None | cf -> Some cf
|
transcriptLang = noneIfBlank this.transcriptLang
|
||||||
chapterType = match this.chapterType.Trim () with "" -> None | ct -> Some ct
|
transcriptCaptions = if this.transcriptCaptions then Some true else None
|
||||||
transcriptUrl = match this.transcriptUrl.Trim () with "" -> None | tu -> Some tu
|
seasonNumber = if this.seasonNumber = 0 then None else Some this.seasonNumber
|
||||||
transcriptType = match this.transcriptType.Trim () with "" -> None | tt -> Some tt
|
seasonDescription = noneIfBlank this.seasonDescription
|
||||||
transcriptLang = match this.transcriptLang.Trim () with "" -> None | tl -> Some tl
|
episodeNumber = match noneIfBlank this.episodeNumber |> Option.map Double.Parse with
|
||||||
transcriptCaptions = this.transcriptCaptions
|
| Some it when it = 0.0 -> None
|
||||||
seasonNumber = this.seasonNumber
|
| Some it -> Some (double it)
|
||||||
seasonDescription = match this.seasonDescription.Trim () with "" -> None | sd -> Some sd
|
| None -> None
|
||||||
episodeNumber = match this.episodeNumber.Trim () with
|
episodeDescription = noneIfBlank this.episodeDescription
|
||||||
| "" -> None
|
|
||||||
| en -> Some (Double.Parse en)
|
|
||||||
episodeDescription = match this.episodeDescription.Trim () with "" -> None | ed -> Some ed
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
None
|
None
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
module MyWebLog.Handlers.Post
|
module MyWebLog.Handlers.Post
|
||||||
|
|
||||||
open System
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
open MyWebLog
|
open MyWebLog
|
||||||
|
|
||||||
/// Parse a slug and page number from an "everything else" URL
|
/// Parse a slug and page number from an "everything else" URL
|
||||||
|
@ -237,13 +238,19 @@ let edit postId : HttpHandler = fun next ctx -> task {
|
||||||
let model = EditPostModel.fromPost webLog post
|
let model = EditPostModel.fromPost webLog post
|
||||||
return!
|
return!
|
||||||
Hash.FromAnonymousObject {|
|
Hash.FromAnonymousObject {|
|
||||||
csrf = csrfToken ctx
|
csrf = csrfToken ctx
|
||||||
model = model
|
model = model
|
||||||
metadata = Array.zip model.metaNames model.metaValues
|
metadata = Array.zip model.metaNames model.metaValues
|
||||||
|> Array.mapi (fun idx (name, value) -> [| string idx; name; value |])
|
|> Array.mapi (fun idx (name, value) -> [| string idx; name; value |])
|
||||||
page_title = title
|
page_title = title
|
||||||
templates = templates
|
templates = templates
|
||||||
categories = cats
|
categories = cats
|
||||||
|
explicit_values = [|
|
||||||
|
KeyValuePair.Create ("", "– Default –")
|
||||||
|
KeyValuePair.Create (ExplicitRating.toString Yes, "Yes")
|
||||||
|
KeyValuePair.Create (ExplicitRating.toString No, "No")
|
||||||
|
KeyValuePair.Create (ExplicitRating.toString Clean, "Clean")
|
||||||
|
|]
|
||||||
|}
|
|}
|
||||||
|> viewForTheme "admin" "post-edit" next ctx
|
|> viewForTheme "admin" "post-edit" next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
| None -> return! Error.notFound next ctx
|
||||||
|
|
|
@ -22,12 +22,14 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<label for="text">Text</label>
|
<label for="text">Text</label>
|
||||||
<input type="radio" name="source" id="source_html" class="btn-check" value="HTML"
|
<div class="btn-group btn-group-sm" role="group" aria-label="Text format button group">
|
||||||
{%- if model.source == "HTML" %} checked="checked"{% endif %}>
|
<input type="radio" name="source" id="source_html" class="btn-check" value="HTML"
|
||||||
<label class="btn btn-sm btn-outline-secondary" for="source_html">HTML</label>
|
{%- if model.source == "HTML" %} checked="checked"{% endif %}>
|
||||||
<input type="radio" name="source" id="source_md" class="btn-check" value="Markdown"
|
<label class="btn btn-sm btn-outline-secondary" for="source_html">HTML</label>
|
||||||
{%- if model.source == "Markdown" %} checked="checked"{% endif %}>
|
<input type="radio" name="source" id="source_md" class="btn-check" value="Markdown"
|
||||||
<label class="btn btn-sm btn-outline-secondary" for="source_md">Markdown</label>
|
{%- if model.source == "Markdown" %} checked="checked"{% endif %}>
|
||||||
|
<label class="btn btn-sm btn-outline-secondary" for="source_md">Markdown</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pb-3">
|
<div class="pb-3">
|
||||||
<textarea name="text" id="text" class="form-control" rows="20">{{ model.text }}</textarea>
|
<textarea name="text" id="text" class="form-control" rows="20">{{ model.text }}</textarea>
|
||||||
|
@ -39,13 +41,194 @@
|
||||||
<div class="form-text">comma-delimited</div>
|
<div class="form-text">comma-delimited</div>
|
||||||
</div>
|
</div>
|
||||||
{% if model.status == "Draft" %}
|
{% if model.status == "Draft" %}
|
||||||
<div class="form-check pb-2">
|
<div class="form-check form-switch pb-2">
|
||||||
<input type="checkbox" name="doPublish" id="doPublish" class="form-check-input" value="true">
|
<input type="checkbox" name="doPublish" id="doPublish" class="form-check-input" value="true">
|
||||||
<label for="doPublish" class="form-check-label">Publish This Post</label>
|
<label for="doPublish" class="form-check-label">Publish This Post</label>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button type="submit" class="btn btn-primary pb-2">Save Changes</button>
|
<button type="submit" class="btn btn-primary pb-2">Save Changes</button>
|
||||||
<hr class="mb-3">
|
<hr class="mb-3">
|
||||||
|
<fieldset class="mb-3">
|
||||||
|
<legend>
|
||||||
|
<span class="form-check form-switch">
|
||||||
|
<small>
|
||||||
|
<input type="checkbox" name="isEpisode" id="isEpisode" class="form-check-input" value="true"
|
||||||
|
data-bs-toggle="collapse" data-bs-target="#episodeItems" onclick="Admin.toggleEpisodeFields()"
|
||||||
|
{%- if model.is_episode %}checked="checked"{% endif %}>
|
||||||
|
</small>
|
||||||
|
<label for="isEpisode">Podcast Episode</label>
|
||||||
|
</span>
|
||||||
|
</legend>
|
||||||
|
<div id="episodeItems" class="container p-0 collapse{% if model.is_episode %} show{% endif %}">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-8 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="media" id="media" class="form-control" placeholder="Media" required
|
||||||
|
value="{{ model.media }}">
|
||||||
|
<label for="media">Media File</label>
|
||||||
|
<div class="form-text">Path to media; relative URL will be appended to base media path</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-4 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="mediaType" id="mediaType" class="form-control" placeholder="Media Type"
|
||||||
|
value="{{ model.media_type }}">
|
||||||
|
<label for="mediaType">Media MIME Type</label>
|
||||||
|
<div class="form-text">Optional; overrides podcast default</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row pb-3">
|
||||||
|
<div class="col">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="number" name="length" id="length" class="form-control" placeholder="Length" required
|
||||||
|
value="{{ model.length }}">
|
||||||
|
<label for="length">Media Length (bytes)</label>
|
||||||
|
<div class="form-text">TODO: derive from above file name</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="duration" id="duration" class="form-control" placeholder="Duration"
|
||||||
|
value="{{ model.duration }}">
|
||||||
|
<label for="duration">Duration</label>
|
||||||
|
<div class="form-text">Recommended; enter in <code>HH:MM:SS</code> format</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row pb-3">
|
||||||
|
<div class="col">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="subtitle" id="subtitle" class="form-control" placeholder="Subtitle"
|
||||||
|
value="{{ model.subtitle }}">
|
||||||
|
<label for="subtitle">Subtitle</label>
|
||||||
|
<div class="form-text">Optional; a subtitle for this episode</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-8 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="imageUrl" id="imageUrl" class="form-control" placeholder="Image URL"
|
||||||
|
value="{{ model.image_url }}">
|
||||||
|
<label for="imageUrl">Image URL</label>
|
||||||
|
<div class="form-text">
|
||||||
|
Optional; overrides podcast default; relative URL served from this web log
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-4 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<select name="explicit" id="explicit" class="form-control">
|
||||||
|
{% for exp_value in explicit_values %}
|
||||||
|
<option value="{{ exp_value[0] }}"
|
||||||
|
{%- if model.explicit == exp_value[0] %} selected="selected"{% endif -%}>
|
||||||
|
{{ exp_value[1] }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<label for="explicit">Explicit Rating</label>
|
||||||
|
<div class="form-text">Optional; overrides podcast default</div>
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-4 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="chapterType" id="chapterType" class="form-control"
|
||||||
|
placeholder="Chapter Type" value="{{ model.chapter_type }}">
|
||||||
|
<label for="chapterType">Chapter MIME Type</label>
|
||||||
|
<div class="form-text">
|
||||||
|
Optional; <code>application/json+chapters</code> assumed if chapter file ends with
|
||||||
|
<code>.json</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-8 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="transcriptUrl" id="transcriptUrl" class="form-control"
|
||||||
|
placeholder="Transcript URL" value="{{ model.transcript_url }}">
|
||||||
|
<label for="transcriptUrl">Transcript URL</label>
|
||||||
|
<div class="form-text">Optional; relative URL served from this web log</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-4 pb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="transcriptType" id="transcriptType" class="form-control"
|
||||||
|
placeholder="Transcript Type" value="{{ model.transcript_type }}">
|
||||||
|
<label for="transcriptType">Transcript MIME Type</label>
|
||||||
|
<div class="form-text">Recommended if transcript file provided</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row pb-3">
|
||||||
|
<div class="col">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="transcriptLang" id="transcriptLang" class="form-control"
|
||||||
|
placeholder="Transcript Language" value="{{ model.transcript_lang }}">
|
||||||
|
<label for="transcriptLang">Transcript Language</label>
|
||||||
|
<div class="form-text">Optional; overrides podcast default</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col d-flex justify-content-center">
|
||||||
|
<div class="form-check form-switch align-self-center pb-3">
|
||||||
|
<input type="checkbox" name="transcriptCaptions" id="transcriptCaptions" class="form-check-input"
|
||||||
|
value="true" {% if model.transcript_captions %} checked="checked"{% endif %}>
|
||||||
|
<label for="transcriptCaptions">This is a captions file</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row pb-3">
|
||||||
|
<div class="col col-md-4">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="number" name="seasonNumber" id="seasonNumber" class="form-control"
|
||||||
|
placeholder="Season Number" value="{{ model.season_number }}">
|
||||||
|
<label for="seasonNumber">Season Number</label>
|
||||||
|
<div class="form-text">Optional</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col col-md-8">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="seasonDescription" id="seasonDescription" class="form-control"
|
||||||
|
placeholder="Season Description" maxlength="128" value="{{ model.season_description }}">
|
||||||
|
<label for="seasonDescription">Season Description</label>
|
||||||
|
<div class="form-text">Optional</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row pb-3">
|
||||||
|
<div class="col col-md-4">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="number" name="episodeNumber" id="episodeNumber" class="form-control" step="0.01"
|
||||||
|
placeholder="Episode Number" value="{{ model.episode_number }}">
|
||||||
|
<label for="episodeNumber">Episode Number</label>
|
||||||
|
<div class="form-text">Optional; up to 2 decimal points</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col col-md-8">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="episodeDescription" id="episodeDescription" class="form-control"
|
||||||
|
placeholder="Episode Description" maxlength="128" value="{{ model.episode_description }}">
|
||||||
|
<label for="episodeDescription">Episode Description</label>
|
||||||
|
<div class="form-text">Optional</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", () => Admin.toggleEpisodeFields())
|
||||||
|
</script>
|
||||||
|
</fieldset>
|
||||||
<fieldset class="pb-3">
|
<fieldset class="pb-3">
|
||||||
<legend>
|
<legend>
|
||||||
Metadata
|
Metadata
|
||||||
|
@ -55,11 +238,6 @@
|
||||||
</button>
|
</button>
|
||||||
</legend>
|
</legend>
|
||||||
<div id="metaItemContainer" class="collapse">
|
<div id="metaItemContainer" class="collapse">
|
||||||
{%- unless model.meta_names contains "episode_media_file" %}
|
|
||||||
<button class="btn btn-sm btn-secondary" onclick="Admin.addPodcastFields()" id="addPodcastFieldButton">
|
|
||||||
Add Podcast Episode Fields
|
|
||||||
</button>
|
|
||||||
{%- endunless %}
|
|
||||||
<div id="metaItems" class="container">
|
<div id="metaItems" class="container">
|
||||||
{%- for meta in metadata %}
|
{%- for meta in metadata %}
|
||||||
<div id="meta_{{ meta[0] }}" class="row mb-3">
|
<div id="meta_{{ meta[0] }}" class="row mb-3">
|
||||||
|
|
|
@ -204,30 +204,14 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add podcast episode metadata fields
|
* Enable or disable podcast fields
|
||||||
*/
|
*/
|
||||||
addPodcastFields() {
|
toggleEpisodeFields() {
|
||||||
[ [ "episode_media_file", true, "required; relative URL will be appended to configured base media path" ],
|
const disabled = !document.getElementById("isEpisode").checked
|
||||||
[ "episode_media_length", true, "required; file size in bytes" ],
|
;[ "media", "mediaType", "length", "duration", "subtitle", "imageUrl", "explicit", "chapterFile", "chapterType",
|
||||||
[ "episode_duration", false, "suggested; format is HH:MM:SS" ],
|
"transcriptUrl", "transcriptType", "transcriptLang", "transcriptCaptions", "seasonNumber", "seasonDescription",
|
||||||
[ "episode_media_type", false, "optional; blank uses podcast default" ],
|
"episodeNumber", "episodeDescription"
|
||||||
[ "episode_image", false, "optional; relative URLs are served from this web log" ],
|
].forEach(it => document.getElementById(it).disabled = disabled)
|
||||||
[ "episode_subtitle", false, "optional" ],
|
|
||||||
[ "episode_explicit", false, "optional; blank uses podcast default"]
|
|
||||||
].forEach(([fieldName, isRequired, hintText]) => {
|
|
||||||
const nameField = this.createMetaNameField()
|
|
||||||
nameField.value = fieldName
|
|
||||||
nameField.readOnly = true
|
|
||||||
|
|
||||||
const valueField = this.createMetaValueField()
|
|
||||||
valueField.required = isRequired
|
|
||||||
|
|
||||||
this.createMetaRow(
|
|
||||||
this.createMetaRemoveColumn(),
|
|
||||||
this.createMetaNameColumn(nameField),
|
|
||||||
this.createMetaValueColumn(valueField, hintText))
|
|
||||||
})
|
|
||||||
document.getElementById("addPodcastFieldButton").remove()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue
Block a user