This commit is contained in:
Nick 2025-06-30 22:00:07 +01:00
parent 7d23a8e337
commit 74bdb093c9
170 changed files with 334 additions and 108 deletions

View File

@ -40,6 +40,18 @@
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\css\bootstrap.rtl.css.map" /> <Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\css\bootstrap.rtl.css.map" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\css\bootstrap.rtl.min.css" /> <Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\css\bootstrap.rtl.min.css" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\css\bootstrap.rtl.min.css.map" /> <Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\css\bootstrap.rtl.min.css.map" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.bundle.js" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.bundle.js.map" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.bundle.min.js" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.bundle.min.js.map" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.esm.js" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.esm.js.map" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.esm.min.js" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.esm.min.js.map" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.js" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.js.map" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.min.js" />
<Content Remove="C:\Users\Nick\.nuget\packages\bootstrap\5.3.3\contentFiles\any\any\wwwroot\js\bootstrap.min.js.map" />
<Content Update="web.config"> <Content Update="web.config">
<CopyToPublishDirectory>Never</CopyToPublishDirectory> <CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content> </Content>
@ -141,7 +153,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="bootstrap" Version="5.3.3" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" /> <PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
<PackageReference Include="Microsoft.Web.Administration" Version="11.1.0" /> <PackageReference Include="Microsoft.Web.Administration" Version="11.1.0" />

View File

@ -67,7 +67,7 @@ namespace CatherineLynwood.Controllers
} }
// Paginate the results based on ResultsPerPage // Paginate the results based on ResultsPerPage
int resultsPerPage = blogFilter.ResultsPerPage > 0 ? blogFilter.ResultsPerPage : 10; // Default to 10 if not specified int resultsPerPage = blogFilter.ResultsPerPage > 0 ? blogFilter.ResultsPerPage : 6; // Default to 6 if not specified
// Calculate the items for the current page // Calculate the items for the current page
blogIndex.Blogs = blogIndex.Blogs blogIndex.Blogs = blogIndex.Blogs

View File

@ -22,6 +22,16 @@
public string ContentTop { get; set; } public string ContentTop { get; set; }
public string DefaultWebpImage
{
get
{
string fileName = Path.GetFileNameWithoutExtension(ImageUrl);
return $"{fileName}-600.webp";
}
}
public string ImageAlt { get; set; } public string ImageAlt { get; set; }
public string ImageDescription { get; set; } public string ImageDescription { get; set; }
@ -34,6 +44,10 @@
public int Likes { get; set; } public int Likes { get; set; }
public string PostedBy { get; set; }
public string PostedImage { get; set; }
public DateTime PublishDate { get; set; } public DateTime PublishDate { get; set; }
public bool ShowThanks { get; set; } public bool ShowThanks { get; set; }
@ -44,18 +58,7 @@
public string Title { get; set; } public string Title { get; set; }
public string DefaultWebpImage public string VideoUrl { get; set; }
{
get
{
string fileName = Path.GetFileNameWithoutExtension(ImageUrl);
return $"{fileName}-600.webp";
}
}
#endregion Public Properties #endregion Public Properties
} }

View File

@ -4,7 +4,7 @@
{ {
public int SortDirection { get; set; } = 1; public int SortDirection { get; set; } = 1;
public int ResultsPerPage { get; set; } = 10; public int ResultsPerPage { get; set; } = 6;
public int PageNumber { get; set; } = 1; public int PageNumber { get; set; } = 1;

View File

@ -198,6 +198,9 @@ namespace CatherineLynwood.Services
blog.SubTitle = GetDataString(rdr, "SubTitle"); blog.SubTitle = GetDataString(rdr, "SubTitle");
blog.Template = GetDataString(rdr, "Template"); blog.Template = GetDataString(rdr, "Template");
blog.Title = GetDataString(rdr, "Title"); blog.Title = GetDataString(rdr, "Title");
blog.VideoUrl = GetDataString(rdr, "VideoUrl");
blog.PostedBy = GetDataString(rdr, "PostedBy");
blog.PostedImage = GetDataString(rdr, "PostedImage");
} }
await rdr.NextResultAsync(); await rdr.NextResultAsync();

