Front End Audio Player Always Available

This commit is contained in:
daniel-c-harvey
2025-09-08 18:42:07 -04:00
parent a25d067dff
commit 73d4b0a9c5
15 changed files with 419 additions and 126 deletions
-6
View File
@@ -33,12 +33,6 @@
<Folder Include="wwwroot\js\" />
</ItemGroup>
<ItemGroup>
<TypeScriptCompile Include="Interop\webaudio.ts">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</TypeScriptCompile>
</ItemGroup>
<!-- Prevent TypeScript compilation issues during publish -->
<PropertyGroup>
<TypeScriptCompileOnSaveEnabled>false</TypeScriptCompileOnSaveEnabled>
+18 -18
View File
@@ -72,12 +72,12 @@ class AudioPlayer {
try {
this.bufferChunks.push(audioBlock);
this.currentSize += audioBlock.length;
if (this.expectedSize > 0 && this.onLoadProgressCallback) {
const progress = (this.currentSize / this.expectedSize) * 100;
this.onLoadProgressCallback(Math.min(progress, 100));
}
return { success: true };
} catch (error) {
return { success: false, error: (error as Error).message };
@@ -89,24 +89,24 @@ class AudioPlayer {
const arrayBuffer = new ArrayBuffer(this.currentSize);
const view = new Uint8Array(arrayBuffer);
let offset = 0;
for (const chunk of this.bufferChunks) {
view.set(chunk, offset);
offset += chunk.length;
}
this.audioBuffer = await this.audioContext!.decodeAudioData(arrayBuffer);
this.duration = this.audioBuffer.duration;
this.bufferChunks = [];
this.currentSize = 0;
if (this.onLoadProgressCallback) {
this.onLoadProgressCallback(100);
}
return {
success: true,
return {
success: true,
duration: this.duration,
sampleRate: this.audioBuffer.sampleRate,
numberOfChannels: this.audioBuffer.numberOfChannels,
@@ -202,18 +202,18 @@ class AudioPlayer {
try {
const wasPlaying = this.isPlaying;
if (this.isPlaying) {
this.source!.stop();
}
this.pauseOffset = position;
if (wasPlaying) {
this.source = this.audioContext!.createBufferSource();
this.source.buffer = this.audioBuffer;
this.source.connect(this.gainNode!);
this.source.onended = () => {
this.isPlaying = false;
this.isPaused = false;
@@ -424,11 +424,11 @@ const DeepDrftAudio = {
if (!player) {
return { success: false, error: "Player not found" };
}
player.setOnProgressCallback((currentTime: number) => {
dotNetObjectReference.invokeMethodAsync(methodName, currentTime);
});
return { success: true };
},
@@ -437,11 +437,11 @@ const DeepDrftAudio = {
if (!player) {
return { success: false, error: "Player not found" };
}
player.setOnEndCallback(() => {
dotNetObjectReference.invokeMethodAsync(methodName);
});
return { success: true };
},
@@ -450,11 +450,11 @@ const DeepDrftAudio = {
if (!player) {
return { success: false, error: "Player not found" };
}
player.setOnLoadProgressCallback((progress: number) => {
dotNetObjectReference.invokeMethodAsync(methodName, progress);
});
return { success: true };
},
@@ -1,74 +0,0 @@
// <auto-generated />
using System;
using DeepDrftWeb.Services.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace DeepDrftWeb.Migrations
{
[DbContext(typeof(DeepDrftContext))]
[Migration("20250904233927_Initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
modelBuilder.Entity("DeepDrftModels.Entities.TrackEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("id");
b.Property<string>("Album")
.HasMaxLength(200)
.HasColumnType("TEXT")
.HasColumnName("album");
b.Property<string>("Artist")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT")
.HasColumnName("artist");
b.Property<string>("EntryKey")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("TEXT")
.HasColumnName("entry_key");
b.Property<string>("Genre")
.HasMaxLength(100)
.HasColumnType("TEXT")
.HasColumnName("genre");
b.Property<string>("ImagePath")
.HasMaxLength(500)
.HasColumnType("TEXT")
.HasColumnName("image_path");
b.Property<DateOnly?>("ReleaseDate")
.HasColumnType("TEXT")
.HasColumnName("release_date");
b.Property<string>("TrackName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT")
.HasColumnName("track_name");
b.HasKey("Id");
b.ToTable("track", (string)null);
});
#pragma warning restore 612, 618
}
}
}
@@ -1,41 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace DeepDrftWeb.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "track",
columns: table => new
{
id = table.Column<long>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
entry_key = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
track_name = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
artist = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
album = table.Column<string>(type: "TEXT", maxLength: 200, nullable: true),
genre = table.Column<string>(type: "TEXT", maxLength: 100, nullable: true),
release_date = table.Column<DateOnly>(type: "TEXT", nullable: true),
image_path = table.Column<string>(type: "TEXT", maxLength: 500, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_track", x => x.id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "track");
}
}
}
@@ -1,71 +0,0 @@
// <auto-generated />
using System;
using DeepDrftWeb.Services.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace DeepDrftWeb.Migrations
{
[DbContext(typeof(DeepDrftContext))]
partial class DeepDrftContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
modelBuilder.Entity("DeepDrftModels.Entities.TrackEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("id");
b.Property<string>("Album")
.HasMaxLength(200)
.HasColumnType("TEXT")
.HasColumnName("album");
b.Property<string>("Artist")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT")
.HasColumnName("artist");
b.Property<string>("EntryKey")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("TEXT")
.HasColumnName("entry_key");
b.Property<string>("Genre")
.HasMaxLength(100)
.HasColumnType("TEXT")
.HasColumnName("genre");
b.Property<string>("ImagePath")
.HasMaxLength(500)
.HasColumnType("TEXT")
.HasColumnName("image_path");
b.Property<DateOnly?>("ReleaseDate")
.HasColumnType("TEXT")
.HasColumnName("release_date");
b.Property<string>("TrackName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT")
.HasColumnName("track_name");
b.HasKey("Id");
b.ToTable("track", (string)null);
});
#pragma warning restore 612, 618
}
}
}
+19 -20
View File
@@ -293,29 +293,28 @@ body, p, span, div,
justify-content: center;
}
/* Audio Player Layout */
.deepdrft-audio-controls {
display: flex;
align-items: center;
width: 140px;
}
.deepdrft-audio-time {
min-width: 120px;
}
/* Layout with overlay audio player - Global layout class */
/*.deepdrft-layout-with-overlay-player {*/
/* position: relative;*/
/* min-height: calc(100vh - 64px);*/
/* padding-bottom: 160px; !* Increased space for overlay player *!*/
/*}*/
.deepdrft-audio-volume-icon {
margin-right: 4px;
}
/*!* Audio player overlay positioning - Global positioning *!*/
/*.deepdrft-layout-with-overlay-player > .AudioPlayerBar,*/
/*.deepdrft-layout-with-overlay-player > *:last-child {*/
/* position: fixed;*/
/* bottom: 0;*/
/* left: 0;*/
/* right: 0;*/
/* z-index: 1000;*/
/* pointer-events: none;*/
/*}*/
.deepdrft-audio-slider {
flex: 1;
}
.deepdrft-audio-slider-seek {
flex: 1;
margin-right: 8px;
}
/*.deepdrft-layout-with-overlay-player > *:last-child > * {*/
/* pointer-events: auto;*/
/*}*/
/* Responsive Utilities */
@media (max-width: 768px) {