divan/GanjooRazor/Pages/Recitations/AudioClip.cshtml
Anas Rashid 223166ef5e Divan: make env config work, ur-PK locale, own-site links relative
- AddEnvironmentVariables() where appsettings.json is read directly
- JWT ValidIssuer follows RSecurityBackend:ApplicationName (was hard-coded Ganjoor)
- deploy/entrypoint.sh: copy settings RSecurityBackend reads only from appsettings.json
- Dockerfile: pin SDK 10.0.302 (newer Razor rejects some upstream views)
- lang=ur-PK; ganjoor.net own-site links -> relative; api.ganjoor.net -> APIRoot; search.xml
- DIVAN.md: first admin password is Test!123 (upstream doc is wrong)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:32:16 +02:00

310 lines
17 KiB
Plaintext

@page
@model GanjooRazor.Pages.AudioClipModel
@using DNTPersianUtils.Core
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="ur-PK" dir="rtl">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="robots" content="noindex, nofollow" />
<title>
دیوان » نمایش خوانش
</title>
<link href="~/dist/skin/pink.flag/css/jplayer.pink.flag.min.css" rel="stylesheet" type="text/css" />
<link href="~/css/p8.css" asp-append-version="true" rel="stylesheet" type="text/css" />
<partial name="_DivanHead" />
<link rel="shortcut icon" href="/favicon.ico?version=1" type="image/x-icon" />
<link rel="search" type="application/opensearchdescription+xml" title="دیوان" href="/search.xml" />
<script>
function wpopen(macagna) {
window.open(macagna, '_blank', 'width=600,height=700,scrollbars=yes,status=yes');
}
</script>
</head>
<body>
@if (!string.IsNullOrEmpty(Model.LastError))
{
<p>@Model.LastError</p>
}
else
{
<div style="width:95%;max-width:500px;min-width:380px;margin:auto">
<div id="jp_container_1" class="jp-video jp-video-270p">
<div class="jp-type-playlist">
<div id="jquery_jplayer_1" class="jp-jplayer"></div>
<div id="textcnt" style="display:table;width:100%;height:400px;vertical-align: bottom;background-image: url('https://source.unsplash.com/500x400/?flowers');color:white">
<div id="title1" style="display: table-row">
<p style="width:100%;height:25%;text-align:center;vertical-align: middle;font-family: 'Vazirmatn';display: table-cell;font-size: 1.5em;text-shadow: 2px 2px 4px white;">
@Model.Poem.Category.Poet.Nickname
</p>
</div>
<div id="title2" style="display: table-row">
<p style="width:100%;height:25%;text-align:center;vertical-align: middle;font-family: 'Vazirmatn';display: table-cell;font-size: 1.5em;text-shadow: 2px 2px 4px white;">
@for (int i = 1; i < Model.Poem.Category.Cat.Ancestors.Count; i++)
{
<text>
@Model.Poem.Category.Cat.Ancestors.ElementAt(i).Title &raquo;
</text>
}
@Model.Poem.Category.Cat.Title
</p>
</div>
<div id="title3" style="display: table-row">
<p style="width:100%;height:25%;text-align:center;vertical-align: middle;font-family: 'Vazirmatn';display: table-cell;font-size: 1.5em;text-shadow: 2px 2px 4px white;">
@Model.Recitation.AudioTitle
</p>
</div>
<div id="title4" style="display: table-row">
<p style="width:100%;height:25%;text-align:center;vertical-align: middle;font-family: 'Vazirmatn';display: table-cell;font-size: 1em;text-shadow: 2px 2px 4px white;">
به خوانش @Model.Recitation.AudioArtist
</p>
</div>
<div id="verse1div" style="display: table-row;visibility: hidden;">
<div style="width:100%;height:50%;text-align:center;vertical-align: bottom;font-family: 'Vazirmatn';display: table-cell;font-size: 1.5em;text-shadow: 2px 2px 4px white;">
<p id="verse1" style="width:80%;margin:auto;background-color:rgba(0, 0, 0, .4);margin-bottom:5px">
</p>
</div>
</div>
<div id="verse2div" style="display: table-row;visibility: hidden">
<div style="width:100%;height:50%;text-align:center;vertical-align: top;font-family: 'Vazirmatn';display: table-cell;font-size: 1.5em;text-shadow: 2px 2px 4px white;">
<p id="verse2" style="width:80%;margin:auto;background-color:rgba(0, 0, 0, .4);margin-top:5px">
</p>
</div>
</div>
<div id="normaldiv" style="display: table-row;visibility: hidden">
<div style="width:100%;height:100%;text-align:center;vertical-align: middle;font-family: 'Vazirmatn';display: table-cell;font-size: 1.5em;text-shadow: 2px 2px 4px white;">
<p id="normal" style="width:80%;margin:auto;background-color:rgba(0, 0, 0, .4);">
</p>
</div>
</div>
</div>
<div class="jp-gui">
<div class="jp-video-play">
<button class="jp-video-play-icon" tabindex="0">پخش</button>
</div>
<div class="jp-interface">
<div class="jp-progress">
<div class="jp-seek-bar">
<div class="jp-play-bar"></div>
</div>
</div>
<div class="jp-current-time">&nbsp;</div>
<div class="jp-duration">&nbsp;</div>
<div class="jp-controls-holder">
<div class="jp-controls">
<button class="jp-previous" tabindex="0">قبلی</button>
<button class="jp-play" tabindex="0">پخش</button>
<button class="jp-next" tabindex="0">بعدی</button>
<button class="jp-stop" tabindex="0">توقف</button>
</div>
<div class="jp-volume-controls">
<button class="jp-mute" tabindex="0">بی‌صدا</button>
<button class="jp-volume-max" tabindex="0">حداکثر صدا</button>
<div class="jp-volume-bar">
<div class="jp-volume-bar-value"></div>
</div>
</div>
<div class="jp-toggles">
<button class="jp-repeat" tabindex="0">تکرار</button>
</div>
</div>
<div class="jp-details">
<div class="jp-title">&nbsp;</div>
</div>
</div>
</div>
<div class="jp-playlist">
<ul>
<!-- The method Playlist.displayPlaylist() uses this unordered list -->
<li>&nbsp;</li>
</ul>
</div>
<div class="jp-no-solution">
<span>نیاز به بروزآوری</span>
جهت خوانش شعر شما یا باید از مرورگر جدیدی همچون <a href="http://getfirefox.com/">فایرفاکس</a> استفاده کنید یا افزونه <a href="http://get.adobe.com/flashplayer/" target="_blank">فلش</a> مرورگرتان را بروزآوری کنید.
</div>
</div>
</div>
</div>
<p style="font-family: 'Vazirmatn';font-size: 0.8em;color:darkgray">
تصویر پس‌زمینهٔ خوانش یک تصویر تصادفی از سایت <a href="https://unsplash.com/" target="_blank">unsplash.com</a> است و ارتباطی با محتوای شعر ندارد.
</p>
<p style="font-family: 'Vazirmatn';font-size: 1em">
فایل صوتی متناظر را می‌توانید در قالب mp3 از <a href="@Model.Recitation.Mp3Url" download>این نشانی</a> (اندازه @Html.Raw($"{(Model.Recitation.Mp3SizeInBytes / (1024.0 * 1024.0)):0.00}".ToPersianNumbers()) مگابایت) دریافت کنید.
</p>
<p style="font-family: 'Vazirmatn';font-size: 1em">
جهت دریافت خوانش با استفاده از دیوان رومیزی <a href="https://blog.ganjoor.net/1395/04/07/dg-274/" target="_blank">این مطلب</a> را مطالعه بفرمایید. امکان ساخت کلیپهای ویدیویی از خوانشها با استفاده از <a href="https://blog.ganjoor.net/category/gsync2vid/" target="_blank">خوانشگر دیوان رومیزی</a> وجود دارد. جهت مشارکت در خوانش اشعار <a href="http://ava.ganjoor.net/about/" target="_blank">این صفحه</a> و ویدیوهای آموزشی آن را ببینید.
</p>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/js/bk.js" asp-append-version="true"></script>
<script src="~/js/jplayersupport.js"></script>
<script src="~/js/vaabd.js?version=10"></script>
<script src="~/dist/jplayer/jquery.jplayer.min.js"></script>
<script src="~/dist/add-on/jplayer.playlist.min.js"></script>
<script>
@* Verse text is untrusted (user-submitted content, sanitized for storage but not for
safe embedding inside a <script> block) - it must never be spliced into a JS string
literal via @Html.Raw as it was here before, since a verse containing a stray "'" or
"</script>" would break out of the string. JsonSerializer.Serialize both quotes the
value correctly and HTML/JS-escapes '<', '>' and '&' by default, so the whole array
can be assigned directly and stays inert as data. *@
var verses = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Model.Poem.Verses.Select(v => v.Text)));
@Html.Raw($" var normaltext = { (Model.Poem.Verses.Where(v => v.VersePosition == RMuseum.Models.Ganjoor.VersePosition.Paragraph).Any() ? "true" : "false") }")
function prepareclip(xmlfilename, poemtitle, auartist, oggurl, mp3url, verseArray) {
var verseStart = [];
var verseEnd = [];
var verseIndex = [];
var nLastHighlight = -1;
var vCount = 0;
audioxmlfiles[0] = xmlfilename;
var jlist = new jPlayerPlaylist({
jPlayer: "#jquery_jplayer_1",
cssSelectorAncestor: "#jp_container_1"
}, [
{
title: poemtitle,
artist: auartist,
mp3: mp3url
},
],
{
setmedia: function(event) {
$.ajax({
type: "GET",
url: audioxmlfiles[jlist.current],
dataType: "xml",
success: function(xml) {
var nOneSecondBugFix = 2000;
$(xml).find('OneSecondBugFix').each(function() {
nOneSecondBugFix = parseInt($(xml).find('OneSecondBugFix').text());
});
var v = 0;
$(xml).find('SyncInfo').each(function() {
verseStart[v] = parseInt($(this).find('AudioMiliseconds').text()) / nOneSecondBugFix;
verseIndex[v] = parseInt($(this).find('VerseOrder').text());
if (v > 0)
verseEnd[v - 1] = verseStart[v];
v++;
});
v--;
if (v > 1)
verseEnd[v] = verseStart[v] + 2 * (verseEnd[v - 1] - verseStart[v - 1]);
vCount = v;
}
});
},
timeupdate: function(event) { // 4Hz
var curTime = event.jPlayer.status.currentTime;
if (curTime > 0) {
if (normaltext == true) {
for (i = 0; i <= vCount; i++) {
if (curTime >= verseStart[i] && curTime <= verseEnd[i]) {
if (nLastHighlight != verseIndex[i]) {
$('#title1').hide();
$('#title2').hide();
$('#title3').hide();
$('#title4').hide();
$('#normal').text(verseArray[verseIndex[i]]);
$('#verse1div').css("visibility", "hidden");
$('#verse2div').css("visibility", "hidden");
$('#verse1div').hide();
$('#verse2div').hide();
$('#normaldiv').css("visibility", "visible");
}
nLastHighlight = verseIndex[i];
break;
}
}
}
else {
for (i = 0; i <= vCount; i += 2) {
if (curTime >= verseStart[i] && curTime <= verseEnd[i]) {
if (nLastHighlight != verseIndex[i]) {
$('#title1').hide();
$('#title2').hide();
$('#title3').hide();
$('#title4').hide();
$('#verse1').text(verseArray[verseIndex[i]]);
$('#verse2').text(verseArray[verseIndex[i] + 1]);
$('#normaldiv').css("visibility", "hidden");
$('#normaldiv').hide();
$('#verse1div').css("visibility", "visible");
$('#verse2div').css("visibility", "visible");
}
nLastHighlight = verseIndex[i];
break;
}
}
}
}
},
ended: function(event) { // 4Hz
},
swfPath: "dist/jplayer",
supplied: "mp3",
wmode: "window",
useStateClassSkin: true,
autoBlur: false,
smoothPlayBar: true,
keyEnabled: true,
remainingDuration: true,
toggleDuration: true
});
return jlist;
}
//<![CDATA[
$(document).ready(function() {
prepareclip('@Html.Raw($"{GanjooRazor.APIRoot.InternetUrl}/api/audio/file/{Model.Recitation.Id}.xml")', '@Model.Recitation.AudioTitle',
'به خوانش @Model.Recitation.AudioArtist', '', '@Model.Recitation.Mp3Url', verses);
});
//]]>
</script>
@if (ViewData["TrackingScript"] != null && !string.IsNullOrEmpty(ViewData["TrackingScript"].ToString()))
{
@Html.Raw(ViewData["TrackingScript"])
}
}
</body>
</html>