View File

@ -20,7 +20,7 @@ namespace CatherineLynwood.TagHelpers
var pinterestUrl = $"https://pinterest.com/pin/create/button/?url={encodedUrl}&description={encodedTitle}"; var pinterestUrl = $"https://pinterest.com/pin/create/button/?url={encodedUrl}&description={encodedTitle}";
var shareHtml = $@" var shareHtml = $@"
<div class='share-section'> <div>
<p>Enjoyed this page? Share it:</p> <p>Enjoyed this page? Share it:</p>
<a href='{facebookUrl}' target='_blank' rel='noopener' class='share-button facebook' aria-label='Facebook share'><i class='fab fa-facebook-square'></i></a> <a href='{facebookUrl}' target='_blank' rel='noopener' class='share-button facebook' aria-label='Facebook share'><i class='fab fa-facebook-square'></i></a>
<a href='{twitterUrl}' target='_blank' rel='noopener' class='share-button twitter' aria-label='X share'><i class='fab fa-twitter'></i></a> <a href='{twitterUrl}' target='_blank' rel='noopener' class='share-button twitter' aria-label='X share'><i class='fab fa-twitter'></i></a>
@ -29,11 +29,14 @@ namespace CatherineLynwood.TagHelpers
</div>"; </div>";
var followHtml = @" var followHtml = @"
<div class='follow-section mt-3'> <div class='mt-3'>
<small>Want to follow Catherine Lynwood?</small><br/> <small>Want to follow Catherine Lynwood?</small><br/>
<a href='https://twitter.com/@cathlynwood' target='_blank' rel='noopener' class='follow-icon' aria-label='Follow on X'><i class='fab fa-twitter'></i></a> <div class='p-2'>
<a href='https://www.linkedin.com/in/catherine-lynwood-73978433a' target='_blank' rel='noopener' class='follow-icon' aria-label='Follow on LinkedIn'><i class='fab fa-linkedin'></i></a> <a href='https://www.facebook.com/CatherineLynwoodAuthor' target='_blank' rel='noopener' class='follow-icon' aria-label='Follow on Facebook'><i class='fab fa-facebook'></i></a>
<a href='https://www.tiktok.com/@catherinelynwood' target='_blank' rel='noopener' class='follow-icon' aria-label='Follow on TikTok'><i class='fab fa-tiktok'></i></a> <a href='https://twitter.com/@cathlynwood' target='_blank' rel='noopener' class='follow-icon' aria-label='Follow on X'><i class='fab fa-twitter'></i></a>
<a href='https://www.linkedin.com/in/catherine-lynwood-73978433a' target='_blank' rel='noopener' class='follow-icon' aria-label='Follow on LinkedIn'><i class='fab fa-linkedin'></i></a>
<a href='https://www.tiktok.com/@catherinelynwood' target='_blank' rel='noopener' class='follow-icon' aria-label='Follow on TikTok'><i class='fab fa-tiktok'></i></a>
</div>
</div>"; </div>";
output.TagName = "div"; output.TagName = "div";

View File

@ -57,15 +57,32 @@
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div id="buy-now" class="my-4"> <div id="buy-now" class="my-4">
<a id="amazonLink" href="https://www.amazon.com/dp/B0FBS427VD" target="_blank" class="btn btn-dark"> <a id="kindleLink" href="https://www.amazon.com/dp/B0FBS427VD" target="_blank" class="btn btn-dark mb-2">
Buy on Amazon Buy Kindle Edition
</a> </a>
<p id="geoNote" class="text-muted small mt-2">Also available on your local Amazon store.</p> <a id="paperbackLink" href="https://www.amazon.co.uk/dp/1068225815" target="_blank" class="btn btn-dark mb-2">
Buy Paperback (Bookshop Edition)
</a>
<p id="geoNote" class="text-muted small mt-2">
Available from your local Amazon store.<br />
Or order from your local bookshop using:
<ul class="small text-muted">
<li>
ISBN 978-1-0682258-1-9 - Bookshop Edition (Paperback)
</li>
<li>
ISBN 978-1-0682258-0-2 - Collector's Eidtion (Hardback)
</li>
</ul>
<span id="extraRetailers"></span>
</p>
</div> </div>
</div> </div>
</div> </div>
<!-- Synopsis Content --> <!-- Synopsis Content -->
<h3 class="card-title">Synopsis</h3> <h3 class="card-title">Synopsis</h3>
<p class="card-text">The Alpha Flame is an unflinching and deeply emotional story of resilience, love, and survival, set against the vibrant yet tumultuous backdrop of 1983. Through the intersecting lives of two heroines, it delves into the extremes of human suffering and the boundless power of the human spirit to endure, adapt, and rise.</p> <p class="card-text">The Alpha Flame is an unflinching and deeply emotional story of resilience, love, and survival, set against the vibrant yet tumultuous backdrop of 1983. Through the intersecting lives of two heroines, it delves into the extremes of human suffering and the boundless power of the human spirit to endure, adapt, and rise.</p>
@ -155,26 +172,40 @@
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
const country = data.country_code; const country = data.country_code;
let amazonLink = "https://www.amazon.com/dp/B0FBS427VD";
let kindleLink = "https://www.amazon.com/dp/B0FBS427VD";
let paperbackLink = "https://www.amazon.com/dp/1068225815";
let extraRetailers = "";
switch (country) { switch (country) {
case "GB": case "GB":
amazonLink = "https://www.amazon.co.uk/dp/B0FBS427VD"; kindleLink = "https://www.amazon.co.uk/dp/B0FBS427VD";
paperbackLink = "https://www.amazon.co.uk/dp/1068225815";
extraRetailers = 'Also available at <a href="https://www.waterstones.com/book/the-alpha-flame/catherine-lynwood/9781068225819" target="_blank">Waterstons</a>';
break; break;
case "US": case "US":
amazonLink = "https://www.amazon.com/dp/B0FBS427VD"; kindleLink = "https://www.amazon.com/dp/B0FBS427VD";
paperbackLink = "https://www.amazon.com/dp/1068225815";
extraRetailers = 'Also available at <a href="https://www.barnesandnoble.com/s/9781068225810" target="_blank">Barnes & Noble</a>';
break; break;
case "CA": case "CA":
amazonLink = "https://www.amazon.ca/dp/B0FBS427VD"; kindleLink = "https://www.amazon.ca/dp/B0FBS427VD";
paperbackLink = "https://www.amazon.ca/dp/1068225815";
break; break;
case "AU": case "AU":
amazonLink = "https://www.amazon.com.au/dp/B0FBS427VD"; kindleLink = "https://www.amazon.com.au/dp/B0FBS427VD";
paperbackLink = "https://www.amazon.com.au/dp/1068225815";
break; break;
} }
document.getElementById("amazonLink").setAttribute("href", amazonLink); document.getElementById("kindleLink").setAttribute("href", kindleLink);
document.getElementById("paperbackLink").setAttribute("href", paperbackLink);
document.getElementById("extraRetailers").innerHTML = extraRetailers;
}); });
</script> </script>
} }

View File

@ -52,10 +52,6 @@
</div> </div>
</div> </div>
@section Scripts { @section Scripts {
<script> <script>
const audioBlobCache = new Map(); // id => Blob const audioBlobCache = new Map(); // id => Blob
@ -337,10 +333,36 @@
fetchAndPlay(); fetchAndPlay();
} }
preloadSegmentsAhead(index + 1, BUFFER_TARGET_SECONDS); maybeTriggerPreload();
} }
function maybeTriggerPreload() {
if (audio.paused) return; // only preload while playing
const bufferedPercent = getBufferedPercent(currentIndex);
if (bufferedPercent < 90) {
preloadSegmentsAhead(currentIndex + 1, BUFFER_TARGET_SECONDS);
}
}
function getBufferedPercent(currentIdx) {
let bufferedSeconds = 0;
let totalChecked = 0;
let i = currentIdx + 1;
while (i < masterPlaylist.length && totalChecked < BUFFER_TARGET_SECONDS) {
const seg = masterPlaylist[i];
totalChecked += seg.duration;
if (audioBlobCache.has(seg.id)) {
bufferedSeconds += seg.duration;
}
i++;
}
return Math.min((bufferedSeconds / BUFFER_TARGET_SECONDS) * 100, 100);
}
function preloadSegmentsAhead(startIndex, targetSeconds) { function preloadSegmentsAhead(startIndex, targetSeconds) {
@ -603,6 +625,11 @@
toggleButton.innerHTML = '<i class="fad fa-play"></i>'; toggleButton.innerHTML = '<i class="fad fa-play"></i>';
}); });
// ✅ These are additional listeners — do not replace existing ones
audio.addEventListener("play", maybeTriggerPreload);
audio.addEventListener("ended", maybeTriggerPreload);
document.getElementById("chapter-progress").addEventListener("click", (e) => { document.getElementById("chapter-progress").addEventListener("click", (e) => {
const container = e.currentTarget; const container = e.currentTarget;
const rect = container.getBoundingClientRect(); const rect = container.getBoundingClientRect();

View File

@ -120,11 +120,11 @@
<footer class="border-top border-2 border-primary footer bg-dark py-3"> <footer class="border-top border-2 border-primary footer bg-dark py-3">
<div class="container"> <div class="container">
<div class="row justify-content-center align-items-center" > <div class="row justify-content-center align-items-center" >
<div class="col-md-4 text-center order-md-2"> <div class="col-md-6 text-center order-md-2">
<social-media-share title="Catherine Lynwood Blog" url="@Context.Request.Path" class="text-center m-3"></social-media-share> <social-media-share title="Catherine Lynwood Blog" url="@Context.Request.Path" class="text-center m-3"></social-media-share>
&copy; 2024 - @DateTime.Now.Year - Catherine Lynwood &copy; 2024 - @DateTime.Now.Year - Catherine Lynwood
</div> </div>
<div class="col-md-4 text-center order-md-1"> <div class="col-md-3 text-center order-md-1">
<p> <p>
<a class="text-light" asp-area="" asp-controller="Home" asp-action="ContactCatherine">Contact Catherine</a> <a class="text-light" asp-area="" asp-controller="Home" asp-action="ContactCatherine">Contact Catherine</a>
</p> </p>
@ -133,7 +133,7 @@
</p> </p>
</div> </div>
<div class="col-md-4 text-center order-md-3"> <div class="col-md-3 text-center order-md-3">
<p> <p>
<a class="text-light" asp-area="" asp-controller="Home" asp-action="AboutCatherineLynwood">About Catherine Lynwood</a> <a class="text-light" asp-area="" asp-controller="Home" asp-action="AboutCatherineLynwood">About Catherine Lynwood</a>
</p> </p>

View File

@ -50,11 +50,11 @@
</select> </select>
<span class="input-group-text d-none d-sm-inline" id="basic-addon1">Results per page</span> <span class="input-group-text d-none d-sm-inline" id="basic-addon1">Results per page</span>
<select asp-for="BlogFilter.ResultsPerPage" class="form-select js-submit-on-change" name="ResultsPerPage" aria-label="Results per page"> <select asp-for="BlogFilter.ResultsPerPage" class="form-select js-submit-on-change" name="ResultsPerPage" aria-label="Results per page">
<option value="10">10</option> <option value="6">6</option>
<option value="20">20</option> <option value="12">12</option>
<option value="18">18</option>
<option value="24">24</option>
<option value="30">30</option> <option value="30">30</option>
<option value="40">40</option>
<option value="50">50</option>
</select> </select>
<button class="btn btn-dark" type="button" data-bs-toggle="collapse" data-bs-target="#categories">Show Categories</button> <button class="btn btn-dark" type="button" data-bs-toggle="collapse" data-bs-target="#categories">Show Categories</button>

View File

@ -28,7 +28,14 @@
<div class="col-12"> <div class="col-12">
<header> <header>
<h1 class="d-inline-block">@Model.Title</h1><br /><h2 class="h4 d-inline-block">@Model.SubTitle</h2> <h1 class="d-inline-block">@Model.Title</h1><br /><h2 class="h4 d-inline-block">@Model.SubTitle</h2>
<p>Posted on @Model.PublishDate.ToString("MMMM d, yyyy") by Catherine Lynwood</p> <div class="row align-items-center">
<div class="col-auto">
<img src="~/images/webp/@Model.PostedImage" class="rounded-circle border border-dark" style="height: 50px;" />
</div>
<div class="col">
Posted on @Model.PublishDate.ToString("MMMM d, yyyy") by @Model.PostedBy Lynwood
</div>
</div>
</header> </header>
</div> </div>
</div> </div>
@ -47,27 +54,59 @@
<div class="col-12"> <div class="col-12">
@if (!string.IsNullOrWhiteSpace(Model.ImageUrl) && !string.IsNullOrWhiteSpace(Model.ContentTop)) @if (!string.IsNullOrWhiteSpace(Model.ImageUrl) && !string.IsNullOrWhiteSpace(Model.ContentTop))
{ {
var posterImage = $"/images/webp/{Model.DefaultWebpImage}";
<responsive-image src="@Model.ImageUrl"
class="d-none"
alt="The Alpha Flame: Discovery by Catherine Lynwood"
display-width-percentage="50">
</responsive-image>
@if (Model.ImageFirst) @if (Model.ImageFirst)
{ {
<div class="float-md-start w-md-50 p-3"> <div class="float-md-start w-md-50 p-3">
<figure> @if (string.IsNullOrEmpty(Model.VideoUrl))
<responsive-image src="@Model.ImageUrl" alt="@Model.ImageAlt" class="img-fluid rounded-5 border border-3 border-dark shadow-lg" display-width-percentage="50"></responsive-image> {
<figcaption>@Html.Raw(Model.ImageDescription)</figcaption> <figure>
</figure> <responsive-image src="@Model.ImageUrl" alt="@Model.ImageAlt" class="img-fluid rounded-5 border border-3 border-dark shadow-lg" display-width-percentage="50"></responsive-image>
<figcaption>@Html.Raw(Model.ImageDescription)</figcaption>
</figure>
}
else
{
<div class="hero-video-container">
<video id="heroVideo" autoplay muted loop playsinline preload="none" poster="@posterImage">
<!-- Source will be injected later -->
</video>
</div>
}
</div>
}
else
{
<div class="float-md-end w-md-50 p-3">
@if (string.IsNullOrEmpty(Model.VideoUrl))
{
<figure>
<responsive-image src="@Model.ImageUrl" alt="@Model.ImageAlt" class="img-fluid rounded-5 border border-3 border-dark shadow-lg" display-width-percentage="50"></responsive-image>
<figcaption>@Html.Raw(Model.ImageDescription)</figcaption>
</figure>
}
else
{
<div class="hero-video-container">
<video id="heroVideo" autoplay muted loop playsinline preload="none" poster="@posterImage">
<!-- Source will be injected later -->
</video>
</div>
}
</div> </div>
} }
@if (!string.IsNullOrWhiteSpace(Model.ContentTop)) @if (!string.IsNullOrWhiteSpace(Model.ContentTop))
{ {
<div>@Html.Raw(Model.ContentTop)</div> @Html.Raw(Model.ContentTop)
}
@if (!Model.ImageFirst)
{
<div class="float-md-end w-md-50 p-3">
<figure>
<responsive-image src="@Model.ImageUrl" alt="@Model.ImageAlt" class="img-fluid rounded-5 border border-3 border-dark shadow-lg" display-width-percentage="50"></responsive-image>
<figcaption>@Html.Raw(Model.ImageDescription)</figcaption>
</figure>
</div>
} }
} }
else if (!string.IsNullOrWhiteSpace(Model.ContentTop)) else if (!string.IsNullOrWhiteSpace(Model.ContentTop))
@ -124,12 +163,46 @@
<script> <script>
const player = new Plyr('audio'); const player = new Plyr('audio');
</script> </script>
@if (!string.IsNullOrEmpty(Model.VideoUrl))
{
<script>
window.addEventListener("load", () => {
setTimeout(() => {
const video = document.getElementById("heroVideo");
const source = document.createElement("source");
source.src = "/videos/@Model.VideoUrl";
source.type = "video/mp4";
video.appendChild(source);
video.load(); // Triggers actual file load
video.play().catch(err => {
console.warn("Autoplay failed:", err);
});
}, 2000); // Adjust delay (e.g., 10003000ms) based on your needs
});
</script>
}
} }
@section Meta { @section Meta {
<meta name="description" content="Explore the blog of Catherine Lynwood, author of *The Alpha Flame*, for updates, insights, and behind-the-scenes stories. Follow Catherines journey as a writer, discover inspiration for her novels, and delve into discussions on womens fiction, mystery, and family secrets."> <MetaTag
<meta name="keywords" content="Catherine Lynwood blog, The Alpha Flame updates, womens fiction blog, author Catherine Lynwood insights, mystery novel blog, family secrets stories, strong female protagonists, 1980s fiction blog, book updates and stories, authors journey blog, Catherine Lynwood writing process"> meta-title="@Model.Title"
<meta name="author" content="Catherine Lynwood"> meta-description="@Model.IndexText"
meta-keywords="Catherine Lynwood blog, The Alpha Flame, psychological thriller, indie fiction, 1980s fiction, dark secrets, strong female characters"
meta-author="Catherine Lynwood"
meta-url="https://www.catherinelynwood.com/@Model.BlogUrl"
meta-image="@(string.IsNullOrWhiteSpace(Model.ImageUrl) ? null : $"https://www.catherinelynwood.com/images/webp/{Model.DefaultWebpImage}")"
meta-image-alt="@Model.ImageAlt"
og-site-name="Catherine Lynwood - The Alpha Flame"
article-published-time="@Model.PublishDate"
article-modified-time="@DateTime.UtcNow"
twitter-card-type="@(string.IsNullOrWhiteSpace(Model.VideoUrl) ? "summary_large_image" : "player")"
twitter-site-handle="@@CathLynwood"
twitter-creator-handle="@@CathLynwood"
twitter-video-url="@(string.IsNullOrWhiteSpace(Model.VideoUrl) ? null : Model.VideoUrl)"
twitter-player-width="@(string.IsNullOrWhiteSpace(Model.VideoUrl) ? null : 480)"
twitter-player-height="@(string.IsNullOrWhiteSpace(Model.VideoUrl) ? null : 80)" />
@functions { @functions {
public static string StripHtml(string input) public static string StripHtml(string input)
@ -140,65 +213,85 @@
@{ @{
string blogUrl = $"https://www.catherinelynwood.com/{Model.BlogUrl}"; string blogUrl = $"https://www.catherinelynwood.com/{Model.BlogUrl}";
string imageUrl = $"https://www.catherinelynwood.com/images/webp/{Model.DefaultWebpImage}"; string imageJson = string.Empty;
string description = $"Listen to my new blog entry: {Model.Title} by Catherine Lynwood"; string videoJson = string.Empty;
string description;
if (!string.IsNullOrWhiteSpace(Model.AudioTeaserUrl))
{
description = $"Listen to my new blog entry: {Model.Title} by Catherine Lynwood";
}
else
{
description = $"Read my latest blog post: {Model.Title} by Catherine Lynwood";
}
var contentTopPlainText = StripHtml(Model.ContentTop); var contentTopPlainText = StripHtml(Model.ContentTop);
var contentBottomPlainText = StripHtml(Model.ContentBottom); var contentBottomPlainText = StripHtml(Model.ContentBottom);
// Image JSON-LD block if ImageUrl is available
if (!string.IsNullOrWhiteSpace(Model.ImageUrl))
{
string imageUrl = $"https://www.catherinelynwood.com/images/webp/{Model.DefaultWebpImage}";
imageJson = $@",
""image"": {{
""@type"": ""ImageObject"",
""url"": ""{imageUrl}"",
""description"": ""{Model.ImageDescription}""
}}";
}
// Video JSON-LD block if VideoUrl is available
if (!string.IsNullOrWhiteSpace(Model.VideoUrl))
{
// You can add extra properties here if your Blog class supports them in future
videoJson = $@",
""video"": {{
""@type"": ""VideoObject"",
""name"": ""{Model.Title} Video Teaser"",
""description"": ""Atmospheric teaser video for {Model.Title}."",
""thumbnailUrl"": ""https://www.catherinelynwood.com/images/webp/{Model.DefaultWebpImage}"",
""uploadDate"": ""{Model.PublishDate:yyyy-MM-dd}"",
""contentUrl"": ""{Model.VideoUrl}"",
""embedUrl"": ""{Model.VideoUrl}""
}}";
}
// Assemble the final JSON-LD with conditional blocks
var blogPostJsonLd = $@" var blogPostJsonLd = $@"
{{ {{
""@context"": ""https://schema.org"", ""@context"": ""https://schema.org"",
""@type"": ""BlogPosting"", ""@type"": ""BlogPosting"",
""headline"": ""{Model.Title}"", ""headline"": ""{Model.Title}"",
""datePublished"": ""{Model.PublishDate:yyyy-MM-dd}"", ""datePublished"": ""{Model.PublishDate:yyyy-MM-dd}"",
""author"": {{ ""author"": {{
""@type"": ""Person"", ""@type"": ""Person"",
""name"": ""Catherine Lynwood"" ""name"": ""Catherine Lynwood""
}}, }},
""description"": ""{Model.IndexText}"", ""description"": ""{Model.IndexText}"",
""articleBody"": ""{contentTopPlainText} {contentBottomPlainText}"", ""articleBody"": ""{contentTopPlainText} {contentBottomPlainText}"",
""url"": ""https://www.catherinelynwood.com/the-alpah-flame/blog/{Model.BlogUrl}"", ""url"": ""{blogUrl}""{imageJson}{videoJson},
""image"": {{ ""interactionStatistic"": {{
""@type"": ""ImageObject"", ""@type"": ""InteractionCounter"",
""url"": ""https://www.catherinelynwood.com/images/webp/{Model.DefaultWebpImage}"", ""interactionType"": {{
""description"": ""{Model.ImageDescription}"" ""@type"": ""http://schema.org/LikeAction""
}}, }},
""interactionStatistic"": {{ ""userInteractionCount"": {Model.Likes}
""@type"": ""InteractionCounter"", }}
""interactionType"": {{ }}";
""@type"": ""http://schema.org/LikeAction""
}},
""userInteractionCount"": {Model.Likes}
}}
}}";
} }
<meta property="og:title" content="The Alpha Flame by Catherine Lynwood" />
<meta property="og:description" content="@Model.IndexText" />
<meta property="og:type" content="article" />
<meta property="og:url" content="@blogUrl" />
<meta property="og:image" content="@imageUrl" />
<meta property="og:image:alt" content="@Model.ImageAlt" />
<meta property="og:site_name" content="Catherine Lynwood - The Alpha Flame" />
<meta property="article:author" content="https://www.catherinelynwood.com" />
<meta property="article:published_time" content="2024-11-19" />
<meta property="article:modified_time" content="2024-11-19" />
<meta name="twitter:card" content="player">
<meta name="twitter:title" content="@Model.Title" />
<meta name="twitter:description" content="@description">
<meta name="twitter:site" content="@@CathLynwood" />
<meta name="twitter:creator" content="@@CathLynwood" />
<meta name="twitter:image" content="@imageUrl" />
<meta name="twitter:image:alt" content="@Model.ImageAlt" />
<meta name="twitter:player" content="@blogUrl">
<meta name="twitter:player:width" content="480">
<meta name="twitter:player:height" content="80">
<script type="application/ld+json"> <script type="application/ld+json">
@Html.Raw(blogPostJsonLd) @Html.Raw(blogPostJsonLd)
</script> </script>
<style>
ul {
list-style-type: none;
padding-left: 0;
margin-left: 0;
}
</style>
} }

View File

@ -58,5 +58,23 @@
"inputFiles": [ "inputFiles": [
"wwwroot/css/plyr.css" "wwwroot/css/plyr.css"
] ]
},
{
"outputFileName": "wwwroot/css/duotone.min.css",
"inputFiles": [
"wwwroot/css/duotone.css"
]
},
{
"outputFileName": "wwwroot/css/brands.min.css",
"inputFiles": [
"wwwroot/css/brands.css"
]
},
{
"outputFileName": "wwwroot/css/fontawesome.min.css",
"inputFiles": [
"wwwroot/css/fontawesome.css"
]
} }
] ]

Binary file not shown.

Binary file not shown.

View File

@ -132,6 +132,20 @@ section {
background-color: #010101; background-color: #010101;
} }
.follow-icon {
color: #FFF;
font-size: 1.5rem;
margin: 0 6px;
transition: color 0.2s ease;
text-decoration: none;
cursor: pointer;
}
.follow-icon:hover {
color: #0077b5;
}
#synopsis-body { #synopsis-body {
overflow-y: auto; overflow-y: auto;
} }
@ -139,3 +153,4 @@ section {
.tagline-shadow { .tagline-shadow {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8); text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
} }

View File

@ -1 +1 @@
html{font-size:14px}@media(min-width:768px){html{font-size:16px}}html{position:relative}.content{min-height:93vh}.content{position:relative;z-index:1}section{padding-bottom:10px}.w-sm-50{width:100%}@media(min-width:576px){.w-sm-50{width:50%}}.w-md-50{width:100%}@media(min-width:768px){.w-md-50{width:50%}}.excerpt-section{background-color:#f8f9fa}.scene-image{max-width:100%;height:auto;border-radius:10px}.audio-player{margin:1rem 0}.chapter-text{font-family:'Georgia',serif;font-size:1.1rem;line-height:1.6;color:#333}.chapter-title{font-size:1.5rem;font-weight:bold;color:#555;margin-bottom:1rem}.responsive-border{border-right:3px solid #000}@media(max-width:575.98px){.responsive-border{border-right:0;border-bottom:3px solid #000}}.fit-image{width:100%;height:100%;object-fit:cover;object-position:center;display:block}.share-button{padding:8px 12px;margin-left:10px;margin-right:10px;color:#fff;text-decoration:none;border-radius:4px;font-size:14px}.share-button.facebook{background-color:#3b5998}.share-button.twitter{background-color:#1da1f2}.share-button.linkedin{background-color:#0077b5}.share-button.pinterest{background-color:#bd081c}.share-button.instagram{background-color:#e4405f}.share-button.tiktok{background-color:#010101}#synopsis-body{overflow-y:auto}.tagline-shadow{text-shadow:2px 2px 4px rgba(0,0,0,.8)} html{font-size:14px}@media(min-width:768px){html{font-size:16px}}html{position:relative}.content{min-height:93vh}.content{position:relative;z-index:1}section{padding-bottom:10px}.w-sm-50{width:100%}@media(min-width:576px){.w-sm-50{width:50%}}.w-md-50{width:100%}@media(min-width:768px){.w-md-50{width:50%}}.excerpt-section{background-color:#f8f9fa}.scene-image{max-width:100%;height:auto;border-radius:10px}.audio-player{margin:1rem 0}.chapter-text{font-family:'Georgia',serif;font-size:1.1rem;line-height:1.6;color:#333}.chapter-title{font-size:1.5rem;font-weight:bold;color:#555;margin-bottom:1rem}.responsive-border{border-right:3px solid #000}@media(max-width:575.98px){.responsive-border{border-right:0;border-bottom:3px solid #000}}.fit-image{width:100%;height:100%;object-fit:cover;object-position:center;display:block}.share-button{padding:8px 12px;margin-left:10px;margin-right:10px;color:#fff;text-decoration:none;border-radius:4px;font-size:14px}.share-button.facebook{background-color:#3b5998}.share-button.twitter{background-color:#1da1f2}.share-button.linkedin{background-color:#0077b5}.share-button.pinterest{background-color:#bd081c}.share-button.instagram{background-color:#e4405f}.share-button.tiktok{background-color:#010101}.follow-icon{color:#fff;font-size:1.5rem;margin:0 6px;transition:color .2s ease;text-decoration:none;cursor:pointer}.follow-icon:hover{color:#0077b5}#synopsis-body{overflow-y:auto}.tagline-shadow{text-shadow:2px 2px 4px rgba(0,0,0,.8)}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 730 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 819 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Some files were not shown because too many files have changed in this diff Show More